update tests to reflect pagination and API changes

This commit is contained in:
smilerz
2024-04-23 09:18:48 -05:00
parent fd0d5813fb
commit 2847721584
18 changed files with 357 additions and 406 deletions

View File

@@ -172,18 +172,20 @@ class SpaceFilterSerializer(serializers.ListSerializer):
if self.context.get('request', None) is None:
return
# Don't return User details to anonymous users
if self.child.Meta.model == User and isinstance(self.context['request'].user, AnonymousUser):
data = []
# paginated data is provided as a list, otherwise as a Queryset
if isinstance(data, QuerySet):
if (isinstance(data, QuerySet) and data.query.is_sliced):
# if query is sliced it came from api request not nested serializer
if data.query.is_sliced:
return super().to_representation(data)
data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all()
return super().to_representation(data)
if self.child.Meta.model == User:
# Don't return User details to anonymous users
if isinstance(self.context['request'].user, AnonymousUser):
data = []
else:
data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all()
elif isinstance(data, list):
data = [d for d in data if getattr(d, self.child.Meta.model.get_space_key()[0]) == self.context['request'].space]
else:
data = data.filter(**{'__'.join(self.child.Meta.model.get_space_key()): self.context['request'].space})
return super().to_representation(data)

View File

@@ -211,7 +211,7 @@ def test_add_with_shopping(u1_s1, meal_type):
@pytest.mark.parametrize("arg", [
[f'', 2],
['', 2],
[f'?from_date={datetime.now().strftime("%Y-%m-%d")}', 1],
[
f'?to_date={(datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")}',

View File

@@ -44,6 +44,7 @@ def test_permissions(arg, request):
to_date_slug = (datetime.now()+timedelta(days=1)).strftime("%Y-%m-%d")
assert c.get(reverse(BOUND_URL, kwargs={'from_date': from_date_slug, 'to_date': to_date_slug})).status_code == arg[1]
def test_bound(obj_1, obj_2, obj_3, u1_s1):
from_date_slug = (datetime.now()+timedelta(days=-1)).strftime("%Y-%m-%d")
to_date_slug = (datetime.now()+timedelta(days=1)).strftime("%Y-%m-%d")

View File

@@ -13,13 +13,17 @@ DETAIL_URL = 'api:property-detail'
@pytest.fixture()
def obj_1(space_1, u1_s1):
pt = PropertyType.objects.get_or_create(name='test_1', space=space_1)[0]
return Property.objects.get_or_create(property_amount=100, property_type=pt, space=space_1)[0]
return Property.objects.get_or_create(property_amount=100,
property_type=pt,
space=space_1)[0]
@pytest.fixture
def obj_2(space_1, u1_s1):
pt = PropertyType.objects.get_or_create(name='test_2', space=space_1)[0]
return Property.objects.get_or_create(property_amount=100, property_type=pt, space=space_1)[0]
return Property.objects.get_or_create(property_amount=100,
property_type=pt,
space=space_1)[0]
@pytest.mark.parametrize("arg", [
@@ -34,14 +38,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)) == 1
assert json.loads(u1_s1.get(reverse(LIST_URL)).content)['count'] == 1
assert json.loads(u1_s2.get(reverse(LIST_URL)).content)['count'] == 1
@pytest.mark.parametrize("arg", [
@@ -55,14 +59,8 @@ def test_list_space(obj_1, obj_2, u1_s1, u1_s2, space_2):
])
def test_update(arg, request, obj_1):
c = request.getfixturevalue(arg[0])
r = c.patch(
reverse(
DETAIL_URL,
args={obj_1.id}
),
{'property_amount': 200},
content_type='application/json'
)
r = c.patch(reverse(DETAIL_URL, args={obj_1.id}), {'property_amount': 200},
content_type='application/json')
response = json.loads(r.content)
assert r.status_code == arg[1]
if r.status_code == 200:
@@ -77,13 +75,17 @@ def test_update(arg, request, obj_1):
])
def test_add(arg, request, u1_s2, space_1):
with scopes_disabled():
pt = PropertyType.objects.get_or_create(name='test_1', space=space_1)[0]
pt = PropertyType.objects.get_or_create(name='test_1',
space=space_1)[0]
c = request.getfixturevalue(arg[0])
r = c.post(
reverse(LIST_URL),
{'property_amount': 100, 'property_type': {'id': pt.id, 'name': pt.name}},
content_type='application/json'
)
r = c.post(reverse(LIST_URL), {
'property_amount': 100,
'property_type': {
'id': pt.id,
'name': pt.name
}
},
content_type='application/json')
response = json.loads(r.content)
assert r.status_code == arg[1]
if r.status_code == 201:
@@ -95,20 +97,10 @@ def test_add(arg, request, u1_s2, space_1):
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():

View File

@@ -32,14 +32,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)) == 1
assert json.loads(u1_s1.get(reverse(LIST_URL)).content)['count'] == 1
assert json.loads(u1_s2.get(reverse(LIST_URL)).content)['count'] == 1
@pytest.mark.parametrize("arg", [

View File

@@ -107,19 +107,19 @@ def test_update(arg, request, recipe_1_s1):
def test_update_share(u1_s1, u2_s1, u1_s2, recipe_1_s1):
with scopes_disabled():
r = u1_s1.patch(
reverse(
DETAIL_URL,
args={recipe_1_s1.id}
),
{'shared': [{'id': auth.get_user(u1_s2).pk, 'username': auth.get_user(u1_s2).username}, {'id': auth.get_user(u2_s1).pk, 'username': auth.get_user(u2_s1).username}]},
content_type='application/json'
)
response = json.loads(r.content)
assert r.status_code == 200
assert len(response['shared']) == 1
assert response['shared'][0]['id'] == auth.get_user(u2_s1).pk
# with scopes_disabled():
r = u1_s1.patch(
reverse(
DETAIL_URL,
args={recipe_1_s1.id}
),
{'shared': [{'id': auth.get_user(u1_s2).pk, 'username': auth.get_user(u1_s2).username}, {'id': auth.get_user(u2_s1).pk, 'username': auth.get_user(u2_s1).username}]},
content_type='application/json'
)
response = json.loads(r.content)
assert r.status_code == 200
assert len(response['shared']) == 1
assert response['shared'][0]['id'] == auth.get_user(u2_s1).pk
def test_update_private_recipe(u1_s1, u2_s1, recipe_1_s1):

View File

@@ -13,12 +13,16 @@ DETAIL_URL = 'api:recipebook-detail'
@pytest.fixture()
def obj_1(space_1, u1_s1):
return RecipeBook.objects.get_or_create(name='test_1', created_by=auth.get_user(u1_s1), space=space_1)[0]
return RecipeBook.objects.get_or_create(name='test_1',
created_by=auth.get_user(u1_s1),
space=space_1)[0]
@pytest.fixture
def obj_2(space_1, u1_s1):
return RecipeBook.objects.get_or_create(name='test_2', created_by=auth.get_user(u1_s1), space=space_1)[0]
return RecipeBook.objects.get_or_create(name='test_2',
created_by=auth.get_user(u1_s1),
space=space_1)[0]
@pytest.mark.parametrize("arg", [
@@ -33,31 +37,33 @@ 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
def test_list_filter(obj_1, obj_2, u1_s1):
r = u1_s1.get(reverse(LIST_URL))
assert r.status_code == 200
response = json.loads(r.content)
assert len(response) == 2
assert response['count'] == 2
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?limit=1').content)
assert len(response) == 1
assert response['count'] == 1
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?query=chicken').content)
assert len(response) == 0
response = json.loads(
u1_s1.get(f'{reverse(LIST_URL)}?query=chicken').content)
assert response['count'] == 0
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?query={obj_1.name[4:]}').content)
assert len(response) == 1
assert response[0]['name'] == obj_1.name
response = json.loads(
u1_s1.get(f'{reverse(LIST_URL)}?query={obj_1.name[4:]}').content)
assert response['count'] == 1
assert response['results'][0]['name'] == obj_1.name
@pytest.mark.parametrize("arg", [
@@ -71,14 +77,7 @@ def test_list_filter(obj_1, obj_2, u1_s1):
])
def test_update(arg, request, obj_1):
c = request.getfixturevalue(arg[0])
r = c.patch(
reverse(
DETAIL_URL,
args={obj_1.id}
),
{'name': 'new'},
content_type='application/json'
)
r = c.patch(reverse(DETAIL_URL, args={obj_1.id}), {'name': 'new'}, content_type='application/json')
response = json.loads(r.content)
assert r.status_code == arg[1]
if r.status_code == 200:
@@ -93,11 +92,11 @@ def test_update(arg, request, obj_1):
])
def test_add(arg, request, u1_s2):
c = request.getfixturevalue(arg[0])
r = c.post(
reverse(LIST_URL),
{'name': 'test', 'shared': []},
content_type='application/json'
)
r = c.post(reverse(LIST_URL), {
'name': 'test',
'shared': []
},
content_type='application/json')
response = json.loads(r.content)
print(r.content)
assert r.status_code == arg[1]
@@ -110,20 +109,10 @@ def test_add(arg, request, u1_s2):
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():

View File

@@ -36,14 +36,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.book.space = space_2
obj_1.book.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

@@ -17,7 +17,9 @@ DETAIL_URL = 'api:shoppinglistentry-detail'
@pytest.fixture
def sle(space_1, u1_s1):
user = auth.get_user(u1_s1)
return ShoppingListEntryFactory.create_batch(10, space=space_1, created_by=user)
return ShoppingListEntryFactory.create_batch(10,
space=space_1,
created_by=user)
@pytest.fixture
@@ -29,10 +31,13 @@ def sle_2(request):
u = request.getfixturevalue(params.get('user', 'u1_s1'))
user = auth.get_user(u)
count = params.get('count', 10)
return ShoppingListEntryFactory.create_batch(count, space=user.userspace_set.filter(active=1).first().space, created_by=user)
return ShoppingListEntryFactory.create_batch(
count,
space=user.userspace_set.filter(active=1).first().space,
created_by=user)
@ pytest.mark.parametrize("arg", [
@pytest.mark.parametrize("arg", [
['a_u', 403],
['g1_s1', 200],
['u1_s1', 200],
@@ -44,27 +49,24 @@ def test_list_permission(arg, request):
def test_list_space(sle, u1_s1, u1_s2, space_2):
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 10
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
assert json.loads(u1_s1.get(reverse(LIST_URL)).content)['count'] == 10
assert json.loads(u1_s2.get(reverse(LIST_URL)).content)['count'] == 0
with scopes_disabled():
e = ShoppingListEntry.objects.first()
e.space = space_2
e.save()
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 9
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
assert json.loads(u1_s1.get(reverse(LIST_URL)).content)['count'] == 9
assert json.loads(u1_s2.get(reverse(LIST_URL)).content)['count'] == 0
def test_get_detail(u1_s1, sle):
r = u1_s1.get(reverse(
DETAIL_URL,
args={sle[0].id}
))
r = u1_s1.get(reverse(DETAIL_URL, args={sle[0].id}))
assert json.loads(r.content)['id'] == sle[0].id
@ pytest.mark.parametrize("arg", [
@pytest.mark.parametrize("arg", [
['a_u', 403],
['g1_s1', 404],
['u1_s1', 200],
@@ -76,14 +78,8 @@ def test_get_detail(u1_s1, sle):
def test_update(arg, request, sle):
c = request.getfixturevalue(arg[0])
new_val = float(sle[0].amount + 1)
r = c.patch(
reverse(
DETAIL_URL,
args={sle[0].id}
),
{'amount': new_val},
content_type='application/json'
)
r = c.patch(reverse(DETAIL_URL, args={sle[0].id}), {'amount': new_val},
content_type='application/json')
assert r.status_code == arg[1]
if r.status_code == 200:
response = json.loads(r.content)
@@ -98,14 +94,14 @@ def test_update(arg, request, sle):
])
def test_add(arg, request, sle):
c = request.getfixturevalue(arg[0])
r = c.post(
reverse(LIST_URL),
{'food': {
r = c.post(reverse(LIST_URL), {
'food': {
'id': sle[0].food.__dict__['id'],
'name': sle[0].food.__dict__['name'],
}, 'amount': 1},
content_type='application/json'
)
},
'amount': 1
},
content_type='application/json')
response = json.loads(r.content)
print(r.content)
assert r.status_code == arg[1]
@@ -114,102 +110,93 @@ def test_add(arg, request, sle):
def test_delete(u1_s1, u1_s2, sle):
r = u1_s2.delete(
reverse(
DETAIL_URL,
args={sle[0].id}
)
)
r = u1_s2.delete(reverse(DETAIL_URL, args={sle[0].id}))
assert r.status_code == 404
r = u1_s1.delete(
reverse(
DETAIL_URL,
args={sle[0].id}
)
)
r = u1_s1.delete(reverse(DETAIL_URL, args={sle[0].id}))
assert r.status_code == 204
@pytest.mark.parametrize("shared, count, sle_2", [
('g1_s1', 20, {'user': 'g1_s1'}),
('g1_s2', 10, {'user': 'g1_s2'}),
('u2_s1', 20, {'user': 'u2_s1'}),
('u1_s2', 10, {'user': 'u1_s2'}),
('a1_s1', 20, {'user': 'a1_s1'}),
('a1_s2', 10, {'user': 'a1_s2'}),
], indirect=['sle_2'])
('g1_s1', 20, {
'user': 'g1_s1'
}),
('g1_s2', 10, {
'user': 'g1_s2'
}),
('u2_s1', 20, {
'user': 'u2_s1'
}),
('u1_s2', 10, {
'user': 'u1_s2'
}),
('a1_s1', 20, {
'user': 'a1_s1'
}),
('a1_s2', 10, {
'user': 'a1_s2'
}),
],
indirect=['sle_2'])
def test_sharing(request, shared, count, sle_2, sle, u1_s1):
user = auth.get_user(u1_s1)
shared_client = request.getfixturevalue(shared)
shared_user = auth.get_user(shared_client)
# confirm shared user can't access shopping list items created by u1_s1
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 10
assert len(json.loads(shared_client.get(reverse(LIST_URL)).content)) == 10
assert json.loads(u1_s1.get(reverse(LIST_URL)).content)['count'] == 10
assert json.loads(shared_client.get(
reverse(LIST_URL)).content)['count'] == 10
user.userpreference.shopping_share.add(shared_user)
# confirm sharing user only sees their shopping list
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 10
assert json.loads(u1_s1.get(reverse(LIST_URL)).content)['count'] == 10
r = shared_client.get(reverse(LIST_URL))
# confirm shared user sees their list and the list that's shared with them
assert len(json.loads(r.content)) == count
assert json.loads(r.content)['count'] == count
# test shared user can mark complete
x = shared_client.patch(
reverse(DETAIL_URL, args={sle[0].id}),
{'checked': True},
content_type='application/json'
)
x = shared_client.patch(reverse(DETAIL_URL, args={sle[0].id}),
{'checked': True},
content_type='application/json')
r = json.loads(shared_client.get(reverse(LIST_URL)).content)
assert len(r) == count
assert r['count'] == count
# count unchecked entries
if not x.status_code == 404:
count = count - 1
assert [x['checked'] for x in r].count(False) == count
assert [x['checked'] for x in r['results']].count(False) == count
# test shared user can delete
x = shared_client.delete(
reverse(
DETAIL_URL,
args={sle[1].id}
)
)
x = shared_client.delete(reverse(DETAIL_URL, args={sle[1].id}))
r = json.loads(shared_client.get(reverse(LIST_URL)).content)
assert len(r) == count
assert r['count'] == count
# count unchecked entries
if not x.status_code == 404:
count = count - 1
assert [x['checked'] for x in r].count(False) == count
assert [x['checked'] for x in r['results']].count(False) == count
def test_completed(sle, u1_s1):
# check 1 entry
u1_s1.patch(
reverse(DETAIL_URL, args={sle[0].id}),
{'checked': True},
content_type='application/json'
)
u1_s1.patch(reverse(DETAIL_URL, args={sle[0].id}), {'checked': True}, content_type='application/json')
r = json.loads(u1_s1.get(reverse(LIST_URL)).content)
assert len(r) == 10
assert r['count'] == 10
# count unchecked entries
assert [x['checked'] for x in r].count(False) == 9
assert [x['checked'] for x in r['results']].count(False) == 9
# confirm completed_at is populated
assert [(x['completed_at'] is not None) for x in r if x['checked']].count(True) == 1
assert [(x['completed_at'] is not None) for x in r['results']
if x['checked']].count(True) == 1
assert len(json.loads(u1_s1.get(f'{reverse(LIST_URL)}?checked=0').content)) == 9
assert len(json.loads(u1_s1.get(f'{reverse(LIST_URL)}?checked=1').content)) == 1
assert json.loads(u1_s1.get(f'{reverse(LIST_URL)}?checked=0').content)['count'] == 9
assert json.loads(u1_s1.get(f'{reverse(LIST_URL)}?checked=1').content)['count'] == 1
# uncheck entry
u1_s1.patch(
reverse(DETAIL_URL, args={sle[0].id}),
{'checked': False},
content_type='application/json'
)
u1_s1.patch(reverse(DETAIL_URL, args={sle[0].id}), {'checked': False}, content_type='application/json')
r = json.loads(u1_s1.get(reverse(LIST_URL)).content)
assert [x['checked'] for x in r].count(False) == 10
assert [x['checked'] for x in r['results']].count(False) == 10
# confirm completed_at value cleared
assert [(x['completed_at'] is not None) for x in r if x['checked']].count(True) == 0
assert [(x['completed_at'] is not None) for x in r['results']
if x['checked']].count(True) == 0
def test_recent(sle, u1_s1):
@@ -220,31 +207,33 @@ def test_recent(sle, u1_s1):
today_start = timezone.now().replace(hour=0, minute=0, second=0)
# past_date within recent_days threshold
past_date = today_start - timedelta(days=user.userpreference.shopping_recent_days - 1)
past_date = today_start - timedelta(
days=user.userpreference.shopping_recent_days - 1)
sle[0].checked = True
sle[0].completed_at = past_date
sle[0].save()
r = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?recent=1').content)
assert len(r) == 10
assert [x['checked'] for x in r].count(False) == 9
assert r['count'] == 10
assert [x['checked'] for x in r['results']].count(False) == 9
# past_date outside recent_days threshold
past_date = today_start - timedelta(days=user.userpreference.shopping_recent_days + 2)
past_date = today_start - timedelta(
days=user.userpreference.shopping_recent_days + 2)
sle[0].completed_at = past_date
sle[0].save()
r = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?recent=1').content)
assert len(r) == 9
assert [x['checked'] for x in r].count(False) == 9
assert r['count'] == 9
assert [x['checked'] for x in r['results']].count(False) == 9
# user preference moved to include entry again
user.userpreference.shopping_recent_days = user.userpreference.shopping_recent_days + 4
user.userpreference.save()
r = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?recent=1').content)
assert len(r) == 10
assert [x['checked'] for x in r].count(False) == 9
assert r['count'] == 10
assert [x['checked'] for x in r['results']].count(False) == 9
# TODO test auto onhand

View File

@@ -7,8 +7,9 @@ from django.contrib import auth
from django.urls import reverse
from django_scopes import scopes_disabled
from cookbook.models import Food, Ingredient, ShoppingListRecipe, ShoppingListEntry
from cookbook.tests.factories import MealPlanFactory, RecipeFactory, StepFactory, UserFactory
from cookbook.models import Food, Ingredient, ShoppingListEntry
from cookbook.tests.factories import (MealPlanFactory, RecipeFactory,
StepFactory, UserFactory)
if settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql':
from django.db.backends.postgresql.features import DatabaseFeatures
@@ -67,7 +68,7 @@ def test_shopping_recipe_method(request, arg, recipe, sle_count, u1_s1, u2_s1):
user.userpreference.mealplan_autoadd_shopping = True
user.userpreference.save()
assert len(json.loads(c.get(reverse(SHOPPING_LIST_URL)).content)) == 0
assert json.loads(c.get(reverse(SHOPPING_LIST_URL)).content)['count'] == 0
url = reverse(SHOPPING_RECIPE_URL, args={recipe.id})
r = c.put(url)
@@ -77,19 +78,16 @@ def test_shopping_recipe_method(request, arg, recipe, sle_count, u1_s1, u2_s1):
r = json.loads(c.get(reverse(SHOPPING_LIST_URL)).content)
# 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 r['count'] == sle_count
assert [x['created_by']['id'] for x in r['results']].count(user.id) == sle_count
# user in space can't see shopping list
assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
assert json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)['count'] == 0
user.userpreference.shopping_share.add(auth.get_user(u2_s1))
# after share, user in space can see shopping list
assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
assert json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)['count'] == sle_count
# confirm that the author of the recipe doesn't have access to shopping list
if c != u1_s1:
assert len(json.loads(
u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
assert json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)['count'] == 0
r = c.get(url)
assert r.status_code == 405
@@ -125,12 +123,12 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
else:
u1_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}))
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['results']].count(user.id) == sle_count
all_ing = list(ShoppingListEntry.objects.filter(list_recipe__recipe=recipe).all().values_list('ingredient', flat=True))
keep_ing = all_ing[1:-1] # remove first and last element
del keep_ing[int(len(keep_ing) / 2)] # remove a middle element
list_recipe = r[0]['list_recipe']
amount_sum = sum([x['amount'] for x in r])
list_recipe = r['results'][0]['list_recipe']
amount_sum = sum([x['amount'] for x in r['results']])
# test modifying shopping list as different user
# test increasing servings size of recipe shopping list
@@ -143,10 +141,9 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
content_type='application/json'
)
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
assert sum([x['amount'] for x in r]) == amount_sum * 2
assert len(r) == sle_count
assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
assert sum([x['amount'] for x in r['results']]) == amount_sum * 2
assert r['count'] == sle_count
assert json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)['count'] == sle_count
# testing decreasing servings size of recipe shopping list
if use_mealplan:
@@ -158,10 +155,9 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
content_type='application/json'
)
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
assert sum([x['amount'] for x in r]) == amount_sum * .5
assert len(r) == sle_count
assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
assert sum([x['amount'] for x in r['results']]) == amount_sum * .5
assert r['count'] == sle_count
assert json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)['count'] == sle_count
# test removing 3 items from shopping list
u2_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}),
@@ -169,9 +165,8 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
content_type='application/json'
)
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
assert len(r) == sle_count - 3
assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count - 3
assert r['count'] == sle_count - 3
assert json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)['count'] == sle_count - 3
# add all ingredients to existing shopping list - don't change serving size
u2_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}),
@@ -179,10 +174,9 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
content_type='application/json'
)
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
assert sum([x['amount'] for x in r]) == amount_sum * .5
assert len(r) == sle_count
assert len(json.loads(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
assert sum([x['amount'] for x in r['results']]) == amount_sum * .5
assert r['count'] == sle_count
assert json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)['count'] == sle_count
@pytest.mark.parametrize("user2, sle_count", [
@@ -202,8 +196,7 @@ def test_shopping_recipe_userpreference(recipe, sle_count, use_mealplan, user2):
food = Food.objects.get(id=ingredients[2].food.id)
food.onhand_users.add(user)
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.onhand_users.add(user)
food.save()
@@ -211,12 +204,10 @@ def test_shopping_recipe_userpreference(recipe, sle_count, use_mealplan, user2):
if use_mealplan:
MealPlanFactory(
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]
assert json.loads(user2.get(reverse(SHOPPING_LIST_URL)).content)['count'] == sle_count[0]
else:
user2.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}))
assert len(json.loads(
user2.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count[1]
assert json.loads(user2.get(reverse(SHOPPING_LIST_URL)).content)['count'] == sle_count[1]
def test_shopping_recipe_mixed_authors(u1_s1, u2_s1, space_1):
@@ -228,24 +219,19 @@ def test_shopping_recipe_mixed_authors(u1_s1, u2_s1, space_1):
recipe1 = RecipeFactory(created_by=user1, space=space)
recipe2 = RecipeFactory(created_by=user2, 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.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()
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(
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
assert json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)['count'] == 29
assert json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)['count'] == 0
@pytest.mark.parametrize("recipe", [{'steps__ingredients__header': 1}], indirect=['recipe'])
def test_shopping_with_header_ingredient(u1_s1, recipe):
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('api:ingredient-list')).content)['results']) == 11
assert json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)['count'] == 10
assert json.loads(u1_s1.get(reverse('api:ingredient-list')).content)['count'] == 11

