193
app/api/tests/unit/test_unit_api_fields.py
Normal file
193
app/api/tests/unit/test_unit_api_fields.py
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from django.contrib.auth.models import ContentType, Permission, User
|
||||||
|
from django.shortcuts import reverse
|
||||||
|
from django.test import Client
|
||||||
|
|
||||||
|
from rest_framework.relations import Hyperlink
|
||||||
|
|
||||||
|
from access.models.team import Team
|
||||||
|
from access.models.team_user import TeamUsers
|
||||||
|
|
||||||
|
from app.tests.common import DoesNotExist
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class APIFieldsTestCases:
|
||||||
|
|
||||||
|
api_fields_common = {
|
||||||
|
'id': int,
|
||||||
|
'display_name': str,
|
||||||
|
'_urls': dict,
|
||||||
|
'_urls._self': str,
|
||||||
|
'_urls.notes': str
|
||||||
|
}
|
||||||
|
|
||||||
|
api_fields_model = {
|
||||||
|
'model_notes': str,
|
||||||
|
'created': str,
|
||||||
|
'modified': str
|
||||||
|
}
|
||||||
|
|
||||||
|
api_fields_tenancy = {
|
||||||
|
'organization': dict,
|
||||||
|
'organization.id': int,
|
||||||
|
'organization.display_name': str,
|
||||||
|
'organization.url': Hyperlink,
|
||||||
|
}
|
||||||
|
|
||||||
|
parametrized_test_data = {
|
||||||
|
**api_fields_common,
|
||||||
|
**api_fields_tenancy,
|
||||||
|
**api_fields_model,
|
||||||
|
}
|
||||||
|
|
||||||
|
url_view_kwargs = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture( scope = 'class')
|
||||||
|
def setup_pre(self,
|
||||||
|
request,
|
||||||
|
model,
|
||||||
|
django_db_blocker,
|
||||||
|
organization_one,
|
||||||
|
organization_two
|
||||||
|
):
|
||||||
|
|
||||||
|
with django_db_blocker.unblock():
|
||||||
|
|
||||||
|
request.cls.organization = organization_one
|
||||||
|
|
||||||
|
request.cls.different_organization = organization_two
|
||||||
|
|
||||||
|
kwargs_create_item = {}
|
||||||
|
|
||||||
|
for base in reversed(request.cls.__mro__):
|
||||||
|
|
||||||
|
if hasattr(base, 'kwargs_create_item'):
|
||||||
|
|
||||||
|
if base.kwargs_create_item is None:
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
kwargs_create_item.update(**base.kwargs_create_item)
|
||||||
|
|
||||||
|
|
||||||
|
if len(kwargs_create_item) > 0:
|
||||||
|
|
||||||
|
request.cls.kwargs_create_item = kwargs_create_item
|
||||||
|
|
||||||
|
|
||||||
|
if 'organization' not in request.cls.kwargs_create_item:
|
||||||
|
|
||||||
|
request.cls.kwargs_create_item.update({
|
||||||
|
'organization': request.cls.organization
|
||||||
|
})
|
||||||
|
|
||||||
|
view_permissions = Permission.objects.get(
|
||||||
|
codename = 'view_' + request.cls.model._meta.model_name,
|
||||||
|
content_type = ContentType.objects.get(
|
||||||
|
app_label = request.cls.model._meta.app_label,
|
||||||
|
model = request.cls.model._meta.model_name,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
view_team = Team.objects.create(
|
||||||
|
team_name = 'cs_api_view_team',
|
||||||
|
organization = request.cls.organization,
|
||||||
|
)
|
||||||
|
|
||||||
|
view_team.permissions.set([view_permissions])
|
||||||
|
|
||||||
|
|
||||||
|
request.cls.view_user = User.objects.create_user(username="cafs_test_user_view", password="password")
|
||||||
|
|
||||||
|
team_user = TeamUsers.objects.create(
|
||||||
|
team = view_team,
|
||||||
|
user = request.cls.view_user
|
||||||
|
)
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
with django_db_blocker.unblock():
|
||||||
|
|
||||||
|
team_user.delete()
|
||||||
|
|
||||||
|
view_team.delete()
|
||||||
|
|
||||||
|
request.cls.view_user.delete()
|
||||||
|
|
||||||
|
del request.cls.kwargs_create_item
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture( scope = 'class')
|
||||||
|
def setup_post(self, request, django_db_blocker):
|
||||||
|
|
||||||
|
with django_db_blocker.unblock():
|
||||||
|
|
||||||
|
request.cls.url_view_kwargs.update({
|
||||||
|
'pk': request.cls.item.id
|
||||||
|
})
|
||||||
|
|
||||||
|
client = Client()
|
||||||
|
url = reverse('v2:' + request.cls.url_ns_name + '-detail', kwargs=request.cls.url_view_kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
client.force_login(request.cls.view_user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
request.cls.api_data = response.data
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
del request.cls.url_view_kwargs['pk']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture( scope = 'class', autouse = True)
|
||||||
|
def class_setup(self,
|
||||||
|
setup_pre,
|
||||||
|
create_model,
|
||||||
|
setup_post,
|
||||||
|
):
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists(self, recursearray, test_name, test_value, expected):
|
||||||
|
"""Test for existance of API Field"""
|
||||||
|
|
||||||
|
api_data = recursearray(self.api_data, test_value)
|
||||||
|
|
||||||
|
if expected is DoesNotExist:
|
||||||
|
|
||||||
|
assert api_data['key'] not in api_data['obj']
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
assert api_data['key'] in api_data['obj']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type(self, recursearray, test_name, test_value, expected):
|
||||||
|
"""Test for type for API Field"""
|
||||||
|
|
||||||
|
api_data = recursearray(self.api_data, test_value)
|
||||||
|
|
||||||
|
if expected is DoesNotExist:
|
||||||
|
|
||||||
|
assert api_data['key'] not in api_data['obj']
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
assert type( api_data['value'] ) is expected
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class APIFieldsInheritedCases(
|
||||||
|
APIFieldsTestCases
|
||||||
|
):
|
||||||
|
|
||||||
|
model = None
|
14
app/app/tests/common.py
Normal file
14
app/app/tests/common.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DoesNotExist:
|
||||||
|
"""Object does not exist
|
||||||
|
|
||||||
|
Use this class as the expected value for a test cases expected value when
|
||||||
|
the object does not exist.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def __name__(self):
|
||||||
|
|
||||||
|
return str('does_not_exist')
|
136
app/conftest.py
136
app/conftest.py
@ -1,4 +1,5 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
import sys
|
||||||
|
|
||||||
from django.test import (
|
from django.test import (
|
||||||
TestCase
|
TestCase
|
||||||
@ -30,6 +31,45 @@ def enable_db_access_for_all_tests(db): # pylint: disable=W0613:unused-argume
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_generate_tests(metafunc):
|
||||||
|
|
||||||
|
# test_no_value = {"test_name", "test_value", "expected"} <= set(metafunc.fixturenames)
|
||||||
|
|
||||||
|
# test_value = {"test_name", "test_value", "return_value", "expected"} <= set(metafunc.fixturenames)
|
||||||
|
|
||||||
|
if {"test_name", "test_value", "expected"} <= set(metafunc.fixturenames):
|
||||||
|
values = {}
|
||||||
|
|
||||||
|
|
||||||
|
cls = getattr(metafunc, "cls", None)
|
||||||
|
|
||||||
|
if cls:
|
||||||
|
|
||||||
|
for base in reversed(cls.__mro__):
|
||||||
|
|
||||||
|
base_values = getattr(base, "parametrized_test_data", [])
|
||||||
|
|
||||||
|
if isinstance(base_values, dict):
|
||||||
|
|
||||||
|
values.update(base_values)
|
||||||
|
|
||||||
|
|
||||||
|
if values:
|
||||||
|
|
||||||
|
metafunc.parametrize(
|
||||||
|
argnames = (
|
||||||
|
"test_name", "test_value", "expected"
|
||||||
|
),
|
||||||
|
argvalues = [
|
||||||
|
(field, field, expected) for field, expected in values.items()
|
||||||
|
],
|
||||||
|
ids = [
|
||||||
|
str( field.replace('.', '_') + '_' + getattr(expected, '__name__', 'None').lower() ) for field, expected in values.items()
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture( scope = 'class')
|
@pytest.fixture( scope = 'class')
|
||||||
def create_model(request, django_db_blocker):
|
def create_model(request, django_db_blocker):
|
||||||
|
|
||||||
@ -86,3 +126,99 @@ def organization_two(django_db_blocker):
|
|||||||
with django_db_blocker.unblock():
|
with django_db_blocker.unblock():
|
||||||
|
|
||||||
item.delete()
|
item.delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def recursearray() -> dict[dict, str, any]:
|
||||||
|
"""Recursive dict lookup
|
||||||
|
|
||||||
|
Search through a dot notation of dict paths and return the data assosiated
|
||||||
|
with that dict.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj (dict): dict to use for the recusive lookup
|
||||||
|
key (str): the dict keys in dot notation. i.e. dict_1.dict_2
|
||||||
|
rtn_dict (bool, optional): return the dictionary and not the value.
|
||||||
|
Defaults to False.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict[dict, str, any]: lowest dict found. dict values are obj, key and value.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _recursearray(obj: dict, key:str) -> dict[dict, str, any]:
|
||||||
|
|
||||||
|
item = None
|
||||||
|
|
||||||
|
if key in obj:
|
||||||
|
|
||||||
|
return {
|
||||||
|
'obj': obj,
|
||||||
|
'key': key,
|
||||||
|
'value': obj[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
keys = []
|
||||||
|
|
||||||
|
if '.' in key:
|
||||||
|
|
||||||
|
keys = key.split('.')
|
||||||
|
|
||||||
|
|
||||||
|
for k, v in obj.items():
|
||||||
|
|
||||||
|
if k == keys[0] and isinstance(v,dict):
|
||||||
|
|
||||||
|
if keys[1] in v:
|
||||||
|
|
||||||
|
item = _recursearray(v, key.replace(keys[0]+'.', ''))
|
||||||
|
|
||||||
|
return {
|
||||||
|
'obj': item['obj'],
|
||||||
|
'key': item['key'],
|
||||||
|
'value': item['value']
|
||||||
|
}
|
||||||
|
|
||||||
|
elif k == keys[0] and isinstance(v,list):
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
list_key = int(keys[1])
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
item = v[list_key]
|
||||||
|
|
||||||
|
v = item
|
||||||
|
|
||||||
|
|
||||||
|
if keys[2] in v:
|
||||||
|
|
||||||
|
item = _recursearray(v, key.replace(keys[0]+'.'+keys[1]+'.', ''))
|
||||||
|
|
||||||
|
return {
|
||||||
|
'obj': item['obj'],
|
||||||
|
'key': item['key'],
|
||||||
|
'value': item['value']
|
||||||
|
}
|
||||||
|
|
||||||
|
except IndexError:
|
||||||
|
|
||||||
|
print( f'Index {keys[1]} does not exist. List had a length of {len(v)}', file = sys.stderr )
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
|
||||||
|
print( f'{keys[1]} does not appear to be a number.', file = sys.stderr )
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
return {
|
||||||
|
'obj': obj,
|
||||||
|
'key': key,
|
||||||
|
'value': None
|
||||||
|
}
|
||||||
|
|
||||||
|
return _recursearray
|
||||||
|
Reference in New Issue
Block a user