294 lines
8.2 KiB
Python
294 lines
8.2 KiB
Python
import copy
|
|
import inspect
|
|
import random
|
|
import uuid
|
|
|
|
import pytest
|
|
from django.contrib import auth
|
|
from django.contrib.auth.models import Group, User
|
|
from django_scopes import scopes_disabled
|
|
from pytest_factoryboy import LazyFixture, register
|
|
|
|
from cookbook.models import Food, Ingredient, Recipe, Space, Step, Unit
|
|
from cookbook.tests.factories import FoodFactory, SpaceFactory, UserFactory
|
|
|
|
register(SpaceFactory, 'space_1')
|
|
register(SpaceFactory, 'space_2')
|
|
# register(FoodFactory, space=LazyFixture('space_2'))
|
|
# TODO refactor clients to be factories
|
|
|
|
# hack from https://github.com/raphaelm/django-scopes to disable scopes for all fixtures
|
|
# does not work on yield fixtures as only one yield can be used per fixture (i think)
|
|
|
|
|
|
@pytest.hookimpl(hookwrapper=True)
|
|
def pytest_fixture_setup(fixturedef, request):
|
|
if inspect.isgeneratorfunction(fixturedef.func):
|
|
yield
|
|
else:
|
|
with scopes_disabled():
|
|
yield
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def enable_db_access_for_all_tests(db):
|
|
pass
|
|
|
|
|
|
# @pytest.fixture()
|
|
# def space_1():
|
|
# with scopes_disabled():
|
|
# return Space.objects.get_or_create(name='space_1')[0]
|
|
|
|
|
|
# @pytest.fixture()
|
|
# def space_2():
|
|
# with scopes_disabled():
|
|
# return Space.objects.get_or_create(name='space_2')[0]
|
|
|
|
|
|
# ---------------------- OBJECT FIXTURES ---------------------
|
|
|
|
def get_random_recipe(space_1, u1_s1):
|
|
r = Recipe.objects.create(
|
|
name=str(uuid.uuid4()),
|
|
waiting_time=20,
|
|
working_time=20,
|
|
servings=4,
|
|
created_by=auth.get_user(u1_s1),
|
|
space=space_1,
|
|
internal=True,
|
|
)
|
|
|
|
s1 = Step.objects.create(name=str(uuid.uuid4()), instruction=str(uuid.uuid4()), space=space_1, )
|
|
s2 = Step.objects.create(name=str(uuid.uuid4()), instruction=str(uuid.uuid4()), space=space_1, )
|
|
|
|
r.steps.add(s1)
|
|
r.steps.add(s2)
|
|
|
|
for x in range(5):
|
|
s1.ingredients.add(
|
|
Ingredient.objects.create(
|
|
amount=1,
|
|
food=Food.objects.get_or_create(name=str(uuid.uuid4()), space=space_1)[0],
|
|
unit=Unit.objects.create(name=str(uuid.uuid4()), space=space_1, ),
|
|
note=str(uuid.uuid4()),
|
|
space=space_1,
|
|
)
|
|
)
|
|
|
|
s2.ingredients.add(
|
|
Ingredient.objects.create(
|
|
amount=1,
|
|
food=Food.objects.get_or_create(name=str(uuid.uuid4()), space=space_1)[0],
|
|
unit=Unit.objects.create(name=str(uuid.uuid4()), space=space_1, ),
|
|
note=str(uuid.uuid4()),
|
|
space=space_1,
|
|
)
|
|
)
|
|
|
|
return r
|
|
|
|
|
|
def get_random_json_recipe():
|
|
return {
|
|
"name": str(uuid.uuid4()),
|
|
"description": str(uuid.uuid4()),
|
|
"keywords": [{"name": str(uuid.uuid4())}, {"name": str(uuid.uuid4())}],
|
|
"steps": [
|
|
{
|
|
"instruction": str(uuid.uuid4()),
|
|
"ingredients": [
|
|
{"food": {"name": str(uuid.uuid4())}, "unit": {"name": str(uuid.uuid4())}, "amount": random.randint(0, 10)},
|
|
{"food": {"name": str(uuid.uuid4())}, "unit": {"name": str(uuid.uuid4())}, "amount": random.randint(0, 10)},
|
|
],
|
|
}
|
|
],
|
|
"working_time": random.randint(0, 120),
|
|
"waiting_time": random.randint(0, 120),
|
|
}
|
|
|
|
|
|
def validate_recipe(expected, recipe):
|
|
expected_lists = {}
|
|
target_lists = {}
|
|
# file and url are metadata not related to the recipe
|
|
[expected.pop(k) for k in ['file', 'url'] if k in expected]
|
|
# if a key is a list remove it to deal with later
|
|
lists = [k for k, v in expected.items() if type(v) == list]
|
|
for k in lists:
|
|
expected_lists[k] = expected.pop(k)
|
|
target_lists[k] = recipe.pop(k)
|
|
try:
|
|
# recipe dicts will have additional keys (IDs, default values, etc)
|
|
# this will check for an exact match from expected key:value to a superset of key:value pairs
|
|
assert expected.items() <= recipe.items()
|
|
except AssertionError:
|
|
for key in expected:
|
|
if expected[key] != recipe[key]:
|
|
print('Expected : ', expected[key], ' got: ', recipe[key])
|
|
|
|
# this is later, it may or may not work with keys that have list values
|
|
# it also may or may not work on complex nested dicts
|
|
for key in expected_lists:
|
|
for k in expected_lists[key]:
|
|
try:
|
|
assert any([dict_compare(k, i) for i in target_lists[key]])
|
|
except AssertionError:
|
|
for result in [dict_compare(k, i, details=True) for i in target_lists[key]]:
|
|
print('Added Keys: ', result[0])
|
|
print('Removed Keys', result[1])
|
|
print('Modified Value Keys', result[2])
|
|
print('Modified Dictionary Keys', result[3])
|
|
|
|
|
|
def dict_compare(d1, d2, details=False):
|
|
d1_keys = set(d1.keys())
|
|
d2_keys = set(d2.keys())
|
|
shared = d1_keys.intersection(d2_keys)
|
|
sub_dicts = [i for i, j in d1.items() if type(j) == dict]
|
|
not_dicts = shared - set(sub_dicts)
|
|
added = d1_keys - d2_keys
|
|
removed = d2_keys - d1_keys
|
|
modified = {o: (d1[o], d2[o]) for o in not_dicts if d1[o] != d2[o]}
|
|
modified_dicts = {o: (d1[o], d2[o]) for o in sub_dicts if not d1[o].items() <= d2[o].items()}
|
|
if details:
|
|
return added, removed, modified, modified_dicts
|
|
else:
|
|
return any([not added, not removed, not modified, not modified_dicts])
|
|
|
|
|
|
def transpose(text, number=2):
|
|
|
|
# select random token
|
|
tokens = text.split()
|
|
positions = list(i for i, e in enumerate(tokens) if len(e) > 1)
|
|
|
|
if positions:
|
|
|
|
token_pos = random.choice(positions)
|
|
|
|
# select random positions in token
|
|
positions = random.sample(range(len(tokens[token_pos])), number)
|
|
|
|
# swap the positions
|
|
l = list(tokens[token_pos])
|
|
for first, second in zip(positions[::2], positions[1::2]):
|
|
l[first], l[second] = l[second], l[first]
|
|
|
|
# replace original tokens with swapped
|
|
tokens[token_pos] = ''.join(l)
|
|
|
|
# return text with the swapped token
|
|
return ' '.join(tokens)
|
|
|
|
|
|
@pytest.fixture
|
|
def recipe_1_s1(space_1, u1_s1):
|
|
return get_random_recipe(space_1, u1_s1)
|
|
|
|
|
|
@pytest.fixture
|
|
def recipe_2_s1(space_1, u1_s1):
|
|
return get_random_recipe(space_1, u1_s1)
|
|
|
|
|
|
@pytest.fixture
|
|
def ext_recipe_1_s1(space_1, u1_s1):
|
|
r = get_random_recipe(space_1, u1_s1)
|
|
r.internal = False
|
|
r.link = 'test'
|
|
r.save()
|
|
return r
|
|
|
|
|
|
# ---------------------- USER FIXTURES -----------------------
|
|
# maybe better with factories but this is very explict so ...
|
|
|
|
def create_user(client, space, **kwargs):
|
|
c = copy.deepcopy(client)
|
|
with scopes_disabled():
|
|
group = kwargs.pop('group', None)
|
|
user = UserFactory(space=space, groups=group)
|
|
|
|
c.force_login(user)
|
|
return c
|
|
|
|
|
|
@pytest.fixture()
|
|
def a_u(client):
|
|
return copy.deepcopy(client)
|
|
|
|
|
|
@pytest.fixture()
|
|
def ng1_s1(client, space_1):
|
|
return create_user(client, space_1)
|
|
|
|
|
|
@pytest.fixture()
|
|
def ng1_s2(client, space_2):
|
|
return create_user(client, space_2)
|
|
|
|
|
|
# guests
|
|
@pytest.fixture()
|
|
def g1_s1(client, space_1):
|
|
return create_user(client, space_1, group='guest')
|
|
|
|
|
|
@pytest.fixture()
|
|
def g2_s1(client, space_1):
|
|
return create_user(client, space_1, group='guest')
|
|
|
|
|
|
@pytest.fixture()
|
|
def g1_s2(client, space_2):
|
|
return create_user(client, space_2, group='guest')
|
|
|
|
|
|
@pytest.fixture()
|
|
def g2_s2(client, space_2):
|
|
return create_user(client, space_2, group='guest')
|
|
|
|
|
|
# users
|
|
@pytest.fixture()
|
|
def u1_s1(client, space_1):
|
|
return create_user(client, space_1, group='user')
|
|
|
|
|
|
@pytest.fixture()
|
|
def u2_s1(client, space_1):
|
|
return create_user(client, space_1, group='user')
|
|
|
|
|
|
@pytest.fixture()
|
|
def u1_s2(client, space_2):
|
|
return create_user(client, space_2, group='user')
|
|
|
|
|
|
@pytest.fixture()
|
|
def u2_s2(client, space_2):
|
|
return create_user(client, space_2, group='user')
|
|
|
|
|
|
# admins
|
|
@pytest.fixture()
|
|
def a1_s1(client, space_1):
|
|
return create_user(client, space_1, group='admin')
|
|
|
|
|
|
@pytest.fixture()
|
|
def a2_s1(client, space_1):
|
|
return create_user(client, space_1, group='admin')
|
|
|
|
|
|
@pytest.fixture()
|
|
def a1_s2(client, space_2):
|
|
return create_user(client, space_2, group='admin')
|
|
|
|
|
|
@pytest.fixture()
|
|
def a2_s2(client, space_2):
|
|
return create_user(client, space_2, group='admin')
|