updated tests to reflect API changes in pagination

This commit is contained in:
smilerz
2024-04-19 07:43:29 -05:00
parent 71765f3542
commit d3abe4db3e
7 changed files with 144 additions and 87 deletions

View File

@@ -3,5 +3,12 @@
"cookbook/tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
"python.testing.pytestEnabled": true,
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[python]": {
"editor.defaultFormatter": "eeyore.yapf",
},
"yapf.args": [],
"isort.args": []
}

View File

@@ -168,7 +168,6 @@ class CustomOnHandField(serializers.Field):
class SpaceFilterSerializer(serializers.ListSerializer):
def to_representation(self, data):
if self.context.get('request', None) is None:
return

View File

@@ -37,12 +37,12 @@ def test_shopping_forbidden_methods(food, u1_s1):
['u1_s2', 404],
['a1_s1', 204],
])
def test_shopping_food_create(request, arg, food):
def test_shopping_food_create(request, arg, food, ids=lambda arg: arg[0]):
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
assert len(json.loads(c.get(reverse(SHOPPING_LIST_URL)).content)['results']) == 1
@pytest.mark.parametrize("arg", [
@@ -52,7 +52,7 @@ def test_shopping_food_create(request, arg, food):
['u1_s2', 404],
['a1_s1', 204],
])
def test_shopping_food_delete(request, arg, food):
def test_shopping_food_delete(request, arg, food, ids=lambda arg: arg[0]):
c = request.getfixturevalue(arg[0])
r = c.put(
reverse(SHOPPING_FOOD_URL, args={food.id}),
@@ -61,7 +61,7 @@ def test_shopping_food_delete(request, arg, food):
)
assert r.status_code == arg[1]
if r.status_code == 204:
assert len(json.loads(c.get(reverse(SHOPPING_LIST_URL)).content)) == 0
assert len(json.loads(c.get(reverse(SHOPPING_LIST_URL)).content)['results']) == 0
def test_shopping_food_share(u1_s1, u2_s1, food, space_1):
@@ -71,8 +71,8 @@ def test_shopping_food_share(u1_s1, u2_s1, food, space_1):
food2 = FoodFactory(space=space_1)
u1_s1.put(reverse(SHOPPING_FOOD_URL, args={food.id}))
u2_s1.put(reverse(SHOPPING_FOOD_URL, args={food2.id}))
sl_1 = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
sl_2 = json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)
sl_1 = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)['results']
sl_2 = json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)['results']
assert len(sl_1) == 1
assert len(sl_2) == 1
sl_1[0]['created_by']['id'] == user1.id
@@ -81,5 +81,5 @@ def test_shopping_food_share(u1_s1, u2_s1, food, space_1):
with scopes_disabled():
user1.userpreference.shopping_share.add(user2)
user1.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
assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)['results']) == 1
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)['results']) == 2

View File

@@ -18,7 +18,7 @@ DETAIL_URL = 'api:invitelink-detail'
['a1_s1', 200, 1],
['a2_s1', 403, 0],
])
def test_list_permission(arg, request, space_1, g1_s1, u1_s1, a1_s1):
def test_list_permission(arg, request, space_1, g1_s1, u1_s1, a1_s1, ids=lambda arg: arg[0]):
space_1.created_by = auth.get_user(a1_s1)
space_1.save()
InviteLink.objects.create(group_id=1, created_by=auth.get_user(a1_s1), space=space_1)
@@ -27,7 +27,7 @@ def test_list_permission(arg, request, space_1, g1_s1, u1_s1, a1_s1):
result = c.get(reverse(LIST_URL))
assert result.status_code == arg[1]
if arg[1] == 200:
assert len(json.loads(result.content)) == arg[2]
assert len(json.loads(result.content)['results']) == arg[2]
@pytest.mark.parametrize("arg", [
@@ -39,7 +39,7 @@ def test_list_permission(arg, request, space_1, g1_s1, u1_s1, a1_s1):
['u1_s2', 403],
['a1_s2', 403],
])
def test_update(arg, request, space_1, u1_s1, a1_s1):
def test_update(arg, request, space_1, u1_s1, a1_s1, ids=lambda arg: arg[0]):
with scopes_disabled():
space_1.created_by = auth.get_user(a1_s1)
space_1.save()

View File

@@ -14,29 +14,44 @@ LIST_URL = 'api:mealplan-list'
DETAIL_URL = 'api:mealplan-detail'
ICAL_URL = 'api:mealplan-ical'
# NOTE: auto adding shopping list from meal plan is tested in test_shopping_recipe as tests are identical
@pytest.fixture()
def meal_type(space_1, u1_s1):
return MealType.objects.get_or_create(name='test', space=space_1, created_by=auth.get_user(u1_s1))[0]
return MealType.objects.get_or_create(name='test',
space=space_1,
created_by=auth.get_user(u1_s1))[0]
@pytest.fixture()
def obj_1(space_1, recipe_1_s1, meal_type, u1_s1):
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now(), to_date=datetime.now(),
return MealPlan.objects.create(recipe=recipe_1_s1,
space=space_1,
meal_type=meal_type,
from_date=datetime.now(),
to_date=datetime.now(),
created_by=auth.get_user(u1_s1))
@pytest.fixture
def obj_2(space_1, recipe_1_s1, meal_type, u1_s1):
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now(), to_date=datetime.now(),
return MealPlan.objects.create(recipe=recipe_1_s1,
space=space_1,
meal_type=meal_type,
from_date=datetime.now(),
to_date=datetime.now(),
created_by=auth.get_user(u1_s1))
@pytest.fixture
def obj_3(space_1, recipe_1_s1, meal_type, u1_s1):
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now() - timedelta(days=30), to_date=datetime.now() - timedelta(days=1),
return MealPlan.objects.create(recipe=recipe_1_s1,
space=space_1,
meal_type=meal_type,
from_date=datetime.now() -
timedelta(days=30),
to_date=datetime.now() - timedelta(days=1),
created_by=auth.get_user(u1_s1))
@@ -52,38 +67,52 @@ def test_list_permission(arg, request):
def test_list_space(obj_1, obj_2, u1_s1, u1_s2, space_2):
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 2
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
assert len(json.loads(u1_s1.get(
reverse(LIST_URL)).content)['results']) == 2
assert len(json.loads(u1_s2.get(
reverse(LIST_URL)).content)['results']) == 0
obj_1.space = space_2
obj_1.save()
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 1
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
assert len(json.loads(u1_s1.get(
reverse(LIST_URL)).content)['results']) == 1
assert len(json.loads(u1_s2.get(
reverse(LIST_URL)).content)['results']) == 0
def test_list_filter(obj_1, u1_s1):
r = u1_s1.get(reverse(LIST_URL))
assert r.status_code == 200
response = json.loads(r.content)
response = json.loads(r.content)['results']
assert len(response) == 1
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?meal_type={response[0]["meal_type"]["id"]}').content)
response = json.loads(
u1_s1.get(
f'{reverse(LIST_URL)}?meal_type={response[0]["meal_type"]["id"]}').
content)['results']
assert len(response) == 1
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?meal_type=0').content)
response = json.loads(
u1_s1.get(f'{reverse(LIST_URL)}?meal_type=0').content)['results']
assert len(response) == 0
response = json.loads(
u1_s1.get(f'{reverse(LIST_URL)}?from_date={(datetime.now() + timedelta(days=2)).strftime("%Y-%m-%d")}').content)
u1_s1.get(
f'{reverse(LIST_URL)}?from_date={(datetime.now() + timedelta(days=2)).strftime("%Y-%m-%d")}'
).content)['results']
assert len(response) == 0
response = json.loads(
u1_s1.get(f'{reverse(LIST_URL)}?to_date={(datetime.now() - timedelta(days=2)).strftime("%Y-%m-%d")}').content)
u1_s1.get(
f'{reverse(LIST_URL)}?to_date={(datetime.now() - timedelta(days=2)).strftime("%Y-%m-%d")}'
).content)['results']
assert len(response) == 0
response = json.loads(u1_s1.get(
f'{reverse(LIST_URL)}?from_date={(datetime.now() - timedelta(days=2)).strftime("%Y-%m-%d")}&to_date={(datetime.now() + timedelta(days=2)).strftime("%Y-%m-%d")}').content)
response = json.loads(
u1_s1.get(
f'{reverse(LIST_URL)}?from_date={(datetime.now() - timedelta(days=2)).strftime("%Y-%m-%d")}&to_date={(datetime.now() + timedelta(days=2)).strftime("%Y-%m-%d")}'
).content)['results']
assert len(response) == 1
@@ -98,14 +127,8 @@ def test_list_filter(obj_1, u1_s1):
])
def test_update(arg, request, obj_1):
c = request.getfixturevalue(arg[0])
r = c.patch(
reverse(
DETAIL_URL,
args={obj_1.id}
),
{'title': 'new'},
content_type='application/json'
)
r = c.patch(reverse(DETAIL_URL, args={obj_1.id}), {'title': 'new'},
content_type='application/json')
response = json.loads(r.content)
assert r.status_code == arg[1]
if r.status_code == 200:
@@ -120,12 +143,23 @@ def test_update(arg, request, obj_1):
])
def test_add(arg, request, u1_s2, recipe_1_s1, meal_type):
c = request.getfixturevalue(arg[0])
r = c.post(
reverse(LIST_URL),
{'recipe': {'id': recipe_1_s1.id, 'name': recipe_1_s1.name, 'keywords': []}, 'meal_type': {'id': meal_type.id, 'name': meal_type.name},
'from_date': (datetime.now()).strftime("%Y-%m-%d"), 'to_date': (datetime.now()).strftime("%Y-%m-%d"), 'servings': 1, 'title': 'test', 'shared': []},
content_type='application/json'
)
r = c.post(reverse(LIST_URL), {
'recipe': {
'id': recipe_1_s1.id,
'name': recipe_1_s1.name,
'keywords': []
},
'meal_type': {
'id': meal_type.id,
'name': meal_type.name
},
'from_date': (datetime.now()).strftime("%Y-%m-%d"),
'to_date': (datetime.now()).strftime("%Y-%m-%d"),
'servings': 1,
'title': 'test',
'shared': []
},
content_type='application/json')
response = json.loads(r.content)
print(response)
assert r.status_code == arg[1]
@@ -138,20 +172,10 @@ def test_add(arg, request, u1_s2, recipe_1_s1, meal_type):
def test_delete(u1_s1, u1_s2, obj_1):
r = u1_s2.delete(
reverse(
DETAIL_URL,
args={obj_1.id}
)
)
r = u1_s2.delete(reverse(DETAIL_URL, args={obj_1.id}))
assert r.status_code == 404
r = u1_s1.delete(
reverse(
DETAIL_URL,
args={obj_1.id}
)
)
r = u1_s1.delete(reverse(DETAIL_URL, args={obj_1.id}))
assert r.status_code == 204
with scopes_disabled():
@@ -162,21 +186,41 @@ def test_add_with_shopping(u1_s1, meal_type):
space = meal_type.space
with scope(space=space):
recipe = RecipeFactory.create(space=space)
u1_s1.post(
reverse(LIST_URL),
{'recipe': {'id': recipe.id, 'name': recipe.name, 'keywords': []}, 'meal_type': {'id': meal_type.id, 'name': meal_type.name},
'from_date': (datetime.now()).strftime("%Y-%m-%d"), 'to_date': (datetime.now()).strftime("%Y-%m-%d"), 'servings': 1, 'title': 'test', 'shared': [], 'addshopping': True},
content_type='application/json'
)
u1_s1.post(reverse(LIST_URL), {
'recipe': {
'id': recipe.id,
'name': recipe.name,
'keywords': []
},
'meal_type': {
'id': meal_type.id,
'name': meal_type.name
},
'from_date': (datetime.now()).strftime("%Y-%m-%d"),
'to_date': (datetime.now()).strftime("%Y-%m-%d"),
'servings': 1,
'title': 'test',
'shared': [],
'addshopping': True
},
content_type='application/json')
assert len(json.loads(u1_s1.get(reverse('api:shoppinglistentry-list')).content)) == 10
assert len(
json.loads(u1_s1.get(
reverse('api:shoppinglistentry-list')).content)['results']) == 10
@pytest.mark.parametrize("arg", [
[f'', 2],
[f'?from_date={datetime.now().strftime("%Y-%m-%d")}', 1],
[f'?to_date={(datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")}', 1],
[f'?from_date={(datetime.now() + timedelta(days=2)).strftime("%Y-%m-%d")}&to_date={(datetime.now() + timedelta(days=2)).strftime("%Y-%m-%d")}', 0],
[
f'?to_date={(datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")}',
1
],
[
f'?from_date={(datetime.now() + timedelta(days=2)).strftime("%Y-%m-%d")}&to_date={(datetime.now() + timedelta(days=2)).strftime("%Y-%m-%d")}',
0
],
])
def test_ical(arg, request, obj_1, obj_3, u1_s1):
r = u1_s1.get(f'{reverse(ICAL_URL)}{arg[0]}')

View File

@@ -33,14 +33,14 @@ def test_list_permission(arg, request):
def test_list_space(obj_1, obj_2, u1_s1, u1_s2, space_2):
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 2
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
assert json.loads(u1_s1.get(reverse(LIST_URL)).content)['count'] == 2
assert json.loads(u1_s2.get(reverse(LIST_URL)).content)['count'] == 0
obj_1.space = space_2
obj_1.save()
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 1
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
assert json.loads(u1_s1.get(reverse(LIST_URL)).content)['count'] == 1
assert json.loads(u1_s2.get(reverse(LIST_URL)).content)['count'] == 0
@pytest.mark.parametrize("arg", [

View File

@@ -14,8 +14,6 @@ from zipfile import ZipFile
import requests
import validators
from annoying.decorators import ajax_request
from annoying.functions import get_object_or_None
from django.contrib import messages
from django.contrib.auth.models import Group, User
from django.contrib.postgres.search import TrigramSimilarity
@@ -52,7 +50,7 @@ from rest_framework.throttling import AnonRateThrottle, UserRateThrottle
from rest_framework.views import APIView
from rest_framework import mixins
from rest_framework.viewsets import ViewSetMixin
from rest_framework.serializers import CharField, IntegerField, UUIDField, FileField
from rest_framework.serializers import CharField, IntegerField, UUIDField
from treebeard.exceptions import InvalidMoveToDescendant, InvalidPosition, PathOverflow
from cookbook.forms import ImportForm
@@ -63,7 +61,7 @@ from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.helper.open_data_importer import OpenDataImporter
from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsOwner, CustomIsOwnerReadOnly, CustomIsShared, CustomIsSpaceOwner, CustomIsUser, CustomIsGuest, CustomRecipePermission,
CustomTokenHasReadWriteScope, CustomTokenHasScope, CustomUserPermission, IsReadOnlyDRF, above_space_limit, group_required,
has_group_permission, is_space_owner, switch_user_active_space,
has_group_permission, is_space_owner, switch_user_active_space
)
from cookbook.helper.recipe_search import RecipeSearch
from cookbook.helper.recipe_url_import import clean_dict, get_from_youtube_scraper, get_images_from_soup
@@ -72,7 +70,7 @@ from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helpe
from cookbook.models import (Automation, BookmarkletImport, ConnectorConfig, CookLog, CustomFilter, ExportLog, Food, FoodInheritField, FoodProperty, ImportLog, Ingredient,
InviteLink, Keyword, MealPlan, MealType, Property, PropertyType, Recipe, RecipeBook, RecipeBookEntry, ShareLink, ShoppingListEntry,
ShoppingListRecipe, Space, Step, Storage, Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
UserFile, UserPreference, UserSpace, ViewLog,
UserFile, UserPreference, UserSpace, ViewLog
)
from cookbook.provider.dropbox import Dropbox
from cookbook.provider.local import Local
@@ -85,7 +83,7 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer, Au
RecipeOverviewSerializer, RecipeSerializer, RecipeShoppingUpdateSerializer, RecipeSimpleSerializer, ShoppingListEntryBulkSerializer,
ShoppingListEntrySerializer, ShoppingListRecipeSerializer, SpaceSerializer, StepSerializer, StorageSerializer,
SupermarketCategoryRelationSerializer, SupermarketCategorySerializer, SupermarketSerializer, SyncLogSerializer, SyncSerializer,
UnitConversionSerializer, UnitSerializer, UserFileSerializer, UserPreferenceSerializer, UserSerializer, UserSpaceSerializer, ViewLogSerializer,
UnitConversionSerializer, UnitSerializer, UserFileSerializer, UserPreferenceSerializer, UserSerializer, UserSpaceSerializer, ViewLogSerializer
)
from cookbook.views.import_export import get_integration
from recipes import settings
@@ -514,7 +512,8 @@ class SupermarketViewSet(StandardFilterModelViewSet):
pagination_class = DefaultPagination
def get_queryset(self):
return self.queryset.filter(space=self.request.space)
self.queryset = self.queryset.filter(space=self.request.space)
return super().get_queryset()
# TODO does supermarket category have settings to support fuzzy filtering and/or merge?
@@ -526,7 +525,8 @@ class SupermarketCategoryViewSet(FuzzyFilterMixin, MergeMixin):
pagination_class = DefaultPagination
def get_queryset(self):
return self.queryset.filter(space=self.request.space).order_by(Lower('name').asc())
self.queryset = self.queryset.filter(space=self.request.space).order_by(Lower('name').asc())
return super().get_queryset()
class SupermarketCategoryRelationViewSet(StandardFilterModelViewSet):
@@ -536,7 +536,8 @@ class SupermarketCategoryRelationViewSet(StandardFilterModelViewSet):
pagination_class = DefaultPagination
def get_queryset(self):
return self.queryset.filter(supermarket__space=self.request.space).order_by('order')
self.queryset = self.queryset.filter(supermarket__space=self.request.space).order_by('order')
return super().get_queryset()
class KeywordViewSet(TreeMixin):
@@ -563,7 +564,8 @@ class FoodInheritFieldViewSet(viewsets.ReadOnlyModelViewSet):
def get_queryset(self):
# exclude fields not yet implemented
return Food.inheritable_fields
self.queryset = Food.inheritable_fields
return super().get_queryset()
class FoodViewSet(TreeMixin):
@@ -720,7 +722,9 @@ class RecipeBookViewSet(StandardFilterModelViewSet):
order_field = 'id'
ordering = f"{'' if order_direction == 'asc' else '-'}{order_field}"
return self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(space=self.request.space).distinct().order_by(ordering)
self.queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(space=self.request.space).distinct().order_by(ordering)
return super().get_queryset()
@extend_schema_view(list=extend_schema(parameters=[
@@ -957,7 +961,7 @@ class RecipePagination(PageNumberPagination):
),
OpenApiParameter(
name='createdon',
description=_('Filter recipes created on or after YYYY-MM-DD. Prepending ''-'' filters on or before date.'),\
description=_('Filter recipes created on or after YYYY-MM-DD. Prepending ''-'' filters on or before date.'),
type=str,
examples=[DateExample, BeforeDateExample]
),
@@ -1325,7 +1329,8 @@ class UserFileViewSet(StandardFilterModelViewSet):
parser_classes = [MultiPartParser]
def get_queryset(self):
return self.queryset.filter(space=self.request.space).all()
self.queryset = self.queryset.filter(space=self.request.space).all()
return super().get_queryset()
class AutomationViewSet(StandardFilterModelViewSet):
@@ -1369,7 +1374,8 @@ class InviteLinkViewSet(StandardFilterModelViewSet):
self.queryset = self.queryset.filter(internal_note=internal_note)
if is_space_owner(self.request.user, self.request.space):
return self.queryset.filter(space=self.request.space).all()
self.queryset = self.queryset.filter(space=self.request.space).all()
return super().get_queryset()
else:
return None
@@ -1394,7 +1400,8 @@ class CustomFilterViewSet(StandardFilterModelViewSet):
filter_type = self.request.query_params.getlist('type', [])
if filter_type:
self.queryset.filter(type__in=filter_type)
return self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(space=self.request.space).distinct()
self.queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(space=self.request.space).distinct()
return super().get_queryset()
class AccessTokenViewSet(viewsets.ModelViewSet):
@@ -1693,7 +1700,7 @@ def get_external_file_link(request, recipe_id):
responses=None,
)
@api_view(['GET'])
@permission_classes([CustomIsGuest & CustomTokenHasReadWriteScope])
@permission_classes([(CustomIsGuest | CustomIsUser) & CustomTokenHasReadWriteScope])
def get_recipe_file(request, recipe_id):
recipe = get_object_or_404(Recipe, pk=recipe_id, space=request.space)
if recipe.storage:
@@ -1771,7 +1778,7 @@ def get_plan_ical(request, from_date=datetime.date.today(), to_date=None):
return meal_plans_to_ical(queryset, f'meal_plan_{from_date}-{to_date}.ics')
# TODO is this replaced now?
def meal_plans_to_ical(queryset, filename):
cal = Calendar()