From 28cd56d9a093d9ee79508b72504e83519a921a1a Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 3 Aug 2025 14:05:39 +0930 Subject: [PATCH] refactor(itim): Serializer Unit Test Suite re-written to Pytest for model Service ref: #932 #931 --- .../service/test_service_serializer.py | 363 --------------- app/itim/tests/unit/service/conftest.py | 6 + .../service/test_unit_service_serializer.py | 421 ++++++++++++++++++ app/tests/fixtures/__init__.py | 1 + app/tests/fixtures/model_cluster.py | 6 +- app/tests/fixtures/model_device.py | 13 +- app/tests/fixtures/model_devicemodel.py | 7 +- app/tests/fixtures/model_service.py | 17 + 8 files changed, 467 insertions(+), 367 deletions(-) delete mode 100644 app/itim/tests/functional/service/test_service_serializer.py create mode 100644 app/itim/tests/unit/service/test_unit_service_serializer.py diff --git a/app/itim/tests/functional/service/test_service_serializer.py b/app/itim/tests/functional/service/test_service_serializer.py deleted file mode 100644 index 57644208..00000000 --- a/app/itim/tests/functional/service/test_service_serializer.py +++ /dev/null @@ -1,363 +0,0 @@ -import pytest - -from django.test import TestCase - -from rest_framework.exceptions import ValidationError - -from access.models.tenant import Tenant as Organization - -from centurion.tests.abstract.mock_view import MockView, User - -from itam.models.device import Device - -from itim.models.services import Port - -from itim.models.clusters import Cluster -from itim.serializers.service import Service, ServiceModelSerializer - - - -class ServiceValidationAPI( - TestCase, -): - - model = Service - - @classmethod - def setUpTestData(self): - """Setup Test - - 1. Create an org - 2. Create an item - """ - - organization = Organization.objects.create(name='test_org') - - self.user = User.objects.create_user(username="test_user_view", password="password") - - self.organization = organization - - self.mock_view = MockView( user = self.user ) - - - self.port = Port.objects.create( - organization = self.organization, - number = 80, - protocol = Port.Protocol.TCP - ) - - self.device = Device.objects.create( - organization = self.organization, - name = 'a-device' - ) - - self.cluster = Cluster.objects.create( - organization = self.organization, - name = 'a cluster' - ) - - - self.item = self.model.objects.create( - organization=organization, - name = 'os name', - cluster = self.cluster, - config_key_variable = 'value' - ) - - self.item_two = self.model.objects.create( - organization=organization, - name = 'os name', - cluster = self.cluster, - ) - - self.item_two.dependent_service.set([ self.item ]) - - - self.item_is_template = self.model.objects.create( - organization=organization, - name = 'os name', - is_template = True, - ) - - self.item_is_template.port.set([ self.port ]) - - - self.item_is_template_no_port = self.model.objects.create( - organization=organization, - name = 'os name', - is_template = True, - ) - - - - - def test_serializer_validation_can_create_device(self): - """Serializer Validation Check - - Ensure that a valid item is serialized - """ - - serializer = ServiceModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - 'organization': self.organization.id, - 'name': 'service', - 'port': [ - self.port.id - ], - 'config_key_variable': 'a_key', - 'device': self.device.id - }, - ) - - assert serializer.is_valid(raise_exception = True) - - - - def test_serializer_validation_can_create_cluster(self): - """Serializer Validation Check - - Ensure that a valid item is serialized - """ - - serializer = ServiceModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - 'organization': self.organization.id, - 'name': 'service', - 'port': [ - self.port.id - ], - 'config_key_variable': 'a_key', - 'cluster': self.cluster.id - }, - ) - - assert serializer.is_valid(raise_exception = True) - - - - def test_serializer_validation_no_name(self): - """Serializer Validation Check - - Ensure that if creating and no name is provided a validation error occurs - """ - - with pytest.raises(ValidationError) as err: - - serializer = ServiceModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - 'organization': self.organization.id, - 'port': [ - self.port.id - ], - 'config_key_variable': 'a_key', - 'device': self.device.id - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['name'][0] == 'required' - - - - def test_serializer_validation_no_port(self): - """Serializer Validation Check - - Ensure that if creating and no port is provided a validation error occurs - """ - - with pytest.raises(ValidationError) as err: - - serializer = ServiceModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - 'organization': self.organization.id, - 'name': 'service', - 'config_key_variable': 'a_key', - 'device': self.device.id - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['port'][0] == 'required' - - - - def test_serializer_validation_no_port_required_if_template_with_port(self): - """Serializer Validation Check - - Ensure that if creating and no port is provided and the template has a port - no validation error occurs - """ - - serializer = ServiceModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - 'organization': self.organization.id, - 'name': 'service', - 'config_key_variable': 'a_key', - 'device': self.device.id, - 'template': self.item_is_template.id - }) - - assert serializer.is_valid(raise_exception = True) - - - - def test_serializer_validation_template_without_port(self): - """Serializer Validation Check - - Ensure that if creating a port is provided and the template has no port - no validation error occurs - """ - - serializer = ServiceModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - 'organization': self.organization.id, - 'name': 'service', - 'port': [ - self.port.id - ], - 'config_key_variable': 'a_key', - 'device': self.device.id, - 'template': self.item_is_template_no_port.id - }) - - assert serializer.is_valid(raise_exception = True) - - - - def test_serializer_validation_no_port_or_template_port(self): - """Serializer Validation Check - - Ensure that if creating and no port is provided and the template - has no port a validation error occurs - """ - - with pytest.raises(ValidationError) as err: - - serializer = ServiceModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - 'organization': self.organization.id, - 'name': 'service', - 'config_key_variable': 'a_key', - 'device': self.device.id, - 'template': self.item_is_template_no_port.id - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['port'][0] == 'required' - - - - def test_serializer_validation_no_device(self): - """Serializer Validation Check - - Ensure that if creating and no device is provided a validation error occurs - """ - - with pytest.raises(ValidationError) as err: - - serializer = ServiceModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - 'organization': self.organization.id, - 'name': 'service', - 'port': [ - self.port.id - ], - 'config_key_variable': 'a_key', - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['non_field_errors'][0] == 'one_of_cluster_or_device' - - - - def test_serializer_validation_device_and_cluster(self): - """Serializer Validation Check - - Ensure that if creating and a cluster and device is provided - a validation error occurs - """ - - with pytest.raises(ValidationError) as err: - - serializer = ServiceModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - 'organization': self.organization.id, - 'name': 'service', - 'port': [ - self.port.id - ], - 'config_key_variable': 'a_key', - 'device': self.device.id, - 'cluster': self.cluster.id - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['non_field_errors'][0] == 'either_cluster_or_device' - - - - def test_serializer_validation_no_circular_dependency(self): - """Serializer Validation Check - - Ensure that if creating and a dependent service loop - a validation error occurs - """ - - with pytest.raises(ValidationError) as err: - - serializer = ServiceModelSerializer( - self.item, - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - 'dependent_service': [ - self.item_two.id - ], - }, - partial = True - ) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['dependent_service'][0] == 'no_circular_dependencies' diff --git a/app/itim/tests/unit/service/conftest.py b/app/itim/tests/unit/service/conftest.py index 300981d7..41849b4e 100644 --- a/app/itim/tests/unit/service/conftest.py +++ b/app/itim/tests/unit/service/conftest.py @@ -17,3 +17,9 @@ def model_kwargs(request, kwargs_service): if hasattr(request.cls, 'kwargs_create_item'): del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_service): + + yield serializer_service diff --git a/app/itim/tests/unit/service/test_unit_service_serializer.py b/app/itim/tests/unit/service/test_unit_service_serializer.py new file mode 100644 index 00000000..b98cfc73 --- /dev/null +++ b/app/itim/tests/unit/service/test_unit_service_serializer.py @@ -0,0 +1,421 @@ +import pytest + +from django.db import models + +from rest_framework.exceptions import ValidationError + +from api.tests.unit.test_unit_serializer import ( + SerializerTestCases +) + +from centurion.tests.abstract.mock_view import MockView + + + +@pytest.mark.model_service +class ServiceSerializerTestCases( + SerializerTestCases +): + + + @pytest.fixture( scope = 'function' ) + def created_model(self, django_db_blocker, model, model_kwargs): + + with django_db_blocker.unblock(): + + kwargs_many_to_many = {} + + kwargs = {} + + for key, value in model_kwargs.items(): + + field = model._meta.get_field(key) + + if isinstance(field, models.ManyToManyField): + + kwargs_many_to_many.update({ + key: value + }) + + else: + + kwargs.update({ + key: value + }) + + + item = model.objects.create( **kwargs ) + + for key, value in kwargs_many_to_many.items(): + + field = getattr(item, key) + + for entry in value: + + field.add(entry) + + yield item + + item.delete() + + + + def test_serializer_validation_no_name(self, + kwargs_api_create, model, model_serializer, request_user + ): + """Serializer Validation Check + + Ensure that if creating and no name is provided a validation error occurs + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_api_create.copy() + del kwargs['name'] + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs, + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['name'][0] == 'required' + + + + def test_serializer_validation_can_create_device(self, + kwargs_api_create, model, model_serializer, request_user + ): + """Serializer Validation Check + + Ensure that a valid item is serialized + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_api_create.copy() + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs, + ) + + assert serializer.is_valid(raise_exception = True) + + + + def test_serializer_validation_no_port(self, + kwargs_api_create, model, model_serializer, request_user + ): + """Serializer Validation Check + + Ensure that if creating and no port is provided a validation error occurs + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_api_create.copy() + del kwargs['port'] + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs, + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['port'][0] == 'required' + + + + def test_serializer_validation_no_port_required_if_template_with_port(self, + kwargs_api_create, model, model_serializer, request_user, + model_kwargs, + ): + """Serializer Validation Check + + Ensure that if creating and no port is provided and the template has a port + no validation error occurs + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = model_kwargs + kwargs['is_template'] = True + ports = kwargs['port'] + del kwargs['port'] + + template = model.objects.create( **kwargs ) + + for port in ports: + template.port.add( port ) + + + kwargs = kwargs_api_create.copy() + kwargs['is_template'] = False + kwargs['template'] = template.id + del kwargs['port'] + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs + ) + + assert serializer.is_valid(raise_exception = True) + template.delete() + + + + def test_serializer_validation_template_without_port(self, + kwargs_api_create, model, model_serializer, request_user, + model_kwargs + ): + """Serializer Validation Check + + Ensure that if creating a port is provided and the template has no port + no validation error occurs + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = model_kwargs + kwargs['is_template'] = True + del kwargs['port'] + + template = model.objects.create( **kwargs ) + + kwargs = kwargs_api_create.copy() + kwargs['is_template'] = False + kwargs['template'] = template.id + + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs + ) + + assert serializer.is_valid(raise_exception = True) + template.delete() + + + + def test_serializer_validation_no_port_or_template_port(self, + kwargs_api_create, model, model_serializer, request_user, + model_kwargs, + ): + """Serializer Validation Check + + Ensure that if creating and no port is provided and the template + has no port a validation error occurs + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = model_kwargs + kwargs['is_template'] = True + del kwargs['port'] + del kwargs['device'] + + template = model.objects.create( **kwargs ) + + kwargs = kwargs_api_create.copy() + kwargs['is_template'] = False + kwargs['template'] = template.id + del kwargs['port'] + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['port'][0] == 'required' + template.delete() + + + + def test_serializer_validation_no_device(self, + kwargs_api_create, model, model_serializer, request_user + ): + """Serializer Validation Check + + Ensure that if creating and no device is provided a validation error occurs + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_api_create.copy() + del kwargs['device'] + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs, + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['non_field_errors'][0] == 'one_of_cluster_or_device' + + + + def test_serializer_validation_device_and_cluster(self, + kwargs_api_create, model, model_serializer, request_user, + model_cluster, kwargs_cluster + ): + """Serializer Validation Check + + Ensure that if creating and a cluster and device is provided + a validation error occurs + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_cluster + nodes = kwargs['nodes'] + del kwargs['nodes'] + cluster = model_cluster.objects.create( **kwargs ) + + for node in nodes: + cluster.nodes.add( node ) + + kwargs = kwargs_api_create.copy() + kwargs['cluster'] = cluster.id + + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['non_field_errors'][0] == 'either_cluster_or_device' + cluster.delete() + + + + def test_serializer_validation_no_circular_dependency(self, + created_model, + kwargs_api_create, model, model_serializer, request_user, + model_kwargs + ): + """Serializer Validation Check + + Ensure that if creating and a dependent service loop + a validation error occurs + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = model_kwargs + del kwargs['port'] + root_service = model.objects.create ( **kwargs ) + root_service.dependent_service.add( created_model ) + + + kwargs = kwargs_api_create.copy() + kwargs['dependent_service'] = [ root_service.id ] + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + created_model, + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs, + partial = True + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['dependent_service'][0] == 'no_circular_dependencies' + root_service.delete() + + + +class ServiceSerializerInheritedCases( + ServiceSerializerTestCases +): + pass + + + +@pytest.mark.module_itim +class ServiceSerializerPyTest( + ServiceSerializerTestCases +): + pass \ No newline at end of file diff --git a/app/tests/fixtures/__init__.py b/app/tests/fixtures/__init__.py index 6be3edb0..49ced6b8 100644 --- a/app/tests/fixtures/__init__.py +++ b/app/tests/fixtures/__init__.py @@ -267,6 +267,7 @@ from .model_role import ( from .model_service import ( kwargs_service, model_service, + serializer_service, ) from .model_slmticket import ( diff --git a/app/tests/fixtures/model_cluster.py b/app/tests/fixtures/model_cluster.py index c2ce209c..bba69f51 100644 --- a/app/tests/fixtures/model_cluster.py +++ b/app/tests/fixtures/model_cluster.py @@ -28,7 +28,11 @@ def kwargs_cluster(kwargs_centurionmodel, django_db_blocker, with django_db_blocker.unblock(): - node = model_device.objects.create( **kwargs_device ) + kwargs = kwargs_device.copy() + kwargs['serial_number'] = 'clu-123-654' + kwargs['uuid'] = '1cf3a2d4-1776-418b-86eb-00404a43d60e' + + node = model_device.objects.create( **kwargs ) cluster_type = model_clustertype.objects.create( **kwargs_clustertype ) kwargs = { diff --git a/app/tests/fixtures/model_device.py b/app/tests/fixtures/model_device.py index 19434ef3..b3a8426c 100644 --- a/app/tests/fixtures/model_device.py +++ b/app/tests/fixtures/model_device.py @@ -1,6 +1,8 @@ import pytest import random +from django.db import models + from itam.models.device import Device from itam.serializers.device import ( DeviceBaseSerializer, @@ -43,8 +45,15 @@ def kwargs_device(django_db_blocker, kwargs_centurionmodel, with django_db_blocker.unblock(): - device_model.delete() - device_type.delete() + try: + device_model.delete() + except models.deletion.ProtectedError: + pass + + try: + device_type.delete() + except models.deletion.ProtectedError: + pass @pytest.fixture( scope = 'class') diff --git a/app/tests/fixtures/model_devicemodel.py b/app/tests/fixtures/model_devicemodel.py index 9db39dfd..8364104b 100644 --- a/app/tests/fixtures/model_devicemodel.py +++ b/app/tests/fixtures/model_devicemodel.py @@ -2,6 +2,8 @@ import datetime import pytest import random +from django.db import models + from itam.models.device import DeviceModel from itam.serializers.device_model import ( DeviceModelBaseSerializer, @@ -41,7 +43,10 @@ def kwargs_devicemodel(kwargs_centurionmodel, django_db_blocker, with django_db_blocker.unblock(): - manufacturer.delete() + try: + manufacturer.delete() + except models.deletion.ProtectedError: + pass @pytest.fixture( scope = 'class') diff --git a/app/tests/fixtures/model_service.py b/app/tests/fixtures/model_service.py index 8a1ab279..09928145 100644 --- a/app/tests/fixtures/model_service.py +++ b/app/tests/fixtures/model_service.py @@ -2,6 +2,11 @@ import datetime import pytest from itim.models.services import Service +from itim.serializers.service import ( + ServiceBaseSerializer, + ServiceModelSerializer, + ServiceViewSerializer, +) @@ -39,6 +44,7 @@ def kwargs_service(django_db_blocker, 'device': device, 'config_key_variable': 'svc', 'port': [ port ], + 'config': { 'config_key_1': 'config_value_1' } } yield kwargs.copy() @@ -51,3 +57,14 @@ def kwargs_service(django_db_blocker, pass port.delete() + + + +@pytest.fixture( scope = 'class') +def serializer_service(): + + yield { + 'base': ServiceBaseSerializer, + 'model': ServiceModelSerializer, + 'view': ServiceViewSerializer + }