View File

@@ -40,14 +40,8 @@ def test_update(arg, request, space_1, a1_s1):
space_1.save()
with scopes_disabled():
c = request.getfixturevalue(arg[0])
r = c.patch(
reverse(
DETAIL_URL,
args={space_1.id}
),
{'message': 'new'},
content_type='application/json'
)
r = c.patch(reverse(DETAIL_URL, args={space_1.id}), {'message': 'new'},
content_type='application/json')
response = json.loads(r.content)
assert r.status_code == arg[1]
if r.status_code == 200:
@@ -62,11 +56,7 @@ def test_update(arg, request, space_1, a1_s1):
])
def test_add(arg, request, u1_s2):
c = request.getfixturevalue(arg[0])
r = c.post(
reverse(LIST_URL),
{'name': 'test'},
content_type='application/json'
)
r = c.post(reverse(LIST_URL), {'name': 'test'}, content_type='application/json')
assert r.status_code == arg[1]
@@ -74,19 +64,9 @@ def test_delete(u1_s1, u1_s2, a1_s1, space_1):
space_1.created_by = auth.get_user(a1_s1)
space_1.save()
# user cannot delete space
r = u1_s1.delete(
reverse(
DETAIL_URL,
args={space_1.id}
)
)
r = u1_s1.delete(reverse(DETAIL_URL, args={space_1.id}))
assert r.status_code == 403
# event the space owner cannot delete his space over the api (this might change later but for now it's only available in the UI)
r = a1_s1.delete(
reverse(
DETAIL_URL,
args={space_1.id}
)
)
r = a1_s1.delete(reverse(DETAIL_URL, args={space_1.id}))
assert r.status_code == 405

