fix food tests

This commit is contained in:
smilerz 2023-04-21 13:14:23 -05:00
parent 8a8be7fb2d
commit 8c3195937b
No known key found for this signature in database
GPG Key ID: 39444C7606D47126
6 changed files with 328 additions and 223 deletions

View File

@ -56,23 +56,32 @@ def obj_tree_1(request, space_1):
params = request.param # request.param is a magic variable params = request.param # request.param is a magic variable
except AttributeError: except AttributeError:
params = {} params = {}
objs = []
inherit = params.pop('inherit', False) inherit = params.pop('inherit', False)
objs.extend(FoodFactory.create_batch(3, space=space_1, **params)) FoodFactory.create_batch(3, space=space_1, **params)
objs = Food.objects.values_list('id', flat=True)
obj_id = objs[1]
child_id = objs[0]
parent_id = objs[2]
# set all foods to inherit everything # set all foods to inherit everything
if inherit: if inherit:
inherit = Food.inheritable_fields inherit = Food.inheritable_fields
Through = Food.objects.filter(space=space_1).first().inherit_fields.through Through = Food.objects.filter(
space=space_1).first().inherit_fields.through
for i in inherit: for i in inherit:
Through.objects.bulk_create([ Through.objects.bulk_create([
Through(food_id=x, foodinheritfield_id=i.id) Through(food_id=x, foodinheritfield_id=i.id)
for x in Food.objects.filter(space=space_1).values_list('id', flat=True) for x in Food.objects.filter(space=space_1).values_list('id', flat=True)
]) ])
objs[0].move(objs[1], node_location) Food.objects.get(id=child_id).move(
objs[1].move(objs[2], node_location) Food.objects.get(id=obj_id), node_location)
return Food.objects.get(id=objs[1].id) # whenever you move/merge a tree it's safest to re-get the object
Food.objects.get(id=obj_id).move(
Food.objects.get(id=parent_id), node_location)
# whenever you move/merge a tree it's safest to re-get the object
return Food.objects.get(id=obj_id)
@pytest.mark.parametrize("arg", [ @pytest.mark.parametrize("arg", [
@ -107,19 +116,23 @@ def test_list_filter(obj_1, obj_2, u1_s1):
assert obj_2.name in [x['name'] for x in response['results']] assert obj_2.name in [x['name'] for x in response['results']]
assert response['results'][0]['name'] < response['results'][1]['name'] assert response['results'][0]['name'] < response['results'][1]['name']
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?page_size=1').content) response = json.loads(
u1_s1.get(f'{reverse(LIST_URL)}?page_size=1').content)
assert len(response['results']) == 1 assert len(response['results']) == 1
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?limit=1').content) response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?limit=1').content)
assert len(response['results']) == 1 assert len(response['results']) == 1
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?query=''&limit=1').content) response = json.loads(
u1_s1.get(f'{reverse(LIST_URL)}?query=''&limit=1').content)
assert len(response['results']) == 1 assert len(response['results']) == 1
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?query=chicken').content) response = json.loads(
u1_s1.get(f'{reverse(LIST_URL)}?query=chicken').content)
assert response['count'] == 0 assert response['count'] == 0
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?query={obj_1.name[:-4]}').content) response = json.loads(
u1_s1.get(f'{reverse(LIST_URL)}?query={obj_1.name[:-4]}').content)
assert response['count'] == 1 assert response['count'] == 1
@ -262,8 +275,9 @@ def test_integrity(u1_s1, recipe_1_s1):
def test_move(u1_s1, obj_tree_1, obj_2, obj_3, space_1): def test_move(u1_s1, obj_tree_1, obj_2, obj_3, space_1):
with scope(space=space_1): with scope(space=space_1):
# for some reason the 'path' attribute changes between the factory and the test when using both obj_tree and obj
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
parent = obj_tree_1.get_parent() parent = obj_tree_1.get_parent()
child = obj_tree_1.get_descendants()[0]
assert parent.get_num_children() == 1 assert parent.get_num_children() == 1
assert parent.get_descendant_count() == 2 assert parent.get_descendant_count() == 2
assert Food.get_root_nodes().filter(space=space_1).count() == 2 assert Food.get_root_nodes().filter(space=space_1).count() == 2
@ -295,8 +309,9 @@ def test_move(u1_s1, obj_tree_1, obj_2, obj_3, space_1):
def test_move_errors(u1_s1, obj_tree_1, obj_3, space_1): def test_move_errors(u1_s1, obj_tree_1, obj_3, space_1):
with scope(space=space_1): with scope(space=space_1):
# for some reason the 'path' attribute changes between the factory and the test when using both obj_tree and obj
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
parent = obj_tree_1.get_parent() parent = obj_tree_1.get_parent()
child = obj_tree_1.get_descendants()[0]
# move child to root # move child to root
r = u1_s1.put(reverse(MOVE_URL, args=[obj_tree_1.id, 0])) r = u1_s1.put(reverse(MOVE_URL, args=[obj_tree_1.id, 0]))
assert r.status_code == 200 assert r.status_code == 200
@ -373,6 +388,8 @@ def test_merge_shopping_entries(obj_tree_1, u1_s1, space_1):
def test_merge(u1_s1, obj_tree_1, obj_1, obj_3, space_1): def test_merge(u1_s1, obj_tree_1, obj_1, obj_3, space_1):
with scope(space=space_1): with scope(space=space_1):
# for some reason the 'path' attribute changes between the factory and the test when using both obj_tree and obj
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
parent = obj_tree_1.get_parent() parent = obj_tree_1.get_parent()
child = obj_tree_1.get_descendants()[0] child = obj_tree_1.get_descendants()[0]
assert parent.get_num_children() == 1 assert parent.get_num_children() == 1
@ -417,7 +434,6 @@ def test_merge(u1_s1, obj_tree_1, obj_1, obj_3, space_1):
def test_merge_errors(u1_s1, obj_tree_1, obj_3, space_1): def test_merge_errors(u1_s1, obj_tree_1, obj_3, space_1):
with scope(space=space_1): with scope(space=space_1):
parent = obj_tree_1.get_parent() parent = obj_tree_1.get_parent()
child = obj_tree_1.get_descendants()[0]
# attempt to merge with non-existent parent # attempt to merge with non-existent parent
r = u1_s1.put( r = u1_s1.put(
@ -451,8 +467,9 @@ def test_merge_errors(u1_s1, obj_tree_1, obj_3, space_1):
def test_root_filter(obj_tree_1, obj_2, obj_3, u1_s1): def test_root_filter(obj_tree_1, obj_2, obj_3, u1_s1):
with scope(space=obj_tree_1.space): with scope(space=obj_tree_1.space):
# for some reason the 'path' attribute changes between the factory and the test when using both obj_tree and obj
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
parent = obj_tree_1.get_parent() parent = obj_tree_1.get_parent()
child = obj_tree_1.get_descendants()[0]
# should return root objects in the space (obj_1, obj_2), ignoring query filters # should return root objects in the space (obj_1, obj_2), ignoring query filters
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?root=0').content) response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?root=0').content)
@ -461,34 +478,46 @@ def test_root_filter(obj_tree_1, obj_2, obj_3, u1_s1):
with scopes_disabled(): with scopes_disabled():
obj_2.move(parent, node_location) obj_2.move(parent, node_location)
# should return direct children of parent (obj_tree_1, obj_2), ignoring query filters # should return direct children of parent (obj_tree_1, obj_2), ignoring query filters
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?root={parent.id}').content) response = json.loads(
u1_s1.get(f'{reverse(LIST_URL)}?root={parent.id}').content)
assert response['count'] == 2 assert response['count'] == 2
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?root={parent.id}&query={obj_2.name[4:]}').content) response = json.loads(u1_s1.get(
f'{reverse(LIST_URL)}?root={parent.id}&query={obj_2.name[4:]}').content)
assert response['count'] == 2 assert response['count'] == 2
def test_tree_filter(obj_tree_1, obj_2, obj_3, u1_s1): def test_tree_filter(obj_tree_1, obj_2, obj_3, u1_s1):
with scope(space=obj_tree_1.space): with scope(space=obj_tree_1.space):
# for some reason the 'path' attribute changes between the factory and the test when using both obj_tree and obj
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
parent = obj_tree_1.get_parent() parent = obj_tree_1.get_parent()
child = obj_tree_1.get_descendants()[0]
obj_2.move(parent, node_location) obj_2.move(parent, node_location)
# should return full tree starting at parent (obj_tree_1, obj_2), ignoring query filters # should return full tree starting at parent (obj_tree_1, obj_2), ignoring query filters
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?tree={parent.id}').content) response = json.loads(
u1_s1.get(f'{reverse(LIST_URL)}?tree={parent.id}').content)
assert response['count'] == 4 assert response['count'] == 4
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?tree={parent.id}&query={obj_2.name[4:]}').content) response = json.loads(u1_s1.get(
f'{reverse(LIST_URL)}?tree={parent.id}&query={obj_2.name[4:]}').content)
assert response['count'] == 4 assert response['count'] == 4
# This is more about the model than the API - should this be moved to a different test? # This is more about the model than the API - should this be moved to a different test?
@pytest.mark.parametrize("obj_tree_1, field, inherit, new_val", [ @pytest.mark.parametrize("obj_tree_1, field, inherit, new_val", [
({'has_category': True, 'inherit': True}, 'supermarket_category', True, 'cat_1'), ({'has_category': True, 'inherit': True},
({'has_category': True, 'inherit': False}, 'supermarket_category', False, 'cat_1'), 'supermarket_category', True, 'cat_1'),
({'has_category': True, 'inherit': False},
'supermarket_category', False, 'cat_1'),
({'ignore_shopping': True, 'inherit': True}, 'ignore_shopping', True, 'false'), ({'ignore_shopping': True, 'inherit': True}, 'ignore_shopping', True, 'false'),
({'ignore_shopping': True, 'inherit': False}, 'ignore_shopping', False, 'false'), ({'ignore_shopping': True, 'inherit': False},
({'substitute_children': True, 'inherit': True}, 'substitute_children', True, 'false'), 'ignore_shopping', False, 'false'),
({'substitute_children': True, 'inherit': False}, 'substitute_children', False, 'false'), ({'substitute_children': True, 'inherit': True},
({'substitute_siblings': True, 'inherit': True}, 'substitute_siblings', True, 'false'), 'substitute_children', True, 'false'),
({'substitute_siblings': True, 'inherit': False}, 'substitute_siblings', False, 'false'), ({'substitute_children': True, 'inherit': False},
'substitute_children', False, 'false'),
({'substitute_siblings': True, 'inherit': True},
'substitute_siblings', True, 'false'),
({'substitute_siblings': True, 'inherit': False},
'substitute_siblings', False, 'false'),
], indirect=['obj_tree_1']) # indirect=True populates magic variable request.param of obj_tree_1 with the parameter ], indirect=['obj_tree_1']) # indirect=True populates magic variable request.param of obj_tree_1 with the parameter
def test_inherit(request, obj_tree_1, field, inherit, new_val, u1_s1): def test_inherit(request, obj_tree_1, field, inherit, new_val, u1_s1):
with scope(space=obj_tree_1.space): with scope(space=obj_tree_1.space):
@ -498,8 +527,10 @@ def test_inherit(request, obj_tree_1, field, inherit, new_val, u1_s1):
new_val = request.getfixturevalue(new_val) new_val = request.getfixturevalue(new_val)
# if this test passes it demonstrates that inheritance works # if this test passes it demonstrates that inheritance works
# when moving to a parent as each food is created with a different category # when moving to a parent as each food is created with a different category
assert (getattr(parent, field) == getattr(obj_tree_1, field)) in [inherit, True] assert (getattr(parent, field) == getattr(
assert (getattr(obj_tree_1, field) == getattr(child, field)) in [inherit, True] obj_tree_1, field)) in [inherit, True]
assert (getattr(obj_tree_1, field) == getattr(
child, field)) in [inherit, True]
# change parent to a new value # change parent to a new value
setattr(parent, field, new_val) setattr(parent, field, new_val)
with scope(space=parent.space): with scope(space=parent.space):
@ -515,7 +546,8 @@ def test_inherit(request, obj_tree_1, field, inherit, new_val, u1_s1):
@pytest.mark.parametrize("obj_tree_1", [ @pytest.mark.parametrize("obj_tree_1", [
({'has_category': True, 'inherit': False, 'ignore_shopping': True, 'substitute_children': True, 'substitute_siblings': True}), ({'has_category': True, 'inherit': False, 'ignore_shopping': True,
'substitute_children': True, 'substitute_siblings': True}),
], indirect=['obj_tree_1']) ], indirect=['obj_tree_1'])
@pytest.mark.parametrize("global_reset", [True, False]) @pytest.mark.parametrize("global_reset", [True, False])
@pytest.mark.parametrize("field", ['ignore_shopping', 'substitute_children', 'substitute_siblings', 'supermarket_category']) @pytest.mark.parametrize("field", ['ignore_shopping', 'substitute_children', 'substitute_siblings', 'supermarket_category'])
@ -534,10 +566,13 @@ def test_reset_inherit_space_fields(obj_tree_1, space_1, global_reset, field):
assert getattr(parent, field) != getattr(obj_tree_1, field) assert getattr(parent, field) != getattr(obj_tree_1, field)
if global_reset: if global_reset:
space_1.food_inherit.add(*Food.inheritable_fields.values_list('id', flat=True)) # set default inherit fields # set default inherit fields
space_1.food_inherit.add(
*Food.inheritable_fields.values_list('id', flat=True))
parent.reset_inheritance(space=space_1) parent.reset_inheritance(space=space_1)
else: else:
obj_tree_1.child_inherit_fields.set(Food.inheritable_fields.values_list('id', flat=True)) obj_tree_1.child_inherit_fields.set(
Food.inheritable_fields.values_list('id', flat=True))
obj_tree_1.save() obj_tree_1.save()
parent.reset_inheritance(space=space_1, food=obj_tree_1) parent.reset_inheritance(space=space_1, food=obj_tree_1)
# djangotree bypasses ORM and need to be retrieved again # djangotree bypasses ORM and need to be retrieved again
@ -545,12 +580,14 @@ def test_reset_inherit_space_fields(obj_tree_1, space_1, global_reset, field):
parent = Food.objects.get(id=parent.id) parent = Food.objects.get(id=parent.id)
child = Food.objects.get(id=child.id) child = Food.objects.get(id=child.id)
assert (getattr(parent, field) == getattr(obj_tree_1, field)) == global_reset assert (getattr(parent, field) == getattr(
obj_tree_1, field)) == global_reset
assert getattr(obj_tree_1, field) == getattr(child, field) assert getattr(obj_tree_1, field) == getattr(child, field)
@pytest.mark.parametrize("obj_tree_1", [ @pytest.mark.parametrize("obj_tree_1", [
({'has_category': True, 'inherit': False, 'ignore_shopping': True, 'substitute_children': True, 'substitute_siblings': True}), ({'has_category': True, 'inherit': False, 'ignore_shopping': True,
'substitute_children': True, 'substitute_siblings': True}),
], indirect=['obj_tree_1']) ], indirect=['obj_tree_1'])
@pytest.mark.parametrize("field", ['ignore_shopping', 'substitute_children', 'substitute_siblings', 'supermarket_category']) @pytest.mark.parametrize("field", ['ignore_shopping', 'substitute_children', 'substitute_siblings', 'supermarket_category'])
def test_reset_inherit_no_food_instances(obj_tree_1, space_1, field): def test_reset_inherit_no_food_instances(obj_tree_1, space_1, field):
@ -558,13 +595,17 @@ def test_reset_inherit_no_food_instances(obj_tree_1, space_1, field):
parent = obj_tree_1.get_parent() parent = obj_tree_1.get_parent()
Food.objects.all().delete() Food.objects.all().delete()
space_1.food_inherit.add(*Food.inheritable_fields.values_list('id', flat=True)) # set default inherit fields # set default inherit fields
space_1.food_inherit.add(
*Food.inheritable_fields.values_list('id', flat=True))
parent.reset_inheritance(space=space_1) parent.reset_inheritance(space=space_1)
def test_onhand(obj_1, u1_s1, u2_s1): def test_onhand(obj_1, u1_s1, u2_s1):
assert json.loads(u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == False assert json.loads(u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)[
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == False 'food_onhand'] == False
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)[
'food_onhand'] == False
u1_s1.patch( u1_s1.patch(
reverse( reverse(
@ -574,10 +615,13 @@ def test_onhand(obj_1, u1_s1, u2_s1):
{'food_onhand': True}, {'food_onhand': True},
content_type='application/json' content_type='application/json'
) )
assert json.loads(u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == True assert json.loads(u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)[
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == False 'food_onhand'] == True
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)[
'food_onhand'] == False
user1 = auth.get_user(u1_s1) user1 = auth.get_user(u1_s1)
user2 = auth.get_user(u2_s1) user2 = auth.get_user(u2_s1)
user1.userpreference.shopping_share.add(user2) user1.userpreference.shopping_share.add(user2)
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == True assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)[
'food_onhand'] == True

View File

@ -1,20 +1,14 @@
import json import json
from datetime import timedelta
import factory
import pytest import pytest
# work around for bug described here https://stackoverflow.com/a/70312265/15762829 # work around for bug described here https://stackoverflow.com/a/70312265/15762829
from django.conf import settings from django.conf import settings
from django.contrib import auth from django.contrib import auth
from django.forms import model_to_dict
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django_scopes import scopes_disabled
from django_scopes import scope, scopes_disabled
from pytest_factoryboy import LazyFixture, register
from cookbook.models import Food, Ingredient, ShoppingListEntry, Step from cookbook.models import Food, Ingredient
from cookbook.tests.factories import (IngredientFactory, MealPlanFactory, RecipeFactory, from cookbook.tests.factories import MealPlanFactory, RecipeFactory, StepFactory, UserFactory
StepFactory, UserFactory)
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
'django.db.backends.postgresql']: 'django.db.backends.postgresql']:
@ -32,9 +26,12 @@ def user2(request, u1_s1):
except AttributeError: except AttributeError:
params = {} params = {}
user = auth.get_user(u1_s1) user = auth.get_user(u1_s1)
user.userpreference.mealplan_autoadd_shopping = params.get('mealplan_autoadd_shopping', True) user.userpreference.mealplan_autoadd_shopping = params.get(
user.userpreference.mealplan_autoinclude_related = params.get('mealplan_autoinclude_related', True) 'mealplan_autoadd_shopping', True)
user.userpreference.mealplan_autoexclude_onhand = params.get('mealplan_autoexclude_onhand', True) user.userpreference.mealplan_autoinclude_related = params.get(
'mealplan_autoinclude_related', True)
user.userpreference.mealplan_autoexclude_onhand = params.get(
'mealplan_autoexclude_onhand', True)
user.userpreference.save() user.userpreference.save()
return u1_s1 return u1_s1
@ -50,7 +47,6 @@ def recipe(request, space_1, u1_s1):
return RecipeFactory(**params) return RecipeFactory(**params)
@pytest.mark.parametrize("arg", [ @pytest.mark.parametrize("arg", [
['g1_s1', 204], ['g1_s1', 204],
['u1_s1', 204], ['u1_s1', 204],
@ -59,9 +55,12 @@ def recipe(request, space_1, u1_s1):
]) ])
@pytest.mark.parametrize("recipe, sle_count", [ @pytest.mark.parametrize("recipe, sle_count", [
({}, 10), ({}, 10),
({'steps__recipe_count': 1}, 20), # shopping list from recipe with StepRecipe # shopping list from recipe with StepRecipe
({'steps__food_recipe_count': {'step': 0, 'count': 1}}, 19), # shopping list from recipe with food recipe ({'steps__recipe_count': 1}, 20),
({'steps__food_recipe_count': {'step': 0, 'count': 1}, 'steps__recipe_count': 1}, 29), # shopping list from recipe with StepRecipe and food recipe # shopping list from recipe with food recipe
({'steps__food_recipe_count': {'step': 0, 'count': 1}}, 19),
# shopping list from recipe with StepRecipe and food recipe
({'steps__food_recipe_count': {'step': 0, 'count': 1}, 'steps__recipe_count': 1}, 29),
], indirect=['recipe']) ], indirect=['recipe'])
def test_shopping_recipe_method(request, arg, recipe, sle_count, u1_s1, u2_s1): def test_shopping_recipe_method(request, arg, recipe, sle_count, u1_s1, u2_s1):
c = request.getfixturevalue(arg[0]) c = request.getfixturevalue(arg[0])
@ -78,16 +77,20 @@ def test_shopping_recipe_method(request, arg, recipe, sle_count, u1_s1, u2_s1):
if r.status_code == 204: # skip anonymous user if r.status_code == 204: # skip anonymous user
r = json.loads(c.get(reverse(SHOPPING_LIST_URL)).content) r = json.loads(c.get(reverse(SHOPPING_LIST_URL)).content)
assert len(r) == sle_count # recipe factory creates 10 ingredients by default # recipe factory creates 10 ingredients by default
assert len(r) == sle_count
assert [x['created_by']['id'] for x in r].count(user.id) == sle_count assert [x['created_by']['id'] for x in r].count(user.id) == sle_count
# user in space can't see shopping list # user in space can't see shopping list
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0 assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
user.userpreference.shopping_share.add(auth.get_user(u2_s1)) user.userpreference.shopping_share.add(auth.get_user(u2_s1))
# after share, user in space can see shopping list # after share, user in space can see shopping list
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
# confirm that the author of the recipe doesn't have access to shopping list # confirm that the author of the recipe doesn't have access to shopping list
if c != u1_s1: if c != u1_s1:
assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0 assert len(json.loads(
u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
r = c.get(url) r = c.get(url)
assert r.status_code == 405 assert r.status_code == 405
@ -99,9 +102,12 @@ def test_shopping_recipe_method(request, arg, recipe, sle_count, u1_s1, u2_s1):
@pytest.mark.parametrize("recipe, sle_count", [ @pytest.mark.parametrize("recipe, sle_count", [
({}, 10), ({}, 10),
({'steps__recipe_count': 1}, 20), # shopping list from recipe with StepRecipe # shopping list from recipe with StepRecipe
({'steps__food_recipe_count': {'step': 0, 'count': 1}}, 19), # shopping list from recipe with food recipe ({'steps__recipe_count': 1}, 20),
({'steps__food_recipe_count': {'step': 0, 'count': 1}, 'steps__recipe_count': 1}, 29), # shopping list from recipe with StepRecipe and food recipe # shopping list from recipe with food recipe
({'steps__food_recipe_count': {'step': 0, 'count': 1}}, 19),
# shopping list from recipe with StepRecipe and food recipe
({'steps__food_recipe_count': {'step': 0, 'count': 1}, 'steps__recipe_count': 1}, 29),
], indirect=['recipe']) ], indirect=['recipe'])
@pytest.mark.parametrize("use_mealplan", [(False), (True), ]) @pytest.mark.parametrize("use_mealplan", [(False), (True), ])
def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u2_s1): def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u2_s1):
@ -115,31 +121,33 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
user.userpreference.save() user.userpreference.save()
if use_mealplan: if use_mealplan:
mealplan = MealPlanFactory(space=recipe.space, created_by=user, servings=recipe.servings, recipe=recipe) mealplan = MealPlanFactory(
space=recipe.space, created_by=user, servings=recipe.servings, recipe=recipe)
else: else:
u1_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id})) u1_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}))
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content) r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
assert [x['created_by']['id'] for x in r].count(user.id) == sle_count assert [x['created_by']['id'] for x in r].count(user.id) == sle_count
all_ing = [x['ingredient'] for x in r] all_ing = [x['ingredient'] for x in r]
keep_ing = all_ing[1:-1] # remove first and last element keep_ing = all_ing[1:-1] # remove first and last element
del keep_ing[int(len(keep_ing)/2)] # remove a middle element del keep_ing[int(len(keep_ing) / 2)] # remove a middle element
list_recipe = r[0]['list_recipe'] list_recipe = r[0]['list_recipe']
amount_sum = sum([x['amount'] for x in r]) amount_sum = sum([x['amount'] for x in r])
# test modifying shopping list as different user # test modifying shopping list as different user
# test increasing servings size of recipe shopping list # test increasing servings size of recipe shopping list
if use_mealplan: if use_mealplan:
mealplan.servings = 2*recipe.servings mealplan.servings = 2 * recipe.servings
mealplan.save() mealplan.save()
else: else:
u2_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}), u2_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}),
{'list_recipe': list_recipe, 'servings': 2*recipe.servings}, {'list_recipe': list_recipe, 'servings': 2 * recipe.servings},
content_type='application/json' content_type='application/json'
) )
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content) r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
assert sum([x['amount'] for x in r]) == amount_sum * 2 assert sum([x['amount'] for x in r]) == amount_sum * 2
assert len(r) == sle_count assert len(r) == sle_count
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
# testing decreasing servings size of recipe shopping list # testing decreasing servings size of recipe shopping list
if use_mealplan: if use_mealplan:
@ -153,7 +161,8 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content) r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
assert sum([x['amount'] for x in r]) == amount_sum * .5 assert sum([x['amount'] for x in r]) == amount_sum * .5
assert len(r) == sle_count assert len(r) == sle_count
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
# test removing 3 items from shopping list # test removing 3 items from shopping list
u2_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}), u2_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}),
@ -162,7 +171,8 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
) )
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content) r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
assert len(r) == sle_count - 3 assert len(r) == sle_count - 3
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count - 3 assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count - 3
# add all ingredients to existing shopping list - don't change serving size # add all ingredients to existing shopping list - don't change serving size
u2_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}), u2_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}),
@ -172,14 +182,16 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content) r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
assert sum([x['amount'] for x in r]) == amount_sum * .5 assert sum([x['amount'] for x in r]) == amount_sum * .5
assert len(r) == sle_count assert len(r) == sle_count
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
@pytest.mark.parametrize("user2, sle_count", [ @pytest.mark.parametrize("user2, sle_count", [
({'mealplan_autoadd_shopping': False}, (0, 18)), ({'mealplan_autoadd_shopping': False}, (0, 18)),
({'mealplan_autoinclude_related': False}, (9, 9)), ({'mealplan_autoinclude_related': False}, (9, 9)),
({'mealplan_autoexclude_onhand': False}, (20, 20)), ({'mealplan_autoexclude_onhand': False}, (20, 20)),
({'mealplan_autoexclude_onhand': False, 'mealplan_autoinclude_related': False}, (10, 10)), ({'mealplan_autoexclude_onhand': False,
'mealplan_autoinclude_related': False}, (10, 10)),
], indirect=['user2']) ], indirect=['user2'])
@pytest.mark.parametrize("use_mealplan", [(False), (True), ]) @pytest.mark.parametrize("use_mealplan", [(False), (True), ])
@pytest.mark.parametrize("recipe", [({'steps__recipe_count': 1})], indirect=['recipe']) @pytest.mark.parametrize("recipe", [({'steps__recipe_count': 1})], indirect=['recipe'])
@ -191,20 +203,24 @@ def test_shopping_recipe_userpreference(recipe, sle_count, use_mealplan, user2):
food = Food.objects.get(id=ingredients[2].food.id) food = Food.objects.get(id=ingredients[2].food.id)
food.onhand_users.add(user) food.onhand_users.add(user)
food.save() food.save()
food = recipe.steps.exclude(step_recipe=None).first().step_recipe.steps.first().ingredients.first().food food = recipe.steps.exclude(step_recipe=None).first(
).step_recipe.steps.first().ingredients.first().food
food = Food.objects.get(id=food.id) food = Food.objects.get(id=food.id)
food.onhand_users.add(user) food.onhand_users.add(user)
food.save() food.save()
if use_mealplan: if use_mealplan:
mealplan = MealPlanFactory(space=recipe.space, created_by=user, servings=recipe.servings, recipe=recipe) MealPlanFactory(
assert len(json.loads(user2.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count[0] space=recipe.space, created_by=user, servings=recipe.servings, recipe=recipe)
assert len(json.loads(
user2.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count[0]
else: else:
user2.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id})) user2.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}))
assert len(json.loads(user2.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count[1] assert len(json.loads(
user2.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count[1]
def test_shopping_recipe_mixed_authors(u1_s1, u2_s1,space_1): def test_shopping_recipe_mixed_authors(u1_s1, u2_s1, space_1):
with scopes_disabled(): with scopes_disabled():
user1 = auth.get_user(u1_s1) user1 = auth.get_user(u1_s1)
user2 = auth.get_user(u2_s1) user2 = auth.get_user(u2_s1)
@ -213,15 +229,19 @@ def test_shopping_recipe_mixed_authors(u1_s1, u2_s1,space_1):
recipe1 = RecipeFactory(created_by=user1, space=space) recipe1 = RecipeFactory(created_by=user1, space=space)
recipe2 = RecipeFactory(created_by=user2, space=space) recipe2 = RecipeFactory(created_by=user2, space=space)
recipe3 = RecipeFactory(created_by=user3, space=space) recipe3 = RecipeFactory(created_by=user3, space=space)
food = Food.objects.get(id=recipe1.steps.first().ingredients.first().food.id) food = Food.objects.get(
id=recipe1.steps.first().ingredients.first().food.id)
food.recipe = recipe2 food.recipe = recipe2
food.save() food.save()
recipe1.steps.add(StepFactory(step_recipe=recipe3, ingredients__count=0, space=space)) recipe1.steps.add(StepFactory(step_recipe=recipe3,
ingredients__count=0, space=space))
recipe1.save() recipe1.save()
u1_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe1.id})) u1_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe1.id}))
assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 29 assert len(json.loads(
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0 u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 29
assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
@pytest.mark.parametrize("recipe", [{'steps__ingredients__header': 1}], indirect=['recipe']) @pytest.mark.parametrize("recipe", [{'steps__ingredients__header': 1}], indirect=['recipe'])
@ -230,4 +250,5 @@ def test_shopping_with_header_ingredient(u1_s1, recipe):
# recipe.step_set.first().ingredient_set.add(IngredientFactory(ingredients__header=1)) # recipe.step_set.first().ingredient_set.add(IngredientFactory(ingredients__header=1))
u1_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id})) u1_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}))
assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 10 assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 10
assert len(json.loads(u1_s1.get(reverse('api:ingredient-list')).content)['results']) == 11 assert len(json.loads(
u1_s1.get(reverse('api:ingredient-list')).content)['results']) == 11

