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 cookbook.models import Food, Ingredient, Recipe, Space, Step, Unit # 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=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]) @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) username = kwargs.pop('username', uuid.uuid4()) user = User.objects.create(username=username, **kwargs) if group: user.groups.add(Group.objects.get(name=group)) user.userpreference.space = space user.userpreference.save() c.force_login(user) return c # anonymous user @pytest.fixture() def a_u(client): return copy.deepcopy(client) # users without any group @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')