From 27e42fac560f3f8fed0d62d22ca54362389b4e73 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 17:44:20 +0930 Subject: [PATCH 01/42] feat(access): Organization -> Tenant Permission Migration ref: #761 #505 --- .../0027_alter_ticketlinkeditem_item_type.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 app/core/migrations/0027_alter_ticketlinkeditem_item_type.py diff --git a/app/core/migrations/0027_alter_ticketlinkeditem_item_type.py b/app/core/migrations/0027_alter_ticketlinkeditem_item_type.py new file mode 100644 index 00000000..903cd524 --- /dev/null +++ b/app/core/migrations/0027_alter_ticketlinkeditem_item_type.py @@ -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'), + ), + ] From b79874d0560a940ee88af3877e2b4beed9090ca0 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 17:46:57 +0930 Subject: [PATCH 02/42] chore(access): Update tenant url ref: #761 #505 --- app/api/react_ui_metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/react_ui_metadata.py b/app/api/react_ui_metadata.py index 02008bad..b5f9d309 100644 --- a/app/api/react_ui_metadata.py +++ b/app/api/react_ui_metadata.py @@ -396,7 +396,7 @@ class ReactUIMetadata(OverRideJSONAPIMetadata): "display_name": "Tenancy", "name": "tenant", "icon": "organization", - "link": "/access/organization" + "link": "/access/tenant" }, } }, From 34b2571a2e0c85293f04df6ad2124cd104079dbb Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 18:33:21 +0930 Subject: [PATCH 03/42] feat(access): New model access.Company ref: #761 #760 --- app/access/models/__init__.py | 1 + app/access/models/company_base.py | 102 ++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 app/access/models/company_base.py diff --git a/app/access/models/__init__.py b/app/access/models/__init__.py index c24760f8..de76019a 100644 --- a/app/access/models/__init__.py +++ b/app/access/models/__init__.py @@ -1,3 +1,4 @@ from . import contact +from . import company_base from . import person from . import role diff --git a/app/access/models/company_base.py b/app/access/models/company_base.py new file mode 100644 index 00000000..93a59ee3 --- /dev/null +++ b/app/access/models/company_base.py @@ -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() From 312267567fc93f171038b9fb656097885c689b49 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 18:33:52 +0930 Subject: [PATCH 04/42] feat(access): Serializer for model access.Company ref: #761 #760 --- app/access/serializers/entity_company.py | 70 ++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 app/access/serializers/entity_company.py diff --git a/app/access/serializers/entity_company.py b/app/access/serializers/entity_company.py new file mode 100644 index 00000000..88f65045 --- /dev/null +++ b/app/access/serializers/entity_company.py @@ -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) From 53f99f620cd4d400f27b7b2a3a6d7509bf0d3e75 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 18:34:03 +0930 Subject: [PATCH 05/42] feat(access): Migration for model access.Company ref: #761 #760 --- app/access/migrations/0010_company.py | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 app/access/migrations/0010_company.py diff --git a/app/access/migrations/0010_company.py b/app/access/migrations/0010_company.py new file mode 100644 index 00000000..5733a568 --- /dev/null +++ b/app/access/migrations/0010_company.py @@ -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',), + ), + ] From b2d1903009b275feb1fbbbb87d98d11af2185659 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 18:34:17 +0930 Subject: [PATCH 06/42] feat(access): URL route for model access.Company ref: #761 #760 --- app/api/react_ui_metadata.py | 38 ++++++++++++++++++++++++------------ app/api/urls_v2.py | 13 +++++++----- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/app/api/react_ui_metadata.py b/app/api/react_ui_metadata.py index b5f9d309..29093844 100644 --- a/app/api/react_ui_metadata.py +++ b/app/api/react_ui_metadata.py @@ -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({ diff --git a/app/api/urls_v2.py b/app/api/urls_v2.py index 14f3d0b1..6c656864 100644 --- a/app/api/urls_v2.py +++ b/app/api/urls_v2.py @@ -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[company]+)', entity.ViewSet, feature_flag = '2025-00008', basename='_api_v2_company') + router.register(f'access/entity/(?P[{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[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[0-9]+)/notes', organization_notes.ViewSet, basename='_api_v2_organization_note') -router.register('access/organization/(?P[0-9]+)/team', team_v2.ViewSet, basename='_api_v2_organization_team') -router.register('access/organization/(?P[0-9]+)/team/(?P[0-9]+)/notes', team_notes.ViewSet, basename='_api_v2_team_note') -router.register('access/organization/(?P[0-9]+)/team/(?P[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[0-9]+)/notes', organization_notes.ViewSet, basename='_api_v2_organization_note') +router.register('access/tenant/(?P[0-9]+)/team', team_v2.ViewSet, basename='_api_v2_organization_team') +router.register('access/tenant/(?P[0-9]+)/team/(?P[0-9]+)/notes', team_notes.ViewSet, basename='_api_v2_team_note') +router.register('access/tenant/(?P[0-9]+)/team/(?P[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[0-9]+)/notes', role_notes.ViewSet, feature_flag = '2025-00003', basename='_api_v2_role_note') From a51a79a76343be6c45485739d032adb2bf16e537 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 18:34:51 +0930 Subject: [PATCH 07/42] feat(access): model access.Company feature flag `2025-00008` ref: #761 #760 --- app/app/settings.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/app/settings.py b/app/app/settings.py index 59a3c1d4..cb039519 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -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": "" + } } ] From b0a6f207cdf79ae4d024e408920e507efd80df75 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 18:40:44 +0930 Subject: [PATCH 08/42] docs(user): Add Conpany page ref: #761 #760 --- docs/projects/centurion_erp/user/access/company.md | 9 +++++++++ docs/projects/centurion_erp/user/access/index.md | 2 ++ mkdocs.yml | 2 ++ 3 files changed, 13 insertions(+) create mode 100644 docs/projects/centurion_erp/user/access/company.md diff --git a/docs/projects/centurion_erp/user/access/company.md b/docs/projects/centurion_erp/user/access/company.md new file mode 100644 index 00000000..8984a625 --- /dev/null +++ b/docs/projects/centurion_erp/user/access/company.md @@ -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. diff --git a/docs/projects/centurion_erp/user/access/index.md b/docs/projects/centurion_erp/user/access/index.md index 30be322f..94c2c061 100644 --- a/docs/projects/centurion_erp/user/access/index.md +++ b/docs/projects/centurion_erp/user/access/index.md @@ -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) diff --git a/mkdocs.yml b/mkdocs.yml index 332bc0e1..922f130a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -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 From 2e9fe29e99491d2bdc9fa8f262bb25e7b7fde58a Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 19:45:04 +0930 Subject: [PATCH 09/42] fix(api): Correct ViewSet Sub-Model lookup ref: #761 --- app/api/viewsets/common.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/app/api/viewsets/common.py b/app/api/viewsets/common.py index bfee9eab..4a5a2629 100644 --- a/app/api/viewsets/common.py +++ b/app/api/viewsets/common.py @@ -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 ._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,23 @@ 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( + str( + related_model._meta.sub_model_type + ).lower().replace(' ', '_') == model_kwarg + ): + + break + + else: + + related_model = None + + + if related_model is None and not is_nested_lookup: related_model = self.base_model From 8e9dca56bc7b9d089b32793172b503d021a292af Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 20:02:39 +0930 Subject: [PATCH 10/42] refactor(access): Update Model Entity to use PyTest API Fields Render ref: #761 #730 --- app/access/tests/unit/entity/conftest.py | 14 ++ .../entity/test_unit_entity_api_fields.py | 78 +++++++ .../unit/entity/test_unit_entity_api_v2.py | 219 ------------------ 3 files changed, 92 insertions(+), 219 deletions(-) create mode 100644 app/access/tests/unit/entity/conftest.py create mode 100644 app/access/tests/unit/entity/test_unit_entity_api_fields.py delete mode 100644 app/access/tests/unit/entity/test_unit_entity_api_v2.py diff --git a/app/access/tests/unit/entity/conftest.py b/app/access/tests/unit/entity/conftest.py new file mode 100644 index 00000000..319de767 --- /dev/null +++ b/app/access/tests/unit/entity/conftest.py @@ -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 diff --git a/app/access/tests/unit/entity/test_unit_entity_api_fields.py b/app/access/tests/unit/entity/test_unit_entity_api_fields.py new file mode 100644 index 00000000..4c0a5ff4 --- /dev/null +++ b/app/access/tests/unit/entity/test_unit_entity_api_fields.py @@ -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 diff --git a/app/access/tests/unit/entity/test_unit_entity_api_v2.py b/app/access/tests/unit/entity/test_unit_entity_api_v2.py deleted file mode 100644 index aee1c266..00000000 --- a/app/access/tests/unit/entity/test_unit_entity_api_v2.py +++ /dev/null @@ -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() \ No newline at end of file From 08dbe1e35b31f4a52b9a220e6145f20677b47f1b Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 20:02:50 +0930 Subject: [PATCH 11/42] refactor(access): Update Model Contact to use PyTest API Fields Render ref: #761 #730 --- app/access/tests/unit/contact/conftest.py | 14 +++ .../contact/test_unit_contact_api_fields.py | 40 +++++++++ .../unit/contact/test_unit_contact_api_v2.py | 90 ------------------- 3 files changed, 54 insertions(+), 90 deletions(-) create mode 100644 app/access/tests/unit/contact/conftest.py create mode 100644 app/access/tests/unit/contact/test_unit_contact_api_fields.py delete mode 100644 app/access/tests/unit/contact/test_unit_contact_api_v2.py diff --git a/app/access/tests/unit/contact/conftest.py b/app/access/tests/unit/contact/conftest.py new file mode 100644 index 00000000..ed285d51 --- /dev/null +++ b/app/access/tests/unit/contact/conftest.py @@ -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 diff --git a/app/access/tests/unit/contact/test_unit_contact_api_fields.py b/app/access/tests/unit/contact/test_unit_contact_api_fields.py new file mode 100644 index 00000000..5558f4e2 --- /dev/null +++ b/app/access/tests/unit/contact/test_unit_contact_api_fields.py @@ -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 diff --git a/app/access/tests/unit/contact/test_unit_contact_api_v2.py b/app/access/tests/unit/contact/test_unit_contact_api_v2.py deleted file mode 100644 index 18a706c4..00000000 --- a/app/access/tests/unit/contact/test_unit_contact_api_v2.py +++ /dev/null @@ -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 From be02061b947c7621381574f8edac97f6da142185 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 20:03:02 +0930 Subject: [PATCH 12/42] refactor(access): Update Model Person to use PyTest API Fields Render ref: #761 #730 --- app/access/tests/unit/person/conftest.py | 14 ++ .../person/test_unit_person_api_fields.py | 50 +++++++ .../unit/person/test_unit_person_api_v2.py | 127 ------------------ 3 files changed, 64 insertions(+), 127 deletions(-) create mode 100644 app/access/tests/unit/person/conftest.py create mode 100644 app/access/tests/unit/person/test_unit_person_api_fields.py delete mode 100644 app/access/tests/unit/person/test_unit_person_api_v2.py diff --git a/app/access/tests/unit/person/conftest.py b/app/access/tests/unit/person/conftest.py new file mode 100644 index 00000000..f0a1063e --- /dev/null +++ b/app/access/tests/unit/person/conftest.py @@ -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 diff --git a/app/access/tests/unit/person/test_unit_person_api_fields.py b/app/access/tests/unit/person/test_unit_person_api_fields.py new file mode 100644 index 00000000..be1cbc2b --- /dev/null +++ b/app/access/tests/unit/person/test_unit_person_api_fields.py @@ -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 diff --git a/app/access/tests/unit/person/test_unit_person_api_v2.py b/app/access/tests/unit/person/test_unit_person_api_v2.py deleted file mode 100644 index b652ba32..00000000 --- a/app/access/tests/unit/person/test_unit_person_api_v2.py +++ /dev/null @@ -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 From e295e53f86713d3077a01d9ff86a913c25b591cc Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 20:03:19 +0930 Subject: [PATCH 13/42] refactor(human_resources): Update Model Employee to use PyTest API Fields Render ref: #761 #730 --- .../tests/unit/employee/conftest.py | 14 ++++ .../employee/test_unit_employee_api_fields.py | 37 ++++++++++ .../employee/test_unit_employee_api_v2.py | 73 ------------------- 3 files changed, 51 insertions(+), 73 deletions(-) create mode 100644 app/human_resources/tests/unit/employee/conftest.py create mode 100644 app/human_resources/tests/unit/employee/test_unit_employee_api_fields.py delete mode 100644 app/human_resources/tests/unit/employee/test_unit_employee_api_v2.py diff --git a/app/human_resources/tests/unit/employee/conftest.py b/app/human_resources/tests/unit/employee/conftest.py new file mode 100644 index 00000000..9300eb74 --- /dev/null +++ b/app/human_resources/tests/unit/employee/conftest.py @@ -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 diff --git a/app/human_resources/tests/unit/employee/test_unit_employee_api_fields.py b/app/human_resources/tests/unit/employee/test_unit_employee_api_fields.py new file mode 100644 index 00000000..2397edba --- /dev/null +++ b/app/human_resources/tests/unit/employee/test_unit_employee_api_fields.py @@ -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 diff --git a/app/human_resources/tests/unit/employee/test_unit_employee_api_v2.py b/app/human_resources/tests/unit/employee/test_unit_employee_api_v2.py deleted file mode 100644 index b7db910c..00000000 --- a/app/human_resources/tests/unit/employee/test_unit_employee_api_v2.py +++ /dev/null @@ -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 From 3c19e9d4cf02190159a9b480eac6b01697b48074 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 20:19:43 +0930 Subject: [PATCH 14/42] fix(api): Dont try to access attribute if not exist in common viewset ref: #761 --- app/api/viewsets/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/viewsets/common.py b/app/api/viewsets/common.py index 4a5a2629..29c2435e 100644 --- a/app/api/viewsets/common.py +++ b/app/api/viewsets/common.py @@ -841,7 +841,7 @@ class SubModelViewSet( if( str( - related_model._meta.sub_model_type + getattr(related_model, '._meta.sub_model_type', '') ).lower().replace(' ', '_') == model_kwarg ): From c2a367bd282d7693d33ec71af6a9940399ddfada Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:15:43 +0930 Subject: [PATCH 15/42] refactor(human_resources): Update Model Employee to use PyTest for Model Test Suite ref: #761 #730 --- .../unit/employee/test_unit_employee_model.py | 102 +++++++++++------- 1 file changed, 63 insertions(+), 39 deletions(-) diff --git a/app/human_resources/tests/unit/employee/test_unit_employee_model.py b/app/human_resources/tests/unit/employee/test_unit_employee_model.py index 87f45428..045886f1 100644 --- a/app/human_resources/tests/unit/employee/test_unit_employee_model.py +++ b/app/human_resources/tests/unit/employee/test_unit_employee_model.py @@ -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 From cf7eeb5bde5512f4c552b22ecb90303302aedd7a Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:16:45 +0930 Subject: [PATCH 16/42] refactor(access): Update Model Person to use PyTest for Model Test Suite ref: #761 #730 --- ...y_entity_type_alter_person_dob_and_more.py | 28 ++++ app/access/models/person.py | 2 - .../unit/person/test_unit_person_model.py | 121 +++++++++++------- 3 files changed, 102 insertions(+), 49 deletions(-) create mode 100644 app/access/migrations/0011_alter_entity_entity_type_alter_person_dob_and_more.py diff --git a/app/access/migrations/0011_alter_entity_entity_type_alter_person_dob_and_more.py b/app/access/migrations/0011_alter_entity_entity_type_alter_person_dob_and_more.py new file mode 100644 index 00000000..b9c25071 --- /dev/null +++ b/app/access/migrations/0011_alter_entity_entity_type_alter_person_dob_and_more.py @@ -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)'), + ), + ] diff --git a/app/access/models/person.py b/app/access/models/person.py index fe4ca11c..61c73335 100644 --- a/app/access/models/person.py +++ b/app/access/models/person.py @@ -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, diff --git a/app/access/tests/unit/person/test_unit_person_model.py b/app/access/tests/unit/person/test_unit_person_model.py index 94a86229..ee3a1b46 100644 --- a/app/access/tests/unit/person/test_unit_person_model.py +++ b/app/access/tests/unit/person/test_unit_person_model.py @@ -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 From 1150e1b04797bddcf97e3da707c739272511915a Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:16:56 +0930 Subject: [PATCH 17/42] refactor(access): Update Model Contact to use PyTest for Model Test Suite ref: #761 #730 --- .../unit/contact/test_unit_contact_model.py | 122 ++++++++++-------- 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/app/access/tests/unit/contact/test_unit_contact_model.py b/app/access/tests/unit/contact/test_unit_contact_model.py index 10335e32..cd7509c8 100644 --- a/app/access/tests/unit/contact/test_unit_contact_model.py +++ b/app/access/tests/unit/contact/test_unit_contact_model.py @@ -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 From d29df73d05c5c9b22f7ba485dbcae93f24663a01 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:17:05 +0930 Subject: [PATCH 18/42] refactor(access): Update Model Entity to use PyTest for Model Test Suite ref: #761 #730 --- app/access/models/entity.py | 2 +- .../unit/entity/test_unit_entity_model.py | 202 +++++++++++++++--- app/app/tests/unit/test_unit_models.py | 36 ++-- 3 files changed, 197 insertions(+), 43 deletions(-) diff --git a/app/access/models/entity.py b/app/access/models/entity.py index 29b4584e..ae602311 100644 --- a/app/access/models/entity.py +++ b/app/access/models/entity.py @@ -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, diff --git a/app/access/tests/unit/entity/test_unit_entity_model.py b/app/access/tests/unit/entity/test_unit_entity_model.py index bb29c6ed..acf45745 100644 --- a/app/access/tests/unit/entity/test_unit_entity_model.py +++ b/app/access/tests/unit/entity/test_unit_entity_model.py @@ -1,27 +1,120 @@ -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 + + with django_db_blocker.unblock(): + + del request.cls.kwargs_create_item + + + @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 +123,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 +143,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 +163,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 +183,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 diff --git a/app/app/tests/unit/test_unit_models.py b/app/app/tests/unit/test_unit_models.py index 8233438a..1fb76de8 100644 --- a/app/app/tests/unit/test_unit_models.py +++ b/app/app/tests/unit/test_unit_models.py @@ -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 + } } - } From 2a977bbf471548380d82edfec34f497ebbf253b6 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:18:29 +0930 Subject: [PATCH 19/42] test(access): Model Test Suite Company model ref: #761 #760 --- app/access/tests/unit/company/conftest.py | 14 +++ .../unit/company/test_unit_company_model.py | 105 ++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 app/access/tests/unit/company/conftest.py create mode 100644 app/access/tests/unit/company/test_unit_company_model.py diff --git a/app/access/tests/unit/company/conftest.py b/app/access/tests/unit/company/conftest.py new file mode 100644 index 00000000..78945022 --- /dev/null +++ b/app/access/tests/unit/company/conftest.py @@ -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 diff --git a/app/access/tests/unit/company/test_unit_company_model.py b/app/access/tests/unit/company/test_unit_company_model.py new file mode 100644 index 00000000..c47efa7f --- /dev/null +++ b/app/access/tests/unit/company/test_unit_company_model.py @@ -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 From 900e03791a33b44274d8d4682abc2261ba3efd8a Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:18:45 +0930 Subject: [PATCH 20/42] test(access): API field render Test Suite Company model ref: #761 #760 --- .../company/test_unit_company_api_fields.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 app/access/tests/unit/company/test_unit_company_api_fields.py diff --git a/app/access/tests/unit/company/test_unit_company_api_fields.py b/app/access/tests/unit/company/test_unit_company_api_fields.py new file mode 100644 index 00000000..2fe835ec --- /dev/null +++ b/app/access/tests/unit/company/test_unit_company_api_fields.py @@ -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 From edc7aedbe03c59007358a3e5e48018e201db5c5c Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:34:12 +0930 Subject: [PATCH 21/42] fix(api): Dont try to access attribute if not exist in common viewset ref: #761 --- .../tests/unit/entity/test_unit_entity_model.py | 3 --- app/api/viewsets/common.py | 12 +++++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/access/tests/unit/entity/test_unit_entity_model.py b/app/access/tests/unit/entity/test_unit_entity_model.py index acf45745..fb7290bf 100644 --- a/app/access/tests/unit/entity/test_unit_entity_model.py +++ b/app/access/tests/unit/entity/test_unit_entity_model.py @@ -87,9 +87,6 @@ class EntityModelTestCases( yield - with django_db_blocker.unblock(): - - del request.cls.kwargs_create_item @pytest.fixture( scope = 'class', autouse = True) diff --git a/app/api/viewsets/common.py b/app/api/viewsets/common.py index 29c2435e..03edde97 100644 --- a/app/api/viewsets/common.py +++ b/app/api/viewsets/common.py @@ -839,17 +839,19 @@ class SubModelViewSet( is_nested_lookup = True - if( + + if not hasattr(related_model, '_meta'): + + related_model = None + + elif( str( - getattr(related_model, '._meta.sub_model_type', '') + getattr(related_model._meta, 'sub_model_type', '') ).lower().replace(' ', '_') == model_kwarg ): break - else: - - related_model = None if related_model is None and not is_nested_lookup: From bdeae5b38b5323da07066514717ddf5408a95f8a Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 23:07:08 +0930 Subject: [PATCH 22/42] test(access): ViewSet Test Suite Company model ref: #761 #760 --- .../unit/company/test_unit_company_viewset.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 app/access/tests/unit/company/test_unit_company_viewset.py diff --git a/app/access/tests/unit/company/test_unit_company_viewset.py b/app/access/tests/unit/company/test_unit_company_viewset.py new file mode 100644 index 00000000..97bc62b9 --- /dev/null +++ b/app/access/tests/unit/company/test_unit_company_viewset.py @@ -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 From 85986ce13e14892bc8423eb66a2d3b6849fb549a Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:14:23 +0930 Subject: [PATCH 23/42] test(access): Functional MetaData Test Suite Company model ref: #761 #760 --- .../tests/functional/company/conftest.py | 24 +++++++ .../test_functional_company_metadata.py | 72 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 app/access/tests/functional/company/conftest.py create mode 100644 app/access/tests/functional/company/test_functional_company_metadata.py diff --git a/app/access/tests/functional/company/conftest.py b/app/access/tests/functional/company/conftest.py new file mode 100644 index 00000000..f9822817 --- /dev/null +++ b/app/access/tests/functional/company/conftest.py @@ -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 diff --git a/app/access/tests/functional/company/test_functional_company_metadata.py b/app/access/tests/functional/company/test_functional_company_metadata.py new file mode 100644 index 00000000..c882ac34 --- /dev/null +++ b/app/access/tests/functional/company/test_functional_company_metadata.py @@ -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 \ No newline at end of file From 46494c033c704a8609e2d1f5fcb24363f82af851 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:14:34 +0930 Subject: [PATCH 24/42] test(access): Functional Permissions Test Suite Company model ref: #761 #760 --- .../test_functional_company_permission.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 app/access/tests/functional/company/test_functional_company_permission.py diff --git a/app/access/tests/functional/company/test_functional_company_permission.py b/app/access/tests/functional/company/test_functional_company_permission.py new file mode 100644 index 00000000..df8239a8 --- /dev/null +++ b/app/access/tests/functional/company/test_functional_company_permission.py @@ -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 From 99c0e923369851c607482f56bc15d60ffa4d68ac Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:14:50 +0930 Subject: [PATCH 25/42] test(access): Functional Serializer Test Suite Company model ref: #761 #760 --- .../test_functional_company_serializer.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 app/access/tests/functional/company/test_functional_company_serializer.py diff --git a/app/access/tests/functional/company/test_functional_company_serializer.py b/app/access/tests/functional/company/test_functional_company_serializer.py new file mode 100644 index 00000000..bafa2650 --- /dev/null +++ b/app/access/tests/functional/company/test_functional_company_serializer.py @@ -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 From 7fee33999fad625181f0acbbd184a33482bf5dfa Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:15:56 +0930 Subject: [PATCH 26/42] refactor(access): Update Functional Metadata to use PyTest for Contact Model ref: #761 #730 --- .../tests/functional/contact/conftest.py | 24 +++++++ .../test_functional_contact_metadata.py | 65 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 app/access/tests/functional/contact/conftest.py create mode 100644 app/access/tests/functional/contact/test_functional_contact_metadata.py diff --git a/app/access/tests/functional/contact/conftest.py b/app/access/tests/functional/contact/conftest.py new file mode 100644 index 00000000..81e40636 --- /dev/null +++ b/app/access/tests/functional/contact/conftest.py @@ -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 diff --git a/app/access/tests/functional/contact/test_functional_contact_metadata.py b/app/access/tests/functional/contact/test_functional_contact_metadata.py new file mode 100644 index 00000000..76125ef4 --- /dev/null +++ b/app/access/tests/functional/contact/test_functional_contact_metadata.py @@ -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 \ No newline at end of file From eb97bfbeeb63047ba753d8b02579383621c2ebaf Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:16:09 +0930 Subject: [PATCH 27/42] refactor(access): Update Functional Metadata to use PyTest for Entity Model ref: #761 #730 --- .../tests/functional/entity/conftest.py | 24 ++ .../entity/test_functional_entity_metadata.py | 260 ++++++++++++++++++ 2 files changed, 284 insertions(+) create mode 100644 app/access/tests/functional/entity/conftest.py create mode 100644 app/access/tests/functional/entity/test_functional_entity_metadata.py diff --git a/app/access/tests/functional/entity/conftest.py b/app/access/tests/functional/entity/conftest.py new file mode 100644 index 00000000..1c393761 --- /dev/null +++ b/app/access/tests/functional/entity/conftest.py @@ -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 diff --git a/app/access/tests/functional/entity/test_functional_entity_metadata.py b/app/access/tests/functional/entity/test_functional_entity_metadata.py new file mode 100644 index 00000000..55f0d01d --- /dev/null +++ b/app/access/tests/functional/entity/test_functional_entity_metadata.py @@ -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' From d0c35377531fc546785d1bc9cda7a2394238f2eb Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:16:19 +0930 Subject: [PATCH 28/42] refactor(access): Update Functional Metadata to use PyTest for Person Model ref: #761 #730 --- .../person/test_functional_person_metadata.py | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 app/access/tests/functional/person/test_functional_person_metadata.py diff --git a/app/access/tests/functional/person/test_functional_person_metadata.py b/app/access/tests/functional/person/test_functional_person_metadata.py new file mode 100644 index 00000000..f59f57bf --- /dev/null +++ b/app/access/tests/functional/person/test_functional_person_metadata.py @@ -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 \ No newline at end of file From 387ffc9adedc92fdfff68de0dbd3a80a049c6360 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:16:38 +0930 Subject: [PATCH 29/42] refactor(human_resources): Update Functional Metadata to use PyTest for Employee Model ref: #761 #730 --- .../test_functional_employee_metadata.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 app/human_resources/tests/functional/employee/test_functional_employee_metadata.py diff --git a/app/human_resources/tests/functional/employee/test_functional_employee_metadata.py b/app/human_resources/tests/functional/employee/test_functional_employee_metadata.py new file mode 100644 index 00000000..df058e46 --- /dev/null +++ b/app/human_resources/tests/functional/employee/test_functional_employee_metadata.py @@ -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 \ No newline at end of file From 7a1aecebd5b2c0c111cf7e3e03aaeba2dba9db30 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:16:49 +0930 Subject: [PATCH 30/42] refactor(human_resources): Update Functional Permissions to use PyTest for Employee Model ref: #761 #730 --- .../test_functional_employee_permission.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 app/human_resources/tests/functional/employee/test_functional_employee_permission.py diff --git a/app/human_resources/tests/functional/employee/test_functional_employee_permission.py b/app/human_resources/tests/functional/employee/test_functional_employee_permission.py new file mode 100644 index 00000000..63b22d6e --- /dev/null +++ b/app/human_resources/tests/functional/employee/test_functional_employee_permission.py @@ -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 From ae6b66b27077a791ded1fa9ec4dbcdd07eb9c1ea Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:16:57 +0930 Subject: [PATCH 31/42] refactor(human_resources): Update Functional Serializer to use PyTest for Employee Model ref: #761 #730 --- .../test_functional_employee_serializer.py | 120 +++--------------- 1 file changed, 16 insertions(+), 104 deletions(-) diff --git a/app/human_resources/tests/functional/employee/test_functional_employee_serializer.py b/app/human_resources/tests/functional/employee/test_functional_employee_serializer.py index 0fc35af7..e71756b9 100644 --- a/app/human_resources/tests/functional/employee/test_functional_employee_serializer.py +++ b/app/human_resources/tests/functional/employee/test_functional_employee_serializer.py @@ -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 From 97e37f34a195fc74adb445c31299f1b5c682afe3 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:17:10 +0930 Subject: [PATCH 32/42] refactor(Access): Update Functional Serializer to use PyTest for Person Model ref: #761 #730 --- .../test_functional_person_serializer.py | 233 +++++------------- 1 file changed, 57 insertions(+), 176 deletions(-) diff --git a/app/access/tests/functional/person/test_functional_person_serializer.py b/app/access/tests/functional/person/test_functional_person_serializer.py index 180d8673..159b51b0 100644 --- a/app/access/tests/functional/person/test_functional_person_serializer.py +++ b/app/access/tests/functional/person/test_functional_person_serializer.py @@ -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 From 254cd02649a6715f2adc8369068efeb98f09ee3e Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:17:21 +0930 Subject: [PATCH 33/42] refactor(Access): Update Functional Serializer to use PyTest for Entity Model ref: #761 #730 --- .../test_functional_entity_serializer.py | 209 +++++++++++++----- 1 file changed, 158 insertions(+), 51 deletions(-) diff --git a/app/access/tests/functional/entity/test_functional_entity_serializer.py b/app/access/tests/functional/entity/test_functional_entity_serializer.py index 6b6e8c01..95348bb2 100644 --- a/app/access/tests/functional/entity/test_functional_entity_serializer.py +++ b/app/access/tests/functional/entity/test_functional_entity_serializer.py @@ -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 From 3505f915c5ba0b0d5992d234363be1ec84aa7b36 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:17:31 +0930 Subject: [PATCH 34/42] refactor(Access): Update Functional Serializer to use PyTest for Contact Model ref: #761 #730 --- .../test_functional_contact_serializer.py | 115 +++--------------- 1 file changed, 16 insertions(+), 99 deletions(-) diff --git a/app/access/tests/functional/contact/test_functional_contact_serializer.py b/app/access/tests/functional/contact/test_functional_contact_serializer.py index c4b278d3..73c6dc5f 100644 --- a/app/access/tests/functional/contact/test_functional_contact_serializer.py +++ b/app/access/tests/functional/contact/test_functional_contact_serializer.py @@ -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 From 2e096cb495bc6781d56b21571df57a20cfa14f06 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:17:44 +0930 Subject: [PATCH 35/42] refactor(Access): Update Functional Permission to use PyTest for Contact Model ref: #761 #730 --- .../test_functional_contact_permission.py | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 app/access/tests/functional/contact/test_functional_contact_permission.py diff --git a/app/access/tests/functional/contact/test_functional_contact_permission.py b/app/access/tests/functional/contact/test_functional_contact_permission.py new file mode 100644 index 00000000..96811ef7 --- /dev/null +++ b/app/access/tests/functional/contact/test_functional_contact_permission.py @@ -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 From 516d6fc1364644d9cbe493ad02b040fce45a7c00 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:17:52 +0930 Subject: [PATCH 36/42] refactor(Access): Update Functional Permission to use PyTest for Entity Model ref: #761 #730 --- .../test_functional_entity_permission.py | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 app/access/tests/functional/entity/test_functional_entity_permission.py diff --git a/app/access/tests/functional/entity/test_functional_entity_permission.py b/app/access/tests/functional/entity/test_functional_entity_permission.py new file mode 100644 index 00000000..4f725f47 --- /dev/null +++ b/app/access/tests/functional/entity/test_functional_entity_permission.py @@ -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 From 8bba04305fe9d983e89aafc5dac7c3d811307796 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:18:00 +0930 Subject: [PATCH 37/42] refactor(Access): Update Functional Permission to use PyTest for Person Model ref: #761 #730 --- .../test_functional_person_permission.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 app/access/tests/functional/person/test_functional_person_permission.py diff --git a/app/access/tests/functional/person/test_functional_person_permission.py b/app/access/tests/functional/person/test_functional_person_permission.py new file mode 100644 index 00000000..983a4f01 --- /dev/null +++ b/app/access/tests/functional/person/test_functional_person_permission.py @@ -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 From a4d1a2bf76a7d723a88fc4bc0611ef4663ae31e8 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 03:07:28 +0930 Subject: [PATCH 38/42] test(access): Functional ViewSet Test Suite Company model ref: #761 #760 --- .../test_functional_company_viewset.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 app/access/tests/functional/company/test_functional_company_viewset.py diff --git a/app/access/tests/functional/company/test_functional_company_viewset.py b/app/access/tests/functional/company/test_functional_company_viewset.py new file mode 100644 index 00000000..253f6fb2 --- /dev/null +++ b/app/access/tests/functional/company/test_functional_company_viewset.py @@ -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 From c39f479b96955973deeacc77b4c25a27eba3311d Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 03:07:46 +0930 Subject: [PATCH 39/42] refactor(Access): Update Functional ViewSet to use PyTest for Contact Model ref: #761 #730 --- .../test_functional_contact_viewset.py | 153 +++--------------- 1 file changed, 21 insertions(+), 132 deletions(-) diff --git a/app/access/tests/functional/contact/test_functional_contact_viewset.py b/app/access/tests/functional/contact/test_functional_contact_viewset.py index d7754f8d..1f52aca1 100644 --- a/app/access/tests/functional/contact/test_functional_contact_viewset.py +++ b/app/access/tests/functional/contact/test_functional_contact_viewset.py @@ -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 From f1016bd9cc7b7547262a36ad9de66770372b6e83 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 03:07:57 +0930 Subject: [PATCH 40/42] refactor(Access): Update Functional ViewSet to use PyTest for Entity Model ref: #761 #730 --- .../entity/test_functional_entity_viewset.py | 324 +++--------------- 1 file changed, 41 insertions(+), 283 deletions(-) diff --git a/app/access/tests/functional/entity/test_functional_entity_viewset.py b/app/access/tests/functional/entity/test_functional_entity_viewset.py index e0445551..7e822b48 100644 --- a/app/access/tests/functional/entity/test_functional_entity_viewset.py +++ b/app/access/tests/functional/entity/test_functional_entity_viewset.py @@ -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 - - From 07277862cf51649f44ed522ff742b8850f531967 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 03:08:05 +0930 Subject: [PATCH 41/42] refactor(Access): Update Functional ViewSet to use PyTest for Person Model ref: #761 #730 --- .../tests/functional/person/conftest.py | 24 +++ .../person/test_functional_person_viewset.py | 151 +++--------------- 2 files changed, 44 insertions(+), 131 deletions(-) create mode 100644 app/access/tests/functional/person/conftest.py diff --git a/app/access/tests/functional/person/conftest.py b/app/access/tests/functional/person/conftest.py new file mode 100644 index 00000000..77450c9a --- /dev/null +++ b/app/access/tests/functional/person/conftest.py @@ -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 diff --git a/app/access/tests/functional/person/test_functional_person_viewset.py b/app/access/tests/functional/person/test_functional_person_viewset.py index bda095e0..3373092b 100644 --- a/app/access/tests/functional/person/test_functional_person_viewset.py +++ b/app/access/tests/functional/person/test_functional_person_viewset.py @@ -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 From 1663f19b2a1f3464e83d2b01d58fae4b0e166476 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 03:10:00 +0930 Subject: [PATCH 42/42] refactor(human_resources): Update Functional ViewSet to use PyTest for Employee Model ref: #761 #730 --- .../tests/functional/employee/conftest.py | 24 +++ .../test_functional_employee_viewset.py | 157 +++--------------- 2 files changed, 46 insertions(+), 135 deletions(-) create mode 100644 app/human_resources/tests/functional/employee/conftest.py diff --git a/app/human_resources/tests/functional/employee/conftest.py b/app/human_resources/tests/functional/employee/conftest.py new file mode 100644 index 00000000..883bcdca --- /dev/null +++ b/app/human_resources/tests/functional/employee/conftest.py @@ -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 diff --git a/app/human_resources/tests/functional/employee/test_functional_employee_viewset.py b/app/human_resources/tests/functional/employee/test_functional_employee_viewset.py index 978017aa..1fb3149b 100644 --- a/app/human_resources/tests/functional/employee/test_functional_employee_viewset.py +++ b/app/human_resources/tests/functional/employee/test_functional_employee_viewset.py @@ -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