View File

@ -4,13 +4,12 @@ from decimal import Decimal
import factory import factory
import pytest import pytest
from django.contrib import auth
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
from faker import Factory as FakerFactory from faker import Factory as FakerFactory
from pytest_factoryboy import register from pytest_factoryboy import register
from cookbook.models import Recipe, Step, UserSpace from cookbook.models import UserSpace
# this code will run immediately prior to creating the model object useful when you want a reverse relationship # this code will run immediately prior to creating the model object useful when you want a reverse relationship
# log = factory.RelatedFactory( # log = factory.RelatedFactory(
@ -53,7 +52,8 @@ class SpaceFactory(factory.django.DjangoModelFactory):
class UserFactory(factory.django.DjangoModelFactory): class UserFactory(factory.django.DjangoModelFactory):
"""User factory.""" """User factory."""
username = factory.LazyAttribute(lambda x: faker.simple_profile()['username']) username = factory.LazyAttribute(
lambda x: faker.simple_profile()['username'])
first_name = factory.LazyAttribute(lambda x: faker.first_name()) first_name = factory.LazyAttribute(lambda x: faker.first_name())
last_name = factory.LazyAttribute(lambda x: faker.last_name()) last_name = factory.LazyAttribute(lambda x: faker.last_name())
email = factory.LazyAttribute(lambda x: faker.email()) email = factory.LazyAttribute(lambda x: faker.email())
@ -65,7 +65,8 @@ class UserFactory(factory.django.DjangoModelFactory):
return return
if extracted: if extracted:
us = UserSpace.objects.create(space=self.space, user=self, active=True) us = UserSpace.objects.create(
space=self.space, user=self, active=True)
us.groups.add(Group.objects.get(name=extracted)) us.groups.add(Group.objects.get(name=extracted))
@factory.post_generation @factory.post_generation
@ -75,7 +76,8 @@ class UserFactory(factory.django.DjangoModelFactory):
if extracted: if extracted:
for prefs in extracted: for prefs in extracted:
self.userpreference[prefs] = extracted[prefs]/0 # intentionally break so it can be debugged later # intentionally break so it can be debugged later
self.userpreference[prefs] = extracted[prefs]/0
class Meta: class Meta:
model = User model = User
@ -98,18 +100,22 @@ class SupermarketCategoryFactory(factory.django.DjangoModelFactory):
class FoodFactory(factory.django.DjangoModelFactory): class FoodFactory(factory.django.DjangoModelFactory):
"""Food factory.""" """Food factory."""
name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10)[:128]) name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10)[:128])
plural_name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=3, variable_nb_words=False)) plural_name = factory.LazyAttribute(
lambda x: faker.sentence(nb_words=3, variable_nb_words=False))
description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10)) description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10))
supermarket_category = factory.Maybe( supermarket_category = factory.Maybe(
factory.LazyAttribute(lambda x: x.has_category), factory.LazyAttribute(lambda x: x.has_category),
yes_declaration=factory.SubFactory(SupermarketCategoryFactory, space=factory.SelfAttribute('..space')), yes_declaration=factory.SubFactory(
SupermarketCategoryFactory, space=factory.SelfAttribute('..space')),
no_declaration=None no_declaration=None
) )
recipe = factory.Maybe( recipe = factory.Maybe(
factory.LazyAttribute(lambda x: x.has_recipe), factory.LazyAttribute(lambda x: x.has_recipe),
yes_declaration=factory.SubFactory('cookbook.tests.factories.RecipeFactory', space=factory.SelfAttribute('..space')), yes_declaration=factory.SubFactory(
'cookbook.tests.factories.RecipeFactory', space=factory.SelfAttribute('..space')),
no_declaration=None no_declaration=None
) )
path = None
space = factory.SubFactory(SpaceFactory) space = factory.SubFactory(SpaceFactory)
@factory.post_generation @factory.post_generation
@ -127,17 +133,19 @@ class FoodFactory(factory.django.DjangoModelFactory):
class Meta: class Meta:
model = 'cookbook.Food' model = 'cookbook.Food'
django_get_or_create = ('name', 'plural_name', 'space',) django_get_or_create = ('name', 'plural_name', 'path', 'space',)
@register @register
class RecipeBookFactory(factory.django.DjangoModelFactory): class RecipeBookFactory(factory.django.DjangoModelFactory):
"""RecipeBook factory.""" """RecipeBook factory."""
name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=3, variable_nb_words=False)) name = factory.LazyAttribute(lambda x: faker.sentence(
nb_words=3, variable_nb_words=False))
description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10)) description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10))
icon = None icon = None
# shared = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space')) # shared = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space'))
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space')) created_by = factory.SubFactory(
UserFactory, space=factory.SelfAttribute('..space'))
filter = None filter = None
space = factory.SubFactory(SpaceFactory) space = factory.SubFactory(SpaceFactory)
@ -149,7 +157,8 @@ class RecipeBookFactory(factory.django.DjangoModelFactory):
@register @register
class RecipeBookEntryFactory(factory.django.DjangoModelFactory): class RecipeBookEntryFactory(factory.django.DjangoModelFactory):
"""RecipeBookEntry factory.""" """RecipeBookEntry factory."""
book = factory.SubFactory(RecipeBookFactory, space=factory.SelfAttribute('..recipe.space')) book = factory.SubFactory(
RecipeBookFactory, space=factory.SelfAttribute('..recipe.space'))
recipe = None recipe = None
class Meta: class Meta:
@ -173,7 +182,8 @@ class UnitFactory(factory.django.DjangoModelFactory):
@register @register
class KeywordFactory(factory.django.DjangoModelFactory): class KeywordFactory(factory.django.DjangoModelFactory):
"""Keyword factory.""" """Keyword factory."""
name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=2, variable_nb_words=False)) name = factory.LazyAttribute(lambda x: faker.sentence(
nb_words=2, variable_nb_words=False))
# icon = models.CharField(max_length=16, blank=True, null=True) # icon = models.CharField(max_length=16, blank=True, null=True)
description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10)) description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10))
space = factory.SubFactory(SpaceFactory) space = factory.SubFactory(SpaceFactory)
@ -184,15 +194,17 @@ class KeywordFactory(factory.django.DjangoModelFactory):
class Meta: class Meta:
model = 'cookbook.Keyword' model = 'cookbook.Keyword'
django_get_or_create = ('name', 'space',) django_get_or_create = ('name', 'space')
exclude = ('num') exclude = ('num')
@register @register
class IngredientFactory(factory.django.DjangoModelFactory): class IngredientFactory(factory.django.DjangoModelFactory):
"""Ingredient factory.""" """Ingredient factory."""
food = factory.SubFactory(FoodFactory, space=factory.SelfAttribute('..space')) food = factory.SubFactory(
unit = factory.SubFactory(UnitFactory, space=factory.SelfAttribute('..space')) FoodFactory, space=factory.SelfAttribute('..space'))
unit = factory.SubFactory(
UnitFactory, space=factory.SelfAttribute('..space'))
amount = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=10)) amount = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=10))
note = factory.LazyAttribute(lambda x: faker.sentence(nb_words=8)) note = factory.LazyAttribute(lambda x: faker.sentence(nb_words=8))
is_header = False is_header = False
@ -210,7 +222,8 @@ class MealTypeFactory(factory.django.DjangoModelFactory):
# icon = # icon =
color = factory.LazyAttribute(lambda x: faker.safe_hex_color()) color = factory.LazyAttribute(lambda x: faker.safe_hex_color())
default = False default = False
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space')) created_by = factory.SubFactory(
UserFactory, space=factory.SelfAttribute('..space'))
space = factory.SubFactory(SpaceFactory) space = factory.SubFactory(SpaceFactory)
class Meta: class Meta:
@ -221,13 +234,17 @@ class MealTypeFactory(factory.django.DjangoModelFactory):
class MealPlanFactory(factory.django.DjangoModelFactory): class MealPlanFactory(factory.django.DjangoModelFactory):
recipe = factory.Maybe( recipe = factory.Maybe(
factory.LazyAttribute(lambda x: x.has_recipe), factory.LazyAttribute(lambda x: x.has_recipe),
yes_declaration=factory.SubFactory('cookbook.tests.factories.RecipeFactory', space=factory.SelfAttribute('..space')), yes_declaration=factory.SubFactory(
'cookbook.tests.factories.RecipeFactory', space=factory.SelfAttribute('..space')),
no_declaration=None no_declaration=None
) )
servings = factory.LazyAttribute(lambda x: Decimal(faker.random_int(min=1, max=1000)/100)) servings = factory.LazyAttribute(
lambda x: Decimal(faker.random_int(min=1, max=1000) / 100))
title = factory.LazyAttribute(lambda x: faker.sentence(nb_words=5)) title = factory.LazyAttribute(lambda x: faker.sentence(nb_words=5))
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space')) created_by = factory.SubFactory(
meal_type = factory.SubFactory(MealTypeFactory, space=factory.SelfAttribute('..space')) UserFactory, space=factory.SelfAttribute('..space'))
meal_type = factory.SubFactory(
MealTypeFactory, space=factory.SelfAttribute('..space'))
note = factory.LazyAttribute(lambda x: faker.paragraph()) note = factory.LazyAttribute(lambda x: faker.paragraph())
date = factory.LazyAttribute(lambda x: faker.future_date()) date = factory.LazyAttribute(lambda x: faker.future_date())
space = factory.SubFactory(SpaceFactory) space = factory.SubFactory(SpaceFactory)
@ -244,11 +261,13 @@ class ShoppingListRecipeFactory(factory.django.DjangoModelFactory):
name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=5)) name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=5))
recipe = factory.Maybe( recipe = factory.Maybe(
factory.LazyAttribute(lambda x: x.has_recipe), factory.LazyAttribute(lambda x: x.has_recipe),
yes_declaration=factory.SubFactory('cookbook.tests.factories.RecipeFactory', space=factory.SelfAttribute('..space')), yes_declaration=factory.SubFactory(
'cookbook.tests.factories.RecipeFactory', space=factory.SelfAttribute('..space')),
no_declaration=None no_declaration=None
) )
servings = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=10)) servings = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=10))
mealplan = factory.SubFactory(MealPlanFactory, space=factory.SelfAttribute('..space')) mealplan = factory.SubFactory(
MealPlanFactory, space=factory.SelfAttribute('..space'))
space = factory.SubFactory(SpaceFactory) space = factory.SubFactory(SpaceFactory)
class Params: class Params:
@ -264,25 +283,32 @@ class ShoppingListEntryFactory(factory.django.DjangoModelFactory):
list_recipe = factory.Maybe( list_recipe = factory.Maybe(
factory.LazyAttribute(lambda x: x.has_mealplan), factory.LazyAttribute(lambda x: x.has_mealplan),
yes_declaration=factory.SubFactory(ShoppingListRecipeFactory, space=factory.SelfAttribute('..space')), yes_declaration=factory.SubFactory(
ShoppingListRecipeFactory, space=factory.SelfAttribute('..space')),
no_declaration=None no_declaration=None
) )
food = factory.SubFactory(FoodFactory, space=factory.SelfAttribute('..space')) food = factory.SubFactory(
unit = factory.SubFactory(UnitFactory, space=factory.SelfAttribute('..space')) FoodFactory, space=factory.SelfAttribute('..space'))
unit = factory.SubFactory(
UnitFactory, space=factory.SelfAttribute('..space'))
# # ingredient = factory.SubFactory(IngredientFactory) # # ingredient = factory.SubFactory(IngredientFactory)
amount = factory.LazyAttribute(lambda x: Decimal(faker.random_int(min=1, max=100))/10) amount = factory.LazyAttribute(
lambda x: Decimal(faker.random_int(min=1, max=100)) / 10)
order = factory.Sequence(int) order = factory.Sequence(int)
checked = False checked = False
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space')) created_by = factory.SubFactory(
UserFactory, space=factory.SelfAttribute('..space'))
created_at = factory.LazyAttribute(lambda x: faker.past_date()) created_at = factory.LazyAttribute(lambda x: faker.past_date())
completed_at = None completed_at = None
delay_until = None delay_until = None
space = factory.SubFactory(SpaceFactory) space = factory.SubFactory(SpaceFactory)
@classmethod @classmethod
def _create(cls, target_class, *args, **kwargs): # override create to prevent auto_add_now from changing the created_at date # override create to prevent auto_add_now from changing the created_at date
def _create(cls, target_class, *args, **kwargs):
created_at = kwargs.pop('created_at', None) created_at = kwargs.pop('created_at', None)
obj = super(ShoppingListEntryFactory, cls)._create(target_class, *args, **kwargs) obj = super(ShoppingListEntryFactory, cls)._create(
target_class, *args, **kwargs)
if created_at is not None: if created_at is not None:
obj.created_at = created_at obj.created_at = created_at
obj.save() obj.save()
@ -298,7 +324,8 @@ class ShoppingListEntryFactory(factory.django.DjangoModelFactory):
@register @register
class StepFactory(factory.django.DjangoModelFactory): class StepFactory(factory.django.DjangoModelFactory):
name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=5)) name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=5))
instruction = factory.LazyAttribute(lambda x: ''.join(faker.paragraphs(nb=5))) instruction = factory.LazyAttribute(
lambda x: ''.join(faker.paragraphs(nb=5)))
# TODO add optional recipe food, make dependent on recipe, make number of recipes a Params # TODO add optional recipe food, make dependent on recipe, make number of recipes a Params
ingredients__count = 10 # default number of ingredients to add ingredients__count = 10 # default number of ingredients to add
ingredients__header = 0 ingredients__header = 0
@ -330,14 +357,16 @@ class StepFactory(factory.django.DjangoModelFactory):
for i in range(num_ing): for i in range(num_ing):
if num_food_recipe > 0: if num_food_recipe > 0:
has_recipe = True has_recipe = True
num_food_recipe = num_food_recipe-1 num_food_recipe = num_food_recipe - 1
else: else:
has_recipe = False has_recipe = False
self.ingredients.add(IngredientFactory(space=self.space, food__has_recipe=has_recipe)) self.ingredients.add(IngredientFactory(
space=self.space, food__has_recipe=has_recipe))
num_header = kwargs.get('header', 0) num_header = kwargs.get('header', 0)
if num_header > 0: if num_header > 0:
for i in range(num_header): for i in range(num_header):
self.ingredients.add(IngredientFactory(food=None, unit=None, amount=0, is_header=True, space=self.space)) self.ingredients.add(IngredientFactory(
food=None, unit=None, amount=0, is_header=True, space=self.space))
elif extracted: elif extracted:
for ing in extracted: for ing in extracted:
self.ingredients.add(ing) self.ingredients.add(ing)
@ -351,20 +380,27 @@ class RecipeFactory(factory.django.DjangoModelFactory):
name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=7)) name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=7))
description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10)) description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10))
servings = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=20)) servings = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=20))
servings_text = factory.LazyAttribute(lambda x: faker.sentence(nb_words=1)) # TODO generate list of expected servings text that can be iterated through # TODO generate list of expected servings text that can be iterated through
servings_text = factory.LazyAttribute(lambda x: faker.sentence(nb_words=1))
keywords__count = 5 # default number of keywords to generate keywords__count = 5 # default number of keywords to generate
steps__count = 1 # default number of steps to create steps__count = 1 # default number of steps to create
steps__recipe_count = 0 # default number of step recipes to create steps__recipe_count = 0 # default number of step recipes to create
steps__food_recipe_count = {} # by default, don't create food recipes, to override {'steps__food_recipe_count': {'step': 0, 'count': 1}} # by default, don't create food recipes, to override {'steps__food_recipe_count': {'step': 0, 'count': 1}}
working_time = factory.LazyAttribute(lambda x: faker.random_int(min=0, max=360)) steps__food_recipe_count = {}
waiting_time = factory.LazyAttribute(lambda x: faker.random_int(min=0, max=360)) working_time = factory.LazyAttribute(
lambda x: faker.random_int(min=0, max=360))
waiting_time = factory.LazyAttribute(
lambda x: faker.random_int(min=0, max=360))
internal = False internal = False
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space')) created_by = factory.SubFactory(
created_at = factory.LazyAttribute(lambda x: faker.date_between_dates(date_start=date(2000, 1, 1), date_end=date(2020, 12, 31))) UserFactory, space=factory.SelfAttribute('..space'))
created_at = factory.LazyAttribute(lambda x: faker.date_between_dates(
date_start=date(2000, 1, 1), date_end=date(2020, 12, 31)))
space = factory.SubFactory(SpaceFactory) space = factory.SubFactory(SpaceFactory)
@classmethod @classmethod
def _create(cls, target_class, *args, **kwargs): # override create to prevent auto_add_now from changing the created_at date # override create to prevent auto_add_now from changing the created_at date
def _create(cls, target_class, *args, **kwargs):
created_at = kwargs.pop('created_at', None) created_at = kwargs.pop('created_at', None)
# updated_at = kwargs.pop('updated_at', None) # updated_at = kwargs.pop('updated_at', None)
obj = super(RecipeFactory, cls)._create(target_class, *args, **kwargs) obj = super(RecipeFactory, cls)._create(target_class, *args, **kwargs)
@ -401,11 +437,13 @@ class RecipeFactory(factory.django.DjangoModelFactory):
ing_recipe_count = 0 ing_recipe_count = 0
if food_recipe_count.get('step', None) == i: if food_recipe_count.get('step', None) == i:
ing_recipe_count = food_recipe_count.get('count', 0) ing_recipe_count = food_recipe_count.get('count', 0)
self.steps.add(StepFactory(space=self.space, ingredients__food_recipe_count=ing_recipe_count, ingredients__header=num_ing_headers)) self.steps.add(StepFactory(
num_ing_headers+-1 space=self.space, ingredients__food_recipe_count=ing_recipe_count, ingredients__header=num_ing_headers))
num_ing_headers + - 1
if num_recipe_steps > 0: if num_recipe_steps > 0:
for j in range(num_recipe_steps): for j in range(num_recipe_steps):
self.steps.add(StepFactory(space=self.space, step_recipe__has_recipe=True, ingredients__count=0)) self.steps.add(StepFactory(
space=self.space, step_recipe__has_recipe=True, ingredients__count=0))
if extracted and (num_steps + num_recipe_steps == 0): if extracted and (num_steps + num_recipe_steps == 0):
for step in extracted: for step in extracted:
self.steps.add(step) self.steps.add(step)
@ -428,15 +466,18 @@ class RecipeFactory(factory.django.DjangoModelFactory):
@register @register
class CookLogFactory(factory.django.DjangoModelFactory): class CookLogFactory(factory.django.DjangoModelFactory):
"""CookLog factory.""" """CookLog factory."""
recipe = factory.SubFactory(RecipeFactory, space=factory.SelfAttribute('..space')) recipe = factory.SubFactory(
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space')) RecipeFactory, space=factory.SelfAttribute('..space'))
created_by = factory.SubFactory(
UserFactory, space=factory.SelfAttribute('..space'))
created_at = factory.LazyAttribute(lambda x: faker.date_this_decade()) created_at = factory.LazyAttribute(lambda x: faker.date_this_decade())
rating = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=5)) rating = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=5))
servings = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=32)) servings = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=32))
space = factory.SubFactory(SpaceFactory) space = factory.SubFactory(SpaceFactory)
@classmethod @classmethod
def _create(cls, target_class, *args, **kwargs): # override create to prevent auto_add_now from changing the created_at date # override create to prevent auto_add_now from changing the created_at date
def _create(cls, target_class, *args, **kwargs):
created_at = kwargs.pop('created_at', None) created_at = kwargs.pop('created_at', None)
obj = super(CookLogFactory, cls)._create(target_class, *args, **kwargs) obj = super(CookLogFactory, cls)._create(target_class, *args, **kwargs)
if created_at is not None: if created_at is not None:
@ -451,13 +492,17 @@ class CookLogFactory(factory.django.DjangoModelFactory):
@register @register
class ViewLogFactory(factory.django.DjangoModelFactory): class ViewLogFactory(factory.django.DjangoModelFactory):
"""ViewLog factory.""" """ViewLog factory."""
recipe = factory.SubFactory(RecipeFactory, space=factory.SelfAttribute('..space')) recipe = factory.SubFactory(
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space')) RecipeFactory, space=factory.SelfAttribute('..space'))
created_at = factory.LazyAttribute(lambda x: faker.past_datetime(start_date='-365d')) created_by = factory.SubFactory(
UserFactory, space=factory.SelfAttribute('..space'))
created_at = factory.LazyAttribute(
lambda x: faker.past_datetime(start_date='-365d'))
space = factory.SubFactory(SpaceFactory) space = factory.SubFactory(SpaceFactory)
@classmethod @classmethod
def _create(cls, target_class, *args, **kwargs): # override create to prevent auto_add_now from changing the created_at date # override create to prevent auto_add_now from changing the created_at date
def _create(cls, target_class, *args, **kwargs):
created_at = kwargs.pop('created_at', None) created_at = kwargs.pop('created_at', None)
obj = super(ViewLogFactory, cls)._create(target_class, *args, **kwargs) obj = super(ViewLogFactory, cls)._create(target_class, *args, **kwargs)
if created_at is not None: if created_at is not None:

View File

@ -259,33 +259,35 @@ def test_fuzzy_lookup(found_recipe, recipes, param_type, user1, space_1):
# commenting this out for general use - it is really slow # commenting this out for general use - it is really slow
# it should be run on occasion to ensure everything still works # it should be run on occasion to ensure everything still works
# @pytest.mark.skipif(sqlite and True, reason="requires PostgreSQL")
# @pytest.mark.parametrize("user1", itertools.product(
# [
# ('fuzzy_search', True), ('fuzzy_search', False),
# ('fulltext', True), ('fulltext', False),
# ('icontains', True), ('icontains', False),
# ('istartswith', True), ('istartswith', False),
# ],
# [('unaccent', True), ('unaccent', False)]
# ), indirect=['user1'])
# @pytest.mark.parametrize("found_recipe", [
# ({'name': True}),
# ({'description': True}),
# ({'instruction': True}),
# ({'keyword': True}),
# ({'food': True}),
# ], indirect=['found_recipe'])
# def test_search_string(found_recipe, recipes, user1, space_1):
# with scope(space=space_1):
# param1 = f"query={user1[3]}"
# param2 = f"query={user1[4]}"
# r = json.loads(user1[0].get(reverse(LIST_URL) + f'?{param1}').content)
# assert len([x['id'] for x in r['results'] if x['id'] in [found_recipe[0].id, found_recipe[1].id]]) == user1[1]
# r = json.loads(user1[0].get(reverse(LIST_URL) + f'?{param2}').content) @pytest.mark.skipif(sqlite and True, reason="requires PostgreSQL")
# assert len([x['id'] for x in r['results'] if x['id'] in [found_recipe[0].id, found_recipe[1].id]]) == user1[2] @pytest.mark.parametrize("user1", itertools.product(
[
('fuzzy_search', True), ('fuzzy_search', False),
('fulltext', True), ('fulltext', False),
('icontains', True), ('icontains', False),
('istartswith', True), ('istartswith', False),
],
[('unaccent', True), ('unaccent', False)]
), indirect=['user1'])
@pytest.mark.parametrize("found_recipe", [
({'name': True}),
({'description': True}),
({'instruction': True}),
({'keyword': True}),
({'food': True}),
], indirect=['found_recipe'])
def test_search_string(found_recipe, recipes, user1, space_1):
with scope(space=space_1):
param1 = f"query={user1[3]}"
param2 = f"query={user1[4]}"
r = json.loads(user1[0].get(reverse(LIST_URL) + f'?{param1}').content)
assert len([x['id'] for x in r['results'] if x['id'] in [found_recipe[0].id, found_recipe[1].id]]) == user1[1]
r = json.loads(user1[0].get(reverse(LIST_URL) + f'?{param2}').content)
assert len([x['id'] for x in r['results'] if x['id'] in [found_recipe[0].id, found_recipe[1].id]]) == user1[2]
@pytest.mark.parametrize("found_recipe, param_type, result", [ @pytest.mark.parametrize("found_recipe, param_type, result", [
@ -322,33 +324,33 @@ def test_search_date(found_recipe, recipes, param_type, result, u1_s1, u2_s1, sp
# TODO this is somehow screwed, probably the search itself, dont want to fix it for now # TODO this is somehow screwed, probably the search itself, dont want to fix it for now
# @pytest.mark.parametrize("found_recipe, param_type", [ @pytest.mark.parametrize("found_recipe, param_type", [
# ({'rating': True}, 'rating'), ({'rating': True}, 'rating'),
# ({'timescooked': True}, 'timescooked'), ({'timescooked': True}, 'timescooked'),
# ], indirect=['found_recipe']) ], indirect=['found_recipe'])
# def test_search_count(found_recipe, recipes, param_type, u1_s1, u2_s1, space_1): def test_search_count(found_recipe, recipes, param_type, u1_s1, u2_s1, space_1):
# param1 = f'?{param_type}=3' param1 = f'?{param_type}=3'
# param2 = f'?{param_type}=-3' param2 = f'?{param_type}=-3'
# param3 = f'?{param_type}=0' param3 = f'?{param_type}=0'
#
# r = json.loads(u1_s1.get(reverse(LIST_URL) + param1).content) r = json.loads(u1_s1.get(reverse(LIST_URL) + param1).content)
# assert r['count'] == 1 assert r['count'] == 1
# assert found_recipe[0].id in [x['id'] for x in r['results']] assert found_recipe[0].id in [x['id'] for x in r['results']]
#
# r = json.loads(u1_s1.get(reverse(LIST_URL) + param2).content) r = json.loads(u1_s1.get(reverse(LIST_URL) + param2).content)
# assert r['count'] == 1 assert r['count'] == 1
# assert found_recipe[1].id in [x['id'] for x in r['results']] assert found_recipe[1].id in [x['id'] for x in r['results']]
#
# # test search for not rated/cooked # test search for not rated/cooked
# r = json.loads(u1_s1.get(reverse(LIST_URL) + param3).content) r = json.loads(u1_s1.get(reverse(LIST_URL) + param3).content)
# assert r['count'] == 11 assert r['count'] == 11
# assert (found_recipe[0].id or found_recipe[1].id) not in [x['id'] for x in r['results']] assert (found_recipe[0].id or found_recipe[1].id) not in [x['id'] for x in r['results']]
#
# # test matched returns for lte and gte searches # test matched returns for lte and gte searches
# r = json.loads(u2_s1.get(reverse(LIST_URL) + param1).content) r = json.loads(u2_s1.get(reverse(LIST_URL) + param1).content)
# assert r['count'] == 1 assert r['count'] == 1
# assert found_recipe[2].id in [x['id'] for x in r['results']] assert found_recipe[2].id in [x['id'] for x in r['results']]
#
# r = json.loads(u2_s1.get(reverse(LIST_URL) + param2).content) r = json.loads(u2_s1.get(reverse(LIST_URL) + param2).content)
# assert r['count'] == 1 assert r['count'] == 1
# assert found_recipe[2].id in [x['id'] for x in r['results']] assert found_recipe[2].id in [x['id'] for x in r['results']]

View File

@ -30,11 +30,11 @@ Jinja2==3.1.2
django-webpack-loader==1.8.1 django-webpack-loader==1.8.1
git+https://github.com/BITSOLVER/django-js-reverse@071e304fd600107bc64bbde6f2491f1fe049ec82 git+https://github.com/BITSOLVER/django-js-reverse@071e304fd600107bc64bbde6f2491f1fe049ec82
django-allauth==0.52.0 django-allauth==0.52.0
recipe-scrapers==14.35.0 recipe-scrapers==14.36.1
django-scopes==1.2.0.post1 django-scopes==1.2.0.post1
pytest==7.2.2 pytest==7.3.1
pytest-django==4.5.2 pytest-django==4.5.2
django-treebeard==4.5.1 django-treebeard==4.7
django-cors-headers==3.13.0 django-cors-headers==3.13.0
django-storages==1.13.2 django-storages==1.13.2
boto3==1.26.41 boto3==1.26.41
@ -42,7 +42,7 @@ django-prometheus==2.2.0
django-hCaptcha==0.2.0 django-hCaptcha==0.2.0
python-ldap==3.4.3 python-ldap==3.4.3
django-auth-ldap==4.2.0 django-auth-ldap==4.2.0
pytest-factoryboy==2.5.0 pytest-factoryboy==2.5.1
pyppeteer==1.0.2 pyppeteer==1.0.2
validators==0.20.0 validators==0.20.0
pytube==12.1.0 pytube==12.1.0

View File

@ -12,18 +12,17 @@
<i class="far fa-check-circle text-primary" v-if="!ingredient.checked"></i> <i class="far fa-check-circle text-primary" v-if="!ingredient.checked"></i>
</td> </td>
<td class="text-nowrap" @click="done"> <td class="text-nowrap" @click="done">
<span v-if="ingredient.amount !== 0 && !ingredient.no_amount" <span v-if="ingredient.amount !== 0 && !ingredient.no_amount" v-html="calculateAmount(ingredient.amount)"></span>
v-html="calculateAmount(ingredient.amount)"></span>
</td> </td>
<td @click="done"> <td @click="done">
<template v-if="ingredient.unit !== null && !ingredient.no_amount"> <template v-if="ingredient.unit !== null && !ingredient.no_amount">
<template > <template>
<template v-if="ingredient.unit.plural_name === '' || ingredient.unit.plural_name === null"> <template v-if="ingredient.unit.plural_name === '' || ingredient.unit.plural_name === null">
<span>{{ ingredient.unit.name }}</span> <span>{{ ingredient.unit.name }}</span>
</template> </template>
<template v-else> <template v-else>
<span v-if="ingredient.always_use_plural_unit">{{ ingredient.unit.plural_name}}</span> <span v-if="ingredient.always_use_plural_unit">{{ ingredient.unit.plural_name }}</span>
<span v-else-if="(ingredient.amount * this.ingredient_factor) > 1">{{ ingredient.unit.plural_name }}</span> <span v-else-if="ingredient.amount * this.ingredient_factor > 1">{{ ingredient.unit.plural_name }}</span>
<span v-else>{{ ingredient.unit.name }}</span> <span v-else>{{ ingredient.unit.name }}</span>
</template> </template>
</template> </template>
@ -31,11 +30,10 @@
</td> </td>
<td @click="done"> <td @click="done">
<template v-if="ingredient.food !== null"> <template v-if="ingredient.food !== null">
<a :href="resolveDjangoUrl('view_recipe', ingredient.food.recipe.id)" <a :href="resolveDjangoUrl('view_recipe', ingredient.food.recipe.id)" v-if="ingredient.food.recipe !== null" target="_blank" rel="noopener noreferrer">{{
v-if="ingredient.food.recipe !== null" target="_blank" ingredient.food.name
rel="noopener noreferrer">{{ ingredient.food.name }}</a> }}</a>
<template v-if="ingredient.food.recipe === null"> <template v-if="ingredient.food.recipe === null">
<template> <template>
<template v-if="ingredient.food.plural_name === '' || ingredient.food.plural_name === null"> <template v-if="ingredient.food.plural_name === '' || ingredient.food.plural_name === null">
<span>{{ ingredient.food.name }}</span> <span>{{ ingredient.food.name }}</span>
@ -43,7 +41,7 @@
<template v-else> <template v-else>
<span v-if="ingredient.always_use_plural_food">{{ ingredient.food.plural_name }}</span> <span v-if="ingredient.always_use_plural_food">{{ ingredient.food.plural_name }}</span>
<span v-else-if="ingredient.no_amount">{{ ingredient.food.name }}</span> <span v-else-if="ingredient.no_amount">{{ ingredient.food.name }}</span>
<span v-else-if="(ingredient.amount * this.ingredient_factor) > 1">{{ ingredient.food.plural_name }}</span> <span v-else-if="ingredient.amount * this.ingredient_factor > 1">{{ ingredient.food.plural_name }}</span>
<span v-else>{{ ingredient.food.name }}</span> <span v-else>{{ ingredient.food.name }}</span>
</template> </template>
</template> </template>
@ -56,10 +54,7 @@
<i class="far fa-comment"></i> <i class="far fa-comment"></i>
</span> </span>
<div class="d-none d-print-block"><i class="far fa-comment-alt d-print-none"></i> {{ <div class="d-none d-print-block"><i class="far fa-comment-alt d-print-none"></i> {{ ingredient.note }}</div>
ingredient.note
}}
</div>
</div> </div>
</td> </td>
</template> </template>
@ -67,19 +62,19 @@
</template> </template>
<script> <script>
import {calculateAmount, ResolveUrlMixin} from "@/utils/utils" import { calculateAmount, ResolveUrlMixin } from "@/utils/utils"
import Vue from "vue" import Vue from "vue"
import VueSanitize from "vue-sanitize"; import VueSanitize from "vue-sanitize"
Vue.use(VueSanitize); Vue.use(VueSanitize)
export default { export default {
name: "IngredientComponent", name: "IngredientComponent",
props: { props: {
ingredient: Object, ingredient: Object,
ingredient_factor: {type: Number, default: 1}, ingredient_factor: { type: Number, default: 1 },
detailed: {type: Boolean, default: true}, detailed: { type: Boolean, default: true },
}, },
mixins: [ResolveUrlMixin], mixins: [ResolveUrlMixin],
data() { data() {
@ -88,9 +83,7 @@ export default {
} }
}, },
watch: {}, watch: {},
mounted() { mounted() {},
},
methods: { methods: {
calculateAmount: function (x) { calculateAmount: function (x) {
return this.$sanitize(calculateAmount(x, this.ingredient_factor)) return this.$sanitize(calculateAmount(x, this.ingredient_factor))
@ -107,8 +100,8 @@ export default {
/* increase size of hover/touchable space without changing spacing */ /* increase size of hover/touchable space without changing spacing */
.touchable { .touchable {
padding-right: 2em; padding-right: 2em;
padding-left: 2em; padding-left: 3em;
margin-right: -2em; margin-right: -3em;
margin-left: -2em; margin-left: -2em;
} }
</style> </style>