From 4b1548afa8430e65d0d9ace40067a3c6b0df95e0 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 10:22:38 +0930 Subject: [PATCH 01/30] refactor(itam): API Metadata Functional Test Suite re-written to Pytest for model OperatingSystem ref: #925 #922 --- .../functional/operating_system/conftest.py | 25 +++++++++++++++++++ ...t_functional_operating_system_metadata.py} | 22 ---------------- 2 files changed, 25 insertions(+), 22 deletions(-) create mode 100644 app/itam/tests/functional/operating_system/conftest.py rename app/itam/tests/functional/operating_system/{test_operating_system_viewset.py => test_functional_operating_system_metadata.py} (94%) diff --git a/app/itam/tests/functional/operating_system/conftest.py b/app/itam/tests/functional/operating_system/conftest.py new file mode 100644 index 00000000..da7cd06f --- /dev/null +++ b/app/itam/tests/functional/operating_system/conftest.py @@ -0,0 +1,25 @@ +import pytest + + + +@pytest.fixture( scope = 'class') +def model(model_operatingsystem): + + yield model_operatingsystem + + +@pytest.fixture( scope = 'class', autouse = True) +def model_kwargs(request, kwargs_operatingsystem): + + request.cls.kwargs_create_item = kwargs_operatingsystem.copy() + + yield kwargs_operatingsystem.copy() + + if hasattr(request.cls, 'kwargs_create_item'): + del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_device): + + yield serializer_device diff --git a/app/itam/tests/functional/operating_system/test_operating_system_viewset.py b/app/itam/tests/functional/operating_system/test_functional_operating_system_metadata.py similarity index 94% rename from app/itam/tests/functional/operating_system/test_operating_system_viewset.py rename to app/itam/tests/functional/operating_system/test_functional_operating_system_metadata.py index 18cff95f..1f4c135f 100644 --- a/app/itam/tests/functional/operating_system/test_operating_system_viewset.py +++ b/app/itam/tests/functional/operating_system/test_functional_operating_system_metadata.py @@ -9,8 +9,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.api_permissions_viewset import APIPermissions -from api.tests.abstract.api_serializer_viewset import SerializersTestCases from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional from itam.models.operating_system import OperatingSystem @@ -218,26 +216,6 @@ class ViewSetBase: -class OperatingSystemPermissionsAPI( - ViewSetBase, - APIPermissions, - TestCase, -): - - pass - - - -class OperatingSystemViewSet( - ViewSetBase, - SerializersTestCases, - TestCase, -): - - pass - - - class OperatingSystemMetadata( ViewSetBase, MetadataAttributesFunctional, From f461b7500fae00373398229dabd89ada90f5fac8 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 10:24:59 +0930 Subject: [PATCH 02/30] refactor(itam): API Fields render Functional Test Suite re-written to Pytest for model OperatingSystem ref: #925 #922 --- ..._functional_operating_system_api_fields.py | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 app/itam/tests/functional/operating_system/test_functional_operating_system_api_fields.py diff --git a/app/itam/tests/functional/operating_system/test_functional_operating_system_api_fields.py b/app/itam/tests/functional/operating_system/test_functional_operating_system_api_fields.py new file mode 100644 index 00000000..89228d38 --- /dev/null +++ b/app/itam/tests/functional/operating_system/test_functional_operating_system_api_fields.py @@ -0,0 +1,57 @@ +import pytest + +# from django.db import models + +from rest_framework.relations import Hyperlink + +from api.tests.functional.test_functional_api_fields import ( + APIFieldsInheritedCases, +) + + + +@pytest.mark.model_device +class OperatingSystemAPITestCases( + APIFieldsInheritedCases, +): + + + @property + def parameterized_api_fields(self): + + return { + 'publisher': { + 'expected': dict + }, + 'publisher.id': { + 'expected': int + }, + 'publisher.display_name': { + 'expected': str + }, + 'publisher.url': { + 'expected': Hyperlink + }, + 'name': { + 'expected': str + }, + 'modified': { + 'expected': str + } + } + + + +class OperatingSystemAPIInheritedCases( + OperatingSystemAPITestCases, +): + pass + + + +@pytest.mark.module_itam +class OperatingSystemAPIPyTest( + OperatingSystemAPITestCases, +): + + pass From 93dc76fc1a9a633c52778fec57ba47aed9fddc21 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 10:25:11 +0930 Subject: [PATCH 03/30] refactor(itam): Model Functional Test Suite re-written to Pytest for model OperatingSystem ref: #925 #922 --- .../test_functional_operating_system_model.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 app/itam/tests/functional/operating_system/test_functional_operating_system_model.py diff --git a/app/itam/tests/functional/operating_system/test_functional_operating_system_model.py b/app/itam/tests/functional/operating_system/test_functional_operating_system_model.py new file mode 100644 index 00000000..87d203d9 --- /dev/null +++ b/app/itam/tests/functional/operating_system/test_functional_operating_system_model.py @@ -0,0 +1,28 @@ +import pytest + +from core.tests.functional.centurion_abstract.test_functional_centurion_abstract_model import ( + CenturionAbstractModelInheritedCases +) + + + +@pytest.mark.model_device +class OperatingSystemModelTestCases( + CenturionAbstractModelInheritedCases +): + pass + + + +class OperatingSystemModelInheritedCases( + OperatingSystemModelTestCases, +): + pass + + + +@pytest.mark.module_itam +class OperatingSystemModelPyTest( + OperatingSystemModelTestCases, +): + pass From 3d4018e306276d47d0ec353531287b4a74cc84c2 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 10:25:31 +0930 Subject: [PATCH 04/30] refactor(itam): Serializer Unit Test Suite re-written to Pytest for model OperatingSystem ref: #925 #922 --- .../test_operating_system_serializer.py | 63 ---------- .../tests/unit/operating_system/conftest.py | 6 + .../test_unit_operating_system_serializer.py | 110 ++++++++++++++++++ app/tests/fixtures/__init__.py | 1 + app/tests/fixtures/model_operatingsystem.py | 15 +++ 5 files changed, 132 insertions(+), 63 deletions(-) delete mode 100644 app/itam/tests/functional/operating_system/test_operating_system_serializer.py create mode 100644 app/itam/tests/unit/operating_system/test_unit_operating_system_serializer.py diff --git a/app/itam/tests/functional/operating_system/test_operating_system_serializer.py b/app/itam/tests/functional/operating_system/test_operating_system_serializer.py deleted file mode 100644 index 1784d710..00000000 --- a/app/itam/tests/functional/operating_system/test_operating_system_serializer.py +++ /dev/null @@ -1,63 +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.serializers.operating_system import OperatingSystem, OperatingSystemModelSerializer - - - -class OperatingSystemValidationAPI( - TestCase, -): - - model = OperatingSystem - - @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.mock_view = MockView( user = self.user ) - - self.organization = organization - - self.item = self.model.objects.create( - organization=organization, - name = 'os name', - ) - - - - 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 = OperatingSystemModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "organization": self.organization.id, - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['name'][0] == 'required' diff --git a/app/itam/tests/unit/operating_system/conftest.py b/app/itam/tests/unit/operating_system/conftest.py index 2f520ec5..6aa3e9cb 100644 --- a/app/itam/tests/unit/operating_system/conftest.py +++ b/app/itam/tests/unit/operating_system/conftest.py @@ -17,3 +17,9 @@ def model_kwargs(request, kwargs_operatingsystem): if hasattr(request.cls, 'kwargs_create_item'): del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_operatingsystem): + + yield serializer_operatingsystem diff --git a/app/itam/tests/unit/operating_system/test_unit_operating_system_serializer.py b/app/itam/tests/unit/operating_system/test_unit_operating_system_serializer.py new file mode 100644 index 00000000..e83394fb --- /dev/null +++ b/app/itam/tests/unit/operating_system/test_unit_operating_system_serializer.py @@ -0,0 +1,110 @@ +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_operatingsystem +class OperatingSystemSerializerTestCases( + 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' + + + + + +class OperatingSystemSerializerInheritedCases( + OperatingSystemSerializerTestCases +): + pass + + + +@pytest.mark.module_itam +class OperatingSystemSerializerPyTest( + OperatingSystemSerializerTestCases +): + pass \ No newline at end of file diff --git a/app/tests/fixtures/__init__.py b/app/tests/fixtures/__init__.py index 7fd59bcc..81d42739 100644 --- a/app/tests/fixtures/__init__.py +++ b/app/tests/fixtures/__init__.py @@ -206,6 +206,7 @@ from .model_manufacturer import ( from .model_operatingsystem import ( kwargs_operatingsystem, model_operatingsystem, + serializer_operatingsystem, ) from .model_operatingsystemversion import ( diff --git a/app/tests/fixtures/model_operatingsystem.py b/app/tests/fixtures/model_operatingsystem.py index bb6ee8f0..57e02884 100644 --- a/app/tests/fixtures/model_operatingsystem.py +++ b/app/tests/fixtures/model_operatingsystem.py @@ -2,6 +2,11 @@ import datetime import pytest from itam.models.operating_system import OperatingSystem +from itam.serializers.operating_system import ( + OperatingSystemBaseSerializer, + OperatingSystemModelSerializer, + OperatingSystemViewSerializer, +) @@ -36,3 +41,13 @@ def kwargs_operatingsystem(django_db_blocker, with django_db_blocker.unblock(): publisher.delete() + + +@pytest.fixture( scope = 'class') +def serializer_operatingsystem(): + + yield { + 'base': OperatingSystemBaseSerializer, + 'model': OperatingSystemModelSerializer, + 'view': OperatingSystemViewSerializer + } From 7413a5686de87503b8af1260e8036be8fcc9180a Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 10:25:45 +0930 Subject: [PATCH 05/30] refactor(itam): ViewSet Unit Test Suite re-written to Pytest for model OperatingSystem ref: #925 #922 --- .../test_unit_operating_system_viewset.py | 92 +++++++++++++------ 1 file changed, 66 insertions(+), 26 deletions(-) diff --git a/app/itam/tests/unit/operating_system/test_unit_operating_system_viewset.py b/app/itam/tests/unit/operating_system/test_unit_operating_system_viewset.py index 262ea0da..e83f17b2 100644 --- a/app/itam/tests/unit/operating_system/test_unit_operating_system_viewset.py +++ b/app/itam/tests/unit/operating_system/test_unit_operating_system_viewset.py @@ -1,44 +1,84 @@ import pytest -from django.test import Client, TestCase - -from rest_framework.reverse import reverse from api.tests.unit.test_unit_common_viewset import ModelViewSetInheritedCases -from itam.viewsets.operating_system import ViewSet +from itam.viewsets.operating_system import ( + OperatingSystem, + ViewSet, +) -@pytest.mark.skip(reason = 'see #895, tests being refactored') @pytest.mark.model_operatingsystem -@pytest.mark.module_itam -class OperatingSystemViewsetList( +class ViewsetTestCases( ModelViewSetInheritedCases, - TestCase, ): - viewset = ViewSet - route_name = 'v2:_api_operatingsystem' + @pytest.fixture( scope = 'function' ) + def viewset(self): + return ViewSet - @classmethod - def setUpTestData(self): - """Setup Test - - 1. make list request - """ - - super().setUpTestData() + @property + def parameterized_class_attributes(self): + return { + '_model_documentation': { + 'type': type(None), + }, + 'back_url': { + 'type': type(None), + }, + 'documentation': { + 'type': str, + 'value': 'itam/operating_system' + }, + 'filterset_fields': { + 'value': [ + 'organization', + 'publisher' + ] + }, + 'model': { + 'value': OperatingSystem + }, + 'model_documentation': { + 'type': type(None), + }, + 'queryset': { + 'type': type(None), + }, + 'serializer_class': { + 'type': type(None), + }, + 'search_fields': { + 'value': [ + 'name' + ] + }, + 'view_description': { + 'value': 'Operating Systems' + }, + 'view_name': { + 'type': type(None), + }, + 'view_serializer_name': { + 'type': type(None), + } + } - client = Client() - - url = reverse( - self.route_name + '-list', - kwargs = self.kwargs - ) - client.force_login(self.view_user) +class OperatingSystemViewsetInheritedCases( + ViewsetTestCases, +): + pass - self.http_options_response_list = client.options(url) + + +@pytest.mark.module_itam +class OperatingSystemViewsetPyTest( + ViewsetTestCases, +): + + pass From d92f967bb94ee8cfa187c53ec0e0a00d25f29b79 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 10:26:46 +0930 Subject: [PATCH 06/30] refactor(itam): Remove old test suites no longer required model OperatingSystem ref: #925 closes #922 --- ..._functional_operating_system_api_fields.py | 2 +- ...st_functional_operating_system_metadata.py | 2 + .../test_functional_operating_system_model.py | 2 +- .../test_operating_system_api_v2.py | 182 ------------------ ...est_operating_system_item_ticket_api_v2.py | 99 ---------- .../test_unit_operating_system_model.py | 32 --- .../test_unit_operating_system_serializer.py | 2 - 7 files changed, 4 insertions(+), 317 deletions(-) delete mode 100644 app/itam/tests/unit/operating_system/test_operating_system_api_v2.py delete mode 100644 app/itam/tests/unit/operating_system/test_operating_system_item_ticket_api_v2.py diff --git a/app/itam/tests/functional/operating_system/test_functional_operating_system_api_fields.py b/app/itam/tests/functional/operating_system/test_functional_operating_system_api_fields.py index 89228d38..b0efa247 100644 --- a/app/itam/tests/functional/operating_system/test_functional_operating_system_api_fields.py +++ b/app/itam/tests/functional/operating_system/test_functional_operating_system_api_fields.py @@ -10,7 +10,7 @@ from api.tests.functional.test_functional_api_fields import ( -@pytest.mark.model_device +@pytest.mark.model_operatingsystem class OperatingSystemAPITestCases( APIFieldsInheritedCases, ): diff --git a/app/itam/tests/functional/operating_system/test_functional_operating_system_metadata.py b/app/itam/tests/functional/operating_system/test_functional_operating_system_metadata.py index 1f4c135f..5c5df049 100644 --- a/app/itam/tests/functional/operating_system/test_functional_operating_system_metadata.py +++ b/app/itam/tests/functional/operating_system/test_functional_operating_system_metadata.py @@ -19,6 +19,7 @@ User = django.contrib.auth.get_user_model() +@pytest.mark.module_itam class ViewSetBase: model = OperatingSystem @@ -216,6 +217,7 @@ class ViewSetBase: +@pytest.mark.module_itam class OperatingSystemMetadata( ViewSetBase, MetadataAttributesFunctional, diff --git a/app/itam/tests/functional/operating_system/test_functional_operating_system_model.py b/app/itam/tests/functional/operating_system/test_functional_operating_system_model.py index 87d203d9..4d2a4341 100644 --- a/app/itam/tests/functional/operating_system/test_functional_operating_system_model.py +++ b/app/itam/tests/functional/operating_system/test_functional_operating_system_model.py @@ -6,7 +6,7 @@ from core.tests.functional.centurion_abstract.test_functional_centurion_abstract -@pytest.mark.model_device +@pytest.mark.model_operatingsystem class OperatingSystemModelTestCases( CenturionAbstractModelInheritedCases ): diff --git a/app/itam/tests/unit/operating_system/test_operating_system_api_v2.py b/app/itam/tests/unit/operating_system/test_operating_system_api_v2.py deleted file mode 100644 index 98ff37c0..00000000 --- a/app/itam/tests/unit/operating_system/test_operating_system_api_v2.py +++ /dev/null @@ -1,182 +0,0 @@ -import django -import pytest -import unittest - -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.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 - -from core.models.manufacturer import Manufacturer - -from itam.models.operating_system import OperatingSystem - -User = django.contrib.auth.get_user_model() - - - -@pytest.mark.model_operatingsystem -@pytest.mark.module_itam -class OperatingSystemAPI( - TestCase, - APITenancyObject -): - - model = OperatingSystem - - @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') - - manufacturer = Manufacturer.objects.create( - organization = self.organization, - name = 'a manufacturer' - ) - - - self.item = self.model.objects.create( - organization = self.organization, - name = 'one', - publisher = manufacturer, - model_notes = 'a note' - ) - - - self.url_view_kwargs = {'pk': self.item.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 = self.organization, - ) - - view_team.permissions.set([view_permissions]) - - self.view_user = User.objects.create_user(username="test_user_view", password="password") - teamuser = TeamUsers.objects.create( - team = view_team, - user = self.view_user - ) - - client = Client() - url = reverse('v2:_api_operatingsystem-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_publisher(self): - """ Test for existance of API Field - - publisher field must exist - """ - - assert 'publisher' in self.api_data - - - def test_api_field_type_publisher(self): - """ Test for type for API Field - - publisher field must be dict - """ - - assert type(self.api_data['publisher']) is dict - - - def test_api_field_exists_publisher_id(self): - """ Test for existance of API Field - - publisher.id field must exist - """ - - assert 'id' in self.api_data['publisher'] - - - def test_api_field_type_publisher_id(self): - """ Test for type for API Field - - publisher.id field must be int - """ - - assert type(self.api_data['publisher']['id']) is int - - - def test_api_field_exists_publisher_display_name(self): - """ Test for existance of API Field - - publisher.display_name field must exist - """ - - assert 'display_name' in self.api_data['publisher'] - - - def test_api_field_type_publisher_display_name(self): - """ Test for type for API Field - - publisher.display_name field must be str - """ - - assert type(self.api_data['publisher']['display_name']) is str - - - def test_api_field_exists_publisher_url(self): - """ Test for existance of API Field - - publisher.url field must exist - """ - - assert 'url' in self.api_data['publisher'] - - - def test_api_field_type_publisher_url(self): - """ Test for type for API Field - - publisher.url field must be Hyperlink - """ - - assert type(self.api_data['publisher']['url']) is Hyperlink - - - - def test_api_field_exists_urls_tickets(self): - """ Test for existance of API Field - - _urls.tickets field must exist - """ - - assert 'tickets' in self.api_data['_urls'] - - - def test_api_field_type_urls_tickets(self): - """ Test for type for API Field - - _urls.tickets field must be str - """ - - assert type(self.api_data['_urls']['tickets']) is str - diff --git a/app/itam/tests/unit/operating_system/test_operating_system_item_ticket_api_v2.py b/app/itam/tests/unit/operating_system/test_operating_system_item_ticket_api_v2.py deleted file mode 100644 index fe4aa300..00000000 --- a/app/itam/tests/unit/operating_system/test_operating_system_item_ticket_api_v2.py +++ /dev/null @@ -1,99 +0,0 @@ -import pytest - -from django.shortcuts import reverse -from django.test import Client, TestCase - -from core.tests.abstract.test_item_ticket_api_v2 import ItemTicketAPI - -from core.models.ticket.ticket_linked_items import TicketLinkedItem - -from itam.models.operating_system import OperatingSystem - - - -@pytest.mark.skip( reason = 'this ticket model is depreciated' ) -@pytest.mark.model_operatingsystem -@pytest.mark.module_itam -class OperatingSystemItemTicketAPI( - ItemTicketAPI, - TestCase, -): - """Test Cases for Item Tickets - - Args: - APITenancyObject (class): Base class for ALL field checks - """ - - item_type = TicketLinkedItem.Modules.OPERATING_SYSTEM - - item_class = 'operating_system' - - item_model = OperatingSystem - - - @classmethod - def setUpTestData(self): - """Setup Test - - 1. Create an organization for user and item - 2. Create an item - - """ - - super().setUpTestData() - - - self.linked_item = self.item_model.objects.create( - organization = self.organization, - name = 'dev' - ) - - - - self.item = self.model.objects.create( - organization = self.organization, - ticket = self.ticket, - - # Item attributes - - item = self.linked_item.id, - item_type = self.item_type, - ) - - - - self.url_view_kwargs = { - 'item_class': self.item_class, - 'item_id': self.item.id, - 'pk': self.item.id, - } - - - client = Client() - url = reverse('v2:_api_v2_item_tickets-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_value_item_id(self): - """ Test for existance of API Field - - item.id field must exist - """ - - assert self.api_data['item']['id'] == self.linked_item.id - - - - def test_api_field_value_item_type(self): - """ Test for type for API Field - - item_type field must be int - """ - - assert self.api_data['item_type'] == self.item_type diff --git a/app/itam/tests/unit/operating_system/test_unit_operating_system_model.py b/app/itam/tests/unit/operating_system/test_unit_operating_system_model.py index a41be2d7..ead27d48 100644 --- a/app/itam/tests/unit/operating_system/test_unit_operating_system_model.py +++ b/app/itam/tests/unit/operating_system/test_unit_operating_system_model.py @@ -67,35 +67,3 @@ class OperatingSystemModelPyTest( OperatingSystemModelTestCases, ): pass - - # def test_method_get_url_kwargs(self, mocker, model_instance, model_kwargs): - # """Test Class Method - - # Ensure method `get_url_kwargs` returns the correct value. - # """ - - - # url = model_instance.get_url_kwargs() - - # assert model_instance.get_url_kwargs() == { - # 'device_id': model_kwargs['device'].id, - # 'pk': model_instance.id - # } - - - # def test_model_tag_defined(self, model): - # """ Model Tag - - # Ensure that the model has a tag defined. - # """ - - # pytest.xfail( reason = 'model does not require' ) - - - # def test_method_value_not_default___str__(self, model, model_instance ): - # """Test Method - - # Ensure method `__str__` does not return the default value. - # """ - - # pytest.xfail( reason = 'model does not require' ) diff --git a/app/itam/tests/unit/operating_system/test_unit_operating_system_serializer.py b/app/itam/tests/unit/operating_system/test_unit_operating_system_serializer.py index e83394fb..920344d3 100644 --- a/app/itam/tests/unit/operating_system/test_unit_operating_system_serializer.py +++ b/app/itam/tests/unit/operating_system/test_unit_operating_system_serializer.py @@ -1,7 +1,5 @@ import pytest -# from django.db import models - from rest_framework.exceptions import ValidationError from api.tests.unit.test_unit_serializer import ( From 8d0d9240a85371c7dbef1c99408307d7803d3a30 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 11:02:24 +0930 Subject: [PATCH 07/30] refactor(itam): API Metadata Functional Test Suite re-written to Pytest for model OperatingSystemVersion ref: #925 #923 --- .../operating_system_version/conftest.py | 25 +++++++++++++++++++ ...test_operating_system_version_metadata.py} | 16 ++---------- 2 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 app/itam/tests/functional/operating_system_version/conftest.py rename app/itam/tests/functional/operating_system_version/{test_operating_system_version_viewset.py => test_operating_system_version_metadata.py} (95%) diff --git a/app/itam/tests/functional/operating_system_version/conftest.py b/app/itam/tests/functional/operating_system_version/conftest.py new file mode 100644 index 00000000..1f99a35c --- /dev/null +++ b/app/itam/tests/functional/operating_system_version/conftest.py @@ -0,0 +1,25 @@ +import pytest + + + +@pytest.fixture( scope = 'class') +def model(model_operatingsystemversion): + + yield model_operatingsystemversion + + +@pytest.fixture( scope = 'class', autouse = True) +def model_kwargs(request, kwargs_operatingsystemversion): + + request.cls.kwargs_create_item = kwargs_operatingsystemversion.copy() + + yield kwargs_operatingsystemversion.copy() + + if hasattr(request.cls, 'kwargs_create_item'): + del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_operatingsystemversion): + + yield serializer_operatingsystemversion diff --git a/app/itam/tests/functional/operating_system_version/test_operating_system_version_viewset.py b/app/itam/tests/functional/operating_system_version/test_operating_system_version_metadata.py similarity index 95% rename from app/itam/tests/functional/operating_system_version/test_operating_system_version_viewset.py rename to app/itam/tests/functional/operating_system_version/test_operating_system_version_metadata.py index 04f9d0ec..7f1f3d91 100644 --- a/app/itam/tests/functional/operating_system_version/test_operating_system_version_viewset.py +++ b/app/itam/tests/functional/operating_system_version/test_operating_system_version_metadata.py @@ -9,8 +9,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.api_permissions_viewset import APIPermissions -from api.tests.abstract.api_serializer_viewset import SerializersTestCases from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional from itam.models.operating_system import OperatingSystem, OperatingSystemVersion @@ -21,6 +19,7 @@ User = django.contrib.auth.get_user_model() +@pytest.mark.model_operatingsystemversion class ViewSetBase: model = OperatingSystemVersion @@ -232,18 +231,7 @@ class ViewSetBase: -class OperatingSystemVersionPermissionsAPI(ViewSetBase, APIPermissions, TestCase): - - pass - - - -class OperatingSystemVersionViewSetBase(ViewSetBase, SerializersTestCases, TestCase): - - pass - - - +@pytest.mark.module_itam class OperatingSystemVersionMetadata( ViewSetBase, MetadataAttributesFunctional, From 69f1c1c3b6f26f5715063b1ea580a52a18dae3a3 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 11:03:12 +0930 Subject: [PATCH 08/30] refactor(itam): API Fields render Functional Test Suite re-written to Pytest for model OperatingSystemVersion ref: #925 #923 --- ...nal_operating_system_version_api_fields.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 app/itam/tests/functional/operating_system_version/test_functional_operating_system_version_api_fields.py diff --git a/app/itam/tests/functional/operating_system_version/test_functional_operating_system_version_api_fields.py b/app/itam/tests/functional/operating_system_version/test_functional_operating_system_version_api_fields.py new file mode 100644 index 00000000..45a024d7 --- /dev/null +++ b/app/itam/tests/functional/operating_system_version/test_functional_operating_system_version_api_fields.py @@ -0,0 +1,55 @@ +import pytest + +from rest_framework.relations import Hyperlink + +from api.tests.functional.test_functional_api_fields import ( + APIFieldsInheritedCases, +) + + + +@pytest.mark.model_operatingsystem +class OperatingSystemVersionAPITestCases( + APIFieldsInheritedCases, +): + + + @property + def parameterized_api_fields(self): + + return { + 'operating_system': { + 'expected': dict + }, + 'operating_system.id': { + 'expected': int + }, + 'operating_system.display_name': { + 'expected': str + }, + 'operating_system.url': { + 'expected': Hyperlink + }, + 'name': { + 'expected': str + }, + 'modified': { + 'expected': str + } + } + + + +class OperatingSystemVersionAPIInheritedCases( + OperatingSystemVersionAPITestCases, +): + pass + + + +@pytest.mark.module_itam +class OperatingSystemVersionAPIPyTest( + OperatingSystemVersionAPITestCases, +): + + pass From 838bc871c15e5e5716200e832e9a7e52073dd556 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 11:03:26 +0930 Subject: [PATCH 09/30] refactor(itam): Model Functional Test Suite re-written to Pytest for model OperatingSystemVersion ref: #925 #923 --- ...nctional_operating_system_version_model.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 app/itam/tests/functional/operating_system_version/test_functional_operating_system_version_model.py diff --git a/app/itam/tests/functional/operating_system_version/test_functional_operating_system_version_model.py b/app/itam/tests/functional/operating_system_version/test_functional_operating_system_version_model.py new file mode 100644 index 00000000..8bf9c47e --- /dev/null +++ b/app/itam/tests/functional/operating_system_version/test_functional_operating_system_version_model.py @@ -0,0 +1,28 @@ +import pytest + +from core.tests.functional.centurion_abstract.test_functional_centurion_abstract_model import ( + CenturionAbstractModelInheritedCases +) + + + +@pytest.mark.model_operatingsystem +class OperatingSystemVersionModelTestCases( + CenturionAbstractModelInheritedCases +): + pass + + + +class OperatingSystemVersionModelInheritedCases( + OperatingSystemVersionModelTestCases, +): + pass + + + +@pytest.mark.module_itam +class OperatingSystemVersionModelPyTest( + OperatingSystemVersionModelTestCases, +): + pass From 66706df727aee6925547209e7e1c8f38669bbd2e Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 11:03:49 +0930 Subject: [PATCH 10/30] refactor(itam): Serializer Unit Test Suite re-written to Pytest for model OperatingSystemVersion ref: #925 #923 --- ...est_operating_system_version_serializer.py | 69 ------------- .../unit/operating_system_version/conftest.py | 6 ++ ...nit_operating_system_version_serializer.py | 98 +++++++++++++++++++ app/tests/fixtures/__init__.py | 1 + .../fixtures/model_operatingsystemversion.py | 15 +++ 5 files changed, 120 insertions(+), 69 deletions(-) delete mode 100644 app/itam/tests/functional/operating_system_version/test_operating_system_version_serializer.py create mode 100644 app/itam/tests/unit/operating_system_version/test_unit_operating_system_version_serializer.py diff --git a/app/itam/tests/functional/operating_system_version/test_operating_system_version_serializer.py b/app/itam/tests/functional/operating_system_version/test_operating_system_version_serializer.py deleted file mode 100644 index caea6688..00000000 --- a/app/itam/tests/functional/operating_system_version/test_operating_system_version_serializer.py +++ /dev/null @@ -1,69 +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.serializers.operating_system_version import OperatingSystem, OperatingSystemVersion, OperatingSystemVersionModelSerializer - - - -class OperatingSystemVersionValidationAPI( - TestCase, -): - - model = OperatingSystemVersion - - @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.mock_view = MockView( user = self.user ) - - self.organization = organization - - os = OperatingSystem.objects.create( - organization=organization, - name = 'os name' - ) - - self.item = self.model.objects.create( - organization=organization, - name = 'os name', - operating_system = os - ) - - - - 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 = OperatingSystemVersionModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "organization": self.organization.id, - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['name'][0] == 'required' diff --git a/app/itam/tests/unit/operating_system_version/conftest.py b/app/itam/tests/unit/operating_system_version/conftest.py index 5e9fe3cd..1f99a35c 100644 --- a/app/itam/tests/unit/operating_system_version/conftest.py +++ b/app/itam/tests/unit/operating_system_version/conftest.py @@ -17,3 +17,9 @@ def model_kwargs(request, kwargs_operatingsystemversion): if hasattr(request.cls, 'kwargs_create_item'): del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_operatingsystemversion): + + yield serializer_operatingsystemversion diff --git a/app/itam/tests/unit/operating_system_version/test_unit_operating_system_version_serializer.py b/app/itam/tests/unit/operating_system_version/test_unit_operating_system_version_serializer.py new file mode 100644 index 00000000..4080557a --- /dev/null +++ b/app/itam/tests/unit/operating_system_version/test_unit_operating_system_version_serializer.py @@ -0,0 +1,98 @@ +import pytest + +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_operatingsystemversion +class OperatingSystemVersionSerializerTestCases( + SerializerTestCases +): + + + + 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' + + + + @pytest.mark.regression + def test_serializer_create_calls_model_full_clean(self, + kwargs_api_create, mocker, model, model_serializer, request_user + ): + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + mock_view.kwargs = { + 'operating_system_id': kwargs_api_create['operating_system'] + } + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs_api_create + ) + + serializer.is_valid(raise_exception = True) + + full_clean = mocker.spy(model, 'full_clean') + + serializer.save() + + full_clean.assert_called_once() + + + + +class OperatingSystemVersionSerializerInheritedCases( + OperatingSystemVersionSerializerTestCases +): + pass + + + +@pytest.mark.module_itam +class OperatingSystemVersionSerializerPyTest( + OperatingSystemVersionSerializerTestCases +): + pass \ No newline at end of file diff --git a/app/tests/fixtures/__init__.py b/app/tests/fixtures/__init__.py index 81d42739..73b41858 100644 --- a/app/tests/fixtures/__init__.py +++ b/app/tests/fixtures/__init__.py @@ -212,6 +212,7 @@ from .model_operatingsystem import ( from .model_operatingsystemversion import ( kwargs_operatingsystemversion, model_operatingsystemversion, + serializer_operatingsystemversion ) from .model_permission import ( diff --git a/app/tests/fixtures/model_operatingsystemversion.py b/app/tests/fixtures/model_operatingsystemversion.py index 5067f59e..dfc43a09 100644 --- a/app/tests/fixtures/model_operatingsystemversion.py +++ b/app/tests/fixtures/model_operatingsystemversion.py @@ -2,6 +2,11 @@ import datetime import pytest from itam.models.operating_system import OperatingSystemVersion +from itam.serializers.operating_system_version import ( + OperatingSystemVersionBaseSerializer, + OperatingSystemVersionModelSerializer, + OperatingSystemVersionViewSerializer, +) @@ -39,3 +44,13 @@ def kwargs_operatingsystemversion(django_db_blocker, with django_db_blocker.unblock(): os.delete() + + +@pytest.fixture( scope = 'class') +def serializer_operatingsystemversion(): + + yield { + 'base': OperatingSystemVersionBaseSerializer, + 'model': OperatingSystemVersionModelSerializer, + 'view': OperatingSystemVersionViewSerializer + } From bcce98e884cbda78460dee35ac7781f3453dc442 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 11:04:06 +0930 Subject: [PATCH 11/30] refactor(itam): ViewSet Unit Test Suite re-written to Pytest for model OperatingSystemVersion ref: #925 #923 --- ...t_unit_operating_system_version_viewset.py | 150 ++++++++++++++---- 1 file changed, 116 insertions(+), 34 deletions(-) diff --git a/app/itam/tests/unit/operating_system_version/test_unit_operating_system_version_viewset.py b/app/itam/tests/unit/operating_system_version/test_unit_operating_system_version_viewset.py index 7ebc6a5f..5ccddd18 100644 --- a/app/itam/tests/unit/operating_system_version/test_unit_operating_system_version_viewset.py +++ b/app/itam/tests/unit/operating_system_version/test_unit_operating_system_version_viewset.py @@ -1,53 +1,135 @@ import pytest -from django.test import Client, TestCase - -from rest_framework.reverse import reverse from api.tests.unit.test_unit_common_viewset import ModelViewSetInheritedCases -from itam.models.operating_system import OperatingSystem -from itam.viewsets.operating_system_version import ViewSet +from itam.viewsets.operating_system_version import ( + OperatingSystemVersion, + ViewSet, +) -@pytest.mark.skip(reason = 'see #895, tests being refactored') @pytest.mark.model_operatingsystemversion -@pytest.mark.module_itam -class DeviceSoftwareViewsetList( +class ViewsetTestCases( ModelViewSetInheritedCases, - TestCase, ): - viewset = ViewSet - route_name = 'v2:_api_operatingsystemversion' + @pytest.fixture( scope = 'function' ) + def viewset(self): + return ViewSet - @classmethod - def setUpTestData(self): - """Setup Test - - 1. make list request - """ - - - super().setUpTestData() - - self.kwargs = { - 'operating_system_id': OperatingSystem.objects.create( - organization = self.organization, - name = 'os' - ).id + @property + def parameterized_class_attributes(self): + return { + '_model_documentation': { + 'type': type(None), + }, + 'back_url': { + 'type': type(None), + }, + 'documentation': { + 'type': type(None), + 'value': None + }, + 'filterset_fields': { + 'value': [ + 'organization', + ] + }, + 'model': { + 'value': OperatingSystemVersion + }, + 'model_documentation': { + 'type': type(None), + }, + 'queryset': { + 'type': type(None), + }, + 'serializer_class': { + 'type': type(None), + }, + 'search_fields': { + 'value': [ + 'name' + ] + }, + 'view_description': { + 'value': 'Operating Systems' + }, + 'view_name': { + 'type': type(None), + }, + 'view_serializer_name': { + 'type': type(None), + } } - client = Client() - - url = reverse( - self.route_name + '-list', - kwargs = self.kwargs - ) + def test_view_func_get_queryset_cache_result(self, viewset_mock_request, + # kwargs_create_item + ): + """Viewset Test - client.force_login(self.view_user) + Ensure that the `get_queryset` function caches the result under + attribute `.queryset` + """ - self.http_options_response_list = client.options(url) + view_set = viewset_mock_request + + view_set.kwargs = { + 'operating_system_id': self.kwargs_create_item['operating_system'].id + } + + assert view_set.queryset is None # Must be empty before init + + q = view_set.get_queryset() + + assert view_set.queryset is not None # Must not be empty after init + + assert q == view_set.queryset + + + def test_view_func_get_queryset_cache_result_used(self, mocker, viewset, viewset_mock_request, + # kwargs_create_item + ): + """Viewset Test + + Ensure that the `get_queryset` function caches the result under + attribute `.queryset` + """ + + qs = mocker.spy(viewset_mock_request.model, 'objects') + + view_set = viewset_mock_request + + view_set.kwargs = { + 'operating_system_id': self.kwargs_create_item['operating_system'].id + } + + view_set.get_queryset() # Initial QuerySet fetch/filter and cache + + assert len(qs.method_calls) == 1 # one call to .all() + assert len(qs.mock_calls) == 3 # calls = .all(), all().filter() + + view_set.get_queryset() # Use Cached results, dont re-fetch QuerySet + + assert len(qs.method_calls) == 1 + assert len(qs.mock_calls) == 3 + + + +class OperatingSystemVersionViewsetInheritedCases( + ViewsetTestCases, +): + pass + + + +@pytest.mark.module_itam +class OperatingSystemVersionViewsetPyTest( + ViewsetTestCases, +): + + pass From 430cbe638522209536d33b2c0c6493e31b6c5608 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 11:04:22 +0930 Subject: [PATCH 12/30] refactor(itam): Remove old test suites no longer required model OperatingSystemVersion ref: #925 closes #923 --- ...st_test_operating_system_version_api_v2.py | 160 ------------------ 1 file changed, 160 deletions(-) delete mode 100644 app/itam/tests/unit/operating_system_version/test_test_operating_system_version_api_v2.py diff --git a/app/itam/tests/unit/operating_system_version/test_test_operating_system_version_api_v2.py b/app/itam/tests/unit/operating_system_version/test_test_operating_system_version_api_v2.py deleted file mode 100644 index ce45ae3f..00000000 --- a/app/itam/tests/unit/operating_system_version/test_test_operating_system_version_api_v2.py +++ /dev/null @@ -1,160 +0,0 @@ -import django -import pytest - -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.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 - -from itam.models.operating_system import OperatingSystem, OperatingSystemVersion - -User = django.contrib.auth.get_user_model() - - - -@pytest.mark.model_operatingsystemversion -@pytest.mark.module_itam -class OperatingSystemVersionAPI( - TestCase, - APITenancyObject -): - - model = OperatingSystemVersion - - @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') - - operating_system = OperatingSystem.objects.create( - organization = self.organization, - name = 'one', - model_notes = 'a note' - ) - - self.item = self.model.objects.create( - organization = self.organization, - name = '10', - model_notes = 'a note', - operating_system = operating_system - ) - - - self.url_view_kwargs = {'operating_system_id': operating_system.id, 'pk': self.item.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 = self.organization, - ) - - view_team.permissions.set([view_permissions]) - - self.view_user = User.objects.create_user(username="test_user_view", password="password") - teamuser = TeamUsers.objects.create( - team = view_team, - user = self.view_user - ) - - client = Client() - url = reverse('v2:_api_operatingsystemversion-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_operating_system(self): - """ Test for existance of API Field - - operating_system field must exist - """ - - assert 'operating_system' in self.api_data - - - def test_api_field_type_operating_system(self): - """ Test for type for API Field - - operating_system field must be dict - """ - - assert type(self.api_data['operating_system']) is dict - - - def test_api_field_exists_operating_system_id(self): - """ Test for existance of API Field - - operating_system.id field must exist - """ - - assert 'id' in self.api_data['operating_system'] - - - def test_api_field_type_operating_system_id(self): - """ Test for type for API Field - - operating_system.id field must be int - """ - - assert type(self.api_data['operating_system']['id']) is int - - - def test_api_field_exists_operating_system_display_name(self): - """ Test for existance of API Field - - operating_system.display_name field must exist - """ - - assert 'display_name' in self.api_data['operating_system'] - - - def test_api_field_type_operating_system_display_name(self): - """ Test for type for API Field - - operating_system.display_name field must be str - """ - - assert type(self.api_data['operating_system']['display_name']) is str - - - def test_api_field_exists_operating_system_url(self): - """ Test for existance of API Field - - operating_system.url field must exist - """ - - assert 'url' in self.api_data['operating_system'] - - - def test_api_field_type_operating_system_url(self): - """ Test for type for API Field - - operating_system.url field must be Hyperlink - """ - - assert type(self.api_data['operating_system']['url']) is Hyperlink - From 76dfcb8bce7c3fc50413c227054c608cc06b9ffa Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 11:31:59 +0930 Subject: [PATCH 13/30] refactor(itam): API Metadata Functional Test Suite re-written to Pytest for model Software ref: #925 #924 --- .../tests/functional/software/conftest.py | 25 +++++++++++++++++++ ...y => test_functional_software_metadata.py} | 16 ++---------- 2 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 app/itam/tests/functional/software/conftest.py rename app/itam/tests/functional/software/{test_software_viewset.py => test_functional_software_metadata.py} (95%) diff --git a/app/itam/tests/functional/software/conftest.py b/app/itam/tests/functional/software/conftest.py new file mode 100644 index 00000000..4fe698d2 --- /dev/null +++ b/app/itam/tests/functional/software/conftest.py @@ -0,0 +1,25 @@ +import pytest + + + +@pytest.fixture( scope = 'class') +def model(model_software): + + yield model_software + + +@pytest.fixture( scope = 'class', autouse = True) +def model_kwargs(request, kwargs_software): + + request.cls.kwargs_create_item = kwargs_software.copy() + + yield kwargs_software.copy() + + if hasattr(request.cls, 'kwargs_create_item'): + del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_software): + + yield serializer_software diff --git a/app/itam/tests/functional/software/test_software_viewset.py b/app/itam/tests/functional/software/test_functional_software_metadata.py similarity index 95% rename from app/itam/tests/functional/software/test_software_viewset.py rename to app/itam/tests/functional/software/test_functional_software_metadata.py index 25f82551..dd39ce1c 100644 --- a/app/itam/tests/functional/software/test_software_viewset.py +++ b/app/itam/tests/functional/software/test_functional_software_metadata.py @@ -9,8 +9,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.api_permissions_viewset import APIPermissions -from api.tests.abstract.api_serializer_viewset import SerializersTestCases from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional from itam.models.software import Software @@ -21,6 +19,7 @@ User = django.contrib.auth.get_user_model() +@pytest.mark.model_software class ViewSetBase: model = Software @@ -219,18 +218,7 @@ class ViewSetBase: -class SoftwarePermissionsAPI(ViewSetBase, APIPermissions, TestCase): - - pass - - - -class SoftwareViewSet(ViewSetBase, SerializersTestCases, TestCase): - - pass - - - +@pytest.mark.module_itam class SoftwareMetadata( ViewSetBase, MetadataAttributesFunctional, From cf09a1569065741ec21cff7493a584f609e38f51 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 11:32:16 +0930 Subject: [PATCH 14/30] refactor(itam): API Fields render Functional Test Suite re-written to Pytest for model Software ref: #925 #924 --- .../test_functional_software_api_fields.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 app/itam/tests/functional/software/test_functional_software_api_fields.py diff --git a/app/itam/tests/functional/software/test_functional_software_api_fields.py b/app/itam/tests/functional/software/test_functional_software_api_fields.py new file mode 100644 index 00000000..881deca1 --- /dev/null +++ b/app/itam/tests/functional/software/test_functional_software_api_fields.py @@ -0,0 +1,69 @@ +import pytest + +# from django.db import models + +from rest_framework.relations import Hyperlink + +from api.tests.functional.test_functional_api_fields import ( + APIFieldsInheritedCases, +) + + + +@pytest.mark.model_software +class SoftwareAPITestCases( + APIFieldsInheritedCases, +): + + + @property + def parameterized_api_fields(self): + + return { + 'publisher': { + 'expected': dict + }, + 'publisher.id': { + 'expected': int + }, + 'publisher.display_name': { + 'expected': str + }, + 'publisher.url': { + 'expected': Hyperlink + }, + 'name': { + 'expected': str + }, + 'category': { + 'expected': dict + }, + 'category.id': { + 'expected': int + }, + 'category.display_name': { + 'expected': str + }, + 'category.url': { + 'expected': Hyperlink + }, + 'modified': { + 'expected': str + } + } + + + +class SoftwareAPIInheritedCases( + SoftwareAPITestCases, +): + pass + + + +@pytest.mark.module_itam +class SoftwareAPIPyTest( + SoftwareAPITestCases, +): + + pass From 7fb56d87d6a50c0758842a93682a8e674dbb4bd6 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 11:32:26 +0930 Subject: [PATCH 15/30] refactor(itam): Model Functional Test Suite re-written to Pytest for model Software ref: #925 #924 --- .../test_functional_software_model.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 app/itam/tests/functional/software/test_functional_software_model.py diff --git a/app/itam/tests/functional/software/test_functional_software_model.py b/app/itam/tests/functional/software/test_functional_software_model.py new file mode 100644 index 00000000..4b92c2e7 --- /dev/null +++ b/app/itam/tests/functional/software/test_functional_software_model.py @@ -0,0 +1,28 @@ +import pytest + +from core.tests.functional.centurion_abstract.test_functional_centurion_abstract_model import ( + CenturionAbstractModelInheritedCases +) + + + +@pytest.mark.model_software +class SoftwareModelTestCases( + CenturionAbstractModelInheritedCases +): + pass + + + +class SoftwareModelInheritedCases( + SoftwareModelTestCases, +): + pass + + + +@pytest.mark.module_itam +class SoftwareModelPyTest( + SoftwareModelTestCases, +): + pass From 6faa185406bd9b08d42c224aaf2bad6430796a42 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 11:32:44 +0930 Subject: [PATCH 16/30] refactor(itam): Serializer Unit Test Suite re-written to Pytest for model Software ref: #925 #924 --- .../software/test_software_serializer.py | 63 ------------------ app/itam/tests/unit/software/conftest.py | 6 ++ .../software/test_unit_software_serializer.py | 66 +++++++++++++++++++ app/tests/fixtures/__init__.py | 1 + app/tests/fixtures/model_software.py | 34 +++++++++- 5 files changed, 106 insertions(+), 64 deletions(-) delete mode 100644 app/itam/tests/functional/software/test_software_serializer.py create mode 100644 app/itam/tests/unit/software/test_unit_software_serializer.py diff --git a/app/itam/tests/functional/software/test_software_serializer.py b/app/itam/tests/functional/software/test_software_serializer.py deleted file mode 100644 index 144fd698..00000000 --- a/app/itam/tests/functional/software/test_software_serializer.py +++ /dev/null @@ -1,63 +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.serializers.software import Software, SoftwareModelSerializer - - - -class SoftwareValidationAPI( - TestCase, -): - - model = Software - - @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.item = self.model.objects.create( - organization=organization, - name = 'os name', - ) - - self.mock_view = MockView( user = self.user ) - - - - 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 = SoftwareModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "organization": self.organization.id, - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['name'][0] == 'required' diff --git a/app/itam/tests/unit/software/conftest.py b/app/itam/tests/unit/software/conftest.py index 713f3c2e..4fe698d2 100644 --- a/app/itam/tests/unit/software/conftest.py +++ b/app/itam/tests/unit/software/conftest.py @@ -17,3 +17,9 @@ def model_kwargs(request, kwargs_software): if hasattr(request.cls, 'kwargs_create_item'): del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_software): + + yield serializer_software diff --git a/app/itam/tests/unit/software/test_unit_software_serializer.py b/app/itam/tests/unit/software/test_unit_software_serializer.py new file mode 100644 index 00000000..a96e2972 --- /dev/null +++ b/app/itam/tests/unit/software/test_unit_software_serializer.py @@ -0,0 +1,66 @@ +import pytest + +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_software +class SoftwareSerializerTestCases( + SerializerTestCases +): + + + + 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' + + + + + +class SoftwareSerializerInheritedCases( + SoftwareSerializerTestCases +): + pass + + + +@pytest.mark.module_itam +class SoftwareSerializerPyTest( + SoftwareSerializerTestCases +): + pass \ No newline at end of file diff --git a/app/tests/fixtures/__init__.py b/app/tests/fixtures/__init__.py index 73b41858..21100a98 100644 --- a/app/tests/fixtures/__init__.py +++ b/app/tests/fixtures/__init__.py @@ -274,6 +274,7 @@ from .model_slmticket import ( from .model_software import ( kwargs_software, model_software, + serializer_software ) from .model_softwarecategory import ( diff --git a/app/tests/fixtures/model_software.py b/app/tests/fixtures/model_software.py index 2a726ff1..6afbc212 100644 --- a/app/tests/fixtures/model_software.py +++ b/app/tests/fixtures/model_software.py @@ -2,6 +2,11 @@ import datetime import pytest from itam.models.software import Software +from itam.serializers.software import ( + SoftwareBaseSerializer, + SoftwareModelSerializer, + SoftwareViewSerializer, +) @@ -12,13 +17,40 @@ def model_software(request): @pytest.fixture( scope = 'class') -def kwargs_software(kwargs_centurionmodel): +def kwargs_software(kwargs_centurionmodel, django_db_blocker, + model_manufacturer, kwargs_manufacturer, + model_softwarecategory, kwargs_softwarecategory +): random_str = str(datetime.datetime.now(tz=datetime.timezone.utc)) + with django_db_blocker.unblock(): + + publisher = model_manufacturer.objects.create( **kwargs_manufacturer ) + + category = model_softwarecategory.objects.create( **kwargs_softwarecategory ) + kwargs = { **kwargs_centurionmodel.copy(), + 'publisher': publisher, 'name': 'software_' + random_str, + 'category': category, } yield kwargs.copy() + + with django_db_blocker.unblock(): + + publisher.delete() + + category.delete() + + +@pytest.fixture( scope = 'class') +def serializer_software(): + + yield { + 'base': SoftwareBaseSerializer, + 'model': SoftwareModelSerializer, + 'view': SoftwareViewSerializer + } From 5db10a4b6dcbe1122cdcd3fdb652f5764349565a Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 11:32:55 +0930 Subject: [PATCH 17/30] refactor(itam): ViewSet Unit Test Suite re-written to Pytest for model Software ref: #925 #924 --- .../software/test_unit_software_viewset.py | 91 +++++++++++++------ 1 file changed, 65 insertions(+), 26 deletions(-) diff --git a/app/itam/tests/unit/software/test_unit_software_viewset.py b/app/itam/tests/unit/software/test_unit_software_viewset.py index d86390ac..373b7aa4 100644 --- a/app/itam/tests/unit/software/test_unit_software_viewset.py +++ b/app/itam/tests/unit/software/test_unit_software_viewset.py @@ -1,46 +1,85 @@ import pytest -from django.test import Client, TestCase - -from rest_framework.reverse import reverse - from api.tests.unit.test_unit_common_viewset import ModelViewSetInheritedCases -from itam.viewsets.software import ViewSet +from itam.viewsets.software import ( + Software, + ViewSet, +) -@pytest.mark.skip(reason = 'see #895, tests being refactored') @pytest.mark.model_software -@pytest.mark.module_itam -class SoftwareViewsetList( +class ViewsetTestCases( ModelViewSetInheritedCases, - TestCase, ): - viewset = ViewSet - route_name = 'v2:_api_software' + @pytest.fixture( scope = 'function' ) + def viewset(self): + return ViewSet - @classmethod - def setUpTestData(self): - """Setup Test - - 1. make list request - """ + @property + def parameterized_class_attributes(self): + return { + '_model_documentation': { + 'type': type(None), + }, + 'back_url': { + 'type': type(None), + }, + 'documentation': { + 'type': type(None), + 'value': None + }, + 'filterset_fields': { + 'value': [ + 'category', + 'organization', + 'publisher' + ] + }, + 'model': { + 'value': Software + }, + 'model_documentation': { + 'type': type(None), + }, + 'queryset': { + 'type': type(None), + }, + 'serializer_class': { + 'type': type(None), + }, + 'search_fields': { + 'value': [ + 'name' + ] + }, + 'view_description': { + 'value': 'Physical Softwares' + }, + 'view_name': { + 'type': type(None), + }, + 'view_serializer_name': { + 'type': type(None), + } + } - super().setUpTestData() + +class SoftwareViewsetInheritedCases( + ViewsetTestCases, +): + pass - client = Client() - - url = reverse( - self.route_name + '-list', - kwargs = self.kwargs - ) - client.force_login(self.view_user) +@pytest.mark.module_itam +class SoftwareViewsetPyTest( + ViewsetTestCases, +): - self.http_options_response_list = client.options(url) + pass From 9104cd021f31d2525f0a03236f1511ff3d872834 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 11:33:04 +0930 Subject: [PATCH 18/30] refactor(itam): Remove old test suites no longer required model Software ref: #925 closes #924 --- .../unit/software/test_software_api_v2.py | 354 ------------------ .../test_software_item_ticket_api_v2.py | 97 ----- 2 files changed, 451 deletions(-) delete mode 100644 app/itam/tests/unit/software/test_software_api_v2.py delete mode 100644 app/itam/tests/unit/software/test_software_item_ticket_api_v2.py diff --git a/app/itam/tests/unit/software/test_software_api_v2.py b/app/itam/tests/unit/software/test_software_api_v2.py deleted file mode 100644 index 1f4c9da0..00000000 --- a/app/itam/tests/unit/software/test_software_api_v2.py +++ /dev/null @@ -1,354 +0,0 @@ -import django -import pytest - -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.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 - -from core.models.manufacturer import Manufacturer - -from itam.models.software import Software, SoftwareCategory - -User = django.contrib.auth.get_user_model() - - - -@pytest.mark.model_software -@pytest.mark.module_itam -class SoftwareAPI( - TestCase, - APITenancyObject -): - - model = Software - - @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') - - manufacturer = Manufacturer.objects.create( - organization = self.organization, - name = 'a manufacturer' - ) - - category = SoftwareCategory.objects.create( - organization = self.organization, - name = 'category' - ) - - - self.item = self.model.objects.create( - organization = self.organization, - name = 'one', - publisher = manufacturer, - category = category, - model_notes = 'a note' - ) - - - self.url_view_kwargs = {'pk': self.item.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 = self.organization, - ) - - view_team.permissions.set([view_permissions]) - - self.view_user = User.objects.create_user(username="test_user_view", password="password") - teamuser = TeamUsers.objects.create( - team = view_team, - user = self.view_user - ) - - client = Client() - url = reverse('v2:_api_software-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_category(self): - """ Test for existance of API Field - - category field must exist - """ - - assert 'category' in self.api_data - - - def test_api_field_type_category(self): - """ Test for type for API Field - - category field must be dict - """ - - assert type(self.api_data['category']) is dict - - - def test_api_field_exists_category_id(self): - """ Test for existance of API Field - - category.id field must exist - """ - - assert 'id' in self.api_data['category'] - - - def test_api_field_type_category_id(self): - """ Test for type for API Field - - category.id field must be int - """ - - assert type(self.api_data['category']['id']) is int - - - def test_api_field_exists_category_display_name(self): - """ Test for existance of API Field - - category.display_name field must exist - """ - - assert 'display_name' in self.api_data['category'] - - - def test_api_field_type_category_display_name(self): - """ Test for type for API Field - - category.display_name field must be str - """ - - assert type(self.api_data['category']['display_name']) is str - - - def test_api_field_exists_category_url(self): - """ Test for existance of API Field - - category.url field must exist - """ - - assert 'url' in self.api_data['category'] - - - def test_api_field_type_category_url(self): - """ Test for type for API Field - - category.url field must be Hyperlink - """ - - assert type(self.api_data['category']['url']) is Hyperlink - - - - def test_api_field_exists_publisher(self): - """ Test for existance of API Field - - publisher field must exist - """ - - assert 'publisher' in self.api_data - - - def test_api_field_type_publisher(self): - """ Test for type for API Field - - publisher field must be dict - """ - - assert type(self.api_data['publisher']) is dict - - - def test_api_field_exists_publisher_id(self): - """ Test for existance of API Field - - publisher.id field must exist - """ - - assert 'id' in self.api_data['publisher'] - - - def test_api_field_type_publisher_id(self): - """ Test for type for API Field - - publisher.id field must be int - """ - - assert type(self.api_data['publisher']['id']) is int - - - def test_api_field_exists_publisher_display_name(self): - """ Test for existance of API Field - - publisher.display_name field must exist - """ - - assert 'display_name' in self.api_data['publisher'] - - - def test_api_field_type_publisher_display_name(self): - """ Test for type for API Field - - publisher.display_name field must be str - """ - - assert type(self.api_data['publisher']['display_name']) is str - - - def test_api_field_exists_publisher_url(self): - """ Test for existance of API Field - - publisher.url field must exist - """ - - assert 'url' in self.api_data['publisher'] - - - def test_api_field_type_publisher_url(self): - """ Test for type for API Field - - publisher.url field must be Hyperlink - """ - - assert type(self.api_data['publisher']['url']) is Hyperlink - - - - def test_api_field_exists_urls_external_links(self): - """ Test for existance of API Field - - _urls.external_links field must exist - """ - - assert 'external_links' in self.api_data['_urls'] - - - def test_api_field_type_urls_external_links(self): - """ Test for type for API Field - - _urls.external_links field must be str - """ - - assert type(self.api_data['_urls']['external_links']) is str - - - - # def test_api_field_exists_urls_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_urls_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_exists_urls_notes(self): - # """ Test for existance of API Field - - # _urls.notes field must exist - # """ - - # assert 'notes' in self.api_data['_urls'] - - - # def test_api_field_type_urls_notes(self): - # """ Test for type for API Field - - # _urls.notes field must be str - # """ - - # assert type(self.api_data['_urls']['notes']) is str - - - - def test_api_field_exists_urls_version(self): - """ Test for existance of API Field - - _urls.version field must exist - """ - - assert 'version' in self.api_data['_urls'] - - - # def test_api_field_type_urls_notes(self): - # """ Test for type for API Field - - # _urls.version field must be str - # """ - - # assert type(self.api_data['_urls']['version']) is str - - - - def test_api_field_exists_urls_tickets(self): - """ Test for existance of API Field - - _urls.tickets field must exist - """ - - assert 'tickets' in self.api_data['_urls'] - - - def test_api_field_type_urls_tickets(self): - """ Test for type for API Field - - _urls.tickets field must be str - """ - - assert type(self.api_data['_urls']['tickets']) is str - - - - def test_api_field_exists_urls_tickets(self): - """ Test for existance of API Field - - _urls.tickets field must exist - """ - - assert 'tickets' in self.api_data['_urls'] - - - def test_api_field_type_urls_tickets(self): - """ Test for type for API Field - - _urls.tickets field must be str - """ - - assert type(self.api_data['_urls']['tickets']) is str diff --git a/app/itam/tests/unit/software/test_software_item_ticket_api_v2.py b/app/itam/tests/unit/software/test_software_item_ticket_api_v2.py deleted file mode 100644 index 0c0c0b83..00000000 --- a/app/itam/tests/unit/software/test_software_item_ticket_api_v2.py +++ /dev/null @@ -1,97 +0,0 @@ -import pytest -from django.shortcuts import reverse -from django.test import Client, TestCase - -from core.tests.abstract.test_item_ticket_api_v2 import ItemTicketAPI - -from core.models.ticket.ticket_linked_items import TicketLinkedItem - -from itam.models.software import Software - - - -@pytest.mark.model_software -@pytest.mark.module_itam -class SoftwareItemTicketAPI( - ItemTicketAPI, - TestCase, -): - """Test Cases for Item Tickets - - Args: - APITenancyObject (class): Base class for ALL field checks - """ - - item_type = TicketLinkedItem.Modules.SOFTWARE - - item_class = 'software' - - item_model = Software - - - @classmethod - def setUpTestData(self): - """Setup Test - - 1. Create an organization for user and item - 2. Create an item - - """ - - super().setUpTestData() - - - self.linked_item = self.item_model.objects.create( - organization = self.organization, - name = 'dev' - ) - - - - self.item = self.model.objects.create( - organization = self.organization, - ticket = self.ticket, - - # Item attributes - - item = self.linked_item.id, - item_type = self.item_type, - ) - - - - self.url_view_kwargs = { - 'item_class': self.item_class, - 'item_id': self.linked_item.id, - 'pk': self.item.id, - } - - - client = Client() - url = reverse('v2:_api_v2_item_tickets-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_value_item_id(self): - """ Test for existance of API Field - - item.id field must exist - """ - - assert self.api_data['item']['id'] == self.linked_item.id - - - - def test_api_field_value_item_type(self): - """ Test for type for API Field - - item_type field must be int - """ - - assert self.api_data['item_type'] == self.item_type From e14780eb22033465121cc4c00e934cd9e6b330e7 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 12:10:20 +0930 Subject: [PATCH 19/30] refactor(itam): API Metadata Functional Test Suite re-written to Pytest for model SoftwareCategory ref: #925 #926 --- .../functional/software_category/conftest.py | 25 +++++++++++++++++++ ..._functional_software_category_metadata.py} | 16 ++---------- 2 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 app/itam/tests/functional/software_category/conftest.py rename app/itam/tests/functional/software_category/{test_software_category_viewset.py => test_functional_software_category_metadata.py} (94%) diff --git a/app/itam/tests/functional/software_category/conftest.py b/app/itam/tests/functional/software_category/conftest.py new file mode 100644 index 00000000..0968a076 --- /dev/null +++ b/app/itam/tests/functional/software_category/conftest.py @@ -0,0 +1,25 @@ +import pytest + + + +@pytest.fixture( scope = 'class') +def model(model_softwarecategory): + + yield model_softwarecategory + + +@pytest.fixture( scope = 'class', autouse = True) +def model_kwargs(request, kwargs_softwarecategory): + + request.cls.kwargs_create_item = kwargs_softwarecategory.copy() + + yield kwargs_softwarecategory.copy() + + if hasattr(request.cls, 'kwargs_create_item'): + del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_softwarecategory): + + yield serializer_softwarecategory diff --git a/app/itam/tests/functional/software_category/test_software_category_viewset.py b/app/itam/tests/functional/software_category/test_functional_software_category_metadata.py similarity index 94% rename from app/itam/tests/functional/software_category/test_software_category_viewset.py rename to app/itam/tests/functional/software_category/test_functional_software_category_metadata.py index 3d6b102b..445efa13 100644 --- a/app/itam/tests/functional/software_category/test_software_category_viewset.py +++ b/app/itam/tests/functional/software_category/test_functional_software_category_metadata.py @@ -9,8 +9,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.api_permissions_viewset import APIPermissions -from api.tests.abstract.api_serializer_viewset import SerializersTestCases from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional from itam.models.software import SoftwareCategory @@ -21,6 +19,7 @@ User = django.contrib.auth.get_user_model() +@pytest.mark.model_softwarecategory class ViewSetBase: model = SoftwareCategory @@ -218,18 +217,7 @@ class ViewSetBase: -class SoftwareCategoryPermissionsAPI(ViewSetBase, APIPermissions, TestCase): - - pass - - - -class SoftwareCategoryViewSet(ViewSetBase, SerializersTestCases, TestCase): - - pass - - - +@pytest.mark.module_itam class SoftwareCategoryMetadata( ViewSetBase, MetadataAttributesFunctional, From df8265dd553ba8e726b6b4f3ae47e4975b8b7758 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 12:10:30 +0930 Subject: [PATCH 20/30] refactor(itam): API Fields render Functional Test Suite re-written to Pytest for model SoftwareCategory ref: #925 #926 --- ...functional_software_category_api_fields.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 app/itam/tests/functional/software_category/test_functional_software_category_api_fields.py diff --git a/app/itam/tests/functional/software_category/test_functional_software_category_api_fields.py b/app/itam/tests/functional/software_category/test_functional_software_category_api_fields.py new file mode 100644 index 00000000..72bbfb83 --- /dev/null +++ b/app/itam/tests/functional/software_category/test_functional_software_category_api_fields.py @@ -0,0 +1,45 @@ +import pytest + +# from django.db import models + +from rest_framework.relations import Hyperlink + +from api.tests.functional.test_functional_api_fields import ( + APIFieldsInheritedCases, +) + + + +@pytest.mark.model_softwarecategory +class SoftwareCategoryAPITestCases( + APIFieldsInheritedCases, +): + + + @property + def parameterized_api_fields(self): + + return { + 'name': { + 'expected': str + }, + 'modified': { + 'expected': str + } + } + + + +class SoftwareCategoryAPIInheritedCases( + SoftwareCategoryAPITestCases, +): + pass + + + +@pytest.mark.module_itam +class SoftwareCategoryAPIPyTest( + SoftwareCategoryAPITestCases, +): + + pass From dfb1af1ff7713aa82051534913d9e3e40a9179a3 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 12:10:43 +0930 Subject: [PATCH 21/30] refactor(itam): Model Functional Test Suite re-written to Pytest for model SoftwareCategory ref: #925 #926 --- ...test_functional_software_category_model.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 app/itam/tests/functional/software_category/test_functional_software_category_model.py diff --git a/app/itam/tests/functional/software_category/test_functional_software_category_model.py b/app/itam/tests/functional/software_category/test_functional_software_category_model.py new file mode 100644 index 00000000..5b6ca16f --- /dev/null +++ b/app/itam/tests/functional/software_category/test_functional_software_category_model.py @@ -0,0 +1,28 @@ +import pytest + +from core.tests.functional.centurion_abstract.test_functional_centurion_abstract_model import ( + CenturionAbstractModelInheritedCases +) + + + +@pytest.mark.model_softwarecategory +class SoftwareCategoryModelTestCases( + CenturionAbstractModelInheritedCases +): + pass + + + +class SoftwareCategoryModelInheritedCases( + SoftwareCategoryModelTestCases, +): + pass + + + +@pytest.mark.module_itam +class SoftwareCategoryModelPyTest( + SoftwareCategoryModelTestCases, +): + pass From 42e191ba7660b9313efdb473a53666c53db7767a Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 12:13:26 +0930 Subject: [PATCH 22/30] refactor(itam): Serializer Unit Test Suite re-written to Pytest for model SoftwareCategory ref: #925 #926 --- .../test_software_category_serializer.py | 63 ------------------ .../tests/unit/software_category/conftest.py | 6 ++ .../test_unit_software_category_serializer.py | 65 +++++++++++++++++++ app/tests/fixtures/__init__.py | 1 + app/tests/fixtures/model_softwarecategory.py | 15 +++++ 5 files changed, 87 insertions(+), 63 deletions(-) delete mode 100644 app/itam/tests/functional/software_category/test_software_category_serializer.py create mode 100644 app/itam/tests/unit/software_category/test_unit_software_category_serializer.py diff --git a/app/itam/tests/functional/software_category/test_software_category_serializer.py b/app/itam/tests/functional/software_category/test_software_category_serializer.py deleted file mode 100644 index b1142c27..00000000 --- a/app/itam/tests/functional/software_category/test_software_category_serializer.py +++ /dev/null @@ -1,63 +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.serializers.software_category import SoftwareCategory, SoftwareCategoryModelSerializer - - - -class SoftwareCategoryValidationAPI( - TestCase, -): - - model = SoftwareCategory - - @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.item = self.model.objects.create( - organization=organization, - name = 'os name', - ) - - self.mock_view = MockView( user = self.user ) - - - - 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 = SoftwareCategoryModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "organization": self.organization.id, - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['name'][0] == 'required' diff --git a/app/itam/tests/unit/software_category/conftest.py b/app/itam/tests/unit/software_category/conftest.py index 0ea0beb8..0968a076 100644 --- a/app/itam/tests/unit/software_category/conftest.py +++ b/app/itam/tests/unit/software_category/conftest.py @@ -17,3 +17,9 @@ def model_kwargs(request, kwargs_softwarecategory): if hasattr(request.cls, 'kwargs_create_item'): del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_softwarecategory): + + yield serializer_softwarecategory diff --git a/app/itam/tests/unit/software_category/test_unit_software_category_serializer.py b/app/itam/tests/unit/software_category/test_unit_software_category_serializer.py new file mode 100644 index 00000000..a1639ba8 --- /dev/null +++ b/app/itam/tests/unit/software_category/test_unit_software_category_serializer.py @@ -0,0 +1,65 @@ +import pytest + +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_softwarecategory +class SoftwareCategorySerializerTestCases( + SerializerTestCases +): + + + 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' + + + + + +class SoftwareCategorySerializerInheritedCases( + SoftwareCategorySerializerTestCases +): + pass + + + +@pytest.mark.module_itam +class SoftwareCategorySerializerPyTest( + SoftwareCategorySerializerTestCases +): + pass \ No newline at end of file diff --git a/app/tests/fixtures/__init__.py b/app/tests/fixtures/__init__.py index 21100a98..b9471126 100644 --- a/app/tests/fixtures/__init__.py +++ b/app/tests/fixtures/__init__.py @@ -280,6 +280,7 @@ from .model_software import ( from .model_softwarecategory import ( kwargs_softwarecategory, model_softwarecategory, + serializer_softwarecategory, ) from .model_softwareenablefeatureflag import ( diff --git a/app/tests/fixtures/model_softwarecategory.py b/app/tests/fixtures/model_softwarecategory.py index d6159349..e5452a31 100644 --- a/app/tests/fixtures/model_softwarecategory.py +++ b/app/tests/fixtures/model_softwarecategory.py @@ -2,6 +2,11 @@ import datetime import pytest from itam.models.software import SoftwareCategory +from itam.serializers.software_category import ( + SoftwareCategoryBaseSerializer, + SoftwareCategoryModelSerializer, + SoftwareCategoryViewSerializer +) @@ -24,3 +29,13 @@ def kwargs_softwarecategory(kwargs_centurionmodel): } yield kwargs.copy() + + +@pytest.fixture( scope = 'class') +def serializer_softwarecategory(): + + yield { + 'base': SoftwareCategoryBaseSerializer, + 'model': SoftwareCategoryModelSerializer, + 'view': SoftwareCategoryViewSerializer + } From dd337b68bc0e1df84f157b0244dfa18c42835b8d Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 12:13:44 +0930 Subject: [PATCH 23/30] refactor(itam): ViewSet Unit Test Suite re-written to Pytest for model SoftwareCategory ref: #925 #926 --- .../test_unit_software_category_viewset.py | 88 +++++++++++++------ 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/app/itam/tests/unit/software_category/test_unit_software_category_viewset.py b/app/itam/tests/unit/software_category/test_unit_software_category_viewset.py index 2ba3debe..9f8db6c6 100644 --- a/app/itam/tests/unit/software_category/test_unit_software_category_viewset.py +++ b/app/itam/tests/unit/software_category/test_unit_software_category_viewset.py @@ -1,45 +1,83 @@ import pytest -from django.test import Client, TestCase - -from rest_framework.reverse import reverse from api.tests.unit.test_unit_common_viewset import ModelViewSetInheritedCases -from itam.viewsets.software_category import ViewSet +from itam.viewsets.software_category import ( + SoftwareCategory, + ViewSet, +) -@pytest.mark.skip(reason = 'see #895, tests being refactored') @pytest.mark.model_softwarecategory -@pytest.mark.module_itam -class SoftwareCategoryViewsetList( +class ViewsetTestCases( ModelViewSetInheritedCases, - TestCase, ): - viewset = ViewSet - route_name = 'v2:_api_softwarecategory' + @pytest.fixture( scope = 'function' ) + def viewset(self): + return ViewSet - @classmethod - def setUpTestData(self): - """Setup Test - - 1. make list request - """ + @property + def parameterized_class_attributes(self): + return { + '_model_documentation': { + 'type': type(None), + }, + 'back_url': { + 'type': type(None), + }, + 'documentation': { + 'type': type(None), + 'value': None + }, + 'filterset_fields': { + 'value': [ + 'organization' + ] + }, + 'model': { + 'value': SoftwareCategory + }, + 'model_documentation': { + 'type': type(None), + }, + 'queryset': { + 'type': type(None), + }, + 'serializer_class': { + 'type': type(None), + }, + 'search_fields': { + 'value': [ + 'name' + ] + }, + 'view_description': { + 'value': 'Physical Softwares' + }, + 'view_name': { + 'type': type(None), + }, + 'view_serializer_name': { + 'type': type(None), + } + } - super().setUpTestData() + +class SoftwareCategoryViewsetInheritedCases( + ViewsetTestCases, +): + pass - client = Client() - - url = reverse( - self.route_name + '-list', - kwargs = self.kwargs - ) - client.force_login(self.view_user) +@pytest.mark.module_itam +class SoftwareCategoryViewsetPyTest( + ViewsetTestCases, +): - self.http_options_response_list = client.options(url) + pass From 207ba2285a713c25774ae5660876e0af095df6db Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 12:13:56 +0930 Subject: [PATCH 24/30] refactor(itam): Remove old test suites no longer required model SoftwareCategory ref: #925 closes #926 --- .../test_software_category_api_v2.py | 87 ------------------- 1 file changed, 87 deletions(-) delete mode 100644 app/itam/tests/unit/software_category/test_software_category_api_v2.py diff --git a/app/itam/tests/unit/software_category/test_software_category_api_v2.py b/app/itam/tests/unit/software_category/test_software_category_api_v2.py deleted file mode 100644 index 6560523e..00000000 --- a/app/itam/tests/unit/software_category/test_software_category_api_v2.py +++ /dev/null @@ -1,87 +0,0 @@ -import django -import pytest - -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.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 - -from core.models.manufacturer import Manufacturer - -from itam.models.software import Software, SoftwareCategory - -User = django.contrib.auth.get_user_model() - - - -@pytest.mark.model_softwarecategory -@pytest.mark.module_itam -class SoftwareCategoryAPI( - TestCase, - APITenancyObject -): - - model = SoftwareCategory - - @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') - - manufacturer = Manufacturer.objects.create( - organization = self.organization, - name = 'a manufacturer' - ) - - self.item = SoftwareCategory.objects.create( - organization = self.organization, - name = 'category', - model_notes = 'a note' - ) - - - self.url_view_kwargs = {'pk': self.item.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 = self.organization, - ) - - view_team.permissions.set([view_permissions]) - - self.view_user = User.objects.create_user(username="test_user_view", password="password") - teamuser = TeamUsers.objects.create( - team = view_team, - user = self.view_user - ) - - client = Client() - url = reverse('v2:_api_softwarecategory-detail', kwargs=self.url_view_kwargs) - - - client.force_login(self.view_user) - response = client.get(url) - - self.api_data = response.data From c1b44c03fe61945ba568edc398928cf5c2db94cd Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 12:14:35 +0930 Subject: [PATCH 25/30] refactor(itam): API Metadata Functional Test Suite re-written to Pytest for model SoftwareVersion ref: #925 #927 --- .../functional/software_version/conftest.py | 25 +++++++++++++++++++ ...t_functional_software_version_metadata.py} | 16 ++---------- 2 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 app/itam/tests/functional/software_version/conftest.py rename app/itam/tests/functional/software_version/{test_software_version_viewset.py => test_functional_software_version_metadata.py} (95%) diff --git a/app/itam/tests/functional/software_version/conftest.py b/app/itam/tests/functional/software_version/conftest.py new file mode 100644 index 00000000..af1d54bd --- /dev/null +++ b/app/itam/tests/functional/software_version/conftest.py @@ -0,0 +1,25 @@ +import pytest + + + +@pytest.fixture( scope = 'class') +def model(model_softwareversion): + + yield model_softwareversion + + +@pytest.fixture( scope = 'class', autouse = True) +def model_kwargs(request, kwargs_softwareversion): + + request.cls.kwargs_create_item = kwargs_softwareversion.copy() + + yield kwargs_softwareversion.copy() + + if hasattr(request.cls, 'kwargs_create_item'): + del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_softwareversion): + + yield serializer_softwareversion diff --git a/app/itam/tests/functional/software_version/test_software_version_viewset.py b/app/itam/tests/functional/software_version/test_functional_software_version_metadata.py similarity index 95% rename from app/itam/tests/functional/software_version/test_software_version_viewset.py rename to app/itam/tests/functional/software_version/test_functional_software_version_metadata.py index 7d9a5748..68ba56f5 100644 --- a/app/itam/tests/functional/software_version/test_software_version_viewset.py +++ b/app/itam/tests/functional/software_version/test_functional_software_version_metadata.py @@ -9,8 +9,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.api_permissions_viewset import APIPermissions -from api.tests.abstract.api_serializer_viewset import SerializersTestCases from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional from itam.models.software import Software, SoftwareVersion @@ -21,6 +19,7 @@ User = django.contrib.auth.get_user_model() +@pytest.mark.model_softwareversion class ViewSetBase: model = SoftwareVersion @@ -231,18 +230,7 @@ class ViewSetBase: -class SoftwareVersionPermissionsAPI(ViewSetBase, APIPermissions, TestCase): - - pass - - - -class SoftwareVersionViewSet(ViewSetBase, SerializersTestCases, TestCase): - - pass - - - +@pytest.mark.module_itam class SoftwareVersionMetadata( ViewSetBase, MetadataAttributesFunctional, From 2890750d50155c73d83ae3d1ba79d82832e1ebd6 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 12:14:52 +0930 Subject: [PATCH 26/30] refactor(itam): API Fields render Functional Test Suite re-written to Pytest for model SoftwareVersion ref: #925 #927 --- ..._functional_software_version_api_fields.py | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 app/itam/tests/functional/software_version/test_functional_software_version_api_fields.py diff --git a/app/itam/tests/functional/software_version/test_functional_software_version_api_fields.py b/app/itam/tests/functional/software_version/test_functional_software_version_api_fields.py new file mode 100644 index 00000000..dd618610 --- /dev/null +++ b/app/itam/tests/functional/software_version/test_functional_software_version_api_fields.py @@ -0,0 +1,57 @@ +import pytest + +# from django.db import models + +from rest_framework.relations import Hyperlink + +from api.tests.functional.test_functional_api_fields import ( + APIFieldsInheritedCases, +) + + + +@pytest.mark.model_softwareversion +class SoftwareVersionAPITestCases( + APIFieldsInheritedCases, +): + + + @property + def parameterized_api_fields(self): + + return { + 'software': { + 'expected': dict + }, + 'software.id': { + 'expected': int + }, + 'software.display_name': { + 'expected': str + }, + 'software.url': { + 'expected': Hyperlink + }, + 'name': { + 'expected': str + }, + 'modified': { + 'expected': str + } + } + + + +class SoftwareVersionAPIInheritedCases( + SoftwareVersionAPITestCases, +): + pass + + + +@pytest.mark.module_itam +class SoftwareVersionAPIPyTest( + SoftwareVersionAPITestCases, +): + + pass From 7d47a9ca497f215151e84f0bab64a2791e14eca3 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 12:15:03 +0930 Subject: [PATCH 27/30] refactor(itam): Model Functional Test Suite re-written to Pytest for model SoftwareVersion ref: #925 #927 --- .../test_functional_software_version_model.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 app/itam/tests/functional/software_version/test_functional_software_version_model.py diff --git a/app/itam/tests/functional/software_version/test_functional_software_version_model.py b/app/itam/tests/functional/software_version/test_functional_software_version_model.py new file mode 100644 index 00000000..13c4376c --- /dev/null +++ b/app/itam/tests/functional/software_version/test_functional_software_version_model.py @@ -0,0 +1,28 @@ +import pytest + +from core.tests.functional.centurion_abstract.test_functional_centurion_abstract_model import ( + CenturionAbstractModelInheritedCases +) + + + +@pytest.mark.model_softwareversion +class SoftwareVersionModelTestCases( + CenturionAbstractModelInheritedCases +): + pass + + + +class SoftwareVersionModelInheritedCases( + SoftwareVersionModelTestCases, +): + pass + + + +@pytest.mark.module_itam +class SoftwareVersionModelPyTest( + SoftwareVersionModelTestCases, +): + pass From fc984c5034425e5af2f7e869db106ca52435d587 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 12:15:34 +0930 Subject: [PATCH 28/30] refactor(itam): Serializer Unit Test Suite re-written to Pytest for model SoftwareVersion ref: #925 #927 --- .../tests/unit/software_version/conftest.py | 6 ++ .../test_unit_software_version_serializer.py | 95 +++++++++++++++++++ app/tests/fixtures/__init__.py | 1 + app/tests/fixtures/model_softwareversion.py | 15 +++ 4 files changed, 117 insertions(+) create mode 100644 app/itam/tests/unit/software_version/test_unit_software_version_serializer.py diff --git a/app/itam/tests/unit/software_version/conftest.py b/app/itam/tests/unit/software_version/conftest.py index 9a3f1ab1..af1d54bd 100644 --- a/app/itam/tests/unit/software_version/conftest.py +++ b/app/itam/tests/unit/software_version/conftest.py @@ -17,3 +17,9 @@ def model_kwargs(request, kwargs_softwareversion): if hasattr(request.cls, 'kwargs_create_item'): del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_softwareversion): + + yield serializer_softwareversion diff --git a/app/itam/tests/unit/software_version/test_unit_software_version_serializer.py b/app/itam/tests/unit/software_version/test_unit_software_version_serializer.py new file mode 100644 index 00000000..420d6ab4 --- /dev/null +++ b/app/itam/tests/unit/software_version/test_unit_software_version_serializer.py @@ -0,0 +1,95 @@ +import pytest + +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_softwareversion +class SoftwareVersionSerializerTestCases( + SerializerTestCases +): + + + 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' + + + @pytest.mark.regression + def test_serializer_create_calls_model_full_clean(self, + kwargs_api_create, mocker, model, model_serializer, request_user + ): + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + mock_view.kwargs = { + 'software_id': kwargs_api_create['software'] + } + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs_api_create + ) + + serializer.is_valid(raise_exception = True) + + full_clean = mocker.spy(model, 'full_clean') + + serializer.save() + + full_clean.assert_called_once() + + + +class SoftwareVersionSerializerInheritedCases( + SoftwareVersionSerializerTestCases +): + pass + + + +@pytest.mark.module_itam +class SoftwareVersionSerializerPyTest( + SoftwareVersionSerializerTestCases +): + pass \ No newline at end of file diff --git a/app/tests/fixtures/__init__.py b/app/tests/fixtures/__init__.py index b9471126..3cd4d474 100644 --- a/app/tests/fixtures/__init__.py +++ b/app/tests/fixtures/__init__.py @@ -291,6 +291,7 @@ from .model_softwareenablefeatureflag import ( from .model_softwareversion import ( kwargs_softwareversion, model_softwareversion, + serializer_softwareversion, ) from .model_ticketbase import ( diff --git a/app/tests/fixtures/model_softwareversion.py b/app/tests/fixtures/model_softwareversion.py index d253e290..262d1702 100644 --- a/app/tests/fixtures/model_softwareversion.py +++ b/app/tests/fixtures/model_softwareversion.py @@ -2,6 +2,11 @@ import datetime import pytest from itam.models.software import SoftwareVersion +from itam.serializers.software_version import ( + SoftwareVersionBaseSerializer, + SoftwareVersionModelSerializer, + SoftwareVersionViewSerializer +) @@ -41,3 +46,13 @@ def kwargs_softwareversion(django_db_blocker, with django_db_blocker.unblock(): software.delete() + + +@pytest.fixture( scope = 'class') +def serializer_softwareversion(): + + yield { + 'base': SoftwareVersionBaseSerializer, + 'model': SoftwareVersionModelSerializer, + 'view': SoftwareVersionViewSerializer + } From 0f9cbfdc13d15295fbbe39e78e67fe40c08eae62 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 12:15:45 +0930 Subject: [PATCH 29/30] refactor(itam): ViewSet Unit Test Suite re-written to Pytest for model SoftwareVersion ref: #925 #927 --- .../test_unit_software_version_viewset.py | 149 ++++++++++++++---- 1 file changed, 115 insertions(+), 34 deletions(-) diff --git a/app/itam/tests/unit/software_version/test_unit_software_version_viewset.py b/app/itam/tests/unit/software_version/test_unit_software_version_viewset.py index 36f4eb79..426a95d6 100644 --- a/app/itam/tests/unit/software_version/test_unit_software_version_viewset.py +++ b/app/itam/tests/unit/software_version/test_unit_software_version_viewset.py @@ -1,53 +1,134 @@ import pytest -from django.test import Client, TestCase - -from rest_framework.reverse import reverse - from api.tests.unit.test_unit_common_viewset import ModelViewSetInheritedCases -from itam.models.software import Software -from itam.viewsets.software_version import ViewSet +from itam.viewsets.software_version import ( + SoftwareVersion, + ViewSet, +) -@pytest.mark.skip(reason = 'see #895, tests being refactored') @pytest.mark.model_softwareversion -@pytest.mark.module_itam -class SoftwareVersionViewsetList( +class ViewsetTestCases( ModelViewSetInheritedCases, - TestCase, ): - viewset = ViewSet - route_name = 'v2:_api_softwareversion' + @pytest.fixture( scope = 'function' ) + def viewset(self): + return ViewSet - @classmethod - def setUpTestData(self): - """Setup Test - - 1. make list request - """ - - super().setUpTestData() - - self.kwargs = { - 'software_id': Software.objects.create( - organization = self.organization, - name = 'soft' - ).id + @property + def parameterized_class_attributes(self): + return { + '_model_documentation': { + 'type': type(None), + }, + 'back_url': { + 'type': type(None), + }, + 'documentation': { + 'type': type(None), + 'value': None + }, + 'filterset_fields': { + 'value': [ + 'organization', + 'software' + ] + }, + 'model': { + 'value': SoftwareVersion + }, + 'model_documentation': { + 'type': type(None), + }, + 'queryset': { + 'type': type(None), + }, + 'serializer_class': { + 'type': type(None), + }, + 'search_fields': { + 'value': [ + 'name' + ] + }, + 'view_description': { + 'value': 'Physical Softwares' + }, + 'view_name': { + 'type': type(None), + }, + 'view_serializer_name': { + 'type': type(None), + } } - client = Client() - - url = reverse( - self.route_name + '-list', - kwargs = self.kwargs - ) - client.force_login(self.view_user) + def test_view_func_get_queryset_cache_result(self, viewset_mock_request): + """Viewset Test - self.http_options_response_list = client.options(url) + Ensure that the `get_queryset` function caches the result under + attribute `.queryset` + """ + + view_set = viewset_mock_request + + view_set.kwargs = { + 'software_id': self.kwargs_create_item['software'].id + } + + assert view_set.queryset is None # Must be empty before init + + q = view_set.get_queryset() + + assert view_set.queryset is not None # Must not be empty after init + + assert q == view_set.queryset + + + def test_view_func_get_queryset_cache_result_used(self, mocker, viewset, viewset_mock_request): + """Viewset Test + + Ensure that the `get_queryset` function caches the result under + attribute `.queryset` + """ + + qs = mocker.spy(viewset_mock_request.model, 'objects') + + view_set = viewset_mock_request + + view_set.kwargs = { + 'software_id': self.kwargs_create_item['software'].id + } + + view_set.get_queryset() # Initial QuerySet fetch/filter and cache + + assert len(qs.method_calls) == 1 # one call to .all() + assert len(qs.mock_calls) == 3 # calls = .all(), all().filter() + + view_set.get_queryset() # Use Cached results, dont re-fetch QuerySet + + assert len(qs.method_calls) == 1 + assert len(qs.mock_calls) == 3 + + + + +class SoftwareVersionViewsetInheritedCases( + ViewsetTestCases, +): + pass + + + +@pytest.mark.module_itam +class SoftwareVersionViewsetPyTest( + ViewsetTestCases, +): + + pass From 6578d672509e2c52a6c5b294bee34ee16666e015 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 2 Aug 2025 12:15:54 +0930 Subject: [PATCH 30/30] refactor(itam): Remove old test suites no longer required model SoftwareVersion ref: #925 closes #927 --- .../test_software_version_api_v2.py | 163 ------------------ 1 file changed, 163 deletions(-) delete mode 100644 app/itam/tests/unit/software_version/test_software_version_api_v2.py diff --git a/app/itam/tests/unit/software_version/test_software_version_api_v2.py b/app/itam/tests/unit/software_version/test_software_version_api_v2.py deleted file mode 100644 index 6d506df1..00000000 --- a/app/itam/tests/unit/software_version/test_software_version_api_v2.py +++ /dev/null @@ -1,163 +0,0 @@ -import django -import pytest - -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.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 - -from itam.models.software import Software, SoftwareVersion - -User = django.contrib.auth.get_user_model() - - - -@pytest.mark.model_softwareversion -@pytest.mark.module_itam -class SoftwareVersionCategoryAPI( - TestCase, - APITenancyObject -): - - model = SoftwareVersion - - @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') - - software = Software.objects.create( - organization = self.organization, - name = 'software' - ) - - self.item = self.model.objects.create( - organization = self.organization, - name = '10', - model_notes = 'a note', - software = software - ) - - - self.url_view_kwargs = {'software_id': software.id, 'pk': self.item.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 = self.organization, - ) - - view_team.permissions.set([view_permissions]) - - self.view_user = User.objects.create_user(username="test_user_view", password="password") - teamuser = TeamUsers.objects.create( - team = view_team, - user = self.view_user - ) - - client = Client() - url = reverse('v2:_api_softwareversion-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_software(self): - """ Test for existance of API Field - - software field must exist - """ - - assert 'software' in self.api_data - - - def test_api_field_type_software(self): - """ Test for type for API Field - - software field must be dict - """ - - assert type(self.api_data['software']) is dict - - - def test_api_field_exists_software_id(self): - """ Test for existance of API Field - - software.id field must exist - """ - - assert 'id' in self.api_data['software'] - - - def test_api_field_type_software_id(self): - """ Test for type for API Field - - software.id field must be int - """ - - assert type(self.api_data['software']['id']) is int - - - def test_api_field_exists_software_display_name(self): - """ Test for existance of API Field - - software.display_name field must exist - """ - - assert 'display_name' in self.api_data['software'] - - - def test_api_field_type_software_display_name(self): - """ Test for type for API Field - - software.display_name field must be str - """ - - assert type(self.api_data['software']['display_name']) is str - - - def test_api_field_exists_software_url(self): - """ Test for existance of API Field - - software.url field must exist - """ - - assert 'url' in self.api_data['software'] - - - def test_api_field_type_software_url(self): - """ Test for type for API Field - - software.url field must be Hyperlink - """ - - assert type(self.api_data['software']['url']) is Hyperlink - -