Merge pull request #761 from nofusscomputing/new-model-access-organization

This commit is contained in:
Jon
2025-05-16 03:26:34 +09:30
committed by GitHub
61 changed files with 2612 additions and 1828 deletions

View File

@ -0,0 +1,28 @@
# Generated by Django 5.1.9 on 2025-05-15 07:47
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0009_migrate_organization_permission_tenant'),
]
operations = [
migrations.CreateModel(
name='Company',
fields=[
('entity_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='access.entity')),
('name', models.CharField(help_text='The name of this entity', max_length=80, verbose_name='Name')),
],
options={
'verbose_name': 'Company',
'verbose_name_plural': 'Companies',
'ordering': ['name'],
'sub_model_type': 'company',
},
bases=('access.entity',),
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 5.1.9 on 2025-05-15 12:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0010_company'),
]
operations = [
migrations.AlterField(
model_name='entity',
name='entity_type',
field=models.CharField(help_text='Type this entity is', max_length=30, verbose_name='Entity Type'),
),
migrations.AlterField(
model_name='person',
name='dob',
field=models.DateField(blank=True, help_text='The Persons Date of Birth (DOB)', null=True, verbose_name='DOB'),
),
migrations.AlterField(
model_name='person',
name='m_name',
field=models.CharField(blank=True, help_text='The persons middle name(s)', max_length=100, null=True, verbose_name='Middle Name(s)'),
),
]

View File

@ -1,3 +1,4 @@
from . import contact
from . import company_base
from . import person
from . import role

View File

@ -0,0 +1,102 @@
from django.db import models
from access.models.entity import Entity
class Company(
Entity
):
# This model is intended to be called `Organization`, however at the time of
# creation this was not possible as Tenant (ne Organization) still has
# references in code to `organization` witch clashes with the intended name of
# this model.
class Meta:
ordering = [
'name',
]
sub_model_type = 'company'
verbose_name = 'Company'
verbose_name_plural = 'Companies'
name = models.CharField(
blank = False,
help_text = 'The name of this entity',
max_length = 80,
unique = False,
verbose_name = 'Name'
)
def __str__(self) -> str:
return self.name
documentation = ''
history_model_name = 'company'
page_layout: dict = [
{
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'organization',
'name',
],
"right": [
'model_notes',
'created',
'modified',
]
}
]
},
{
"name": "Knowledge Base",
"slug": "kb_articles",
"sections": [
{
"layout": "table",
"field": "knowledge_base",
}
]
},
{
"name": "Tickets",
"slug": "tickets",
"sections": [
{
"layout": "table",
"field": "tickets",
}
]
},
{
"name": "Notes",
"slug": "notes",
"sections": []
},
]
table_fields: list = [
'name',
'organization',
'created',
]
def clean(self):
super().clean()

View File

@ -37,9 +37,9 @@ class Entity(
verbose_name = 'ID'
)
entity_type = models.CharField(
blank = False,
default = Meta.verbose_name.lower(),
help_text = 'Type this entity is',
max_length = 30,
unique = False,

View File

@ -36,7 +36,6 @@ class Person(
m_name = models.CharField(
blank = True,
default = None,
help_text = 'The persons middle name(s)',
max_length = 100,
null = True,
@ -54,7 +53,6 @@ class Person(
dob = models.DateField(
blank = True,
default = None,
help_text = 'The Persons Date of Birth (DOB)',
null = True,
unique = False,

View File

@ -0,0 +1,70 @@
from drf_spectacular.utils import extend_schema_serializer
from access.models.company_base import Company
from access.serializers.entity import (
BaseSerializer as BaseBaseSerializer,
ModelSerializer as BaseModelSerializer,
)
from access.serializers.organization import TenantBaseSerializer
class BaseSerializer(
BaseBaseSerializer,
):
pass
@extend_schema_serializer(component_name = 'CompanyEntityModelSerializer')
class ModelSerializer(
BaseSerializer,
BaseModelSerializer,
):
"""Company Model
This model inherits from the Entity base model.
"""
class Meta:
model = Company
fields = [
'id',
'entity_ptr_id',
'organization',
'entity_type',
'display_name',
'name',
'model_notes',
'is_global',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'entity_type',
'created',
'modified',
'_urls',
]
@extend_schema_serializer(component_name = 'CompanyEntityViewSerializer')
class ViewSerializer(
ModelSerializer,
):
"""Company View Model
This model inherits from the Entity base model.
"""
organization = TenantBaseSerializer(many=False, read_only=True)

View File

@ -0,0 +1,24 @@
import pytest
from access.models.company_base import Company
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Company
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from access.serializers.entity_company import ModelSerializer
yield ModelSerializer

View File

@ -0,0 +1,72 @@
from django.test import TestCase
from access.models.company_base import Company
from access.tests.functional.entity.test_functional_entity_metadata import (
EntityMetadataInheritedCases
)
class CompanyMetadataTestCases(
EntityMetadataInheritedCases,
):
add_data: dict = {
'name': 'Ian1'
}
kwargs_create_item: dict = {
'name': 'Ian2',
}
kwargs_create_item_diff_org: dict = {
'name': 'Ian3',
}
model = Company
class CompanyMetadataInheritedCases(
CompanyMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
# self.url_kwargs = {
# 'entity_model': self.model._meta.sub_model_type
# }
# self.url_view_kwargs = {
# 'entity_model': self.model._meta.sub_model_type
# }
super().setUpTestData()
class CompanyMetadataTest(
CompanyMetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,43 @@
import pytest
from access.tests.functional.entity.test_functional_entity_permission import (
EntityPermissionsAPIInheritedCases
)
class CompanyPermissionsAPITestCases(
EntityPermissionsAPIInheritedCases,
):
add_data: dict = {
'name': 'Ian1',
}
kwargs_create_item: dict = {
'name': 'Ian2',
}
kwargs_create_item_diff_org: dict = {
'name': 'Ian3',
}
class CompanyPermissionsAPIInheritedCases(
CompanyPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
class CompanyPermissionsAPIPyTest(
CompanyPermissionsAPITestCases,
):
pass

View File

@ -0,0 +1,46 @@
import pytest
from rest_framework.exceptions import ValidationError
from access.tests.functional.entity.test_functional_entity_serializer import (
MockView,
EntitySerializerInheritedCases
)
class CompanySerializerTestCases(
EntitySerializerInheritedCases
):
parameterized_test_data: dict = {
"name": {
'will_create': False,
'exception_key': 'required'
},
}
valid_data: dict = {
'name': 'Ian',
}
"""Valid data used by serializer to create object"""
class CompanySerializerInheritedCases(
CompanySerializerTestCases,
):
parameterized_test_data: dict = None
valid_data: dict = None
"""Valid data used by serializer to create object"""
class CompanySerializerPyTest(
CompanySerializerTestCases,
):
parameterized_test_data: dict = None

View File

@ -0,0 +1,58 @@
from django.test import TestCase
from access.models.company_base import Company
from access.tests.functional.entity.test_functional_entity_viewset import (
EntityViewSetInheritedCases
)
class ViewSetTestCases(
EntityViewSetInheritedCases,
):
add_data: dict = {
'name': 'Ian',
}
kwargs_create_item: dict = {
'name': 'Ian2',
}
kwargs_create_item_diff_org: dict = {
'name': 'Ian3',
}
model = Company
class CompanyViewSetInheritedCases(
ViewSetTestCases,
):
model = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
super().setUpTestData()
class CompanyViewSetTest(
ViewSetTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,24 @@
import pytest
from access.models.contact import Contact
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Contact
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from access.serializers.entity_contact import ModelSerializer
yield ModelSerializer

View File

@ -0,0 +1,65 @@
from django.test import TestCase
from access.models.contact import Contact
from access.tests.functional.person.test_functional_person_metadata import (
PersonMetadataInheritedCases
)
class ContactMetadataTestCases(
PersonMetadataInheritedCases,
):
add_data: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item: dict = {
'email': 'ipweird@unit.test',
}
kwargs_create_item_diff_org: dict = {
'email': 'ipstrange@unit.test',
}
model = Contact
class ContactMetadataInheritedCases(
ContactMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
super().setUpTestData()
class ContactMetadataTest(
ContactMetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,70 @@
import pytest
from access.tests.functional.person.test_functional_person_permission import (
PersonPermissionsAPIInheritedCases
)
class ContactPermissionsAPITestCases(
PersonPermissionsAPIInheritedCases,
):
add_data: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item: dict = {
'email': 'ipweird@unit.test',
}
kwargs_create_item_diff_org: dict = {
'email': 'ipstrange@unit.test',
}
class ContactPermissionsAPIInheritedCases(
ContactPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
# url_name = '_api_v2_entity_sub'
# @pytest.fixture(scope='class')
# def inherited_var_setup(self, request):
# request.cls.url_kwargs.update({
# 'entity_model': self.model._meta.sub_model_type
# })
# request.cls.url_view_kwargs.update({
# 'entity_model': self.model._meta.sub_model_type
# })
# @pytest.fixture(scope='class', autouse = True)
# def class_setup(self, request, django_db_blocker,
# model,
# var_setup,
# prepare,
# inherited_var_setup,
# diff_org_model,
# create_model,
# ):
# pass
class ContactPermissionsAPIPyTest(
ContactPermissionsAPITestCases,
):
pass

View File

@ -1,129 +1,46 @@
import pytest
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.serializers.entity_contact import (
Contact,
ModelSerializer
)
from access.tests.functional.person.test_functional_person_serializer import (
MockView,
PersonSerializerInheritedCases
)
class SerializerTestCases(
PersonSerializerInheritedCases,
class ContactSerializerTestCases(
PersonSerializerInheritedCases
):
duplicate_f_name_l_name_dob = {
'email': 'contactentityduplicateone@unit.test',
parameterized_test_data: dict = {
"email": {
'will_create': False,
'exception_key': 'required'
}
}
kwargs_create_item: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item_duplicate_f_name_l_name_dob = {
'email': 'contactentityduplicatetwo@unit.test',
}
model = Contact
"""Model to test"""
create_model_serializer = ModelSerializer
"""Serializer to test"""
valid_data: dict = {
'email': 'ipweird@unit.test',
'email': 'contactentityduplicatetwo@unit.test',
}
def test_serializer_validation_no_email_exception(self):
"""Serializer Validation Check
Ensure that when creating with valid data and field email is missing
a validation error occurs.
"""
data = self.valid_data.copy()
del data['email']
with pytest.raises(ValidationError) as err:
serializer = self.create_model_serializer(
data = data
)
serializer.is_valid(raise_exception = True)
assert err.value.get_codes()['email'][0] == 'required'
"""Valid data used by serializer to create object"""
class ContactSerializerInheritedCases(
SerializerTestCases,
ContactSerializerTestCases,
):
create_model_serializer = None
"""Serializer to test"""
duplicate_f_name_l_name_dob: dict = None
""" Duplicate model serializer dict
used for testing for duplicate f_name, l_name and dob fields.
"""
kwargs_create_item: dict = None
""" Model kwargs to create item"""
kwargs_create_item_duplicate_f_name_l_name_dob: dict = None
"""model kwargs to create object
**None:** Ensure that the fields of sub-model to person do not match
`self.duplicate_f_name_l_name_dob`. if they do the wrong exception will be thrown.
used for testing for duplicate f_name, l_name and dob fields.
"""
model = None
"""Model to test"""
parameterized_test_data: dict = None
valid_data: dict = None
"""Valid data used by serializer to create object"""
@classmethod
def setUpTestData(self):
"""Setup Test"""
self.duplicate_f_name_l_name_dob.update(
super().duplicate_f_name_l_name_dob
)
self.kwargs_create_item_duplicate_f_name_l_name_dob.update(
super().kwargs_create_item_duplicate_f_name_l_name_dob
)
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.valid_data.update(
super().valid_data
)
super().setUpTestData()
class ContactSerializerTest(
SerializerTestCases,
TestCase,
class ContactSerializerPyTest(
ContactSerializerTestCases,
):
pass
parameterized_test_data: dict = None

View File

@ -2,91 +2,28 @@ from django.test import TestCase
from access.models.contact import Contact
from access.tests.functional.person.test_functional_person_viewset import (
PersonMetadataInheritedCases,
PersonPermissionsAPIInheritedCases,
PersonViewSetInheritedCases
)
class ViewSetBase:
add_data = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item_diff_org = {
'email': 'ipstrange@unit.test',
}
kwargs_create_item = {
'email': 'ipweird@unit.test',
}
model = Contact
url_kwargs: dict = {}
url_view_kwargs: dict = {}
class PermissionsAPITestCases(
ViewSetBase,
PersonPermissionsAPIInheritedCases,
):
pass
class ContactPermissionsAPIInheritedCases(
PermissionsAPITestCases,
):
add_data: dict = None
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.add_data.update(
super().add_data
)
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
super().setUpTestData()
class ContactPermissionsAPITest(
PermissionsAPITestCases,
TestCase,
):
pass
class ViewSetTestCases(
ViewSetBase,
PersonViewSetInheritedCases,
):
pass
add_data: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item: dict = {
'email': 'ipweird@unit.test',
}
kwargs_create_item_diff_org: dict = {
'email': 'ipstrange@unit.test',
}
model = Contact
@ -96,21 +33,19 @@ class ContactViewSetInheritedCases(
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
super().setUpTestData()
@ -120,50 +55,4 @@ class ContactViewSetTest(
ViewSetTestCases,
TestCase,
):
pass
class MetadataTestCases(
ViewSetBase,
PersonMetadataInheritedCases,
):
pass
class ContactMetadataInheritedCases(
MetadataTestCases,
):
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
super().setUpTestData()
class ContactMetadataTest(
MetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,24 @@
import pytest
from access.models.entity import Entity
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Entity
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from access.serializers.entity import ModelSerializer
yield ModelSerializer

View File

@ -0,0 +1,260 @@
import django
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from access.models.entity import Entity
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from accounting.models.asset_base import AssetBase
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
User = django.contrib.auth.get_user_model()
class EntityMetadataTestCases(
MetadataAttributesFunctional,
):
add_data: dict = {}
app_namespace = 'v2'
base_model = Entity
"""Base model for this sub model
don't change or override this value
"""
change_data = None
delete_data = {}
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
model = Entity
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = None
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
. create an organization that is different to item
2. Create a team
3. create teams with each permission: view, add, change, delete
4. create a user per team
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.different_organization = Organization.objects.create(name='test_different_organization')
self.view_user = User.objects.create_user(username="test_user_view", password="password")
self.item = self.model.objects.create(
organization = organization,
**self.kwargs_create_item
)
self.other_org_item = self.model.objects.create(
organization = self.different_organization,
**self.kwargs_create_item_diff_org
)
self.url_view_kwargs.update({ 'pk': self.item.id })
if self.add_data is not None:
self.add_data.update({
'organization': self.organization.id,
})
view_permissions = Permission.objects.get(
codename = 'view_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = organization,
)
view_team.permissions.set([view_permissions])
add_permissions = Permission.objects.get(
codename = 'add_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
add_team = Team.objects.create(
team_name = 'add_team',
organization = organization,
)
add_team.permissions.set([add_permissions])
change_permissions = Permission.objects.get(
codename = 'change_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
change_team = Team.objects.create(
team_name = 'change_team',
organization = organization,
)
change_team.permissions.set([change_permissions])
delete_permissions = Permission.objects.get(
codename = 'delete_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
delete_team = Team.objects.create(
team_name = 'delete_team',
organization = organization,
)
delete_team.permissions.set([delete_permissions])
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
self.add_user = User.objects.create_user(username="test_user_add", password="password")
TeamUsers.objects.create(
team = add_team,
user = self.add_user
)
self.change_user = User.objects.create_user(username="test_user_change", password="password")
TeamUsers.objects.create(
team = change_team,
user = self.change_user
)
self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
TeamUsers.objects.create(
team = delete_team,
user = self.delete_user
)
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
different_organization_team = Team.objects.create(
team_name = 'different_organization_team',
organization = self.different_organization,
)
different_organization_team.permissions.set([
view_permissions,
add_permissions,
change_permissions,
delete_permissions,
])
TeamUsers.objects.create(
team = different_organization_team,
user = self.different_organization_user
)
def test_sanity_is_entity_sub_model(self):
"""Sanity Test
This test ensures that the model being tested `self.model` is a
sub-model of `self.base_model`.
This test is required as the same viewset is used for all sub-models
of `AssetBase`
"""
assert issubclass(self.model, self.base_model)
class EntityMetadataInheritedCases(
EntityMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
url_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
self.url_kwargs = {
'entity_model': self.model._meta.sub_model_type
}
self.url_view_kwargs = {
'entity_model': self.model._meta.sub_model_type
}
super().setUpTestData()
class EntityMetadataTest(
EntityMetadataTestCases,
TestCase,
):
url_name = '_api_v2_entity'

View File

@ -0,0 +1,89 @@
import pytest
from api.tests.functional.test_functional_api_permissions import (
APIPermissionsInheritedCases,
)
class EntityPermissionsAPITestCases(
APIPermissionsInheritedCases,
):
add_data: dict = {}
app_namespace = 'v2'
change_data = {}
delete_data = {}
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
url_kwargs: dict = {}
url_name = '_api_v2_entity'
url_view_kwargs: dict = {}
def test_returned_data_from_user_and_global_organizations_only(self):
"""Check items returned
This test case is a over-ride of a test case with the same name.
This model is not a tenancy model making this test not-applicable.
Items returned from the query Must be from the users organization and
global ONLY!
"""
pass
class EntityPermissionsAPIInheritedCases(
EntityPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_v2_entity_sub'
@pytest.fixture(scope='class')
def inherited_var_setup(self, request):
request.cls.url_kwargs.update({
'entity_model': self.model._meta.sub_model_type
})
request.cls.url_view_kwargs.update({
'entity_model': self.model._meta.sub_model_type
})
@pytest.fixture(scope='class', autouse = True)
def class_setup(self, request, django_db_blocker,
model,
var_setup,
prepare,
inherited_var_setup,
diff_org_model,
create_model,
):
pass
class EntityPermissionsAPIPyTest(
EntityPermissionsAPITestCases,
):
pass

View File

@ -1,94 +1,171 @@
import django
import pytest
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.models.tenant import Tenant as Organization
from access.serializers.entity import (
Entity,
ModelSerializer
)
User = django.contrib.auth.get_user_model()
class SerializerTestCases:
class MockView:
kwargs_create_item: dict = {}
""" Model kwargs to create item"""
_has_import: bool = False
"""User Permission
model = Entity
"""Model to test"""
get_permission_required() sets this to `True` when user has import permission.
"""
create_model_serializer = ModelSerializer
"""Serializer to test"""
_has_purge: bool = False
"""User Permission
valid_data: dict = {}
get_permission_required() sets this to `True` when user has purge permission.
"""
_has_triage: bool = False
"""User Permission
get_permission_required() sets this to `True` when user has triage permission.
"""
class EntitySerializerTestCases:
parameterized_test_data: dict = {
"model_notes": {
'will_create': True,
}
}
valid_data: dict = {
'model_notes': 'model notes field'
}
"""Valid data used by serializer to create object"""
@classmethod
def setUpTestData(self):
"""Setup Test"""
self.organization = Organization.objects.create(name='test_org')
self.kwargs_create_item.update({
'model_notes': 'model notes field'
})
@pytest.fixture( scope = 'class')
def setup_data(self,
request,
model,
django_db_blocker,
organization_one,
):
self.valid_data.update({
'organization': self.organization.pk,
'model_notes': 'model notes field'
})
with django_db_blocker.unblock():
self.item = self.model.objects.create(
organization = self.organization,
**self.kwargs_create_item,
)
request.cls.organization = organization_one
valid_data = {}
for base in reversed(request.cls.__mro__):
if hasattr(base, 'valid_data'):
if base.valid_data is None:
continue
valid_data.update(**base.valid_data)
if len(valid_data) > 0:
request.cls.valid_data = valid_data
if 'organization' not in request.cls.valid_data:
request.cls.valid_data.update({
'organization': request.cls.organization.pk
})
request.cls.view_user = User.objects.create_user(username="cafs_test_user_view", password="password")
yield
with django_db_blocker.unblock():
request.cls.view_user.delete()
del request.cls.valid_data
def test_serializer_valid_data(self):
@pytest.fixture( scope = 'class', autouse = True)
def class_setup(self,
setup_data,
):
pass
def test_serializer_valid_data(self, create_serializer):
"""Serializer Validation Check
Ensure that when creating an object with valid data, no validation
error occurs.
"""
serializer = self.create_model_serializer(
view_set = MockView()
serializer = create_serializer(
context = {
'view': view_set,
},
data = self.valid_data
)
assert serializer.is_valid(raise_exception = True)
def test_serializer_validation_no_model_notes(self):
def test_serializer_valid_data_missing_field_is_valid(self, parameterized, param_key_test_data,
create_serializer,
param_value,
param_will_create,
):
"""Serializer Validation Check
Ensure that if creating and no model_notes is provided no validation
error occurs
Ensure that when creating an object with a user with import permission
and with valid data, no validation error occurs.
"""
data = self.valid_data.copy()
del data['model_notes']
valid_data = self.valid_data.copy()
serializer = self.create_model_serializer(
data = data
del valid_data[param_value]
view_set = MockView()
view_set._has_import = True
serializer = create_serializer(
context = {
'view': view_set,
},
data = valid_data
)
assert serializer.is_valid(raise_exception = True)
is_valid = serializer.is_valid(raise_exception = False)
assert (
(
not param_will_create
and param_will_create == is_valid
)
or param_will_create == is_valid
)
class EntitySerializerInheritedCases(
SerializerTestCases,
EntitySerializerTestCases,
):
create_model_serializer = None
"""Serializer to test"""
kwargs_create_item: dict = None
""" Model kwargs to create item"""
parameterized_test_data: dict = None
model = None
"""Model to test"""
@ -97,10 +174,40 @@ class EntitySerializerInheritedCases(
"""Valid data used by serializer to create object"""
def test_serializer_valid_data_missing_field_raises_exception(self, parameterized, param_key_test_data,
create_serializer,
param_value,
param_exception_key,
):
"""Serializer Validation Check
class EntitySerializerTest(
SerializerTestCases,
TestCase,
Ensure that when creating an object with a user with import permission
and with valid data, no validation error occurs.
"""
valid_data = self.valid_data.copy()
del valid_data[param_value]
view_set = MockView()
with pytest.raises(ValidationError) as err:
serializer = create_serializer(
context = {
'view': view_set,
},
data = valid_data
)
is_valid = serializer.is_valid(raise_exception = True)
assert err.value.get_codes()[param_value][0] == param_exception_key
class EntitySerializerPyTest(
EntitySerializerTestCases,
):
pass
parameterized_test_data: dict = None

View File

@ -1,5 +1,4 @@
import django
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
@ -9,8 +8,6 @@ from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
from api.tests.abstract.api_permissions_viewset import APIPermissions
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
User = django.contrib.auth.get_user_model()
@ -19,23 +16,34 @@ User = django.contrib.auth.get_user_model()
class ViewSetBase:
add_data: dict = None
add_data: dict = {
'model_notes': 'added model note'
}
app_namespace = 'v2'
base_model = Entity
"""Base model for this sub model
don't change or override this value
"""
change_data = None
delete_data = {}
kwargs_create_item: dict = {}
kwargs_create_item: dict = {
'model_notes': 'added model note'
}
kwargs_create_item_diff_org: dict = {}
kwargs_create_item_diff_org: dict = {
'model_notes': 'added model note'
}
model = None
url_kwargs: dict = None
url_kwargs: dict = {}
url_view_kwargs: dict = None
url_view_kwargs: dict = {}
url_name = None
@ -57,16 +65,15 @@ class ViewSetBase:
self.different_organization = Organization.objects.create(name='test_different_organization')
self.view_user = User.objects.create_user(username="test_user_view", password="password")
self.item = self.model.objects.create(
organization = organization,
model_notes = 'some notes',
**self.kwargs_create_item
)
self.other_org_item = self.model.objects.create(
organization = self.different_organization,
model_notes = 'some more notes',
**self.kwargs_create_item_diff_org
)
@ -75,7 +82,9 @@ class ViewSetBase:
if self.add_data is not None:
self.add_data.update({'organization': self.organization.id})
self.add_data.update({
'organization': self.organization.id,
})
view_permissions = Permission.objects.get(
@ -148,7 +157,6 @@ class ViewSetBase:
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
self.view_user = User.objects.create_user(username="test_user_view", password="password")
TeamUsers.objects.create(
team = view_team,
user = self.view_user
@ -194,98 +202,16 @@ class ViewSetBase:
)
class PermissionsAPITestCases(
ViewSetBase,
APIPermissions,
):
add_data: dict = {}
change_data = {'model_notes': 'device'}
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_kwargs: dict = None
url_view_kwargs: dict = None
url_name = None
@classmethod
def setUpTestData(self):
self.add_data.update({ 'model_note': 'added model note' })
super().setUpTestData()
def test_returned_data_from_user_and_global_organizations_only(self):
"""Check items returned
This test case is a over-ride of a test case with the same name.
This model is not a tenancy model making this test not-applicable.
Items returned from the query Must be from the users organization and
global ONLY!
def test_sanity_is_asset_sub_model(self):
"""Sanity Test
This test ensures that the model being tested `self.model` is a
sub-model of `self.base_model`.
This test is required as the same viewset is used for all sub-models
of `Entity`
"""
pass
class EntityPermissionsAPIInheritedCases(
PermissionsAPITestCases,
):
add_data: dict = None
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.url_kwargs = {
'entity_model': self.model._meta.model_name
}
self.url_view_kwargs = {
'entity_model': self.model._meta.model_name
}
super().setUpTestData()
class EntityPermissionsAPITest(
PermissionsAPITestCases,
TestCase,
):
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
model = Entity
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = '_api_v2_entity'
assert issubclass(self.model, self.base_model)
@ -294,17 +220,7 @@ class ViewSetTestCases(
SerializersTestCases,
):
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
model = None
url_kwargs: dict = None
url_view_kwargs: dict = None
url_name = None
model = Entity
@ -314,22 +230,28 @@ class EntityViewSetInheritedCases(
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
self.url_kwargs = {
'entity_model': self.model._meta.model_name
'entity_model': self.model._meta.sub_model_type
}
self.url_view_kwargs = {
'entity_model': self.model._meta.model_name
'entity_model': self.model._meta.sub_model_type
}
super().setUpTestData()
@ -341,168 +263,4 @@ class EntityViewSetTest(
TestCase,
):
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
model = Entity
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = '_api_v2_entity'
class MetadataTestCases(
ViewSetBase,
MetadataAttributesFunctional,
):
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
model = None
url_kwargs: dict = None
url_view_kwargs: dict = None
url_name = None
class EntityMetadataInheritedCases(
MetadataTestCases,
):
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.url_kwargs = {
'entity_model': self.model._meta.model_name
}
self.url_view_kwargs = {
'entity_model': self.model._meta.model_name
}
super().setUpTestData()
class EntityMetadataTest(
MetadataTestCases,
TestCase,
):
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
model = Entity
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = '_api_v2_entity'
# def test_method_options_request_detail_data_has_key_urls_back(self):
# """Test HTTP/Options Method
# Ensure the request data returned has key `urls.back`
# """
# client = Client()
# client.force_login(self.view_user)
# response = client.options(
# reverse(
# self.app_namespace + ':' + self.url_name + '-detail',
# kwargs=self.url_view_kwargs
# ),
# content_type='application/json'
# )
# assert 'back' in response.data['urls']
# def test_method_options_request_detail_data_key_urls_back_is_str(self):
# """Test HTTP/Options Method
# Ensure the request data key `urls.back` is str
# """
# client = Client()
# client.force_login(self.view_user)
# response = client.options(
# reverse(
# self.app_namespace + ':' + self.url_name + '-detail',
# kwargs=self.url_view_kwargs
# ),
# content_type='application/json'
# )
# assert type(response.data['urls']['back']) is str
# def test_method_options_request_list_data_has_key_urls_return_url(self):
# """Test HTTP/Options Method
# Ensure the request data returned has key `urls.return_url`
# """
# client = Client()
# client.force_login(self.view_user)
# if self.url_kwargs:
# url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
# else:
# url = reverse(self.app_namespace + ':' + self.url_name + '-list')
# response = client.options( url, content_type='application/json' )
# assert 'return_url' in response.data['urls']
# def test_method_options_request_list_data_key_urls_return_url_is_str(self):
# """Test HTTP/Options Method
# Ensure the request data key `urls.return_url` is str
# """
# client = Client()
# client.force_login(self.view_user)
# if self.url_kwargs:
# url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
# else:
# url = reverse(self.app_namespace + ':' + self.url_name + '-list')
# response = client.options( url, content_type='application/json' )
# assert type(response.data['urls']['return_url']) is str

View File

@ -0,0 +1,24 @@
import pytest
from access.models.person import Person
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Person
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from access.serializers.entity_person import ModelSerializer
yield ModelSerializer

View File

@ -0,0 +1,83 @@
from django.test import TestCase
from access.models.person import Person
from access.tests.functional.entity.test_functional_entity_metadata import (
EntityMetadataInheritedCases
)
from accounting.models.asset_base import AssetBase
class PersonMetadataTestCases(
EntityMetadataInheritedCases,
):
add_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Strange',
'dob': '2025-04-08',
}
kwargs_create_item: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Weird',
'dob': '2025-04-08',
}
kwargs_create_item_diff_org: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
model = Person
class PersonMetadataInheritedCases(
PersonMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
# self.url_kwargs = {
# 'entity_model': self.model._meta.sub_model_type
# }
# self.url_view_kwargs = {
# 'entity_model': self.model._meta.sub_model_type
# }
super().setUpTestData()
class PersonMetadataTest(
PersonMetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,91 @@
import pytest
from access.tests.functional.entity.test_functional_entity_permission import (
EntityPermissionsAPIInheritedCases
)
class PersonPermissionsAPITestCases(
EntityPermissionsAPIInheritedCases,
):
add_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Strange',
'dob': '2025-04-08',
}
# app_namespace = 'v2'
# change_data = {}
# delete_data = {}
kwargs_create_item: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Weird',
'dob': '2025-04-08',
}
kwargs_create_item_diff_org: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
# url_kwargs: dict = {}
# url_name = '_api_v2_entity'
# url_view_kwargs: dict = {}
class PersonPermissionsAPIInheritedCases(
PersonPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
# url_name = '_api_v2_entity_sub'
# @pytest.fixture(scope='class')
# def inherited_var_setup(self, request):
# request.cls.url_kwargs.update({
# 'entity_model': self.model._meta.sub_model_type
# })
# request.cls.url_view_kwargs.update({
# 'entity_model': self.model._meta.sub_model_type
# })
# @pytest.fixture(scope='class', autouse = True)
# def class_setup(self, request, django_db_blocker,
# model,
# var_setup,
# prepare,
# inherited_var_setup,
# diff_org_model,
# create_model,
# ):
# pass
class PersonPermissionsAPIPyTest(
PersonPermissionsAPITestCases,
):
pass

View File

@ -1,161 +1,88 @@
import pytest
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.serializers.entity_person import (
Person,
ModelSerializer
)
from access.tests.functional.entity.test_functional_entity_serializer import (
MockView,
EntitySerializerInheritedCases
)
class SerializerTestCases(
EntitySerializerInheritedCases,
class PersonSerializerTestCases(
EntitySerializerInheritedCases
):
create_model_serializer = ModelSerializer
"""Serializer to test"""
duplicate_f_name_l_name_dob: dict = {
'f_name': 'fred',
'm_name': 'D',
'l_name': 'Flinstone',
'dob': '2025-04-08',
parameterized_test_data: dict = {
"model_notes": {
'will_create': True,
},
"f_name": {
'will_create': False,
'exception_key': 'required'
},
"m_name": {
'will_create': True,
},
"l_name": {
'will_create': False,
'exception_key': 'required'
},
"dob": {
'will_create': True,
}
}
kwargs_create_item_duplicate_f_name_l_name_dob: dict = {
'f_name': 'fred',
'm_name': 'D',
'l_name': 'Flinstone',
'dob': '2025-04-08',
}
kwargs_create_item: dict = {
valid_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
model = Person
"""Model to test"""
valid_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Strange',
'dob': '2025-04-08',
}
"""Valid data used by serializer to create object"""
def test_serializer_validation_no_f_name_exception(self):
"""Serializer Validation Check
Ensure that when creating with valid data and field f_name is missing
a validation error occurs.
"""
data = self.valid_data.copy()
del data['f_name']
with pytest.raises(ValidationError) as err:
serializer = self.create_model_serializer(
data = data
)
serializer.is_valid(raise_exception = True)
assert err.value.get_codes()['f_name'][0] == 'required'
def test_serializer_validation_no_m_name(self):
"""Serializer Validation Check
Ensure that when creating with valid data and field f_name is missing
no validation error occurs.
"""
data = self.valid_data.copy()
del data['m_name']
serializer = self.create_model_serializer(
data = data
)
assert serializer.is_valid(raise_exception = True)
def test_serializer_validation_no_l_name_exception(self):
"""Serializer Validation Check
Ensure that when creating with valid data and field l_name is missing
a validation error occurs.
"""
data = self.valid_data.copy()
del data['l_name']
with pytest.raises(ValidationError) as err:
serializer = self.create_model_serializer(
data = data
)
serializer.is_valid(raise_exception = True)
assert err.value.get_codes()['l_name'][0] == 'required'
def test_serializer_validation_no_dob(self):
"""Serializer Validation Check
Ensure that when creating with valid data and field dob is missing
no validation error occurs.
"""
data = self.valid_data.copy()
del data['dob']
serializer = self.create_model_serializer(
data = data
)
assert serializer.is_valid(raise_exception = True)
def test_serializer_validation_duplicate_f_name_l_name_dob(self):
def test_serializer_validation_duplicate_f_name_l_name_dob(self, model, create_serializer):
"""Serializer Validation Check
Ensure that when creating with valid data and fields f_name, l_name and
dob already exists in the db a validation error occurs.
"""
self.model.objects.create(
organization = self.organization,
**self.kwargs_create_item_duplicate_f_name_l_name_dob
valid_data = self.valid_data.copy()
valid_data['f_name'] = 'duplicate'
valid_data['organization'] = self.organization
obj = model.objects.create(
**valid_data
)
data = self.duplicate_f_name_l_name_dob.copy()
valid_data['organization'] = self.organization.id
if 'email' in valid_data: # Contact Entity
valid_data['email'] = 'abc@xyz.qwe'
if 'name' in valid_data: # Company Entity
valid_data['name'] = 'diff'
if 'employee_number' in valid_data: # Employee Entity
valid_data['employee_number'] = 13579
view_set = MockView()
with pytest.raises(ValidationError) as err:
serializer = self.create_model_serializer(
data = data
serializer = create_serializer(
context = {
'view': view_set,
},
data = valid_data
)
serializer.is_valid(raise_exception = True)
@ -167,64 +94,18 @@ class SerializerTestCases(
class PersonSerializerInheritedCases(
SerializerTestCases,
PersonSerializerTestCases,
):
create_model_serializer = None
"""Serializer to test"""
duplicate_f_name_l_name_dob: dict = None
""" Duplicate model serializer dict
used for testing for duplicate f_name, l_name and dob fields.
"""
kwargs_create_item: dict = None
""" Model kwargs to create item"""
kwargs_create_item_duplicate_f_name_l_name_dob: dict = None
"""model kwargs to create object
**None:** Ensure that the fields of sub-model to person do not match
`self.duplicate_f_name_l_name_dob`. if they do the wrong exception will be thrown.
used for testing for duplicate f_name, l_name and dob fields.
"""
model = None
"""Model to test"""
parameterized_test_data: dict = None
valid_data: dict = None
"""Valid data used by serializer to create object"""
@classmethod
def setUpTestData(self):
"""Setup Test"""
self.duplicate_f_name_l_name_dob.update(
super().duplicate_f_name_l_name_dob
)
self.kwargs_create_item_duplicate_f_name_l_name_dob.update(
super().kwargs_create_item_duplicate_f_name_l_name_dob
)
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.valid_data.update(
super().valid_data
)
super().setUpTestData()
class PersonSerializerTest(
SerializerTestCases,
TestCase,
class PersonSerializerPyTest(
PersonSerializerTestCases,
):
pass
parameterized_test_data: dict = None

View File

@ -2,101 +2,38 @@ from django.test import TestCase
from access.models.person import Person
from access.tests.functional.entity.test_functional_entity_viewset import (
EntityMetadataInheritedCases,
EntityPermissionsAPIInheritedCases,
EntityViewSetInheritedCases
)
class ViewSetBase:
class ViewSetTestCases(
EntityViewSetInheritedCases,
):
add_data = {
add_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Strange',
'dob': '2025-04-08',
}
kwargs_create_item_diff_org = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
kwargs_create_item = {
kwargs_create_item: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Weird',
'dob': '2025-04-08',
}
kwargs_create_item_diff_org: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
model = Person
url_kwargs: dict = {}
url_view_kwargs: dict = {}
class PermissionsAPITestCases(
ViewSetBase,
EntityPermissionsAPIInheritedCases,
):
pass
class PersonPermissionsAPIInheritedCases(
PermissionsAPITestCases,
):
add_data: dict = None
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.add_data.update(
super().add_data
)
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
super().setUpTestData()
class PersonPermissionsAPITest(
PermissionsAPITestCases,
TestCase,
):
pass
class ViewSetTestCases(
ViewSetBase,
EntityViewSetInheritedCases,
):
pass
class PersonViewSetInheritedCases(
@ -105,21 +42,19 @@ class PersonViewSetInheritedCases(
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
super().setUpTestData()
@ -129,50 +64,4 @@ class PersonViewSetTest(
ViewSetTestCases,
TestCase,
):
pass
class MetadataTestCases(
ViewSetBase,
EntityMetadataInheritedCases,
):
pass
class PersonMetadataInheritedCases(
MetadataTestCases,
):
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
super().setUpTestData()
class PersonMetadataTest(
MetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,14 @@
import pytest
from access.models.company_base import Company
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Company
yield request.cls.model
del request.cls.model

View File

@ -0,0 +1,37 @@
from access.tests.unit.entity.test_unit_entity_api_fields import (
EntityAPIInheritedCases
)
class CompanyAPITestCases(
EntityAPIInheritedCases,
):
parameterized_test_data = {
'name': {
'expected': str
}
}
kwargs_create_item: dict = {
'name': 'Ian'
}
class CompanyAPIInheritedCases(
CompanyAPITestCases,
):
kwargs_create_item: dict = None
model = None
class CompanyAPIPyTest(
CompanyAPITestCases,
):
pass

View File

@ -0,0 +1,105 @@
import pytest
from django.db import models
from access.models.company_base import Company
from access.tests.unit.entity.test_unit_entity_model import (
EntityModelInheritedCases
)
class CompanyModelTestCases(
EntityModelInheritedCases,
):
kwargs_create_item: dict = {
'name': 'Ian',
}
sub_model_type = 'company'
"""Sub Model Type
sub-models must have this attribute defined in `ModelName.Meta.sub_model_type`
"""
parameterized_fields: dict = {
"name": {
'field_type': models.fields.CharField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
}
}
def test_class_inherits_company(self):
""" Class inheritence
TenancyObject must inherit SaveHistory
"""
assert issubclass(self.model, Company)
def test_attribute_value_history_app_label(self):
"""Attribute Type
history_app_label has been set, override this test case with the value
of attribute `history_app_label`
"""
assert self.model.history_app_label == 'access'
def test_attribute_value_history_model_name(self):
"""Attribute Type
history_model_name has been set, override this test case with the value
of attribute `history_model_name`
"""
assert self.model.history_model_name == 'company'
def test_function_value_get_url(self):
assert self.item.get_url() == '/api/v2/access/entity/company/' + str(self.item.id)
class CompanyModelInheritedCases(
CompanyModelTestCases,
):
"""Sub-Ticket Test Cases
Test Cases for Ticket models that inherit from model Entity
"""
kwargs_create_item: dict = {}
model = None
sub_model_type = None
"""Ticket Sub Model Type
Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type`
"""
class CompanyModelPyTest(
CompanyModelTestCases,
):
def test_function_value_get_related_model(self):
"""Function test
Confirm function `get_related_model` is None for base model
"""
assert self.item.get_related_model() is None

View File

@ -0,0 +1,36 @@
from django.test import TestCase
from access.models.company_base import Company
from access.tests.unit.entity.test_unit_entity_viewset import (
EntityViewsetInheritedCases
)
class ViewsetTestCases(
EntityViewsetInheritedCases,
):
model: str = Company
class CompanyViewsetInheritedCases(
ViewsetTestCases,
):
"""Sub-Entity Test Cases
Test Cases for Entity models that inherit from model Company
"""
model: str = None
"""name of the model to test"""
class CompanyViewsetTest(
ViewsetTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,14 @@
import pytest
from access.models.contact import Contact
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Contact
yield request.cls.model
del request.cls.model

View File

@ -0,0 +1,40 @@
from access.tests.unit.person.test_unit_person_api_fields import (
PersonAPIInheritedCases
)
class ContactAPITestCases(
PersonAPIInheritedCases,
):
parameterized_test_data = {
'email': {
'expected': str
},
'directory': {
'expected': bool
}
}
kwargs_create_item: dict = {
'email': 'ipfunny@unit.test',
}
class ContactAPIInheritedCases(
ContactAPITestCases,
):
kwargs_create_item: dict = None
model = None
class ContactAPIPyTest(
ContactAPITestCases,
):
pass

View File

@ -1,90 +0,0 @@
from django.test import TestCase
from access.models.contact import Contact
from access.tests.unit.person.test_unit_person_api_v2 import (
PersonAPIInheritedCases,
)
class APITestCases(
PersonAPIInheritedCases,
):
model = Contact
kwargs_item_create: dict = {
'email': 'ipfunny@unit.test',
}
url_ns_name = '_api_v2_entity_sub'
def test_api_field_exists_email(self):
""" Test for existance of API Field
email field must exist
"""
assert 'email' in self.api_data
def test_api_field_type_email(self):
""" Test for type for API Field
email field must be str
"""
assert type(self.api_data['email']) is str
def test_api_field_exists_directory(self):
""" Test for existance of API Field
directory field must exist
"""
assert 'directory' in self.api_data
def test_api_field_type_directory(self):
""" Test for type for API Field
directory field must be bool
"""
assert type(self.api_data['directory']) is bool
class ContactAPIInheritedCases(
APITestCases,
):
"""Sub-Entity Test Cases
Test Cases for Entity models that inherit from model Contact
"""
kwargs_item_create: dict = None
model = None
@classmethod
def setUpTestData(self):
self.kwargs_item_create.update(
super().kwargs_item_create
)
super().setUpTestData()
class ContactAPITest(
APITestCases,
TestCase,
):
pass

View File

@ -1,100 +1,110 @@
from django.db.models.fields import NOT_PROVIDED
from django.test import TestCase
import pytest
from django.db import models
from access.models.contact import Contact
from access.tests.unit.person.test_unit_person_model import (
Person,
PersonModelInheritedCases
)
class ModelTestCases(
class ContactModelTestCases(
PersonModelInheritedCases,
):
model = Contact
kwargs_item_create: dict = {
kwargs_create_item: dict = {
'email': 'ipweird@unit.test',
}
sub_model_type = 'contact'
"""Sub Model Type
sub-models must have this attribute defined in `ModelName.Meta.sub_model_type`
"""
parameterized_fields: dict = {
"email": {
'field_type': models.fields.CharField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
},
"directory": {
'field_type': models.fields.BooleanField,
'field_parameter_default_exists': True,
'field_parameter_default_value': True,
'field_parameter_verbose_name_type': str,
}
}
def test_model_field_directory_optional(self):
"""Test Field
def test_class_inherits_contact(self):
""" Class inheritence
Field `dob` must be an optional field
TenancyObject must inherit SaveHistory
"""
assert self.model._meta.get_field('directory').blank
assert issubclass(self.model, Contact)
def test_model_field_directory_optional_default(self):
"""Test Field
# def test_attribute_value_history_app_label(self):
# """Attribute Type
Field `directory` default value is `True`
# history_app_label has been set, override this test case with the value
# of attribute `history_app_label`
# """
# assert self.model.history_app_label == 'access'
def test_attribute_value_history_model_name(self):
"""Attribute Type
history_model_name has been set, override this test case with the value
of attribute `history_model_name`
"""
assert (
self.model._meta.get_field('directory').default is True
and self.model._meta.get_field('directory').null is False
)
assert self.model.history_model_name == 'contact'
def test_model_field_email_mandatory(self):
"""Test Field
Field `email` must be a mandatory field
"""
assert(
not (
self.model._meta.get_field('email').blank
and self.model._meta.get_field('email').null
)
and self.model._meta.get_field('email').default is NOT_PROVIDED
)
def test_model_inherits_person(self):
"""Test model inheritence
model must inherit from Entity sub-model `Person`
"""
assert issubclass(self.model, Person)
def test_function_value_get_url(self):
assert self.item.get_url() == '/api/v2/access/entity/contact/' + str(self.item.id)
class ContactModelInheritedCases(
ModelTestCases,
ContactModelTestCases,
):
"""Sub-Entity Test Cases
"""Sub-Ticket Test Cases
Test Cases for Entity models that inherit from model Contact
Test Cases for Ticket models that inherit from model Entity
"""
kwargs_item_create: dict = None
kwargs_create_item: dict = {}
model = None
@classmethod
def setUpTestData(self):
self.kwargs_item_create.update(
super().kwargs_item_create
)
super().setUpTestData()
sub_model_type = None
"""Ticket Sub Model Type
Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type`
"""
class ContactModelTest(
ModelTestCases,
TestCase,
class ContactModelPyTest(
ContactModelTestCases,
):
pass
def test_function_value_get_related_model(self):
"""Function test
Confirm function `get_related_model` is None for base model
"""
assert self.item.get_related_model() is None

View File

@ -0,0 +1,14 @@
import pytest
from access.models.entity import Entity
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Entity
yield request.cls.model
del request.cls.model

View File

@ -0,0 +1,78 @@
import pytest
from access.models.entity import Entity
from api.tests.unit.test_unit_api_fields import (
APIFieldsInheritedCases,
)
class EntityAPITestCases(
APIFieldsInheritedCases,
):
base_model = Entity
@pytest.fixture( scope = 'class')
def setup_model(self, request,
model,
):
if model != self.base_model:
request.cls.url_view_kwargs.update({
'entity_model': model._meta.sub_model_type,
})
@pytest.fixture( scope = 'class', autouse = True)
def class_setup(self,
setup_pre,
setup_model,
create_model,
setup_post,
):
pass
parameterized_test_data = {
'entity_type': {
'expected': str
},
'_urls.history': {
'expected': str
},
'_urls.knowledge_base': {
'expected': str
}
}
kwargs_create_item: dict = {
'entity_type': 'entity',
}
url_ns_name = '_api_v2_entity'
"""Url namespace (optional, if not required) and url name"""
class EntityAPIInheritedCases(
EntityAPITestCases,
):
kwargs_create_item: dict = None
model = None
url_ns_name = '_api_v2_entity_sub'
class EntityAPIPyTest(
EntityAPITestCases,
):
pass

View File

@ -1,219 +0,0 @@
import django
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import Client, TestCase
# from rest_framework.relations import Hyperlink
from access.models.entity import Entity
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.api_fields import APITenancyObject
User = django.contrib.auth.get_user_model()
class APITestCases(
APITenancyObject,
):
model = None
kwargs_item_create: dict = None
url_ns_name = None
"""Url namespace (optional, if not required) and url name"""
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
2. Create an item
"""
self.organization = Organization.objects.create(name='test_org')
self.item = self.model.objects.create(
organization = self.organization,
model_notes = 'random notes',
**self.kwargs_item_create
)
self.url_view_kwargs = {
'pk': self.item.id
}
if self.model._meta.model_name != 'entity':
self.url_view_kwargs.update({
'entity_model': self.item.entity_type,
})
view_permissions = Permission.objects.get(
codename = 'view_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = self.organization,
)
view_team.permissions.set([view_permissions])
self.view_user = User.objects.create_user(username="test_user_view", password="password")
TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
client = Client()
url = reverse('v2:' + self.url_ns_name + '-detail', kwargs=self.url_view_kwargs)
client.force_login(self.view_user)
response = client.get(url)
self.api_data = response.data
def test_api_field_exists_entity_type(self):
""" Test for existance of API Field
entity_type field must exist
"""
assert 'entity_type' in self.api_data
def test_api_field_type_entity_type(self):
""" Test for type for API Field
entity_type field must be str
"""
assert type(self.api_data['entity_type']) is str
def test_api_field_exists_url_history(self):
""" Test for existance of API Field
_urls.history field must exist
"""
assert 'history' in self.api_data['_urls']
def test_api_field_type_url_history(self):
""" Test for type for API Field
_urls.history field must be str
"""
assert type(self.api_data['_urls']['history']) is str
def test_api_field_type_url_history_value(self):
""" Test for url value
_urls.history field must use the endpoint for entity model
"""
assert str(self.api_data['_urls']['history']).endswith('/' + str(self.item._meta.app_label) + '/' + str(self.item._meta.model_name) + '/' + str(self.item.pk) + '/history')
def test_api_field_exists_url_knowledge_base(self):
""" Test for existance of API Field
_urls.knowledge_base field must exist
"""
assert 'knowledge_base' in self.api_data['_urls']
def test_api_field_type_url_knowledge_base(self):
""" Test for type for API Field
_urls.knowledge_base field must be str
"""
assert type(self.api_data['_urls']['knowledge_base']) is str
def test_api_field_type_url_knowledge_base_value(self):
""" Test for url value
_urls.knowledge_base field must use the endpoint for entity model
"""
assert str(self.api_data['_urls']['knowledge_base']).endswith('/assistance/entity/' + str(self.item.pk) + '/knowledge_base')
class EntityAPIInheritedCases(
APITestCases,
):
kwargs_item_create: dict = None
model = None
url_ns_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.kwargs_item_create.update({
'entity_type': self.model._meta.model_name
})
super().setUpTestData()
def test_api_field_exists_entity_value(self):
""" Test for value of API Field
entity_type field must match model name
"""
assert self.api_data['entity_type'] == self.model._meta.model_name
class EntityAPITest(
APITestCases,
TestCase,
):
kwargs_item_create: dict = None
model = Entity
url_ns_name = '_api_v2_entity'
@classmethod
def setUpTestData(self):
self.kwargs_item_create = {
'entity_type': 'entity'
}
super().setUpTestData()

View File

@ -1,27 +1,117 @@
from django.test import TestCase
import pytest
from django.db import models
from access.models.entity import Entity
from app.tests.unit.test_unit_models import (
TenancyObjectInheritedCases
PyTestTenancyObjectInheritedCases,
)
class EntityModelTestCases(
TenancyObjectInheritedCases,
PyTestTenancyObjectInheritedCases,
):
model = Entity
base_model = Entity
kwargs_item_create: dict = {}
kwargs_create_item: dict = {}
sub_model_type = 'entity'
"""Sub Model Type
sub-models must have this attribute defined in `ModelName.Meta.sub_model_type`
"""
parameterized_fields: dict = {
"entity_type": {
'field_type': models.fields.CharField,
'field_parameter_default_exists': False,
# 'field_parameter_default_value': 'entity',
'field_parameter_verbose_name_type': str
},
# "asset_number": {
# 'field_type': models.fields.CharField,
# 'field_parameter_default_exists': False,
# 'field_parameter_verbose_name_type': str,
# },
# "serial_number": {
# 'field_type': models.fields.CharField,
# 'field_parameter_default_exists': False,
# 'field_parameter_verbose_name_type': str,
# }
}
@pytest.fixture( scope = 'class')
def setup_model(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
})
yield
@pytest.fixture( scope = 'class', autouse = True)
def class_setup(self,
setup_model,
create_model,
):
pass
def test_class_inherits_entity(self):
""" Class inheritence
TenancyObject must inherit SaveHistory
"""
assert issubclass(self.model, Entity)
def test_attribute_type_history_app_label(self):
"""Attribute Type
history_app_name is of type str
history_app_label is of type str
"""
assert type(self.model.history_app_label) is str
@ -30,11 +120,11 @@ class EntityModelTestCases(
def test_attribute_value_history_app_label(self):
"""Attribute Type
history_app_name is of type str
history_app_label has been set, override this test case with the value
of attribute `history_app_label`
"""
assert self.model.history_app_label == self.model._meta.app_label
assert self.model.history_app_label == 'access'
@ -50,10 +140,11 @@ class EntityModelTestCases(
def test_attribute_value_history_model_name(self):
"""Attribute Type
history_model_name is of type str
history_model_name has been set, override this test case with the value
of attribute `history_model_name`
"""
assert self.model.history_model_name == self.model._meta.model_name
assert self.model.history_model_name == 'entity'
@ -69,7 +160,8 @@ class EntityModelTestCases(
def test_attribute_value_kb_model_name(self):
"""Attribute Type
kb_model_name is of type str
kb_model_name has been set, override this test case with the value
of attribute `kb_model_name`
"""
assert self.model.kb_model_name == 'entity'
@ -88,40 +180,95 @@ class EntityModelTestCases(
def test_attribute_value_note_basename(self):
"""Attribute Type
note_basename is of type str
note_basename has been set, override this test case with the value
of attribute `note_basename`
"""
assert self.model.note_basename == '_api_v2_entity_note'
# def test_function_is_property_get_model_type(self):
# """Function test
# Confirm function `get_model_type` is a property
# """
# assert type(self.model.get_model_type) is property
# def test_function_value_get_model_type(self):
# """Function test
# Confirm function `get_model_type` does not have a value of None
# value should be equaul to Meta.sub_model_type
# """
# assert self.item.get_model_type == self.item._meta.sub_model_type
def test_function_value_get_related_model(self):
"""Function test
Confirm function `get_related_model` is of the sub-model type
"""
assert type(self.item.get_related_model()) == self.model
def test_function_value_get_url(self):
assert self.item.get_url() == '/api/v2/access/entity/' + str(self.item.id)
class EntityModelInheritedCases(
EntityModelTestCases,
):
"""Sub-Entity Test Cases
"""Sub-Ticket Test Cases
Test Cases for Entity models that inherit from model Entity
Test Cases for Ticket models that inherit from model Entity
"""
kwargs_item_create: dict = None
kwargs_create_item: dict = {}
model = None
@classmethod
def setUpTestData(self):
sub_model_type = None
"""Ticket Sub Model Type
Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type`
"""
self.kwargs_item_create.update(
super().kwargs_item_create
)
super().setUpTestData()
# def test_function_value_get_model_type(self):
# """Function test
# Confirm function `get_model_type` does not have a value of None
# value should be equaul to Meta.sub_model_type
# """
# assert self.item.get_model_type == self.item._meta.sub_model_type
class EntityModelTest(
class EntityModelPyTest(
EntityModelTestCases,
TestCase,
):
pass
def test_function_value_get_related_model(self):
"""Function test
Confirm function `get_related_model` is None for base model
"""
assert self.item.get_related_model() is None

View File

@ -0,0 +1,14 @@
import pytest
from access.models.person import Person
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Person
yield request.cls.model
del request.cls.model

View File

@ -0,0 +1,50 @@
from access.tests.unit.entity.test_unit_entity_api_fields import (
EntityAPIInheritedCases
)
class PersonAPITestCases(
EntityAPIInheritedCases,
):
parameterized_test_data = {
'f_name': {
'expected': str
},
'm_name': {
'expected': str
},
'l_name': {
'expected': str
},
'dob': {
'expected': str
}
}
kwargs_create_item: dict = {
'entity_type': 'person',
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
class PersonAPIInheritedCases(
PersonAPITestCases,
):
kwargs_create_item: dict = None
model = None
class PersonAPIPyTest(
PersonAPITestCases,
):
pass

View File

@ -1,127 +0,0 @@
from django.test import TestCase
from access.models.person import Person
from access.tests.unit.entity.test_unit_entity_api_v2 import (
EntityAPIInheritedCases,
)
class APITestCases(
EntityAPIInheritedCases,
):
model = Person
kwargs_item_create: dict = {}
url_ns_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.kwargs_item_create.update({
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
})
super().setUpTestData()
def test_api_field_exists_f_name(self):
""" Test for existance of API Field
f_name field must exist
"""
assert 'f_name' in self.api_data
def test_api_field_type_f_name(self):
""" Test for type for API Field
f_name field must be str
"""
assert type(self.api_data['f_name']) is str
def test_api_field_exists_m_name(self):
""" Test for existance of API Field
m_name field must exist
"""
assert 'm_name' in self.api_data
def test_api_field_type_f_name(self):
""" Test for type for API Field
m_name field must be str
"""
assert type(self.api_data['m_name']) is str
def test_api_field_exists_l_name(self):
""" Test for existance of API Field
l_name field must exist
"""
assert 'l_name' in self.api_data
def test_api_field_type_f_name(self):
""" Test for type for API Field
l_name field must be str
"""
assert type(self.api_data['l_name']) is str
def test_api_field_exists_dob(self):
""" Test for existance of API Field
dob field must exist
"""
assert 'dob' in self.api_data
def test_api_field_type_dob(self):
""" Test for type for API Field
dob field must be str
"""
assert type(self.api_data['dob']) is str
class PersonAPIInheritedCases(
APITestCases,
):
"""Sub-Entity Test Cases
Test Cases for Entity models that inherit from model Person
"""
kwargs_item_create: dict = None
model = None
class PersonAPITest(
APITestCases,
TestCase,
):
pass

View File

@ -1,5 +1,6 @@
from django.db.models.fields import NOT_PROVIDED
from django.test import TestCase
import pytest
from django.db import models
from access.models.person import Person
from access.tests.unit.entity.test_unit_entity_model import (
@ -8,88 +9,114 @@ from access.tests.unit.entity.test_unit_entity_model import (
class ModelTestCases(
class PersonModelTestCases(
EntityModelInheritedCases,
):
model = Person
kwargs_item_create: dict = {
kwargs_create_item: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
sub_model_type = 'person'
"""Sub Model Type
sub-models must have this attribute defined in `ModelName.Meta.sub_model_type`
"""
def test_model_field_dob_optional(self):
"""Test Field
parameterized_fields: dict = {
"f_name": {
'field_type': models.fields.CharField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
},
"m_name": {
'field_type': models.fields.CharField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
},
"l_name": {
'field_type': models.fields.CharField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
},
"dob": {
'field_type': models.fields.DateField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
},
}
Field `dob` must be an optional field
def test_class_inherits_person(self):
""" Class inheritence
TenancyObject must inherit SaveHistory
"""
assert self.model._meta.get_field('dob').blank
assert issubclass(self.model, Person)
def test_model_field_f_name_mandatory(self):
"""Test Field
def test_attribute_value_history_app_label(self):
"""Attribute Type
Field `f_name` must be a mandatory field
history_app_label has been set, override this test case with the value
of attribute `history_app_label`
"""
assert(
not (
self.model._meta.get_field('f_name').blank
and self.model._meta.get_field('f_name').null
)
and self.model._meta.get_field('f_name').default is NOT_PROVIDED
)
assert self.model.history_app_label == 'access'
def test_model_field_l_name_mandatory(self):
"""Test Field
def test_attribute_value_history_model_name(self):
"""Attribute Type
Field `l_name` must be a mandatory field
history_model_name has been set, override this test case with the value
of attribute `history_model_name`
"""
assert (
not (
self.model._meta.get_field('l_name').blank
and self.model._meta.get_field('l_name').null
)
and self.model._meta.get_field('l_name').default is NOT_PROVIDED
)
assert self.model.history_model_name == 'person'
def test_function_value_get_url(self):
assert self.item.get_url() == '/api/v2/access/entity/person/' + str(self.item.id)
class PersonModelInheritedCases(
ModelTestCases,
PersonModelTestCases,
):
"""Sub-Entity Test Cases
"""Sub-Ticket Test Cases
Test Cases for Entity models that inherit from model Person
Test Cases for Ticket models that inherit from model Entity
"""
kwargs_item_create: dict = None
kwargs_create_item: dict = {}
model = None
@classmethod
def setUpTestData(self):
self.kwargs_item_create.update(
super().kwargs_item_create
)
super().setUpTestData()
sub_model_type = None
"""Ticket Sub Model Type
Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type`
"""
class PersonModelTest(
ModelTestCases,
TestCase,
class PersonModelPyTest(
PersonModelTestCases,
):
pass
def test_function_value_get_related_model(self):
"""Function test
Confirm function `get_related_model` is None for base model
"""
assert self.item.get_related_model() is None

View File

@ -396,7 +396,7 @@ class ReactUIMetadata(OverRideJSONAPIMetadata):
"display_name": "Tenancy",
"name": "tenant",
"icon": "organization",
"link": "/access/organization"
"link": "/access/tenant"
},
}
},
@ -587,9 +587,33 @@ class ReactUIMetadata(OverRideJSONAPIMetadata):
}
})
if request.feature_flag['2025-00002']:
if request.feature_flag['2025-00003']:
nav['access']['pages'].update({
'view_role': {
"display_name": "Roles",
"name": "roles",
"icon": 'roles',
"link": "/access/role"
}
})
if request.feature_flag['2025-00008']:
nav['access']['pages'].update({
'view_company': {
"display_name": "Companies",
"name": "organization",
"icon": 'organization',
"link": "/access/company"
}
})
nav['access']['pages'].update({
'view_contact': {
"display_name": "Directory",
@ -598,6 +622,7 @@ class ReactUIMetadata(OverRideJSONAPIMetadata):
}
})
if request.feature_flag['2025-00005']:
nav['human_resources']['pages'].update({
@ -610,17 +635,6 @@ class ReactUIMetadata(OverRideJSONAPIMetadata):
})
if request.feature_flag['2025-00003']:
nav['access']['pages'].update({
'view_role': {
"display_name": "Roles",
"name": "roles",
"icon": 'roles',
"link": "/access/role"
}
})
if request.feature_flag['2025-00004']:
nav['accounting']['pages'].update({

View File

@ -162,15 +162,18 @@ entity_type_names = str(entity_type_names)[:-1]
router.register('access', access_v2.Index, basename='_api_v2_access_home')
router.register('access/(?P<entity_model>[company]+)', entity.ViewSet, feature_flag = '2025-00008', basename='_api_v2_company')
router.register(f'access/entity/(?P<entity_model>[{entity_type_names}]+)?', entity.ViewSet, feature_flag = '2025-00002', basename='_api_v2_entity_sub')
router.register('access/entity', entity.NoDocsViewSet, feature_flag = '2025-00002', basename='_api_v2_entity')
router.register('access/entity/(?P<model_id>[0-9]+)/notes', entity_notes.ViewSet, feature_flag = '2025-00002', basename='_api_v2_entity_note')
router.register('access/organization', organization_v2.ViewSet, basename='_api_v2_organization')
router.register('access/organization/(?P<model_id>[0-9]+)/notes', organization_notes.ViewSet, basename='_api_v2_organization_note')
router.register('access/organization/(?P<organization_id>[0-9]+)/team', team_v2.ViewSet, basename='_api_v2_organization_team')
router.register('access/organization/(?P<organization_id>[0-9]+)/team/(?P<model_id>[0-9]+)/notes', team_notes.ViewSet, basename='_api_v2_team_note')
router.register('access/organization/(?P<organization_id>[0-9]+)/team/(?P<team_id>[0-9]+)/user', team_user_v2.ViewSet, basename='_api_v2_organization_team_user')
router.register('access/tenant', organization_v2.ViewSet, basename='_api_v2_organization')
router.register('access/tenant/(?P<model_id>[0-9]+)/notes', organization_notes.ViewSet, basename='_api_v2_organization_note')
router.register('access/tenant/(?P<organization_id>[0-9]+)/team', team_v2.ViewSet, basename='_api_v2_organization_team')
router.register('access/tenant/(?P<organization_id>[0-9]+)/team/(?P<model_id>[0-9]+)/notes', team_notes.ViewSet, basename='_api_v2_team_note')
router.register('access/tenant/(?P<organization_id>[0-9]+)/team/(?P<team_id>[0-9]+)/user', team_user_v2.ViewSet, basename='_api_v2_organization_team_user')
router.register('access/role', role.ViewSet, feature_flag = '2025-00003', basename='_api_v2_role')
router.register('access/role/(?P<model_id>[0-9]+)/notes', role_notes.ViewSet, feature_flag = '2025-00003', basename='_api_v2_role_note')

View File

@ -793,7 +793,8 @@ class SubModelViewSet(
def related_objects(self, model, model_kwarg):
"""Recursive relate_objects fetch
Fetch the model that is lowest in the chain of inherited models
Fetch the model where <model>._meta.sub_model_type matches the
model_kwarg value.
Args:
model (django.db.models.Model): The model to obtain the
@ -801,13 +802,15 @@ class SubModelViewSet(
model_kwarg (str): The URL Kwarg of the model.
Returns:
_type_: _description_
Model: The model for the ViewSet
"""
related_model = None
if model_kwarg:
is_nested_lookup = False
for related_object in model._meta.related_objects:
if(
@ -833,9 +836,25 @@ class SubModelViewSet(
related_model = self.related_objects(model = related_object.related_model, model_kwarg = model_kwarg)
is_nested_lookup = True
if related_model is None:
if not hasattr(related_model, '_meta'):
related_model = None
elif(
str(
getattr(related_model._meta, 'sub_model_type', '')
).lower().replace(' ', '_') == model_kwarg
):
break
if related_model is None and not is_nested_lookup:
related_model = self.base_model

View File

@ -685,6 +685,15 @@ if FEATURE_FLAGGING_ENABLED:
"created": "",
"modified": ""
}
},
{
"2025-00008": {
"name": "access.Company",
"description": "Company Entity Role. See https://github.com/nofusscomputing/centurion_erp/issues/704",
"enabled": True,
"created": "",
"modified": ""
}
}
]

View File

@ -402,23 +402,27 @@ class NonTenancyObjectInheritedCases(
class ModelFieldsTestCasesReWrite:
parameterized_fields: dict = {
"organization": {
'field_type': fields.Field,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str
},
"model_notes": {
'field_type': fields.TextField,
'field_parameter_verbose_name_type': str
},
"is_global": {
'field_type': fields.BooleanField,
'field_parameter_default_exists': True,
'field_parameter_default_value': False,
'field_parameter_verbose_name_type': str
@property
def parameterized_fields(self) -> dict:
return {
"organization": {
'field_type': fields.Field,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str
},
"model_notes": {
'field_type': fields.TextField,
'field_parameter_verbose_name_type': str
},
"is_global": {
'field_type': fields.BooleanField,
'field_parameter_default_exists': True,
'field_parameter_default_value': False,
'field_parameter_verbose_name_type': str
}
}
}

View File

@ -0,0 +1,18 @@
# Generated by Django 5.1.9 on 2025-05-15 07:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0026_alter_manufacturer_organization_and_more'),
]
operations = [
migrations.AlterField(
model_name='ticketlinkeditem',
name='item_type',
field=models.IntegerField(choices=[(1, 'Cluster'), (2, 'Config Group'), (3, 'Device'), (4, 'Operating System'), (5, 'Service'), (6, 'Software'), (7, 'Knowledge Base'), (8, 'Tenant'), (9, 'Team'), (10, 'Feature Flag'), (11, 'Software Version'), (12, 'Ticket Category'), (13, 'Ticket Comment Category'), (14, 'Project State'), (15, 'Git Repository'), (16, 'Entity'), (17, 'Role'), (18, 'Asset'), (19, 'IT Asset')], help_text='Python Model location for linked item', verbose_name='Item Type'),
),
]

View File

@ -0,0 +1,24 @@
import pytest
from human_resources.models.employee import Employee
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Employee
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from human_resources.serializers.entity_employee import ModelSerializer
yield ModelSerializer

View File

@ -0,0 +1,48 @@
from django.test import TestCase
from access.tests.functional.contact.test_functional_contact_metadata import (
ContactMetadataInheritedCases
)
from human_resources.models.employee import Employee
class EmployeeMetadataTestCases(
ContactMetadataInheritedCases,
):
add_data: dict = {
'employee_number': 123456,
}
kwargs_create_item: dict = {
'employee_number': 1234568,
}
kwargs_create_item_diff_org: dict = {
'employee_number': 1234567,
}
model = Employee
class EmployeeMetadataInheritedCases(
EmployeeMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
class EmployeeMetadataTest(
EmployeeMetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,40 @@
from access.tests.functional.contact.test_functional_contact_permission import (
ContactPermissionsAPIInheritedCases
)
class EmployeePermissionsAPITestCases(
ContactPermissionsAPIInheritedCases,
):
add_data: dict = {
'employee_number': 123456,
}
kwargs_create_item: dict = {
'employee_number': 1234568,
}
kwargs_create_item_diff_org: dict = {
'employee_number': 1234567,
}
class EmployeePermissionsAPIInheritedCases(
EmployeePermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
class EmployeePermissionsAPIPyTest(
EmployeePermissionsAPITestCases,
):
pass

View File

@ -1,133 +1,45 @@
import pytest
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from human_resources.serializers.entity_employee import (
Employee,
ModelSerializer
)
from access.tests.functional.contact.test_functional_contact_serializer import (
ContactSerializerInheritedCases
)
class SerializerTestCases(
ContactSerializerInheritedCases,
class EmployeeSerializerTestCases(
ContactSerializerInheritedCases
):
duplicate_f_name_l_name_dob = {
'email': 'contactentityduplicateone@unit.test',
'employee_number': 123456,
parameterized_test_data: dict = {
"employee_number": {
'will_create': False,
'exception_key': 'required'
}
}
kwargs_create_item: dict = {
'email': 'ipfunny@unit.test',
'employee_number': 1234567,
}
kwargs_create_item_duplicate_f_name_l_name_dob = {
'email': 'contactentityduplicatetwo@unit.test',
'employee_number': 1234568,
}
model = Employee
"""Model to test"""
create_model_serializer = ModelSerializer
"""Serializer to test"""
valid_data: dict = {
'email': 'ipweird@unit.test',
'employee_number': 1234569,
'employee_number': 123456,
}
"""Valid data used by serializer to create object"""
def test_serializer_validation_no_employee_number_exception(self):
"""Serializer Validation Check
Ensure that when creating with valid data and field employee_number is missing
a validation error occurs.
"""
data = self.valid_data.copy()
del data['employee_number']
with pytest.raises(ValidationError) as err:
serializer = self.create_model_serializer(
data = data
)
serializer.is_valid(raise_exception = True)
assert err.value.get_codes()['employee_number'][0] == 'required'
class ContactSerializerInheritedCases(
SerializerTestCases,
class EmployeeSerializerInheritedCases(
EmployeeSerializerTestCases,
):
create_model_serializer = None
"""Serializer to test"""
duplicate_f_name_l_name_dob: dict = None
""" Duplicate model serializer dict
used for testing for duplicate f_name, l_name and dob fields.
"""
kwargs_create_item: dict = None
""" Model kwargs to create item"""
kwargs_create_item_duplicate_f_name_l_name_dob: dict = None
"""model kwargs to create object
**None:** Ensure that the fields of sub-model to person do not match
`self.duplicate_f_name_l_name_dob`. if they do the wrong exception will be thrown.
used for testing for duplicate f_name, l_name and dob fields.
"""
model = None
"""Model to test"""
parameterized_test_data: dict = None
valid_data: dict = None
"""Valid data used by serializer to create object"""
@classmethod
def setUpTestData(self):
"""Setup Test"""
self.duplicate_f_name_l_name_dob.update(
super().duplicate_f_name_l_name_dob
)
self.kwargs_create_item_duplicate_f_name_l_name_dob.update(
super().kwargs_create_item_duplicate_f_name_l_name_dob
)
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.valid_data.update(
super().valid_data
)
super().setUpTestData()
class ContactSerializerTest(
SerializerTestCases,
TestCase,
class EmployeeSerializerPyTest(
EmployeeSerializerTestCases,
):
pass
parameterized_test_data: dict = None

View File

@ -1,8 +1,7 @@
from django.test import TestCase
from access.tests.functional.contact.test_functional_contact_viewset import (
ContactMetadataInheritedCases,
ContactPermissionsAPIInheritedCases,
ContactViewSetInheritedCases
)
@ -10,87 +9,23 @@ from human_resources.models.employee import Employee
class ViewSetBase:
add_data = {
'email': 'ipfunny@unit.test',
'employee_number': 123456,
}
kwargs_create_item_diff_org = {
'email': 'ipstrange@unit.test',
'employee_number': 1234567,
}
kwargs_create_item = {
'email': 'ipweird@unit.test',
'employee_number': 1234568,
}
model = Employee
url_kwargs: dict = {}
url_view_kwargs: dict = {}
class PermissionsAPITestCases(
ViewSetBase,
ContactPermissionsAPIInheritedCases,
):
pass
class EmployeePermissionsAPIInheritedCases(
PermissionsAPITestCases,
):
add_data: dict = None
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.add_data.update(
super().add_data
)
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
super().setUpTestData()
class EmployeePermissionsAPITest(
PermissionsAPITestCases,
TestCase,
):
pass
class ViewSetTestCases(
ViewSetBase,
ContactViewSetInheritedCases,
):
pass
add_data: dict = {
'employee_number': 123,
}
kwargs_create_item: dict = {
'employee_number': 456,
}
kwargs_create_item_diff_org: dict = {
'employee_number': 789,
}
model = Employee
@ -100,21 +35,19 @@ class EmployeeViewSetInheritedCases(
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
super().setUpTestData()
@ -124,50 +57,4 @@ class EmployeeViewSetTest(
ViewSetTestCases,
TestCase,
):
pass
class MetadataTestCases(
ViewSetBase,
ContactMetadataInheritedCases,
):
pass
class EmployeeMetadataInheritedCases(
MetadataTestCases,
):
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
super().setUpTestData()
class EmployeeMetadataTest(
MetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,14 @@
import pytest
from human_resources.models.employee import Employee
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Employee
yield request.cls.model
del request.cls.model

View File

@ -0,0 +1,37 @@
from access.tests.unit.contact.test_unit_contact_api_fields import (
ContactAPIInheritedCases
)
class EmployeeAPITestCases(
ContactAPIInheritedCases,
):
parameterized_test_data = {
'employee_number': {
'expected': int
}
}
kwargs_create_item: dict = {
'employee_number': 12345,
}
class EmployeeAPIInheritedCases(
EmployeeAPITestCases,
):
kwargs_create_item: dict = None
model = None
class EmployeeAPIPyTest(
EmployeeAPITestCases,
):
pass

View File

@ -1,73 +0,0 @@
from django.test import TestCase
from access.tests.unit.contact.test_unit_contact_api_v2 import (
ContactAPIInheritedCases,
)
from human_resources.models.employee import Employee
class APITestCases(
ContactAPIInheritedCases,
):
model = Employee
kwargs_item_create: dict = {
'email': 'ipfunny@unit.test',
'employee_number': 123456,
}
url_ns_name = '_api_v2_entity_sub'
def test_api_field_exists_employee_number(self):
""" Test for existance of API Field
employee_number field must exist
"""
assert 'employee_number' in self.api_data
def test_api_field_type_employee_number(self):
""" Test for type for API Field
employee_number field must be str
"""
assert type(self.api_data['employee_number']) is int
class EmployeeAPIInheritedCases(
APITestCases,
):
"""Sub-Entity Test Cases
Test Cases for Entity models that inherit from model Employee
"""
kwargs_item_create: dict = None
model = None
@classmethod
def setUpTestData(self):
self.kwargs_item_create.update(
super().kwargs_item_create
)
super().setUpTestData()
class EmployeeAPITest(
APITestCases,
TestCase,
):
pass

View File

@ -1,8 +1,8 @@
from django.db.models.fields import NOT_PROVIDED
from django.test import TestCase
import pytest
from django.db import models
from access.tests.unit.contact.test_unit_contact_model import (
Contact,
ContactModelInheritedCases
)
@ -10,72 +10,96 @@ from human_resources.models.employee import Employee
class ModelTestCases(
class EmployeeModelTestCases(
ContactModelInheritedCases,
):
model = Employee
kwargs_create_item: dict = {
'employee_number': 12345,
}
kwargs_item_create: dict = {
'email': 'ipweird@unit.test',
'employee_number': 123456,
sub_model_type = 'employee'
"""Sub Model Type
sub-models must have this attribute defined in `ModelName.Meta.sub_model_type`
"""
parameterized_fields: dict = {
"employee_number": {
'field_type': models.fields.IntegerField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
}
}
def test_model_field_employee_number_mandatory(self):
"""Test Field
def test_class_inherits_employee(self):
""" Class inheritence
Field `employee_number` must be a mandatory field
TenancyObject must inherit SaveHistory
"""
assert(
not (
self.model._meta.get_field('employee_number').blank
and self.model._meta.get_field('employee_number').null
)
and self.model._meta.get_field('employee_number').default is NOT_PROVIDED
)
assert issubclass(self.model, Employee)
def test_model_inherits_contact(self):
"""Test model inheritence
def test_attribute_value_history_app_label(self):
"""Attribute Type
model must inherit from Entity sub-model `Contact`
history_app_label has been set, override this test case with the value
of attribute `history_app_label`
"""
assert issubclass(self.model, Contact)
assert self.model.history_app_label == 'human_resources'
def test_attribute_value_history_model_name(self):
"""Attribute Type
history_model_name has been set, override this test case with the value
of attribute `history_model_name`
"""
assert self.model.history_model_name == 'employee'
def test_function_value_get_url(self):
assert self.item.get_url() == '/api/v2/access/entity/employee/' + str(self.item.id)
class EmployeeModelInheritedCases(
ModelTestCases,
EmployeeModelTestCases,
):
"""Sub-Entity Test Cases
"""Sub-Ticket Test Cases
Test Cases for Entity models that inherit from model Employee
Test Cases for Ticket models that inherit from model Entity
"""
kwargs_item_create: dict = None
kwargs_create_item: dict = {}
model = None
@classmethod
def setUpTestData(self):
self.kwargs_item_create.update(
super().kwargs_item_create
)
super().setUpTestData()
sub_model_type = None
"""Ticket Sub Model Type
Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type`
"""
class EmployeeModelTest(
ModelTestCases,
TestCase,
class EmployeeModelPyTest(
EmployeeModelTestCases,
):
pass
def test_function_value_get_related_model(self):
"""Function test
Confirm function `get_related_model` is None for base model
"""
assert self.item.get_related_model() is None

View File

@ -0,0 +1,9 @@
---
title: Company Entity
description: Centurion ERP Company Entity user documentation
date: 2025-04-04
template: project.html
about: https://github.com/nofusscomputing/centurion_erp
---
The Company model is a sub-model of entity. Its purpose is to be a base model for the different types of companies. With this in mind it is also the single location to fetch any type of company, regardless of its actual model name.

View File

@ -13,6 +13,8 @@ The Access module provides the multi-tenancy for this application. Tenancy is or
- [Contact / Corporate Directory](./contact.md)
- [Company](./company.md)
- [Tenant](./tenant.md)
- [Roles](./role.md)

View File

@ -169,6 +169,8 @@ nav:
- projects/centurion_erp/user/access/contact.md
- projects/centurion_erp/user/access/company.md
- projects/centurion_erp/user/access/role.md
- projects/centurion_erp/user/access/team.md