test(core): TicketBase Updated, yet incomplete Test Suite for Serializer

ref: #733 #723
This commit is contained in:
2025-05-01 01:22:42 +09:30
parent 64757826da
commit 1718a1cf14
3 changed files with 501 additions and 130 deletions

View File

@ -185,64 +185,114 @@ class ModelSerializer(
read_only_fields = [
'id',
'display_name',
'external_system',
'external_ref',
# 'ticket_type',
'created',
'modified',
'_urls',
]
def validate_field_milestone( self ) -> bool:
def __init__(self, *args, **kwargs):
is_valid: bool = False
super().__init__(*args, **kwargs)
if self.instance is not None:
if self.context.get('view', None) is not None:
if self.instance.milestone is None:
read_only_fields = [
'id',
'display_name',
'created',
'modified',
'_urls',
]
return True
if not self.context['view']._has_import:
else:
read_only_fields += [
'external_system',
'external_ref',
'ticket_type',
]
if self.instance.project is None:
raise centurion_exception.ValidationError(
details = 'Milestones require a project',
code = 'milestone_requires_project',
)
return False
if self.instance.project.id == self.instance.milestone.project.id:
return True
else:
raise centurion_exception.ValidationError(
detail = 'Milestone must be from the same project',
code = 'milestone_same_project',
)
return is_valid
self.Meta.read_only_fields = read_only_fields
def validate_field_milestone( self, attrs, raise_exception = False ) -> bool:
def validate(self, attrs):
milestone = attrs.get('milestone', None)
attrs = super().validate(attrs)
project = attrs.get('project', None)
if not self.validate_field_milestone:
if milestone is not None:
del attrs['milestone']
if project is None:
raise centurion_exception.ValidationError(
detail = {
'milestone': 'Milestones require a project'
},
code = 'milestone_requires_project',
)
elif project.id != milestone.project.id:
del attrs['milestone']
raise centurion_exception.ValidationError(
detail = {
'milestone': 'Milestone must be from the same project'
},
code = 'milestone_same_project',
)
return attrs
def validate_field_external_system( self, attrs, raise_exception = False ) -> bool:
external_system = attrs.get('external_system', None)
external_ref = attrs.get('external_ref', None)
if external_system is None and external_ref is not None:
raise centurion_exception.ValidationError(
detail = {
'external_system': 'External System is required when an External Ref is defined'
},
code = 'external_system_missing',
)
elif external_system is not None and external_ref is None:
raise centurion_exception.ValidationError(
detail = {
'external_ref': 'External Ref is required when an External System is defined'
},
code = 'external_ref_missing',
)
return attrs
def validate(self, attrs):
attrs = super().validate( attrs )
attrs = self.validate_field_milestone( attrs )
attrs = self.validate_field_external_system( attrs )
return attrs
def is_valid(self, raise_exception = False):
is_valid = super().is_valid( raise_exception = raise_exception )
return is_valid
@extend_schema_serializer(component_name = 'TicketBaseViewSerializer')

View File

@ -12,3 +12,13 @@ def model(request):
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from core.serializers.ticket import ModelSerializer
yield ModelSerializer

View File

@ -1,13 +1,16 @@
import pytest
from django.contrib.auth.models import User
from django.test import TestCase
from access.models.organization import Organization
from core.serializers.ticket import (
TicketBase,
ModelSerializer
from rest_framework.exceptions import (
ValidationError
)
from access.models.entity import Entity
from core.models.ticket.ticket_category import TicketCategory
from core.models.ticket_base import TicketBase
from project_management.models.project_milestone import (
Project,
ProjectMilestone,
@ -15,104 +18,330 @@ from project_management.models.project_milestone import (
class SerializerTestCases:
model = TicketBase
"""Model to test"""
class MockView:
create_model_serializer = ModelSerializer
"""Serializer to test"""
_has_import: bool = False
"""User Permission
get_permission_required() sets this to `True` when user has import permission.
"""
_has_purge: bool = False
"""User Permission
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 TicketBaseSerializerTestCases:
parametrized_test_data: dict = {
"organization": {
'will_create': False,
'exception_obj': ValidationError,
'exception_code': 'required',
'exception_code_key': None,
'permission_import_required': False,
},
"external_system": {
'will_create': False,
'exception_obj': ValidationError,
'exception_code': 'external_system_missing',
'exception_code_key': None,
'permission_import_required': True,
},
"external_ref": {
'will_create': False,
'exception_obj': ValidationError,
'exception_code': 'external_ref_missing',
'exception_code_key': None,
'permission_import_required': True,
},
"parent_ticket": {
'will_create': True,
'permission_import_required': False,
},
# "ticket_type": "request",
"status": {
'will_create': True,
'permission_import_required': False,
},
"category": True,
"title": {
'will_create': False,
'exception_obj': ValidationError,
'exception_code': 'required',
'exception_code_key': None,
'permission_import_required': False,
},
"description": {
'will_create': False,
'exception_obj': ValidationError,
'exception_code': 'required',
'exception_code_key': None,
'permission_import_required': False,
},
"project": {
'will_create': False,
'exception_obj': ValidationError,
'exception_code': 'milestone_requires_project',
'exception_code_key': 'milestone',
'permission_import_required': False,
},
"milestone": {
'will_create': True,
'permission_import_required': False,
},
"urgency": {
'will_create': True,
'permission_import_required': False,
},
"impact": {
'will_create': True,
'permission_import_required': False,
},
"priority": {
'will_create': True,
'permission_import_required': False,
},
"opened_by": {
'will_create': False,
'exception_obj': ValidationError,
'exception_code': 'required',
'exception_code_key': None,
'permission_import_required': True,
},
"subscribed_to": {
'will_create': True,
'permission_import_required': False,
},
"assigned_to": {
'will_create': True,
'permission_import_required': False,
},
"planned_start_date": {
'will_create': True,
'permission_import_required': False,
},
"planned_finish_date": {
'will_create': True,
'permission_import_required': False,
},
"real_start_date": {
'will_create': True,
'permission_import_required': False,
},
"real_finish_date": {
'will_create': True,
'permission_import_required': False,
},
"is_deleted": {
'will_create': True,
'permission_import_required': False,
},
"is_solved": {
'will_create': True,
'permission_import_required': False,
},
"date_solved": {
'will_create': True,
'permission_import_required': False,
},
"is_closed": {
'will_create': True,
'permission_import_required': False,
},
"date_closed": {
'will_create': True,
'permission_import_required': False,
},
}
valid_data: dict = {
"display_name": "tester ticket",
"organization": None,
"external_system": TicketBase.Ticket_ExternalSystem.CUSTOM_1,
"external_ref": 1,
"parent_ticket": None,
"ticket_type": "request",
"status": TicketBase.TicketStatus.NEW,
"category": None,
"title": "title2",
"description": "the description",
"project": None,
"milestone": None,
"urgency": TicketBase.TicketUrgency.LOW,
"impact": TicketBase.TicketImpact.LOW,
"priority": TicketBase.TicketPriority.LOW,
"priority_badge": None,
"opened_by": None,
"subscribed_to": [],
"assigned_to": [],
"planned_start_date": '2025-04-29T00:00:00Z',
"planned_finish_date": '2025-04-29T01:00:00Z',
"real_start_date": '2025-04-29T00:02:00Z',
"real_finish_date": '2025-04-29T00:03:00Z',
"tto": 0,
"ttr": 0,
"is_deleted": False,
"is_solved": False,
"date_solved": None,
"is_closed": False,
"date_closed": None,
'external_ref': 1,
'title': 'ticket title',
'description': 'the ticket description',
'status': TicketBase.TicketStatus.NEW,
'planned_start_date': '2025-04-16T00:00:01',
'planned_finish_date': '2025-04-16T00:00:02',
'real_start_date': '2025-04-16T00:00:03',
'real_finish_date': '2025-04-16T00:00:04',
'is_deleted': False,
'is_solved': False,
'date_solved': '2025-04-16T00:00:04',
'is_closed': False,
'date_closed': '2025-04-16T00:00:04',
}
"""Valid data used by serializer to create object"""
@classmethod
def setUpTestData(self):
"""Setup Test"""
self.organization = Organization.objects.create(name='test_org')
self.user = User.objects.create_user(username="test_user_view", password="password")
@pytest.fixture( scope = 'class')
def setup_data(self,
request,
model,
django_db_blocker,
organization_one,
):
self.project_one = Project.objects.create(
organization = self.organization,
name = 'proj 1'
)
with django_db_blocker.unblock():
self.milestone_one = ProjectMilestone.objects.create(
organization = self.organization,
name = 'milestone one',
project = self.project_one
)
request.cls.organization = organization_one
self.milestone_two = ProjectMilestone.objects.create(
organization = self.organization,
name = 'milestone two',
project = self.project_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)
self.project_two = Project.objects.create(
organization = self.organization,
name = 'proj 2'
)
if len(valid_data) > 0:
self.milestone_three = ProjectMilestone.objects.create(
organization = self.organization,
name = 'milestone three',
project = self.project_two
)
request.cls.valid_data = valid_data
self.valid_data.update({
'organization': self.organization.pk,
'opened_by': self.user.pk,
'project': self.project_one.pk,
'milestone': self.milestone_one.pk,
})
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
@pytest.fixture( scope = 'class')
def setup_model_data(self, request, django_db_blocker):
def test_serializer_valid_data(self):
with django_db_blocker.unblock():
request.cls.entity_user = Entity.objects.create(
organization = request.cls.organization,
model_notes = 'asdas'
)
project = Project.objects.create(
organization = request.cls.organization,
name = 'project'
)
parent_ticket = request.cls.model.objects.create(
organization = request.cls.organization,
title = 'parent ticket',
description = 'bla bla',
opened_by = request.cls.view_user,
)
project_milestone = ProjectMilestone.objects.create(
organization = request.cls.organization,
name = 'project milestone one',
project = project
)
request.cls.valid_data.update({
'category': TicketCategory.objects.create(
organization = request.cls.organization,
name = 'a category'
).pk,
'opened_by': request.cls.view_user.pk,
'project': project.pk,
'milestone': project_milestone.pk,
'parent_ticket': parent_ticket.pk,
'external_system': int(request.cls.model.Ticket_ExternalSystem.CUSTOM_1),
'impact': int(request.cls.model.TicketImpact.MEDIUM),
'priority': int(request.cls.model.TicketPriority.HIGH),
'urgency': TicketBase.TicketUrgency.LOW,
'assigned_to': [
request.cls.entity_user.id,
],
'subscribed_to': [
request.cls.entity_user.id,
],
})
yield
with django_db_blocker.unblock():
request.cls.entity_user.delete()
parent_ticket.delete()
project_milestone.delete()
project.delete()
@pytest.fixture( scope = 'class', autouse = True)
def class_setup(self,
setup_data,
setup_model_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_valid_data_permission_import(self, create_serializer):
"""Serializer Validation Check
Ensure that when creating an object with valid data, no validation
error occurs. when the user has permission import.
"""
view_set = MockView()
view_set._has_import = True
serializer = create_serializer(
context = {
'view': view_set,
},
data = self.valid_data
)
@ -120,35 +349,117 @@ class SerializerTestCases:
def test_serializer_valid_data_missing_field_raises_exception(self, parameterized, param_key_test_data,
create_serializer,
param_value,
param_exception_obj,
param_exception_code_key,
param_exception_code
):
"""Serializer Validation Check
Ensure that when creating and the milestone is not from the project
assigned to the ticket that a validation error occurs.
Requires that all permissions be assigned so that test case can
function.
"""
valid_data = self.valid_data.copy()
del valid_data[param_value]
view_set = MockView()
view_set._has_import = True
with pytest.raises(param_exception_obj) as err:
serializer = create_serializer(
context = {
'view': view_set,
},
data = valid_data,
)
serializer.is_valid(raise_exception = True)
exception_code_key = param_value
if param_exception_code_key is not None:
exception_code_key = param_exception_code_key
assert err.value.get_codes()[exception_code_key][0] == param_exception_code
def test_serializer_valid_data_missing_field_is_valid_permission_import(self, parameterized, param_key_test_data,
create_serializer,
param_value,
param_will_create,
param_permission_import_required
):
"""Serializer Validation Check
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()
view_set._has_import = True
serializer = create_serializer(
context = {
'view': view_set,
},
data = valid_data
)
is_valid = serializer.is_valid(raise_exception = False)
assert (
( # import permission
param_permission_import_required
and not param_will_create
and param_will_create == is_valid
)
or
( # does not require import permission
not param_permission_import_required
and param_will_create == is_valid
)
)
class TicketBaseSerializerInheritedCases(
SerializerTestCases,
TicketBaseSerializerTestCases,
):
parametrized_test_data: dict = None
create_model_serializer = None
"""Serializer to test"""
model = None
"""Model to test"""
valid_data: dict = {}
valid_data: dict = None
"""Valid data used by serializer to create object"""
@classmethod
def setUpTestData(self):
self.valid_data = {
**super().valid_data,
**self.valid_data
}
super().setUpTestData()
class TicketBaseSerializerTest(
SerializerTestCases,
TestCase,
class TicketBaseSerializerPyTest(
TicketBaseSerializerTestCases,
):
pass
parametrized_test_data: dict = None