test shoppingFood API
This commit is contained in:
parent
1364f75f21
commit
ee4ab41c1c
@ -104,18 +104,18 @@ def list_from_recipe(list_recipe=None, recipe=None, mealplan=None, servings=None
|
|||||||
x_ing = Ingredient.objects.filter(step__recipe=x, food__on_hand=False, space=space)
|
x_ing = Ingredient.objects.filter(step__recipe=x, food__on_hand=False, space=space)
|
||||||
else:
|
else:
|
||||||
x_ing = Ingredient.objects.filter(step__recipe=x, space=space)
|
x_ing = Ingredient.objects.filter(step__recipe=x, space=space)
|
||||||
for i in [x for x in x_ing if not x.food.ignore_shopping]:
|
for i in [x for x in x_ing if not x.food.ignore_shopping]:
|
||||||
ShoppingListEntry.objects.create(
|
ShoppingListEntry.objects.create(
|
||||||
list_recipe=list_recipe,
|
list_recipe=list_recipe,
|
||||||
food=i.food,
|
food=i.food,
|
||||||
unit=i.unit,
|
unit=i.unit,
|
||||||
ingredient=i,
|
ingredient=i,
|
||||||
amount=i.amount * Decimal(servings_factor),
|
amount=i.amount * Decimal(servings_factor),
|
||||||
created_by=created_by,
|
created_by=created_by,
|
||||||
space=space,
|
space=space,
|
||||||
)
|
)
|
||||||
# dont' add food to the shopping list that are actually recipes that will be added as ingredients
|
# dont' add food to the shopping list that are actually recipes that will be added as ingredients
|
||||||
ingredients = ingredients.exclude(food__recipe=x)
|
ingredients = ingredients.exclude(food__recipe=x)
|
||||||
|
|
||||||
add_ingredients = list(ingredients.values_list('id', flat=True)) + related_step_ing
|
add_ingredients = list(ingredients.values_list('id', flat=True)) + related_step_ing
|
||||||
if not append:
|
if not append:
|
||||||
|
92
cookbook/tests/api/test_api_food_shopping.py
Normal file
92
cookbook/tests/api/test_api_food_shopping.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
# test create
|
||||||
|
# test create units
|
||||||
|
# test amounts
|
||||||
|
# test create wrong space
|
||||||
|
# test sharing
|
||||||
|
# test delete
|
||||||
|
# test delete checked (nothing should happen)
|
||||||
|
# test delete not shared (nothing happens)
|
||||||
|
# test delete shared
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from django.contrib import auth
|
||||||
|
from django.urls import reverse
|
||||||
|
from django_scopes import scope, scopes_disabled
|
||||||
|
|
||||||
|
from cookbook.models import Food, ShoppingListEntry
|
||||||
|
from cookbook.tests.factories import FoodFactory
|
||||||
|
|
||||||
|
SHOPPING_LIST_URL = 'api:shoppinglistentry-list'
|
||||||
|
SHOPPING_FOOD_URL = 'api:food-shopping'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def food(request, space_1, u1_s1):
|
||||||
|
return FoodFactory(space=space_1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_shopping_forbidden_methods(food, u1_s1):
|
||||||
|
r = u1_s1.post(
|
||||||
|
reverse(SHOPPING_FOOD_URL, args={food.id}))
|
||||||
|
assert r.status_code == 405
|
||||||
|
|
||||||
|
r = u1_s1.delete(
|
||||||
|
reverse(SHOPPING_FOOD_URL, args={food.id}))
|
||||||
|
assert r.status_code == 405
|
||||||
|
|
||||||
|
r = u1_s1.get(
|
||||||
|
reverse(SHOPPING_FOOD_URL, args={food.id}))
|
||||||
|
assert r.status_code == 405
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("arg", [
|
||||||
|
['a_u', 403],
|
||||||
|
['g1_s1', 403],
|
||||||
|
['u1_s1', 204],
|
||||||
|
['u1_s2', 404],
|
||||||
|
['a1_s1', 204],
|
||||||
|
])
|
||||||
|
def test_shopping_food_create(request, arg, food):
|
||||||
|
c = request.getfixturevalue(arg[0])
|
||||||
|
r = c.put(reverse(SHOPPING_FOOD_URL, args={food.id}))
|
||||||
|
assert r.status_code == arg[1]
|
||||||
|
if r.status_code == 204:
|
||||||
|
assert len(json.loads(c.get(reverse(SHOPPING_LIST_URL)).content)) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("arg", [
|
||||||
|
['a_u', 403],
|
||||||
|
['g1_s1', 403],
|
||||||
|
['u1_s1', 204],
|
||||||
|
['u1_s2', 404],
|
||||||
|
['a1_s1', 204],
|
||||||
|
])
|
||||||
|
def test_shopping_food_delete(request, arg, food):
|
||||||
|
c = request.getfixturevalue(arg[0])
|
||||||
|
r = c.put(
|
||||||
|
reverse(SHOPPING_FOOD_URL, args={food.id}),
|
||||||
|
{'_delete': "true"},
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
assert r.status_code == arg[1]
|
||||||
|
if r.status_code == 204:
|
||||||
|
assert len(json.loads(c.get(reverse(SHOPPING_LIST_URL)).content)) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_shopping_food_share(u1_s1, u2_s1, food, space_1):
|
||||||
|
with scope(space=space_1):
|
||||||
|
food2 = FoodFactory(space=space_1)
|
||||||
|
r = u1_s1.put(reverse(SHOPPING_FOOD_URL, args={food.id}))
|
||||||
|
r = u2_s1.put(reverse(SHOPPING_FOOD_URL, args={food.id}))
|
||||||
|
assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 1
|
||||||
|
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 1
|
||||||
|
|
||||||
|
with scopes_disabled():
|
||||||
|
user = auth.get_user(u1_s1)
|
||||||
|
user2 = auth.get_user(u2_s1)
|
||||||
|
user.userpreference.shopping_share.add(user2)
|
||||||
|
user.userpreference.save()
|
||||||
|
assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 1
|
||||||
|
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 2
|
@ -10,7 +10,7 @@ from django.utils import timezone
|
|||||||
from django_scopes import scopes_disabled
|
from django_scopes import scopes_disabled
|
||||||
from pytest_factoryboy import LazyFixture, register
|
from pytest_factoryboy import LazyFixture, register
|
||||||
|
|
||||||
from cookbook.models import Food, Ingredient, ShoppingListEntry
|
from cookbook.models import Food, Ingredient, ShoppingListEntry, Step
|
||||||
from cookbook.tests.factories import MealPlanFactory, RecipeFactory, StepFactory, UserFactory
|
from cookbook.tests.factories import MealPlanFactory, RecipeFactory, StepFactory, UserFactory
|
||||||
|
|
||||||
SHOPPING_LIST_URL = 'api:shoppinglistentry-list'
|
SHOPPING_LIST_URL = 'api:shoppinglistentry-list'
|
||||||
@ -176,8 +176,9 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
|
|||||||
|
|
||||||
@pytest.mark.parametrize("user2, sle_count", [
|
@pytest.mark.parametrize("user2, sle_count", [
|
||||||
({'mealplan_autoadd_shopping': False}, (0, 17)),
|
({'mealplan_autoadd_shopping': False}, (0, 17)),
|
||||||
({'mealplan_autoinclude_related': False}, (7, 7)),
|
({'mealplan_autoinclude_related': False}, (8, 8)),
|
||||||
({'mealplan_autoexclude_onhand': False}, (19, 19)),
|
({'mealplan_autoexclude_onhand': False}, (19, 19)),
|
||||||
|
({'mealplan_autoexclude_onhand': False, 'mealplan_autoinclude_related': False}, (9, 9)),
|
||||||
], 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'])
|
||||||
@ -186,13 +187,14 @@ def test_shopping_recipe_userpreference(recipe, sle_count, use_mealplan, user2):
|
|||||||
user = auth.get_user(user2)
|
user = auth.get_user(user2)
|
||||||
# setup recipe with 10 ingredients, 1 step recipe with 10 ingredients, 2 food onhand(from recipe and step_recipe), 1 food ignore shopping
|
# setup recipe with 10 ingredients, 1 step recipe with 10 ingredients, 2 food onhand(from recipe and step_recipe), 1 food ignore shopping
|
||||||
ingredients = Ingredient.objects.filter(step__recipe=recipe)
|
ingredients = Ingredient.objects.filter(step__recipe=recipe)
|
||||||
food = Food.objects.get(id=ingredients[0].food.id)
|
|
||||||
food.on_hand = True
|
|
||||||
food.save()
|
|
||||||
food = Step.objects.filter(type=Step.RECIPE).first().ingredients.first()
|
|
||||||
food.on_hand = True
|
|
||||||
food.save()
|
|
||||||
food = Food.objects.get(id=ingredients[2].food.id)
|
food = Food.objects.get(id=ingredients[2].food.id)
|
||||||
|
food.on_hand = True
|
||||||
|
food.save()
|
||||||
|
food = recipe.steps.filter(type=Step.RECIPE).first().step_recipe.steps.first().ingredients.first().food
|
||||||
|
food = Food.objects.get(id=food.id)
|
||||||
|
food.on_hand = True
|
||||||
|
food.save()
|
||||||
|
food = Food.objects.get(id=ingredients[4].food.id)
|
||||||
food.ignore_shopping = True
|
food.ignore_shopping = True
|
||||||
food.save()
|
food.save()
|
||||||
|
|
||||||
|
@ -413,7 +413,10 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
|
|||||||
pagination_class = DefaultPagination
|
pagination_class = DefaultPagination
|
||||||
|
|
||||||
@decorators.action(detail=True, methods=['PUT'], serializer_class=FoodShoppingUpdateSerializer,)
|
@decorators.action(detail=True, methods=['PUT'], serializer_class=FoodShoppingUpdateSerializer,)
|
||||||
|
# TODO DRF only allows one action in a decorator action without overriding get_operation_id_base() this should be PUT and DELETE probably
|
||||||
def shopping(self, request, pk):
|
def shopping(self, request, pk):
|
||||||
|
if self.request.space.demo:
|
||||||
|
raise PermissionDenied(detail='Not available in demo', code=None)
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
shared_users = list(self.request.user.get_shopping_share())
|
shared_users = list(self.request.user.get_shopping_share())
|
||||||
shared_users.append(request.user)
|
shared_users.append(request.user)
|
||||||
@ -652,12 +655,16 @@ class RecipeViewSet(viewsets.ModelViewSet):
|
|||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
return Response(serializer.errors, 400)
|
return Response(serializer.errors, 400)
|
||||||
|
|
||||||
|
# TODO: refactor API to use post/put/delete or leave as put and change VUE to use list_recipe after creating
|
||||||
|
# DRF only allows one action in a decorator action without overriding get_operation_id_base()
|
||||||
@decorators.action(
|
@decorators.action(
|
||||||
detail=True,
|
detail=True,
|
||||||
methods=['PUT'],
|
methods=['PUT'],
|
||||||
serializer_class=RecipeShoppingUpdateSerializer,
|
serializer_class=RecipeShoppingUpdateSerializer,
|
||||||
)
|
)
|
||||||
def shopping(self, request, pk):
|
def shopping(self, request, pk):
|
||||||
|
if self.request.space.demo:
|
||||||
|
raise PermissionDenied(detail='Not available in demo', code=None)
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
ingredients = request.data.get('ingredients', None)
|
ingredients = request.data.get('ingredients', None)
|
||||||
servings = request.data.get('servings', None)
|
servings = request.data.get('servings', None)
|
||||||
|
@ -254,13 +254,13 @@ def latest_shopping_list(request):
|
|||||||
|
|
||||||
|
|
||||||
@group_required('user')
|
@group_required('user')
|
||||||
def shopping_list(request, pk=None):
|
def shopping_list(request, pk=None): # TODO deprecate
|
||||||
html_list = request.GET.getlist('r')
|
html_list = request.GET.getlist('r')
|
||||||
|
|
||||||
recipes = []
|
recipes = []
|
||||||
for r in html_list:
|
for r in html_list:
|
||||||
r = r.replace('[', '').replace(']', '')
|
r = r.replace('[', '').replace(']', '')
|
||||||
if re.match(r'^([0-9])+,([0-9])+[.]*([0-9])*$', r):
|
if re.match(r'^([0-9])+,([0-9])+[.]*([0-9])*$', r): # vulnerable to DoS
|
||||||
rid, multiplier = r.split(',')
|
rid, multiplier = r.split(',')
|
||||||
if recipe := Recipe.objects.filter(pk=int(rid), space=request.space).first():
|
if recipe := Recipe.objects.filter(pk=int(rid), space=request.space).first():
|
||||||
recipes.append({'recipe': recipe.id, 'multiplier': multiplier})
|
recipes.append({'recipe': recipe.id, 'multiplier': multiplier})
|
||||||
|
Loading…
Reference in New Issue
Block a user