mirror of
https://github.com/shouptech/humulus.git
synced 2026-02-03 20:49:44 +00:00
* Adds a recipe type field for recipe form. (Closes #7) * Abort w/ 400 is view is not found
386 lines
12 KiB
Python
386 lines
12 KiB
Python
# Copyright 2019 Mike Shoup
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import json
|
|
from decimal import Decimal
|
|
from io import BytesIO
|
|
|
|
from humulus.couch import get_db, get_doc, put_doc
|
|
from humulus.recipes import FermentableForm, HopForm, RecipeForm, YeastForm
|
|
|
|
|
|
def test_index(client):
|
|
"""Test success in retrieving index."""
|
|
# Test for bad request
|
|
response = client.get('/recipes/?sort_by=foobar')
|
|
assert response.status_code == 400
|
|
|
|
# Verify defaults
|
|
response = client.get('/recipes/')
|
|
assert response.status_code == 200
|
|
# Assert recipes are returned
|
|
assert b'Awesome Lager' in response.data
|
|
assert b'Awesome Beer' in response.data
|
|
assert (
|
|
b'"/recipes/?descending=true&sort_by=name">Name ↑' in
|
|
response.data
|
|
)
|
|
assert (
|
|
b'"/recipes/?descending=false&sort_by=date">Created On' in
|
|
response.data
|
|
)
|
|
|
|
|
|
# Test sort by name descending
|
|
response = client.get('/recipes/?descending=true&sort_by=name')
|
|
assert (
|
|
b'"/recipes/?descending=false&sort_by=name">Name ↓' in
|
|
response.data
|
|
)
|
|
assert (
|
|
b'"/recipes/?descending=false&sort_by=date">Created On' in
|
|
response.data
|
|
)
|
|
|
|
# Test sort by date ascending
|
|
response = client.get('/recipes/?descending=false&sort_by=date')
|
|
assert (
|
|
b'"/recipes/?descending=false&sort_by=name">Name' in
|
|
response.data
|
|
)
|
|
assert (
|
|
b'"/recipes/?descending=true&sort_by=date">Created On ↑' in
|
|
response.data
|
|
)
|
|
|
|
# Test sort by date descending
|
|
response = client.get('/recipes/?descending=true&sort_by=date')
|
|
assert (
|
|
b'"/recipes/?descending=false&sort_by=name">Name' in
|
|
response.data
|
|
)
|
|
assert (
|
|
b'"/recipes/?descending=false&sort_by=date">Created On ↓' in
|
|
response.data
|
|
)
|
|
|
|
# Test sort by volume ascending
|
|
response = client.get('/recipes/?descending=false&sort_by=volume')
|
|
assert (
|
|
b'"/recipes/?descending=false&sort_by=name">Name' in
|
|
response.data
|
|
)
|
|
assert (
|
|
b'"/recipes/?descending=true&sort_by=volume">Batch Size ↑' in
|
|
response.data
|
|
)
|
|
|
|
# Test sort by volume descending
|
|
response = client.get('/recipes/?descending=true&sort_by=volume')
|
|
assert (
|
|
b'"/recipes/?descending=false&sort_by=name">Name' in
|
|
response.data
|
|
)
|
|
assert (
|
|
b'"/recipes/?descending=false&sort_by=volume">Batch Size ↓' in
|
|
response.data
|
|
)
|
|
|
|
# Test sort by type ascending
|
|
response = client.get('/recipes/?descending=false&sort_by=type')
|
|
assert (
|
|
b'"/recipes/?descending=false&sort_by=name">Name' in
|
|
response.data
|
|
)
|
|
assert (
|
|
b'"/recipes/?descending=true&sort_by=type">Type ↑' in
|
|
response.data
|
|
)
|
|
|
|
# Test sort by type descending
|
|
response = client.get('/recipes/?descending=true&sort_by=type')
|
|
assert (
|
|
b'"/recipes/?descending=false&sort_by=name">Name' in
|
|
response.data
|
|
)
|
|
assert (
|
|
b'"/recipes/?descending=false&sort_by=type">Type ↓' in
|
|
response.data
|
|
)
|
|
|
|
def test_create(client, app, auth):
|
|
"""Test success in creating a recipe document."""
|
|
# Test GET without login
|
|
response = client.get('/recipes/create')
|
|
assert response.status_code == 302
|
|
|
|
# Test GET with login
|
|
auth.login()
|
|
response = client.get('/recipes/create')
|
|
assert response.status_code == 200
|
|
|
|
# Test POST
|
|
data = {
|
|
'efficiency': '65',
|
|
'name': 'Test',
|
|
'notes': 'Test',
|
|
'volume': '5.5',
|
|
}
|
|
response = client.post('/recipes/create', data=data)
|
|
assert response.status_code == 302
|
|
|
|
with app.app_context():
|
|
doc = get_doc('test')
|
|
|
|
assert doc['name'] == 'Test'
|
|
assert doc['notes'] == 'Test'
|
|
assert doc['volume'] == '5.5'
|
|
assert doc['efficiency'] == '65'
|
|
|
|
|
|
def test_update(client, app, auth):
|
|
"""Test success in updating a recipe document."""
|
|
# Test GET without login
|
|
response = client.get('/recipes/update/awesome-lager')
|
|
assert response.status_code == 302
|
|
|
|
auth.login()
|
|
# Test GET on a bare minimum recipe
|
|
response = client.get('/recipes/update/awesome-lager')
|
|
assert response.status_code == 200
|
|
assert b'Awesome Lager' in response.data
|
|
|
|
# Test GET on a more complete recipe
|
|
response = client.get('/recipes/update/full-recipe')
|
|
assert response.status_code == 200
|
|
test_items = [
|
|
b'Awesome Beer',
|
|
b'2row',
|
|
b'Dextrose',
|
|
b'Nugget (US)',
|
|
b'CTZ (US)',
|
|
b'Northern California Ale'
|
|
]
|
|
for item in test_items:
|
|
assert item in response.data
|
|
|
|
# Test GET on a recipe missing most yeast fields
|
|
response = client.get('/recipes/update/partial-yeast-recipe')
|
|
assert response.status_code == 200
|
|
test_items = [
|
|
b'Partial Beer',
|
|
b'US-05'
|
|
]
|
|
for item in test_items:
|
|
assert item in response.data
|
|
|
|
# Get a doc, make an update, and test a POST
|
|
id = 'awesome-lager'
|
|
with app.app_context():
|
|
doc = get_doc(id)
|
|
# Remove unneeded fields
|
|
doc.pop('_id')
|
|
rev = doc.pop('_rev')
|
|
response = client.post('/recipes/update/awesome-lager',
|
|
query_string={'rev': rev}, data=doc)
|
|
assert response.status_code == 302
|
|
|
|
# Test response without valid/conflicted rev
|
|
response = client.post('/recipes/update/awesome-lager',
|
|
query_string={'rev': ''}, data=doc)
|
|
assert response.status_code == 302
|
|
with client.session_transaction() as session:
|
|
flash_message = dict(session['_flashes']).pop('danger', None)
|
|
assert 'Update conflict' in flash_message
|
|
|
|
|
|
def test_info(client):
|
|
"""Test success in retrieving a recipe document."""
|
|
# Validate 404
|
|
response = client.get('/recipes/info/thisdoesnotexist')
|
|
assert response.status_code == 404
|
|
|
|
# Validate response for existing doc
|
|
response = client.get('/recipes/info/awesome-lager')
|
|
assert response.status_code == 200
|
|
assert b'Awesome Lager' in response.data
|
|
|
|
|
|
def test_info_json(client):
|
|
"""Test success in retrieving a JSON recipe."""
|
|
# Validate 404
|
|
response = client.get('/recipes/info/thisdoesnotexist/json')
|
|
assert response.status_code == 404
|
|
|
|
# Validate response for existing doc
|
|
response = client.get('/recipes/info/awesome-lager/json')
|
|
assert response.status_code == 200
|
|
assert response.is_json
|
|
assert response.get_json()['name'] == 'Awesome Lager'
|
|
|
|
|
|
def test_yeast_form_doc(app):
|
|
"""Evaluates conditionals in generation of doc from a yeast form."""
|
|
yeast = YeastForm()
|
|
yeast.name.data = 'Test'
|
|
yeast.low_attenuation.data = Decimal('60')
|
|
yeast.high_attenuation.data = Decimal('75')
|
|
|
|
assert yeast.doc == {
|
|
'name': 'Test',
|
|
'low_attenuation': '60',
|
|
'high_attenuation': '75'
|
|
}
|
|
|
|
yeast.type.data = 'Dry'
|
|
yeast.code.data = 'INIS-001'
|
|
yeast.lab.data = 'Inland Island'
|
|
yeast.flocculation.data = 'Low'
|
|
yeast.min_temperature.data = Decimal('40')
|
|
yeast.max_temperature.data = Decimal('50')
|
|
yeast.abv_tolerance.data = Decimal('15')
|
|
|
|
assert yeast.doc == {
|
|
'name': 'Test',
|
|
'low_attenuation': '60',
|
|
'high_attenuation': '75',
|
|
'flocculation': 'Low',
|
|
'type': 'Dry',
|
|
'code': 'INIS-001',
|
|
'lab': 'Inland Island',
|
|
'min_temperature': '40',
|
|
'max_temperature': '50',
|
|
'abv_tolerance': '15'
|
|
}
|
|
|
|
|
|
def test_recipe_form_doc(app):
|
|
"""Test if a recipeform can be turned into a document.
|
|
|
|
This test also tests that subforms can be turned into a document. Subforms
|
|
are not tested individually since they will never be used individually.
|
|
"""
|
|
with app.app_context():
|
|
recipe = RecipeForm()
|
|
|
|
recipe.name.data = 'Test'
|
|
recipe.efficiency.data = Decimal('65')
|
|
recipe.volume.data = Decimal('5.5')
|
|
recipe.notes.data = 'This is a test'
|
|
recipe.type.data = 'All-Grain'
|
|
|
|
assert recipe.doc == {
|
|
'name': 'Test',
|
|
'efficiency': '65',
|
|
'type': 'All-Grain',
|
|
'volume': '5.5',
|
|
'notes': 'This is a test',
|
|
'fermentables': [],
|
|
'hops': [],
|
|
'$type': 'recipe',
|
|
}
|
|
|
|
ferm = FermentableForm()
|
|
ferm.name.data = 'Test'
|
|
ferm.type.data = 'Grain'
|
|
ferm.amount.data = Decimal('5.5')
|
|
ferm.ppg.data = Decimal('37')
|
|
ferm.color.data = Decimal('1.8')
|
|
|
|
hop = HopForm()
|
|
hop.name.data = 'Test'
|
|
hop.use.data = 'Boil'
|
|
hop.alpha.data = Decimal('12.5')
|
|
hop.duration.data = Decimal('60')
|
|
hop.amount.data = Decimal('0.5')
|
|
|
|
yeast = YeastForm()
|
|
yeast.name.data = 'Test'
|
|
yeast.low_attenuation.data = '70'
|
|
yeast.high_attenuation.data = '75'
|
|
|
|
recipe.fermentables = [ferm]
|
|
recipe.hops = [hop]
|
|
recipe.yeast = yeast
|
|
|
|
assert recipe.doc == {
|
|
'name': 'Test',
|
|
'efficiency': '65',
|
|
'type': 'All-Grain',
|
|
'volume': '5.5',
|
|
'notes': 'This is a test',
|
|
'$type': 'recipe',
|
|
'fermentables': [{
|
|
'name': 'Test',
|
|
'type': 'Grain',
|
|
'amount': '5.5',
|
|
'ppg': '37',
|
|
'color': '1.8',
|
|
}],
|
|
'hops': [{
|
|
'name': 'Test',
|
|
'use': 'Boil',
|
|
'alpha': '12.5',
|
|
'duration': '60',
|
|
'amount': '0.5'
|
|
}],
|
|
'yeast': {
|
|
'name': 'Test',
|
|
'low_attenuation': '70',
|
|
'high_attenuation': '75'
|
|
}
|
|
}
|
|
|
|
|
|
def test_recipe_delete(client, auth):
|
|
"""Test success in deleting a document."""
|
|
# Try to delete a document without logging in
|
|
response = client.post('/recipes/delete/awesome-lager')
|
|
response = client.get('/recipes/info/awesome-lager')
|
|
assert response.status_code == 200
|
|
|
|
# Delete document after login
|
|
auth.login()
|
|
# Try to delete a document without logging in
|
|
response = client.post('/recipes/delete/awesome-lager')
|
|
response = client.get('/recipes/info/awesome-lager')
|
|
assert response.status_code == 404
|
|
|
|
|
|
def test_recipe_create_json(client, sample_recipes, auth):
|
|
"""Test uploading JSON recipe."""
|
|
# Test GET without logging in
|
|
response = client.get('/recipes/create/json')
|
|
assert response.status_code == 302
|
|
|
|
# Test GET after logging in
|
|
auth.login()
|
|
response = client.get('/recipes/create/json')
|
|
assert response.status_code == 200
|
|
|
|
# Test upload some good data
|
|
data = {
|
|
'upload': (BytesIO(json.dumps(sample_recipes['sweetstout']).encode()),
|
|
'sweetstout.json')
|
|
}
|
|
response = client.post('/recipes/create/json', buffered=True,
|
|
content_type='multipart/form-data', data=data)
|
|
assert response.status_code == 302
|
|
assert 'recipes/info/sweet-stout' in response.headers['Location']
|
|
|
|
# Test upload with some bad data
|
|
data = {'upload': (BytesIO(b'NOT JSON'), 'file')}
|
|
response = client.post('/recipes/create/json', buffered=True,
|
|
content_type='multipart/form-data', data=data)
|
|
assert response.status_code == 200
|