View File

@@ -32,30 +32,30 @@ 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)) == 1
assert json.loads(u1_s1.get(reverse(LIST_URL)).content)['count'] == 1
assert json.loads(u1_s2.get(reverse(LIST_URL)).content)['count'] == 1
def test_list_filter(obj_1, obj_2, u1_s1):
r = u1_s1.get(reverse(LIST_URL))
assert r.status_code == 200
response = json.loads(r.content)
assert len(response) == 2
assert response['count'] == 2
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?limit=1').content)
assert len(response) == 1
assert response['count'] == 1
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?query=chicken').content)
assert len(response) == 0
assert response['count'] == 0
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?query={obj_1.name[4:]}').content)
assert len(response) == 1
assert response['count'] == 1
@pytest.mark.parametrize("arg", [

View File

@@ -13,14 +13,38 @@ DETAIL_URL = 'api:sync-detail'
@pytest.fixture()
def obj_1(space_1, u1_s1):
s = Storage.objects.create(name='Test Storage', username='test', password='password', token='token', url='url', created_by=auth.get_user(u1_s1), space=space_1, )
return Sync.objects.create(storage=s, path='path', space=space_1, )
s = Storage.objects.create(
name='Test Storage',
username='test',
password='password',
token='token',
url='url',
created_by=auth.get_user(u1_s1),
space=space_1,
)
return Sync.objects.create(
storage=s,
path='path',
space=space_1,
)
@pytest.fixture
def obj_2(space_1, u1_s1):
s = Storage.objects.create(name='Test Storage', username='test', password='password', token='token', url='url', created_by=auth.get_user(u1_s1), space=space_1, )
return Sync.objects.create(storage=s, path='path', space=space_1, )
s = Storage.objects.create(
name='Test Storage',
username='test',
password='password',
token='token',
url='url',
created_by=auth.get_user(u1_s1),
space=space_1,
)
return Sync.objects.create(
storage=s,
path='path',
space=space_1,
)
@pytest.mark.parametrize("arg", [
@@ -35,14 +59,14 @@ def test_list_permission(arg, request):
def test_list_space(obj_1, obj_2, a1_s1, a1_s2, space_2):
assert len(json.loads(a1_s1.get(reverse(LIST_URL)).content)) == 2
assert len(json.loads(a1_s2.get(reverse(LIST_URL)).content)) == 0
assert json.loads(a1_s1.get(reverse(LIST_URL)).content)['count'] == 2
assert json.loads(a1_s2.get(reverse(LIST_URL)).content)['count'] == 0
obj_1.space = space_2
obj_1.save()
assert len(json.loads(a1_s1.get(reverse(LIST_URL)).content)) == 1
assert len(json.loads(a1_s2.get(reverse(LIST_URL)).content)) == 1
assert json.loads(a1_s1.get(reverse(LIST_URL)).content)['count'] == 1
assert json.loads(a1_s2.get(reverse(LIST_URL)).content)['count'] == 1
@pytest.mark.parametrize("arg", [
@@ -56,14 +80,8 @@ def test_list_space(obj_1, obj_2, a1_s1, a1_s2, space_2):
])
def test_update(arg, request, obj_1):
c = request.getfixturevalue(arg[0])
r = c.patch(
reverse(
DETAIL_URL,
args={obj_1.id}
),
{'path': 'new'},
content_type='application/json'
)
r = c.patch(reverse(DETAIL_URL, args={obj_1.id}), {'path': 'new'},
content_type='application/json')
response = json.loads(r.content)
assert r.status_code == arg[1]
if r.status_code == 200:
@@ -78,11 +96,11 @@ def test_update(arg, request, obj_1):
])
def test_add(arg, request, a1_s2, obj_1):
c = request.getfixturevalue(arg[0])
r = c.post(
reverse(LIST_URL),
{'storage': obj_1.storage.pk, 'path': 'test'},
content_type='application/json'
)
r = c.post(reverse(LIST_URL), {
'storage': obj_1.storage.pk,
'path': 'test'
},
content_type='application/json')
response = json.loads(r.content)
print(r.content)
assert r.status_code == arg[1]
@@ -95,20 +113,10 @@ def test_add(arg, request, a1_s2, obj_1):
def test_delete(a1_s1, a1_s2, obj_1):
r = a1_s2.delete(
reverse(
DETAIL_URL,
args={obj_1.id}
)
)
r = a1_s2.delete(reverse(DETAIL_URL, args={obj_1.id}))
assert r.status_code == 404
r = a1_s1.delete(
reverse(
DETAIL_URL,
args={obj_1.id}
)
)
r = a1_s1.delete(reverse(DETAIL_URL, args={obj_1.id}))
assert r.status_code == 204
with scopes_disabled():

View File

@@ -21,8 +21,7 @@ def obj_1(space_1, u1_s1):
converted_amount=100,
converted_unit=get_random_unit(space_1, u1_s1),
created_by=auth.get_user(u1_s1),
space=space_1
)[0]
space=space_1)[0]
@pytest.fixture
@@ -34,8 +33,7 @@ def obj_2(space_1, u1_s1):
converted_amount=100,
converted_unit=get_random_unit(space_1, u1_s1),
created_by=auth.get_user(u1_s1),
space=space_1
)[0]
space=space_1)[0]
@pytest.mark.parametrize("arg", [
@@ -50,14 +48,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)) == 1
assert json.loads(u1_s1.get(reverse(LIST_URL)).content)['count'] == 1
assert json.loads(u1_s2.get(reverse(LIST_URL)).content)['count'] == 1
@pytest.mark.parametrize("arg", [
@@ -71,14 +69,8 @@ def test_list_space(obj_1, obj_2, u1_s1, u1_s2, space_2):
])
def test_update(arg, request, obj_1):
c = request.getfixturevalue(arg[0])
r = c.patch(
reverse(
DETAIL_URL,
args={obj_1.id}
),
{'base_amount': 1000},
content_type='application/json'
)
r = c.patch(reverse(DETAIL_URL, args={obj_1.id}), {'base_amount': 1000},
content_type='application/json')
response = json.loads(r.content)
assert r.status_code == arg[1]
if r.status_code == 200:
@@ -97,18 +89,23 @@ def test_add(arg, request, u1_s2, space_1, u1_s1):
random_unit_1 = get_random_unit(space_1, u1_s1)
random_unit_2 = get_random_unit(space_1, u1_s1)
random_food_1 = get_random_unit(space_1, u1_s1)
r = c.post(
reverse(LIST_URL),
{
'food': {'id': random_food_1.id, 'name': random_food_1.name},
'base_amount': 100,
'base_unit': {'id': random_unit_1.id, 'name': random_unit_1.name},
'converted_amount': 100,
'converted_unit': {'id': random_unit_2.id, 'name': random_unit_2.name}
r = c.post(reverse(LIST_URL), {
'food': {
'id': random_food_1.id,
'name': random_food_1.name
},
content_type='application/json'
)
'base_amount': 100,
'base_unit': {
'id': random_unit_1.id,
'name': random_unit_1.name
},
'converted_amount': 100,
'converted_unit': {
'id': random_unit_2.id,
'name': random_unit_2.name
}
},
content_type='application/json')
response = json.loads(r.content)
print(response)
@@ -122,53 +119,54 @@ def test_add(arg, request, u1_s2, space_1, u1_s1):
def test_add_duplicate(u1_s1, u1_s2, obj_1):
r = u1_s1.post(
reverse(LIST_URL),
{
'food': {'id': obj_1.food.id, 'name': obj_1.food.name},
'base_amount': 100,
'base_unit': {'id': obj_1.base_unit.id, 'name': obj_1.base_unit.name},
'converted_amount': 100,
'converted_unit': {'id': obj_1.converted_unit.id, 'name': obj_1.converted_unit.name}
r = u1_s1.post(reverse(LIST_URL), {
'food': {
'id': obj_1.food.id,
'name': obj_1.food.name
},
content_type='application/json'
)
'base_amount': 100,
'base_unit': {
'id': obj_1.base_unit.id,
'name': obj_1.base_unit.name
},
'converted_amount': 100,
'converted_unit': {
'id': obj_1.converted_unit.id,
'name': obj_1.converted_unit.name
}
},
content_type='application/json')
response = json.loads(r.content)
assert r.status_code == 201
assert response['id'] == obj_1.id
r = u1_s2.post(
reverse(LIST_URL),
{
'food': {'id': obj_1.food.id, 'name': obj_1.food.name},
'base_amount': 100,
'base_unit': {'id': obj_1.base_unit.id, 'name': obj_1.base_unit.name},
'converted_amount': 100,
'converted_unit': {'id': obj_1.converted_unit.id, 'name': obj_1.converted_unit.name}
r = u1_s2.post(reverse(LIST_URL), {
'food': {
'id': obj_1.food.id,
'name': obj_1.food.name
},
content_type='application/json'
)
'base_amount': 100,
'base_unit': {
'id': obj_1.base_unit.id,
'name': obj_1.base_unit.name
},
'converted_amount': 100,
'converted_unit': {
'id': obj_1.converted_unit.id,
'name': obj_1.converted_unit.name
}
},
content_type='application/json')
response = json.loads(r.content)
assert r.status_code == 201
assert response['id'] != obj_1.id
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():

View File

@@ -131,22 +131,20 @@ def test_makenow_sibling_substitute(recipes, makenow_recipe, user1, space_1):
request = type('', (object,), {'space': space_1, 'user': user1})()
search = RecipeSearch(request, makenow='true')
with scope(space=space_1):
food = Food.objects.filter(
ingredient__step__recipe=makenow_recipe.id).first()
food = Food.objects.filter(ingredient__step__recipe=makenow_recipe.id).first()
onhand_user = food.onhand_users.first()
food.onhand_users.clear()
food.substitute_siblings = True
food.save()
assert search.get_queryset(Recipe.objects.all()).count() == 0
new_parent = FoodFactory.create(space=space_1)
new_sibling = FoodFactory.create(
space=space_1, onhand_users=[onhand_user])
new_sibling = FoodFactory.create(space=space_1, onhand_users=[onhand_user])
new_sibling.move(new_parent, node_location)
food.move(new_parent, node_location)
assert Food.objects.filter(
ingredient__step__recipe=makenow_recipe.id, onhand_users__isnull=False).count() == 9
assert Food.objects.filter(
ingredient__step__recipe=makenow_recipe.id, depth=2).count() == 1
# force refresh from database, treebeard bypasses ORM
food = Food.objects.get(id=food.id)
assert Food.objects.filter(ingredient__step__recipe=makenow_recipe.id, onhand_users__isnull=False).count() == 9
assert Food.objects.filter(ingredient__step__recipe=makenow_recipe.id, depth=2).count() == 1
search = search.get_queryset(Recipe.objects.all())
assert search.count() == 1
assert search.first().id == makenow_recipe.id

View File

@@ -5,32 +5,35 @@ from rest_framework.viewsets import ModelViewSet
from cookbook.urls import urlpatterns
skipped_schemas = ['api_sync']
def has_choice_field(model):
model_fields = model._meta.get_fields()
return any(field.get_internal_type() == 'CharField' and hasattr(field, 'choices') and field.choices for field in model_fields)
return any(field.get_internal_type() == 'CharField'
and hasattr(field, 'choices') and field.choices
for field in model_fields)
def is_list_function(callback):
return (
hasattr(callback, 'initkwargs')
and callback.initkwargs.get('detail') == False
and hasattr(callback, 'cls')
and hasattr(callback.cls, 'list')
)
return (hasattr(callback, 'initkwargs')
and callback.initkwargs.get('detail') == False
and hasattr(callback, 'cls') and hasattr(callback.cls, 'list'))
# generates list of all api enpoints
def enumerate_urlpatterns(urlpatterns, base_url=''):
for i, url_pattern in enumerate(urlpatterns):
# api-root isn't an endpoint, so skip it
if isinstance(url_pattern, URLPattern) and url_pattern.name == 'api-root':
if isinstance(url_pattern,
URLPattern) and url_pattern.name == 'api-root':
continue
# if the url pattern starts with 'api/' it is an api endpoint and should be part of the list
if isinstance(url_pattern, URLPattern):
pattern = f"{base_url}{str(url_pattern.pattern)}"
# DRF endpoints generate two patterns, no need to test both
if pattern[:4] == 'api/' and pattern[-25:] != '.(?P<format>[a-z0-9]+)/?$':
if pattern[:4] == 'api/' and pattern[
-25:] != '.(?P<format>[a-z0-9]+)/?$':
api_endpoints.append(url_pattern)
# if the pattern is a URLResolver then it is a list of URLPatterns and needs to be enumerated again, prepending the url_pattern
elif isinstance(url_pattern, URLResolver):
@@ -44,7 +47,9 @@ enumerate_urlpatterns(urlpatterns)
list_api_endpoints = [a for a in api_endpoints if is_list_function(a.callback)]
# filtered list of api_endpoints that only includes endpoints that have type ModelViewSet and a Choice CharField
enum_api_endpoints = [
a for a in list_api_endpoints if hasattr(a.callback, 'cls') and issubclass(a.callback.cls, ModelViewSet) and has_choice_field(a.callback.cls.serializer_class.Meta.model)
a for a in list_api_endpoints
if hasattr(a.callback, 'cls') and issubclass(a.callback.cls, ModelViewSet)
and has_choice_field(a.callback.cls.serializer_class.Meta.model)
]
@@ -52,16 +57,17 @@ enum_api_endpoints = [
def test_pagination_exists(api):
assert hasattr(api.callback.cls, 'pagination_class') and (
api.callback.cls.pagination_class is not None
or getattr(api.callback.cls, 'pagination_disabled')
), f"API {api.name} is not paginated."
or getattr(api.callback.cls,
'pagination_disabled')), f"API {api.name} is not paginated."
@pytest.mark.parametrize("api", api_endpoints, ids=lambda api: api.name)
def test_autoschema_exists(api):
if api.name in skipped_schemas:
return
assert issubclass(api.callback.cls.schema.__class__, AutoSchema)
# @pytest.mark.parametrize("api", enum_api_endpoints, ids=lambda api: api.name)
# def test_schema_enum(api):
# model = api.callback.cls.serializer_class.Meta.model

View File

@@ -19,7 +19,6 @@ from .views.api import CustomAuthToken, ImportOpenData
# extend DRF default router class to allow including additional routers
class DefaultRouter(routers.DefaultRouter):
def extend(self, r):
self.registry.extend(r.registry)
@@ -121,7 +120,7 @@ urlpatterns = [
path('api/get_recipe_file/<int:recipe_id>/', api.get_recipe_file, name='api_get_recipe_file'),
path('api/sync_all/', api.sync_all, name='api_sync'),
path('api/plan-ical/<slug:from_date>/<slug:to_date>/', api.get_plan_ical, name='api_get_plan_ical'),
# path('api/plan-ical/<slug:from_date>/<slug:to_date>/', api.get_plan_ical, name='api_get_plan_ical'),
path('api/recipe-from-source/', api.RecipeUrlImportView.as_view(), name='api_recipe_from_source'),
path('api/ingredient-from-string/', api.ingredient_from_string, name='api_ingredient_from_string'),

View File

@@ -416,10 +416,11 @@ class GroupViewSet(viewsets.ModelViewSet):
http_method_names = ['get', ]
class SpaceViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet):
class SpaceViewSet(viewsets.ModelViewSet):
queryset = Space.objects
serializer_class = SpaceSerializer
permission_classes = [IsReadOnlyDRF & CustomIsUser | CustomIsOwner & CustomIsAdmin & CustomTokenHasReadWriteScope]
pagination_disabled = True
http_method_names = ['get', 'patch']
def get_queryset(self):
@@ -453,10 +454,11 @@ class UserSpaceViewSet(viewsets.ModelViewSet):
return self.queryset.filter(user=self.request.user, space=self.request.space)
class UserPreferenceViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet):
class UserPreferenceViewSet(viewsets.ModelViewSet):
queryset = UserPreference.objects
serializer_class = UserPreferenceSerializer
permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope]
pagination_disabled = True
http_method_names = ['get', 'patch', ]
def get_queryset(self):
@@ -1756,29 +1758,30 @@ def share_link(request, pk):
return JsonResponse({'error': 'sharing_disabled'}, status=403)
@extend_schema(
request=inline_serializer(name="PlanIcalSerializer", fields={'from_date': CharField(), 'to_date': CharField()}),
responses=None,
parameters=[
OpenApiParameter(name='from_date', location=OpenApiParameter.PATH, description=_('Get meal plans from date (inclusive).'), type=str, examples=[DateExample]),
OpenApiParameter(name='to_date', location=OpenApiParameter.PATH, description=_('Get meal plans to date (inclusive).'), type=str, examples=[DateExample]),
]
)
@api_view(['GET'])
@permission_classes([CustomIsUser & CustomTokenHasReadWriteScope])
def get_plan_ical(request, from_date=datetime.date.today(), to_date=None):
queryset = MealPlan.objects.filter(Q(created_by=request.user)
| Q(shared=request.user)).filter(space=request.user.userspace_set.filter(active=1).first().space).distinct().all()
# NOTE: I think this was replaced by icalMealPlanApi?
# @extend_schema(
# request=inline_serializer(name="PlanIcalSerializer", fields={'from_date': CharField(), 'to_date': CharField()}),
# responses=None,
# parameters=[
# OpenApiParameter(name='from_date', location=OpenApiParameter.PATH, description=_('Get meal plans from date (inclusive).'), type=str, examples=[DateExample]),
# OpenApiParameter(name='to_date', location=OpenApiParameter.PATH, description=_('Get meal plans to date (inclusive).'), type=str, examples=[DateExample]),
# ]
# )
# @api_view(['GET'])
# @permission_classes([CustomIsUser & CustomTokenHasReadWriteScope])
# def get_plan_ical(request, from_date=datetime.date.today(), to_date=None):
# queryset = MealPlan.objects.filter(Q(created_by=request.user)
# | Q(shared=request.user)).filter(space=request.user.userspace_set.filter(active=1).first().space).distinct().all()
if from_date is not None:
queryset = queryset.filter(from_date__gte=from_date)
# if from_date is not None:
# queryset = queryset.filter(from_date__gte=from_date)
if to_date is not None:
queryset = queryset.filter(to_date__lte=to_date)
# if to_date is not None:
# queryset = queryset.filter(to_date__lte=to_date)
# return meal_plans_to_ical(queryset, f'meal_plan_{from_date}-{to_date}.ics')
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()