Merge pull request #883 from nofusscomputing/2025-07-24

This commit is contained in:
Jon
2025-07-24 19:15:40 +09:30
committed by GitHub
33 changed files with 714 additions and 938 deletions

View File

@ -22,3 +22,14 @@ def create_serializer():
yield ModelSerializer
@pytest.fixture( scope = 'class')
def model_kwargs(request, kwargs_company):
request.cls.kwargs_create_item = kwargs_company.copy()
yield kwargs_company.copy()
if hasattr(request.cls, 'kwargs_create_item'):
del request.cls.kwargs_create_item

View File

@ -1,6 +1,6 @@
import pytest
from access.tests.unit.entity.test_unit_entity_api_fields import (
from access.tests.functional.entity.test_functional_entity_api_fields import (
EntityAPIInheritedCases
)
@ -11,25 +11,20 @@ class CompanyAPITestCases(
EntityAPIInheritedCases,
):
parameterized_test_data = {
'name': {
'expected': str
@property
def parameterized_api_fields(self):
return {
'name': {
'expected': str
}
}
}
kwargs_create_item: dict = {
'name': 'Ian'
}
class CompanyAPIInheritedCases(
CompanyAPITestCases,
):
kwargs_create_item: dict = None
model = None
pass

View File

@ -22,3 +22,13 @@ def create_serializer():
yield ModelSerializer
@pytest.fixture( scope = 'class')
def model_kwargs(request, kwargs_contact):
request.cls.kwargs_create_item = kwargs_contact.copy()
yield kwargs_contact.copy()
if hasattr(request.cls, 'kwargs_create_item'):
del request.cls.kwargs_create_item

View File

@ -0,0 +1,41 @@
import pytest
from access.tests.functional.person.test_functional_person_api_fields import (
PersonAPIInheritedCases
)
@pytest.mark.model_contact
class ContactAPITestCases(
PersonAPIInheritedCases,
):
property
def parameterized_api_fields(self):
return {
'email': {
'expected': str
},
'directory': {
'expected': bool
}
}
class ContactAPIInheritedCases(
ContactAPITestCases,
):
pass
@pytest.mark.module_access
class ContactAPIPyTest(
ContactAPITestCases,
):
pass

View File

@ -22,3 +22,13 @@ def create_serializer():
yield ModelSerializer
@pytest.fixture( scope = 'class')
def model_kwargs(request, kwargs_entity):
request.cls.kwargs_create_item = kwargs_entity.copy()
yield kwargs_entity.copy()
if hasattr(request.cls, 'kwargs_create_item'):
del request.cls.kwargs_create_item

View File

@ -0,0 +1,48 @@
import pytest
from access.models.entity import Entity
from api.tests.functional.test_functional_api_fields import (
APIFieldsInheritedCases,
)
@pytest.mark.model_entity
class EntityAPITestCases(
APIFieldsInheritedCases,
):
base_model = Entity
@property
def parameterized_api_fields(self):
return {
'entity_type': {
'expected': str
},
'_urls.history': {
'expected': str
},
'_urls.knowledge_base': {
'expected': str
}
}
class EntityAPIInheritedCases(
EntityAPITestCases,
):
pass
@pytest.mark.module_access
class EntityAPIPyTest(
EntityAPITestCases,
):
pass

View File

@ -22,3 +22,13 @@ def create_serializer():
yield ModelSerializer
@pytest.fixture( scope = 'class')
def model_kwargs(request, kwargs_person):
request.cls.kwargs_create_item = kwargs_person.copy()
yield kwargs_person.copy()
if hasattr(request.cls, 'kwargs_create_item'):
del request.cls.kwargs_create_item

View File

@ -0,0 +1,47 @@
import pytest
from access.tests.functional.entity.test_functional_entity_api_fields import (
EntityAPIInheritedCases
)
@pytest.mark.model_person
class PersonAPITestCases(
EntityAPIInheritedCases,
):
property
def parameterized_api_fields(self):
return {
'f_name': {
'expected': str
},
'm_name': {
'expected': str
},
'l_name': {
'expected': str
},
'dob': {
'expected': str
}
}
class PersonAPIInheritedCases(
PersonAPITestCases,
):
pass
@pytest.mark.module_access
class PersonAPIPyTest(
PersonAPITestCases,
):
pass

View File

@ -9,24 +9,7 @@ class TenancyAbstractModelTestCases(
ModelTestCases
):
@pytest.fixture( scope = 'class', autouse = True)
def setup_organization(cls, request, model, organization_one, model_kwargs):
request.cls.organization = organization_one
if request.cls.kwargs_create_item:
request.cls.kwargs_create_item.update({
'organization': organization_one,
})
else:
request.cls.kwargs_create_item = {
'organization': organization_one,
}
pass

View File

@ -1,44 +0,0 @@
import pytest
from access.tests.unit.person.test_unit_person_api_fields import (
PersonAPIInheritedCases
)
@pytest.mark.model_contact
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
@pytest.mark.module_access
class ContactAPIPyTest(
ContactAPITestCases,
):
pass

View File

@ -1,80 +0,0 @@
import pytest
from access.models.entity import Entity
from api.tests.functional.test_functional_api_fields import (
APIFieldsInheritedCases,
)
@pytest.mark.model_entity
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({
'model_name': 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_entity'
"""Url namespace (optional, if not required) and url name"""
class EntityAPIInheritedCases(
EntityAPITestCases,
):
kwargs_create_item: dict = None
model = None
url_ns_name = '_api_entity_sub'
@pytest.mark.module_access
class EntityAPIPyTest(
EntityAPITestCases,
):
pass

View File

@ -1,54 +0,0 @@
import pytest
from access.tests.unit.entity.test_unit_entity_api_fields import (
EntityAPIInheritedCases
)
@pytest.mark.model_person
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
@pytest.mark.module_access
class PersonAPIPyTest(
PersonAPITestCases,
):
pass

View File

@ -22,3 +22,17 @@ def create_serializer():
yield ModelSerializer
@pytest.fixture( scope = 'class', autouse = True)
def model_kwargs(request, kwargs_assetbase):
request.cls.kwargs_create_item = kwargs_assetbase.copy()
yield kwargs_assetbase.copy()
if hasattr(request.cls, 'kwargs_create_item'):
try:
del request.cls.kwargs_create_item
except:
pass

View File

@ -0,0 +1,49 @@
import pytest
from accounting.models.asset_base import AssetBase
from api.tests.functional.test_functional_api_fields import (
APIFieldsInheritedCases,
)
@pytest.mark.model_assetbase
class AssetBaseAPITestCases(
APIFieldsInheritedCases,
):
base_model = AssetBase
@property
def parameterized_api_fields(self):
return {
'asset_number': {
'expected': str
},
'serial_number': {
'expected': str
},
'asset_type': {
'expected': str
}
}
class AssetBaseAPIInheritedCases(
AssetBaseAPITestCases,
):
pass
@pytest.mark.module_accounting
class AssetBaseAPIPyTest(
AssetBaseAPITestCases,
):
pass

View File

@ -1,81 +0,0 @@
import pytest
from accounting.models.asset_base import AssetBase
from api.tests.functional.test_functional_api_fields import (
APIFieldsInheritedCases,
)
@pytest.mark.model_assetbase
class AssetBaseAPITestCases(
APIFieldsInheritedCases,
):
base_model = AssetBase
@pytest.fixture( scope = 'class')
def setup_model(self, request,
model,
):
if model != self.base_model:
request.cls.url_view_kwargs.update({
'model_name': model._meta.model_name,
})
@pytest.fixture( scope = 'class', autouse = True)
def class_setup(self,
setup_pre,
setup_model,
create_model,
setup_post,
):
pass
parameterized_test_data = {
'asset_number': {
'expected': str
},
'serial_number': {
'expected': str
},
'asset_type': {
'expected': str
}
}
kwargs_create_item: dict = {
'asset_number': '123123',
'serial_number': '65756756756',
}
url_ns_name = 'accounting:_api_asset'
"""Url namespace (optional, if not required) and url name"""
class AssetBaseAPIInheritedCases(
AssetBaseAPITestCases,
):
kwargs_create_item: dict = None
model = None
url_ns_name = 'accounting:_api_asset_sub'
@pytest.mark.module_accounting
class AssetBaseAPIPyTest(
AssetBaseAPITestCases,
):
pass

View File

@ -1,20 +1,10 @@
import datetime
import django
import pytest
from django.contrib.auth.models import ContentType, Permission
from django.shortcuts import reverse
from django.db import models
from django.test import Client
from rest_framework.relations import Hyperlink
from access.models.team import Team
from access.models.team_user import TeamUsers
from centurion.tests.common import DoesNotExist
User = django.contrib.auth.get_user_model()
class APIFieldsTestCases:
@ -25,21 +15,20 @@ class APIFieldsTestCases:
## Additional Items
You may find a scenario where you are unable to have all fileds available
within a single request. to overcome this this test suite has the features
within a single request. to overcome this, this test suite has the features
available wherein you can prepare an additional item for an additional
check. the following is required before the API request is made
(setup_post fixture):
check. the following is required before the API request is made:
- additional item created and stored in attribute `self.item_two`
- additional url as a string and stored in attribute `self.url_two`
Once you have these two objects, an additional check will be done and each
test will check both API requests. if the field is found in either api
request the test will pass
This object should be created in fixture `create_model` to which you should
override to add this object.Once you have these two objects, an additional
check will be done and each test will check both API requests. If the field
is found in either api request the test will pass
"""
@property
def parameterized_test_data(self) -> dict:
def parameterized_api_fields(self) -> dict:
api_fields_common = {
'id': {
@ -92,157 +81,76 @@ class APIFieldsTestCases:
**api_fields_model.copy(),
}
url_view_kwargs = {}
@pytest.fixture( scope = 'class')
def setup_pre(self,
request,
model,
django_db_blocker,
organization_one,
organization_two
def create_model(self, request, django_db_blocker,
model, model_kwargs
):
request.cls.url_view_kwargs = {}
request.cls.model = model
item = None
with django_db_blocker.unblock():
random_str = datetime.datetime.now(tz=datetime.timezone.utc)
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
})
if hasattr(request.cls.model(), 'model_notes'):
for field in request.cls.model()._meta.fields:
if field.attname == 'model_notes':
request.cls.kwargs_create_item.update({
'model_notes': 'notes',
})
view_permissions = Permission.objects.get(
codename = 'view_' + request.cls.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = request.cls.model._meta.app_label,
model = request.cls.model._meta.model_name,
)
)
view_team = Team.objects.create(
team_name = 'cs_api_view_team' + str(random_str),
organization = request.cls.organization,
item = model.objects.create(
**model_kwargs
)
request.cls.view_team = view_team
request.cls.item = item
view_team.permissions.set([view_permissions])
request.cls.view_user = User.objects.create_user(username="cafs_test_user_view" + str(random_str), password="password", is_superuser = True)
team_user = TeamUsers.objects.create(
team = view_team,
user = request.cls.view_user
)
yield
yield item
with django_db_blocker.unblock():
team_user.delete()
item.delete()
view_team.delete()
try:
request.cls.view_user.delete()
except django.db.models.deletion.ProtectedError:
pass
del request.cls.kwargs_create_item
@pytest.fixture( scope = 'class')
def setup_post(self, request, django_db_blocker):
def make_request(self,
request,
api_request_permissions,
):
with django_db_blocker.unblock():
client = Client()
request.cls.url_view_kwargs.update({
'pk': request.cls.item.id
})
client.force_login( api_request_permissions['user']['view'] )
response = client.get( self.item.get_url() )
client = Client()
url = reverse('v2:' + request.cls.url_ns_name + '-detail', kwargs=request.cls.url_view_kwargs)
request.cls.api_data = response.data
client.force_login(request.cls.view_user)
response = client.get(url)
request.cls.api_data = response.data
item_two = getattr(request.cls, 'item_two', None)
item_two = getattr(request.cls, 'url_two', None)
if item_two:
if item_two:
response_two = client.get( self.item_two.get_url() )
response_two = client.get(request.cls.url_two)
request.cls.api_data_two = response_two.data
request.cls.api_data_two = response_two.data
else:
else:
request.cls.api_data_two = {}
request.cls.api_data_two = {}
yield
del request.cls.url_view_kwargs['pk']
del request.cls.api_data_two
@pytest.fixture( scope = 'class', autouse = True)
def class_setup(self,
setup_pre,
create_model,
setup_post,
make_request,
):
pass
def test_api_field_exists(self, recursearray, parameterized, param_key_test_data,
@pytest.mark.regression
def test_api_field_exists(self, recursearray,
parameterized, param_key_api_fields,
param_value,
param_expected
):
@ -252,7 +160,7 @@ class APIFieldsTestCases:
api_data_two = recursearray(self.api_data_two, param_value)
if param_expected is DoesNotExist:
if param_expected is models.NOT_PROVIDED:
assert(
api_data['key'] not in api_data['obj']
@ -268,7 +176,9 @@ class APIFieldsTestCases:
def test_api_field_type(self, recursearray, parameterized, param_key_test_data,
@pytest.mark.regression
def test_api_field_type(self, recursearray,
parameterized, param_key_api_fields,
param_value,
param_expected
):
@ -278,7 +188,7 @@ class APIFieldsTestCases:
api_data_two = recursearray(self.api_data_two, param_value)
if param_expected is DoesNotExist:
if param_expected is models.NOT_PROVIDED:
assert(
api_data['key'] not in api_data['obj']
@ -298,6 +208,4 @@ class APIFieldsInheritedCases(
APIFieldsTestCases
):
model = None
parameterized_test_data = {}
pass

View File

@ -10,43 +10,38 @@ class ModelTestCases:
This test suite contains all of the functional common tests for **ALL**
Centurion Models.
For this test suite to function the following class attributes must be set
for all classes that inherit from this class:
For this test suite to function the following fixtures must be available
for this class:
- `kwargs_create_item: dict = {}`
- model
_Dict of the models fields and the values required for
`model.objects.create()`_
- model_kwargs
This attribute can either be a variable or a property. This attribute along
with any prefixed `paremetized_` will be merged from each class in the
Attribute prefixed `paremetized_` will be merged from each class in the
inheritence chain. In addition this object must return a dict if defined.
"""
@pytest.fixture( scope = 'function' )
def created_model(self, request, django_db_blocker, model, user):
def created_model(self, django_db_blocker, model, model_kwargs):
if model._meta.abstract:
model_object = None
yield None
else:
if not model._meta.abstract:
with django_db_blocker.unblock():
model_object = model.objects.create(
**request.cls.kwargs_create_item
**model_kwargs
)
yield model_object
yield model_object
with django_db_blocker.unblock():
@property
def kwargs_create_item(self):
return {}
if model_object:
model_object.delete()

View File

@ -12,3 +12,11 @@ def model(request):
yield request.cls.model
del request.cls.model
@pytest.fixture( scope = 'class', autouse = True)
def model_kwargs(request, kwargs_ticketcommentaction):
request.cls.kwargs_create_item = kwargs_ticketcommentaction.copy()
yield kwargs_ticketcommentaction.copy()

View File

@ -1,19 +1,17 @@
import pytest
from core.tests.unit.ticket_comment_base.test_unit_ticket_comment_base_api_fields import (
TicketCommentBaseAPIInheritedCases
from core.tests.functional.ticket_comment_base.test_functional_ticket_comment_base_api_fields import (
TicketCommentBaseAPIFieldsInheritedCases
)
@pytest.mark.model_ticketcommentaction
class TicketCommentActionAPITestCases(
TicketCommentBaseAPIInheritedCases,
TicketCommentBaseAPIFieldsInheritedCases,
):
parameterized_test_data = {}
kwargs_create_item: dict = {}
pass
@ -21,9 +19,7 @@ class TicketCommentActionAPIInheritedCases(
TicketCommentActionAPITestCases,
):
kwargs_create_item: dict = {None}
model = None
pass

View File

@ -12,3 +12,11 @@ def model(request):
yield request.cls.model
del request.cls.model
@pytest.fixture( scope = 'class', autouse = True)
def model_kwargs(request, kwargs_ticketcommentbase):
request.cls.kwargs_create_item = kwargs_ticketcommentbase.copy()
yield kwargs_ticketcommentbase.copy()

View File

@ -0,0 +1,240 @@
import pytest
import random
from django.db import models
from rest_framework.relations import Hyperlink
from api.tests.functional.test_functional_api_fields import (
APIFieldsInheritedCases,
)
from core.models.ticket_comment_base import (
TicketBase,
TicketCommentBase,
)
@pytest.mark.model_ticketcommentbase
class TicketCommentBaseAPIFieldsTestCases(
APIFieldsInheritedCases,
):
base_model = TicketCommentBase
@pytest.fixture( scope = 'class')
def create_model(self, request, django_db_blocker,
model, model_kwargs
):
with django_db_blocker.unblock():
kwargs = model_kwargs.copy()
kwargs['body'] = 'the template comment'
del kwargs['external_ref']
del kwargs['external_system']
del kwargs['category']
kwargs['comment_type'] = model._meta.sub_model_type
kwargs['is_template'] = True
template_comment = model.objects.create(
**kwargs
)
kwargs = model_kwargs.copy()
kwargs['template'] = template_comment
kwargs['ticket'].is_closed = False
kwargs['ticket'].date_closed = None
kwargs['ticket'].is_solved = False
kwargs['ticket'].date_solved = None
kwargs['ticket'].status = TicketBase.TicketStatus.NEW
kwargs['ticket'].save()
kwargs['external_ref'] = random.randint(333, 33333)
item = model.objects.create(
**kwargs
)
request.cls.item = item
kwargs = model_kwargs.copy()
kwargs['body'] = 'the child comment'
kwargs['comment_type'] = model._meta.sub_model_type
kwargs['parent'] = request.cls.item
# kwargs['ticket'] = request.cls.item.ticket
del kwargs['external_ref']
del kwargs['external_system']
del kwargs['category']
kwargs['ticket'].is_closed = False
kwargs['ticket'].date_closed = None
kwargs['ticket'].is_solved = False
kwargs['ticket'].date_solved = None
kwargs['ticket'].status = TicketBase.TicketStatus.NEW
kwargs['ticket'].save()
item_two = model.objects.create(
**kwargs
)
request.cls.item_two = item_two
yield item
item_two.delete()
item.delete()
template_comment.delete()
@property
def parameterized_api_fields(self):
return {
'parent': {
'expected': dict
},
'parent.id': {
'expected': int
},
'parent.display_name': {
'expected': str
},
'parent.url': {
'expected': str
},
'ticket': {
'expected': dict
},
'ticket.id': {
'expected': int
},
'ticket.display_name': {
'expected': str
},
'ticket.url': {
'expected': str
},
'external_ref': {
'expected': int
},
'external_system': {
'expected': int
},
'comment_type': {
'expected': str
},
'category': {
'expected': dict
},
'category.id': {
'expected': int
},
'category.display_name': {
'expected': str
},
'category.url': {
'expected': Hyperlink
},
'body': {
'expected': str
},
'private': {
'expected': bool
},
'duration': {
'expected': int
},
'estimation': {
'expected': int
},
'template': {
'expected': dict
},
'template.id': {
'expected': int
},
'template.display_name': {
'expected': str
},
'template.url': {
'expected': str
},
'is_template': {
'expected': bool
},
'source': {
'expected': int
},
'user': {
'expected': dict
},
'user.id': {
'expected': int
},
'user.display_name': {
'expected': str
},
'user.url': {
'expected': str
},
'is_closed': {
'expected': bool
},
'date_closed': {
'expected': str
},
'_urls.threads': {
'expected': str
},
# Below fields dont exist.
'display_name': {
'expected': models.NOT_PROVIDED
},
'model_notes': {
'expected': models.NOT_PROVIDED
},
'_urls.notes': {
'expected': models.NOT_PROVIDED
},
}
class TicketCommentBaseAPIFieldsInheritedCases(
TicketCommentBaseAPIFieldsTestCases,
):
pass
@pytest.mark.module_core
class TicketCommentBaseAPIFieldsPyTest(
TicketCommentBaseAPIFieldsTestCases,
):
pass

View File

@ -12,3 +12,11 @@ def model(request):
yield request.cls.model
del request.cls.model
@pytest.fixture( scope = 'class', autouse = True)
def model_kwargs(request, kwargs_ticketcommentsolution):
request.cls.kwargs_create_item = kwargs_ticketcommentsolution.copy()
yield kwargs_ticketcommentsolution.copy()

View File

@ -1,19 +1,17 @@
import pytest
from core.tests.unit.ticket_comment_base.test_unit_ticket_comment_base_api_fields import (
TicketCommentBaseAPIInheritedCases
from core.tests.functional.ticket_comment_base.test_functional_ticket_comment_base_api_fields import (
TicketCommentBaseAPIFieldsInheritedCases
)
@pytest.mark.model_ticketcommentsolution
class TicketCommentSolutionAPITestCases(
TicketCommentBaseAPIInheritedCases,
TicketCommentBaseAPIFieldsInheritedCases,
):
parameterized_test_data = {}
kwargs_create_item: dict = {}
pass
@ -21,9 +19,7 @@ class TicketCommentSolutionAPIInheritedCases(
TicketCommentSolutionAPITestCases,
):
kwargs_create_item: dict = {None}
model = None
pass

View File

@ -1,13 +1,14 @@
import django
import pytest
from django.db import models
from rest_framework.relations import Hyperlink
from access.models.entity import Entity
from api.tests.functional.test_functional_api_fields import (
APIFieldsInheritedCases,
DoesNotExist,
)
from core.models.ticket.ticket_category import TicketCategory
@ -127,10 +128,10 @@ class APITestCases(
parameterized_test_data = {
'model_notes': {
'expected': DoesNotExist
'expected': models.NOT_PROVIDED
},
'_urls.notes': {
'expected': DoesNotExist
'expected': models.NOT_PROVIDED
},
'external_system': {
'expected': int

View File

@ -1,385 +0,0 @@
import django
import pytest
from django.contrib.auth.models import ContentType, Permission
from django.shortcuts import reverse
from rest_framework.relations import Hyperlink
from centurion.tests.common import DoesNotExist
from api.tests.functional.test_functional_api_fields import (
APIFieldsInheritedCases,
)
from core.models.ticket_comment_base import (
Entity,
TicketBase,
TicketCommentBase,
TicketCommentCategory
)
User = django.contrib.auth.get_user_model()
@pytest.mark.model_ticketcommentbase
class TicketCommentBaseAPITestCases(
APIFieldsInheritedCases,
):
base_model = TicketCommentBase
@pytest.fixture( scope = 'class')
def setup_model(self, request, django_db_blocker,
model,
):
with django_db_blocker.unblock():
ticket_view_permission = Permission.objects.get(
codename = 'view_' + TicketBase._meta.model_name,
content_type = ContentType.objects.get(
app_label = TicketBase._meta.app_label,
model = TicketBase._meta.model_name,
)
)
request.cls.view_team.permissions.add( ticket_view_permission )
category = TicketCommentCategory.objects.create(
organization = request.cls.organization,
name = 'comment category'
)
ticket_user = User.objects.create_user(username="ticket_user", password="password")
ticket = TicketBase.objects.create(
organization = request.cls.organization,
title = 'ticket comment title',
opened_by = ticket_user,
)
comment_user = Entity.objects.create(
organization = request.cls.organization,
)
request.cls.comment_user = comment_user
valid_data = request.cls.kwargs_create_item.copy()
valid_data['body'] = 'the template comment'
del valid_data['external_ref']
del valid_data['external_system']
del valid_data['category']
del valid_data['template']
del valid_data['parent']
valid_data['comment_type'] = TicketCommentBase._meta.sub_model_type
valid_data['ticket'] = ticket
valid_data['user'] = request.cls.comment_user
template_comment = TicketCommentBase.objects.create(
**valid_data
)
request.cls.kwargs_create_item.update({
'category': category,
'ticket': ticket,
'user': comment_user,
'parent': None,
'template': template_comment,
'comment_type': model._meta.sub_model_type
})
yield
with django_db_blocker.unblock():
try:
template_comment.delete()
except:
pass
try:
category.delete()
except:
pass
del request.cls.comment_user
for comment in ticket.ticketcommentbase_set.all():
comment.delete()
ticket.delete()
ticket_user.delete()
@pytest.fixture( scope = 'class')
def post_model(self, request, model, django_db_blocker ):
request.cls.url_view_kwargs.update({
'ticket_id': request.cls.item.ticket.id
})
if (
model != self.base_model
or self.item.parent
):
request.cls.url_view_kwargs.update({
'ticket_comment_model': model._meta.sub_model_type
})
valid_data = request.cls.kwargs_create_item.copy()
valid_data['body'] = 'the child comment'
valid_data['comment_type'] = TicketCommentBase._meta.sub_model_type
valid_data['parent'] = request.cls.item
valid_data['ticket'] = request.cls.item.ticket
valid_data['user'] = request.cls.comment_user
del valid_data['external_ref']
del valid_data['external_system']
del valid_data['category']
del valid_data['template']
with django_db_blocker.unblock():
request.cls.item.ticket.is_closed = False
request.cls.item.ticket.date_closed = None
request.cls.item.ticket.is_solved = False
request.cls.item.ticket.date_solved = None
request.cls.item.ticket.status = TicketBase.TicketStatus.NEW
request.cls.item.ticket.save()
request.cls.item_two = model.objects.create(
**valid_data
)
url_ns_name = '_api_ticket_comment_base_sub_thread'
request.cls.url_two = reverse(
'v2:' + url_ns_name + '-detail',
kwargs = {
**request.cls.url_view_kwargs,
'pk': request.cls.item_two.id,
'parent_id': request.cls.item.id,
'ticket_comment_model': model._meta.sub_model_type
}
)
yield
with django_db_blocker.unblock():
request.cls.item_two.delete(keep_parents = False)
del request.cls.item_two
del request.cls.url_two
@pytest.fixture( scope = 'class', autouse = True)
def class_setup(self, request, django_db_blocker,
setup_pre,
setup_model,
create_model,
post_model,
setup_post,
):
pass
@property
def parameterized_test_data(self):
return {
'parent': {
'expected': dict
},
'parent.id': {
'expected': int
},
'parent.display_name': {
'expected': str
},
'parent.url': {
'expected': str
},
'ticket': {
'expected': dict
},
'ticket.id': {
'expected': int
},
'ticket.display_name': {
'expected': str
},
'ticket.url': {
'expected': str
},
'external_ref': {
'expected': int
},
'external_system': {
'expected': int
},
'comment_type': {
'expected': str
},
'category': {
'expected': dict
},
'category.id': {
'expected': int
},
'category.display_name': {
'expected': str
},
'category.url': {
'expected': Hyperlink
},
'body': {
'expected': str
},
'private': {
'expected': bool
},
'duration': {
'expected': int
},
'estimation': {
'expected': int
},
'template': {
'expected': dict
},
'template.id': {
'expected': int
},
'template.display_name': {
'expected': str
},
'template.url': {
'expected': str
},
'is_template': {
'expected': bool
},
'source': {
'expected': int
},
'user': {
'expected': dict
},
'user.id': {
'expected': int
},
'user.display_name': {
'expected': str
},
'user.url': {
'expected': str
},
'is_closed': {
'expected': bool
},
'date_closed': {
'expected': str
},
'_urls.threads': {
'expected': str
},
# Below fields dont exist.
'display_name': {
'expected': DoesNotExist
},
'model_notes': {
'expected': DoesNotExist
},
'_urls.notes': {
'expected': DoesNotExist
},
}
kwargs_create_item: dict = {
'parent': '',
'ticket': '',
'external_ref': 123,
'external_system': TicketBase.Ticket_ExternalSystem.CUSTOM_1,
'comment_type': '',
'category': '',
'body': 'the ticket comment',
'private': False,
'duration': 1,
'estimation': 2,
'template': '',
'is_template': True,
'source': TicketBase.TicketSource.HELPDESK,
'user': '',
'is_closed': True,
'date_closed': '2025-05-09T19:32Z',
}
url_ns_name = '_api_ticket_comment_base'
"""Url namespace (optional, if not required) and url name"""
class TicketCommentBaseAPIInheritedCases(
TicketCommentBaseAPITestCases,
):
kwargs_create_item: dict = None
model = None
url_ns_name = '_api_ticket_comment_base_sub'
@pytest.mark.module_core
class TicketCommentBaseAPIPyTest(
TicketCommentBaseAPITestCases,
):
pass

View File

@ -8,8 +8,6 @@ from core.tests.functional.centurion_abstract.test_functional_centurion_abstract
CenturionAbstractModelInheritedCases
)
from devops.models.git_group import GitGroup
@pytest.mark.model_gitgroup
@ -18,16 +16,6 @@ class GitGroupModelTestCases(
):
kwargs_create_item = {
'parent_group': None,
'provider': GitGroup.GitProvider.GITHUB,
'provider_pk': 1,
'name': 'a name',
'path': 'a_path',
'description': 'a random bit of text.'
}
@pytest.mark.skip( reason = 'test must be as part of serializer and viewset tests, not model' )
def test_model_create_has_history_entry(self, model_contenttype, created_model, model):
"""Model Created
@ -39,22 +27,22 @@ class GitGroupModelTestCases(
pass
def test_model_create_with_parent_sets_tenancy(self, created_model, model):
def test_model_create_with_parent_sets_tenancy(self, created_model, model, model_kwargs):
"""Model Created
Ensure that the model when created with a parent git group, that its
tenancy is set to that of the parent group
"""
kwargs_create_item = self.kwargs_create_item.copy()
kwargs = model_kwargs.copy()
kwargs_create_item['provider'] = model.GitProvider.GITLAB
kwargs['provider'] = model.GitProvider.GITLAB
del kwargs_create_item['organization']
kwargs_create_item['parent_group'] = created_model
del kwargs['organization']
kwargs['parent_group'] = created_model
child_group = model.objects.create(
**kwargs_create_item
**kwargs
)
organization = child_group.organization
@ -65,7 +53,7 @@ class GitGroupModelTestCases(
def test_model_create_with_parent_exception_github(self, created_model, model):
def test_model_create_with_parent_exception_github(self, created_model, model, model_kwargs):
"""Model Created
Ensure that the model when created with a parent git group, with the
@ -73,17 +61,17 @@ class GitGroupModelTestCases(
can't have parents/nesting.
"""
kwargs_create_item = self.kwargs_create_item.copy()
kwargs = model_kwargs.copy()
kwargs_create_item['provider'] = model.GitProvider.GITHUB
kwargs['provider'] = model.GitProvider.GITHUB
del kwargs_create_item['organization']
kwargs_create_item['parent_group'] = created_model
del kwargs['organization']
kwargs['parent_group'] = created_model
with pytest.raises( ValidationError ) as e:
child_group = model.objects.create(
**kwargs_create_item
**kwargs
)
child_group.delete()
@ -91,7 +79,6 @@ class GitGroupModelTestCases(
assert e.value.error_dict['__all__'][0].code == 'no_parent_for_github_group'
class GitGroupModelInheritedCases(
GitGroupModelTestCases,
):
@ -99,6 +86,7 @@ class GitGroupModelInheritedCases(
@pytest.mark.module_access
class GitGroupModelPyTest(
GitGroupModelTestCases,
):

View File

@ -22,3 +22,14 @@ def create_serializer():
yield ModelSerializer
@pytest.fixture( scope = 'class')
def model_kwargs(request, kwargs_employee):
request.cls.kwargs_create_item = kwargs_employee.copy()
yield kwargs_employee.copy()
if hasattr(request.cls, 'kwargs_create_item'):
del request.cls.kwargs_create_item

View File

@ -1,6 +1,6 @@
import pytest
from access.tests.unit.contact.test_unit_contact_api_fields import (
from access.tests.functional.contact.test_functional_contact_api_fields import (
ContactAPIInheritedCases
)
@ -11,15 +11,14 @@ class EmployeeAPITestCases(
ContactAPIInheritedCases,
):
parameterized_test_data = {
'employee_number': {
'expected': int
}
}
@property
def parameterized_api_fields(self):
kwargs_create_item: dict = {
'employee_number': 12345,
}
return {
'employee_number': {
'expected': int
}
}
@ -27,9 +26,7 @@ class EmployeeAPIInheritedCases(
EmployeeAPITestCases,
):
kwargs_create_item: dict = None
model = None
pass

View File

@ -22,3 +22,17 @@ def create_serializer():
yield ModelSerializer
@pytest.fixture( scope = 'class', autouse = True)
def model_kwargs(request, kwargs_itamassetbase):
request.cls.kwargs_create_item = kwargs_itamassetbase.copy()
yield kwargs_itamassetbase.copy()
if hasattr(request.cls, 'kwargs_create_item'):
try:
del request.cls.kwargs_create_item
except:
pass

View File

@ -1,6 +1,6 @@
import pytest
from accounting.tests.unit.asset_base.test_unit_asset_base_api_fields import (
from accounting.tests.functional.asset_base.test_functional_asset_base_api_fields import (
AssetBaseAPIInheritedCases
)
@ -12,13 +12,14 @@ class ITAMAssetBaseAPITestCases(
):
parameterized_test_data = {
'itam_type': {
'expected': str
},
}
@property
def parameterized_api_fields(self):
kwargs_create_item: dict = {}
return {
'itam_type': {
'expected': str
},
}
@ -26,9 +27,7 @@ class ITAMAssetBaseAPIInheritedCases(
ITAMAssetBaseAPITestCases,
):
kwargs_create_item: dict = None
model = None
pass

View File

@ -22,7 +22,9 @@ def kwargs_person( kwargs_entity ):
**kwargs_entity.copy(),
'entity_type': 'person',
'f_name': 'p' + random_str,
'l_name': 'p' + random_str
'm_name': 'p' + random_str,
'l_name': 'p' + random_str,
'dob': '2025-04-08'
}
yield kwargs.copy()

View File

@ -1,6 +1,8 @@
import datetime
import pytest
from django.db import models
from core.models.ticket_comment_base import TicketCommentBase
@ -14,7 +16,8 @@ def model_ticketcommentbase(request):
@pytest.fixture( scope = 'class')
def kwargs_ticketcommentbase(django_db_blocker, kwargs_centurionmodel,
model_person, kwargs_person, model_ticketcommentbase,
model_ticketbase, kwargs_ticketbase
model_ticketbase, kwargs_ticketbase,
model_ticketcommentcategory, kwargs_ticketcommentcategory
):
random_str = str(datetime.datetime.now(tz=datetime.timezone.utc))
@ -27,19 +30,42 @@ def kwargs_ticketcommentbase(django_db_blocker, kwargs_centurionmodel,
ticket = model_ticketbase.objects.create( **kwargs_ticketbase )
category = model_ticketcommentcategory.objects.create(
**kwargs_ticketcommentcategory
)
kwargs = kwargs_centurionmodel.copy()
del kwargs['model_notes']
kwargs = {
**kwargs,
'body': 'a comment body',
'comment_type': model_ticketcommentbase._meta.sub_model_type,
# 'parent': '',
'ticket': ticket,
'external_ref': 123,
'external_system': model_ticketbase.Ticket_ExternalSystem.CUSTOM_1,
'comment_type': model_ticketcommentbase._meta.sub_model_type,
'category': category,
'body': 'a comment body',
'private': False,
'duration': 1,
'estimation': 2,
# 'template': '',
'is_template': False,
'source': model_ticketbase.TicketSource.HELPDESK,
'user': person,
'is_closed': True,
'date_closed': '2025-05-09T19:32Z',
}
yield kwargs.copy()
with django_db_blocker.unblock():
person.delete()
person.delete()
try:
category.delete()
except models.deletion.ProtectedError:
pass

View File

@ -123,6 +123,11 @@ All models must contain the core features, being:
_Enables the possibility within markdown fields to use its [tag](../user/core/markdown.md#model-reference--model-tag) to create a link to the model._
### History
Adding [History](./core/model_history.md) to a model is automatic. If there is a desire not to have model history it can be disabled by adding attribute `_audit_enabled` to the model class and setting its value to `False.`
## Validation Methods
Within All of our models including when they are created via an [API serializer](./serializers.md), the models [validators](https://docs.djangoproject.com/en/5.1/ref/models/instances/#validating-objects) are called. The models validators are responsible for ensuring that no data goes into the database that may create an inconsistancy.
@ -213,6 +218,44 @@ table_fields: list = [
```
## Tests
The following Unit test suites exists for models:
- Unit Tests
- model (Base Model) `core.tests.unit.centurion_abstract.test_unit_centurion_abstract_model.CenturionAbstractModelInheritedCases`
- model (Sub-Model) `core.tests.unit.centurion_sub_abstract.test_unit_centurion_sub_abstract_model.CenturionSubAbstractModelInheritedCases`
- Serializer `api.tests.unit.test_unit_serializer.SerializerTestCases`
- ViewSet `api.tests.unit.test_unit_common_viewset.*`
- Functional Tests
- model `core.tests.functional.centurion_abstract.test_functional_centurion_abstract_model.CenturionAbstractModelInheritedCases`
- API Fields Render `api.tests.functional.test_functional_api_fields.APIFieldsInheritedCases`
!!! info
If you add a feature you will have to [write the test cases](./testing.md) for that feature if they are not covered by existing test cases.
Each model has the following Test Suites auto-magic created:
- API Permissions checks `api.tests.functional.test_functional_meta_permissions_api`
_Checks the CRUD permissions against the models API endpoints_
- Audit History Model checks, `core.tests.unit.centurion_audit_meta.test_unit_meta_audit_history_model`
_Confirms the model has a [`AuditHistory`](./api/models/audit_history.md) model and other checks as required for an `AuditHistory` model._
These auto-magic tests require no input and will be created on a model inheriting from [`CenturionModel`](./api/models/centurion.md) and run every time the tests are run.
## Depreciated Docs undergoing re-write
- ToDo
@ -259,44 +302,6 @@ This section details the additional items that may need to be done when adding a
- If the model is a primary model, add it to the model link slash command in `app/core/lib/slash_commands/linked_model.py` function `command_linked_model`
## History
Adding [History](./core/model_history.md) to a model is automatic. If there is a desire not to have model history it can be disabled by adding attribute `_audit_enabled` to the model class and setting its value to `False.`
## Tests
The following Unit test suites exists for models:
- Unit Tests
- model (Base Model) `core.tests.unit.centurion_abstract.test_unit_centurion_abstract_model.CenturionAbstractModelInheritedCases`
- model (Sub-Model) `core.tests.unit.centurion_sub_abstract.test_unit_centurion_sub_abstract_model.CenturionSubAbstractModelInheritedCases`
- Functional Tests
- model `core.tests.functional.centurion_abstract.test_functional_centurion_abstract_model.CenturionAbstractModelInheritedCases`
- API Fields Render `api.tests.functional.test_functional_api_fields.APIFieldsInheritedCases`
!!! info
If you add a feature you will have to [write the test cases](./testing.md) for that feature if they are not covered by existing test cases.
Each model has the following Test Suites auto-magic created:
- API Permissions checks `api.tests.functional.test_functional_meta_permissions_api`
_Checks the CRUD permissions against the models API endpoints_
- Audit History Model checks, `core.tests.unit.centurion_audit_meta.test_unit_meta_audit_history_model`
_Confirms the model has a [`AuditHistory`](./api/models/audit_history.md) model and other checks as required for an `AuditHistory` model._
These auto-magic tests require no input and will be created on a model inheriting from [`CenturionModel`](./api/models/centurion.md) and run every time the tests are run.
## Knowledge Base Article linking
All Tenancy Models must have the ability to be able to have a knowledge base article linked to it. To do so the following must be done: