diff --git a/app/api/tests/functional/test_functional_api_fields.py b/app/api/tests/functional/test_functional_api_fields.py index 15046b85..4466b090 100644 --- a/app/api/tests/functional/test_functional_api_fields.py +++ b/app/api/tests/functional/test_functional_api_fields.py @@ -92,10 +92,39 @@ class APIFieldsTestCases: 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( - **model_kwargs + **kwargs ) + for key, value in kwargs_many_to_many.items(): + + field = getattr(item, 'target_team') + + for entry in value: + + field.add(entry) + request.cls.item = item yield item @@ -198,7 +227,7 @@ class APIFieldsTestCases: else: assert( - type( api_data['value'] ) is param_expected + type( api_data.get('value', 'is empty') ) is param_expected or type( api_data_two.get('value', 'is empty') ) is param_expected ) diff --git a/app/assistance/migrations/0014_alter_knowledgebase_responsible_user.py b/app/assistance/migrations/0014_alter_knowledgebase_responsible_user.py new file mode 100644 index 00000000..fd838365 --- /dev/null +++ b/app/assistance/migrations/0014_alter_knowledgebase_responsible_user.py @@ -0,0 +1,29 @@ +# Generated by Django 5.1.10 on 2025-07-29 04:13 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("assistance", "0013_alter_modelknowledgebasearticle_model"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name="knowledgebase", + name="responsible_user", + field=models.ForeignKey( + blank=True, + help_text="User(s) whom is considered the articles owner.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="responsible_user", + to=settings.AUTH_USER_MODEL, + verbose_name="Responsible User", + ), + ), + ] diff --git a/app/assistance/models/knowledge_base.py b/app/assistance/models/knowledge_base.py index 79e1e7fb..e29e895a 100644 --- a/app/assistance/models/knowledge_base.py +++ b/app/assistance/models/knowledge_base.py @@ -103,7 +103,7 @@ class KnowledgeBase( responsible_user = models.ForeignKey( User, - blank = False, + blank = True, help_text = 'User(s) whom is considered the articles owner.', null = True, on_delete = models.PROTECT, diff --git a/app/assistance/tests/functional/knowledge_base/conftest.py b/app/assistance/tests/functional/knowledge_base/conftest.py new file mode 100644 index 00000000..2215ce68 --- /dev/null +++ b/app/assistance/tests/functional/knowledge_base/conftest.py @@ -0,0 +1,25 @@ +import pytest + + + +@pytest.fixture( scope = 'class') +def model(model_knowledgebase): + + yield model_knowledgebase + + +@pytest.fixture( scope = 'class', autouse = True) +def model_kwargs(request, kwargs_knowledgebase): + + request.cls.kwargs_create_item = kwargs_knowledgebase.copy() + + yield kwargs_knowledgebase.copy() + + if hasattr(request.cls, 'kwargs_create_item'): + del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_knowledgebase): + + yield serializer_knowledgebase diff --git a/app/assistance/tests/functional/knowledge_base/knowledge_base_serializer.py b/app/assistance/tests/functional/knowledge_base/knowledge_base_serializer.py deleted file mode 100644 index 0ab296d9..00000000 --- a/app/assistance/tests/functional/knowledge_base/knowledge_base_serializer.py +++ /dev/null @@ -1,216 +0,0 @@ -import django -import json -import pytest - -from django.test import TestCase - -from rest_framework.exceptions import ValidationError - -from access.models.tenant import Tenant as Organization -from access.models.team import Team - -from centurion.tests.abstract.mock_view import MockView - -from assistance.models.knowledge_base import KnowledgeBase -from assistance.serializers.knowledge_base import KnowledgeBaseModelSerializer - -User = django.contrib.auth.get_user_model() - - - -class KnowledgeBaseValidationAPI( - TestCase, -): - - model = KnowledgeBase - - app_namespace = 'API' - - url_name = '_api_knowledgebase' - - @classmethod - def setUpTestData(self): - """Setup Test - - 1. Create an org - 2. Create a team - 4. Add user to add team - """ - - 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.add_team = Team.objects.create( - organization=organization, - team_name = 'teamone', - model_notes = 'random note' - ) - - self.add_user = User.objects.create_user(username="test_user_add", password="password") - - self.item_has_target_user = self.model.objects.create( - organization=organization, - title = 'random title', - content = 'random note', - summary = 'a summary', - target_user = self.add_user, - release_date = '2024-01-01 12:00:00', - expiry_date = '2024-01-01 12:00:01', - responsible_user = self.add_user, - ) - - self.item_has_target_team = self.model.objects.create( - organization=organization, - title = 'random title', - content = 'random note', - summary = 'a summary', - release_date = '2024-01-01 12:00:00', - expiry_date = '2024-01-01 12:00:01', - responsible_user = self.add_user, - ) - - self.item_has_target_team.target_team.set([ self.add_team ]) - - - - def test_serializer_validation_no_title(self): - """Serializer Validation Check - - Ensure that if creating and no title is provided a validation error occurs - """ - - with pytest.raises(ValidationError) as err: - - serializer = KnowledgeBaseModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "organization": self.organization.id, - "content": "random note", - "target_user": self.add_user.id, - "target_team": [ - self.add_team.id - ], - "responsible_user": self.add_user.id, - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['title'][0] == 'required' - - - - def test_serializer_validation_both_target_team_target_user(self): - """Serializer Validation Check - - Ensure that both target user and target team raises a validation error - """ - - with pytest.raises(ValidationError) as err: - - serializer = KnowledgeBaseModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "organization": self.organization.id, - "title": "teamone", - "content": "random note", - "target_user": self.add_user.id, - "target_team": [ - self.add_team.id - ], - "responsible_user": self.add_user.id, - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['non_field_errors'][0] == 'invalid_not_both_target_team_user' - - - - def test_serializer_validation_no_target_team_target_user(self): - """Serializer Validation Check - - Ensure that if either target user and target team is missing it raises validation error - """ - - - with pytest.raises(ValidationError) as err: - - serializer = KnowledgeBaseModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "organization": self.organization.id, - "title": 'teamone', - "content": 'random note', - "responsible_user": self.add_user.id, - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['non_field_errors'][0] == 'invalid_need_target_team_or_user' - - - - def test_serializer_validation_update_existing_target_user(self): - """Serializer Validation Check - - Ensure that if an existing item with target user is updated to include a target_team - it raises a validation error - """ - - with pytest.raises(ValidationError) as err: - - serializer = KnowledgeBaseModelSerializer( - self.item_has_target_user, - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "target_team": [ self.add_team.id ] - }, - partial=True, - ) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['non_field_errors'][0] == 'invalid_not_both_target_team_user' - - - def test_serializer_validation_update_existing_target_team(self): - """Serializer Validation Check - - Ensure that if an existing item with target team is updated to include a target_user - it raises a validation error - """ - - with pytest.raises(ValidationError) as err: - - serializer = KnowledgeBaseModelSerializer( - self.item_has_target_team, - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "target_user": self.add_user.id - }, - partial=True, - ) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['non_field_errors'][0] == 'invalid_not_both_target_team_user' diff --git a/app/assistance/tests/functional/knowledge_base/test_functional_knowledge_base_api_fields.py b/app/assistance/tests/functional/knowledge_base/test_functional_knowledge_base_api_fields.py new file mode 100644 index 00000000..edcde16d --- /dev/null +++ b/app/assistance/tests/functional/knowledge_base/test_functional_knowledge_base_api_fields.py @@ -0,0 +1,191 @@ +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_knowledgebase +class knowledgeBaseAPITestCases( + APIFieldsInheritedCases, +): + + @pytest.fixture( scope = 'class') + def second_model(self, request, django_db_blocker, + model, model_kwargs + ): + + item = None + + 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 + }) + + + # Switch model fields so all fields can be checked + kwargs_many_to_many.update({ 'responsible_teams': kwargs_many_to_many['target_team']}) + del kwargs_many_to_many['target_team'] + + kwargs.update({ 'target_user': kwargs['responsible_user']}) + del kwargs['responsible_user'] + + + item_two = model.objects.create( + **kwargs + ) + + + for key, value in kwargs_many_to_many.items(): + + field = getattr(item_two, key) + + for entry in value: + + field.add(entry) + + + request.cls.item_two = item_two + + yield item_two + + with django_db_blocker.unblock(): + + item_two.delete() + + del request.cls.item_two + + + @pytest.fixture( scope = 'class', autouse = True) + def class_setup(self, + create_model, + second_model, + make_request, + ): + + pass + + @property + def parameterized_api_fields(self): + + return { + 'title': { + 'expected': str + }, + 'summary': { + 'expected': str + }, + 'content': { + 'expected': str + }, + 'category': { + 'expected': dict + }, + 'category.id': { + 'expected': int + }, + 'category.display_name': { + 'expected': str + }, + 'category.url': { + 'expected': str + }, + 'release_date': { + 'expected': str + }, + 'expiry_date': { + 'expected': str + }, + 'target_team': { + 'expected': list + }, + 'target_team.0.id': { + 'expected': int + }, + 'target_team.0.display_name': { + 'expected': str + }, + 'target_team.0.url': { + 'expected': str + }, + 'target_user': { + 'expected': dict + }, + 'target_user.id': { + 'expected': int + }, + 'target_user.display_name': { + 'expected': str + }, + 'target_user.url': { + 'expected': Hyperlink + }, + 'responsible_user': { + 'expected': dict + }, + 'responsible_user.id': { + 'expected': int + }, + 'responsible_user.display_name': { + 'expected': str + }, + 'responsible_user.url': { + 'expected': Hyperlink + }, + 'responsible_teams': { + 'expected': list + }, + 'responsible_teams.0.id': { + 'expected': int + }, + 'responsible_teams.0.display_name': { + 'expected': str + }, + 'responsible_teams.0.url': { + 'expected': str + }, + 'public': { + 'expected': bool + }, + 'modified': { + 'expected': str + } + } + + + +class knowledgeBaseAPIInheritedCases( + knowledgeBaseAPITestCases, +): + pass + + + +@pytest.mark.module_assistance +class knowledgeBaseAPIPyTest( + knowledgeBaseAPITestCases, +): + + pass diff --git a/app/assistance/tests/functional/knowledge_base/test_functional_knowledge_base_model.py b/app/assistance/tests/functional/knowledge_base/test_functional_knowledge_base_model.py new file mode 100644 index 00000000..0c1992c6 --- /dev/null +++ b/app/assistance/tests/functional/knowledge_base/test_functional_knowledge_base_model.py @@ -0,0 +1,28 @@ +import pytest + +from core.tests.functional.centurion_abstract.test_functional_centurion_abstract_model import ( + CenturionAbstractModelInheritedCases +) + + + +@pytest.mark.model_knowledgebase +class knowledgeBaseModelTestCases( + CenturionAbstractModelInheritedCases +): + pass + + + +class knowledgeBaseModelInheritedCases( + knowledgeBaseModelTestCases, +): + pass + + + +@pytest.mark.module_assistance +class knowledgeBaseModelPyTest( + knowledgeBaseModelTestCases, +): + pass diff --git a/app/assistance/tests/functional/knowledge_base/knowledge_base_viewset.py b/app/assistance/tests/functional/knowledge_base/test_knowledge_base_metadata.py similarity index 87% rename from app/assistance/tests/functional/knowledge_base/knowledge_base_viewset.py rename to app/assistance/tests/functional/knowledge_base/test_knowledge_base_metadata.py index f2ce16fc..c7c48d64 100644 --- a/app/assistance/tests/functional/knowledge_base/knowledge_base_viewset.py +++ b/app/assistance/tests/functional/knowledge_base/test_knowledge_base_metadata.py @@ -1,11 +1,7 @@ import django import pytest -import unittest -import requests - -from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -13,11 +9,9 @@ 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 assistance.models.knowledge_base import KnowledgeBase +from assistance.models.knowledge_base import KnowledgeBase, KnowledgeBaseCategory from settings.models.app_settings import AppSettings @@ -25,7 +19,6 @@ User = django.contrib.auth.get_user_model() - class ViewSetBase: model = KnowledgeBase @@ -65,11 +58,17 @@ class ViewSetBase: name = 'test_global_organization' ) + category = KnowledgeBaseCategory.objects.create( + organization = self.global_organization, + name = 'cat2' + ) + self.global_org_item = self.model.objects.create( organization = self.global_organization, title = 'one', content = 'some text for bodygfdgdf', - target_user = self.view_user + target_user = self.view_user, + category = category, ) app_settings = AppSettings.objects.get( @@ -163,19 +162,29 @@ class ViewSetBase: user = self.view_user ) + category_item = KnowledgeBaseCategory.objects.create( + organization = self.organization, + name = 'cat' + ) self.item = self.model.objects.create( organization = self.organization, title = 'one', content = 'some text for body', - target_user = self.view_user + target_user = self.view_user, + category = category_item, ) + category = KnowledgeBaseCategory.objects.create( + organization = self.different_organization, + name = 'cat1' + ) self.other_org_item = self.model.objects.create( organization = self.different_organization, title = 'two', content = 'some text for body', - target_user = self.view_user_b + target_user = self.view_user_b, + category = category, ) @@ -185,7 +194,8 @@ class ViewSetBase: 'title': 'team_post', 'organization': self.organization.id, 'content': 'article text', - 'target_user': self.view_user.id + 'target_user': self.view_user.id, + 'category': category_item.id, } @@ -229,24 +239,6 @@ class ViewSetBase: ) -class KnowledgeBasePermissionsAPI( - ViewSetBase, - APIPermissions, - TestCase, -): - - pass - - -class KnowledgeBaseViewSet( - ViewSetBase, - SerializersTestCases, - TestCase, -): - - pass - - class KnowledgeBaseMetadata( ViewSetBase, diff --git a/app/assistance/tests/functional/knowledge_base_category/conftest.py b/app/assistance/tests/functional/knowledge_base_category/conftest.py new file mode 100644 index 00000000..02f54423 --- /dev/null +++ b/app/assistance/tests/functional/knowledge_base_category/conftest.py @@ -0,0 +1,25 @@ +import pytest + + + +@pytest.fixture( scope = 'class') +def model(model_knowledgebasecategory): + + yield model_knowledgebasecategory + + +@pytest.fixture( scope = 'class', autouse = True) +def model_kwargs(request, kwargs_knowledgebasecategory): + + request.cls.kwargs_create_item = kwargs_knowledgebasecategory.copy() + + yield kwargs_knowledgebasecategory.copy() + + if hasattr(request.cls, 'kwargs_create_item'): + del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_knowledgebasecategory): + + yield serializer_knowledgebasecategory diff --git a/app/assistance/tests/functional/knowledge_base_category/knowledge_base_category_serializer.py b/app/assistance/tests/functional/knowledge_base_category/knowledge_base_category_serializer.py deleted file mode 100644 index 31b2663b..00000000 --- a/app/assistance/tests/functional/knowledge_base_category/knowledge_base_category_serializer.py +++ /dev/null @@ -1,222 +0,0 @@ -import django -import json -import pytest - -from django.test import TestCase - -from rest_framework.exceptions import ValidationError - -from access.models.tenant import Tenant as Organization -from access.models.team import Team - -from centurion.tests.abstract.mock_view import MockView - -from assistance.serializers.knowledge_base_category import KnowledgeBaseCategory, KnowledgeBaseCategoryModelSerializer - -User = django.contrib.auth.get_user_model() - - - -class KnowledgeBaseCategoryValidationAPI( - TestCase, -): - - model = KnowledgeBaseCategory - - app_namespace = 'API' - - url_name = '_api_v2_knowledge_base' - - @classmethod - def setUpTestData(self): - """Setup Test - - 1. Create an org - 2. Create a team - 4. Add user to add team - """ - - organization = Organization.objects.create(name='test_org') - - self.organization = organization - - self.add_team = Team.objects.create( - organization=organization, - team_name = 'teamone', - model_notes = 'random note' - ) - - self.add_user = User.objects.create_user(username="test_user_add", password="password") - - self.mock_view = MockView( user = self.add_user ) - - self.item_has_target_user = self.model.objects.create( - organization=organization, - name = 'random title', - target_user = self.add_user, - ) - - self.item_has_target_team = self.model.objects.create( - organization=organization, - name = 'random title0', - ) - - self.item_has_target_team.target_team.set([ self.add_team ]) - - - - 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 = KnowledgeBaseCategoryModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "organization": self.organization.id, - "target_user": self.add_user.id, - "target_team": [ - self.add_team.id - ] - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['name'][0] == 'required' - - - - def test_serializer_validation_parent_category_not_self(self): - """Serializer Validation Check - - Ensure that you cant assisgn self as parent category - """ - - with pytest.raises(ValidationError) as err: - - serializer = KnowledgeBaseCategoryModelSerializer( - self.item_has_target_user, - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "parent_category": self.item_has_target_user.id - }, - partial=True, - ) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['parent_category'][0] == 'parent_category_not_self' - - - - def test_serializer_validation_both_target_team_target_user(self): - """Serializer Validation Check - - Ensure that both target user and target team raises a validation error - """ - - with pytest.raises(ValidationError) as err: - - serializer = KnowledgeBaseCategoryModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "organization": self.organization.id, - "name": "teamone", - "target_user": self.add_user.id, - "target_team": [ - self.add_team.id - ] - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['non_field_errors'][0] == 'invalid_not_both_target_team_user' - - - - def test_serializer_validation_no_target_team_target_user(self): - """Serializer Validation Check - - Ensure that if either target user and target team is missing it raises validation error - """ - - with pytest.raises(ValidationError) as err: - - serializer = KnowledgeBaseCategoryModelSerializer( - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "organization": self.organization.id, - "name": 'teamone' - }) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['non_field_errors'][0] == 'invalid_need_target_team_or_user' - - - - def test_serializer_validation_update_existing_target_user(self): - """Serializer Validation Check - - Ensure that if an existing item with target user is updated to include a target_team - it raises a validation error - """ - - with pytest.raises(ValidationError) as err: - - serializer = KnowledgeBaseCategoryModelSerializer( - self.item_has_target_user, - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "target_team": [ self.add_team.id ] - }, - partial=True, - ) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['non_field_errors'][0] == 'invalid_not_both_target_team_user' - - - def test_serializer_validation_update_existing_target_team(self): - """Serializer Validation Check - - Ensure that if an existing item with target team is updated to include a target_user - it raises a validation error - """ - - with pytest.raises(ValidationError) as err: - - serializer = KnowledgeBaseCategoryModelSerializer( - self.item_has_target_team, - context = { - 'request': self.mock_view.request, - 'view': self.mock_view, - }, - data={ - "target_user": self.add_user.id - }, - partial=True, - ) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['non_field_errors'][0] == 'invalid_not_both_target_team_user' diff --git a/app/assistance/tests/functional/knowledge_base_category/test_functional_knowledge_base_category_api_fields.py b/app/assistance/tests/functional/knowledge_base_category/test_functional_knowledge_base_category_api_fields.py new file mode 100644 index 00000000..246a62df --- /dev/null +++ b/app/assistance/tests/functional/knowledge_base_category/test_functional_knowledge_base_category_api_fields.py @@ -0,0 +1,154 @@ +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_knowledgebasecategory +class knowledgeBaseCategoryAPITestCases( + APIFieldsInheritedCases, +): + + @pytest.fixture( scope = 'class') + def second_model(self, request, django_db_blocker, + model, model_kwargs, model_team, kwargs_team + ): + + item = None + + 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 + }) + + + # Switch model fields so all fields can be checked + team = model_team.objects.create( **kwargs_team ) + kwargs_many_to_many.update({ 'target_team': [ team ]}) + del kwargs['target_user'] + + kwargs['parent_category'] = request.cls.item + + + item_two = model.objects.create( + **kwargs + ) + + + for key, value in kwargs_many_to_many.items(): + + field = getattr(item_two, key) + + for entry in value: + + field.add(entry) + + + request.cls.item_two = item_two + + yield item_two + + with django_db_blocker.unblock(): + + item_two.delete() + + team.delete() + + del request.cls.item_two + + + @pytest.fixture( scope = 'class', autouse = True) + def class_setup(self, + create_model, + second_model, + make_request, + ): + + pass + + @property + def parameterized_api_fields(self): + + return { + 'parent_category': { + 'expected': dict + }, + 'parent_category.id': { + 'expected': int + }, + 'parent_category.display_name': { + 'expected': str + }, + 'parent_category.url': { + 'expected': str + }, + 'name': { + 'expected': str + }, + 'target_team': { + 'expected': list + }, + 'target_team.0.id': { + 'expected': int + }, + 'target_team.0.display_name': { + 'expected': str + }, + 'target_team.0.url': { + 'expected': str + }, + 'target_user': { + 'expected': dict + }, + 'target_user.id': { + 'expected': int + }, + 'target_user.display_name': { + 'expected': str + }, + 'target_user.url': { + 'expected': Hyperlink + }, + 'modified': { + 'expected': str + } + } + + + +class knowledgeBaseCategoryAPIInheritedCases( + knowledgeBaseCategoryAPITestCases, +): + pass + + + +@pytest.mark.module_assistance +class knowledgeBaseCategoryAPIPyTest( + knowledgeBaseCategoryAPITestCases, +): + + pass diff --git a/app/assistance/tests/functional/knowledge_base_category/test_functional_knowledge_base_category_model.py b/app/assistance/tests/functional/knowledge_base_category/test_functional_knowledge_base_category_model.py new file mode 100644 index 00000000..f14cea9d --- /dev/null +++ b/app/assistance/tests/functional/knowledge_base_category/test_functional_knowledge_base_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_knowledgebasecategory +class knowledgeBaseCategoryModelTestCases( + CenturionAbstractModelInheritedCases +): + pass + + + +class knowledgeBaseCategoryModelInheritedCases( + knowledgeBaseCategoryModelTestCases, +): + pass + + + +@pytest.mark.module_assistance +class knowledgeBaseCategoryModelPyTest( + knowledgeBaseCategoryModelTestCases, +): + pass diff --git a/app/assistance/tests/functional/knowledge_base_category/knowledge_base_category_viewset.py b/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_metadata.py similarity index 93% rename from app/assistance/tests/functional/knowledge_base_category/knowledge_base_category_viewset.py rename to app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_metadata.py index bbda85aa..eb846231 100644 --- a/app/assistance/tests/functional/knowledge_base_category/knowledge_base_category_viewset.py +++ b/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_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 assistance.models.knowledge_base_category import KnowledgeBaseCategory @@ -26,8 +24,8 @@ class ViewSetBase: model = KnowledgeBaseCategory app_namespace = 'v2' - - url_name = '_api_v2_knowledge_base_category' + + url_name = '_api_knowledgebasecategory' change_data = {'name': 'device'} @@ -218,25 +216,6 @@ class ViewSetBase: -class KnowledgeBaseCategoryPermissionsAPI( - ViewSetBase, - APIPermissions, - TestCase, -): - - pass - - -class KnowledgeBaseCategoryViewSet( - ViewSetBase, - SerializersTestCases, - TestCase, -): - - pass - - - class KnowledgeBaseCategoryMetadata( ViewSetBase, MetadataAttributesFunctional, diff --git a/app/assistance/tests/unit/knowledge_base/conftest.py b/app/assistance/tests/unit/knowledge_base/conftest.py index 0461cb75..2215ce68 100644 --- a/app/assistance/tests/unit/knowledge_base/conftest.py +++ b/app/assistance/tests/unit/knowledge_base/conftest.py @@ -17,3 +17,9 @@ def model_kwargs(request, kwargs_knowledgebase): if hasattr(request.cls, 'kwargs_create_item'): del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_knowledgebase): + + yield serializer_knowledgebase diff --git a/app/assistance/tests/unit/knowledge_base/knowledge_base_api_v2.py b/app/assistance/tests/unit/knowledge_base/knowledge_base_api_v2.py deleted file mode 100644 index 38258bc1..00000000 --- a/app/assistance/tests/unit/knowledge_base/knowledge_base_api_v2.py +++ /dev/null @@ -1,485 +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 assistance.models.knowledge_base import KnowledgeBase -from assistance.models.knowledge_base_category import KnowledgeBaseCategory - -User = django.contrib.auth.get_user_model() - - -class KnowledgeBaseAPI( - TestCase, - APITenancyObject -): - - model = KnowledgeBase - - app_namespace = 'v2' - - url_name = '_api_knowledgebase' - - @classmethod - def setUpTestData(self): - """Setup Test - - 1. Create the object - 2. create view user - 4. make api request - """ - - organization = Organization.objects.create(name='test_org') - - self.organization = organization - - different_organization = Organization.objects.create(name='test_different_organization') - - - self.view_team = Team.objects.create( - organization=organization, - team_name = 'teamone', - model_notes = 'random note' - ) - - 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, - ) - ) - - self.view_team.permissions.set([view_permissions]) - - self.view_user = User.objects.create_user(username="test_user_view", password="password") - - - - self.item = self.model.objects.create( - organization=organization, - title = 'teamone', - content = 'random note', - summary = 'a summary', - target_user = self.view_user, - release_date = '2024-01-01 12:00:00', - expiry_date = '2024-01-01 12:00:01', - responsible_user = self.view_user, - category = KnowledgeBaseCategory.objects.create( - name='cat', - target_user = self.view_user, - organization=organization, - ) - ) - - self.item.responsible_teams.set([self.view_team]) - - self.url_view_kwargs = {'pk': self.item.id} - - teamuser = TeamUsers.objects.create( - team = self.view_team, - user = self.view_user - ) - - organization.manager = self.view_user - - organization.save() - - client = Client() - url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs) - - - client.force_login(self.view_user) - response = client.get(url) - - self.api_data = response.data - - - - def test_api_field_exists_model_notes(self): - """ Test for existance of API Field - - model_notes field does not exist for KB articles - """ - - assert 'model_notes' not in self.api_data - - - def test_api_field_type_model_notes(self): - """ Test for type for API Field - - model_notes does not exist for KB articles - """ - - pass - - - - 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 int - """ - - 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 int - """ - - assert type(self.api_data['category']['url']) is str - - - - def test_api_field_exists_summary(self): - """ Test for existance of API Field - - summary field must exist - """ - - assert 'summary' in self.api_data - - - def test_api_field_type_summary(self): - """ Test for type for API Field - - summary field must be str - """ - - assert type(self.api_data['summary']) is str - - - - def test_api_field_exists_content(self): - """ Test for existance of API Field - - content field must exist - """ - - assert 'content' in self.api_data - - - def test_api_field_type_summary(self): - """ Test for type for API Field - - content field must be str - """ - - assert type(self.api_data['content']) is str - - - - def test_api_field_exists_release_date(self): - """ Test for existance of API Field - - release_date field must exist - """ - - assert 'release_date' in self.api_data - - - def test_api_field_type_release_date(self): - """ Test for type for API Field - - release_date field must be str - """ - - assert type(self.api_data['release_date']) is str - - - - def test_api_field_exists_expiry_date(self): - """ Test for existance of API Field - - expiry_date field must exist - """ - - assert 'expiry_date' in self.api_data - - - def test_api_field_type_expiry_date(self): - """ Test for type for API Field - - expiry_date field must be str - """ - - assert type(self.api_data['expiry_date']) is str - - - - def test_api_field_exists_public(self): - """ Test for existance of API Field - - public field must exist - """ - - assert 'public' in self.api_data - - - def test_api_field_type_public(self): - """ Test for type for API Field - - public field must be bool - """ - - assert type(self.api_data['public']) is bool - - - - def test_api_field_type_target_user(self): - """ Test for type for API Field - - target_user field must be dict - """ - - assert type(self.api_data['target_user']) is dict - - - def test_api_field_exists_target_user_id(self): - """ Test for existance of API Field - - target_user.id field must exist - """ - - assert 'id' in self.api_data['target_user'] - - - def test_api_field_type_target_user_id(self): - """ Test for type for API Field - - target_user.id field must be int - """ - - assert type(self.api_data['target_user']['id']) is int - - - def test_api_field_exists_target_user_display_name(self): - """ Test for existance of API Field - - target_user.display_name field must exist - """ - - assert 'display_name' in self.api_data['target_user'] - - - def test_api_field_type_target_user_display_name(self): - """ Test for type for API Field - - target_user.display_name field must be int - """ - - assert type(self.api_data['target_user']['display_name']) is str - - - def test_api_field_exists_target_user_url(self): - """ Test for existance of API Field - - target_user.url field must exist - """ - - assert 'url' in self.api_data['target_user'] - - - def test_api_field_type_target_user_url(self): - """ Test for type for API Field - - target_user.url field must be int - """ - - assert type(self.api_data['target_user']['url']) is Hyperlink - - - - def test_api_field_type_responsible_user(self): - """ Test for type for API Field - - responsible_user field must be dict - """ - - assert type(self.api_data['responsible_user']) is dict - - - def test_api_field_exists_responsible_user_id(self): - """ Test for existance of API Field - - responsible_user.id field must exist - """ - - assert 'id' in self.api_data['responsible_user'] - - - def test_api_field_type_responsible_user_id(self): - """ Test for type for API Field - - responsible_user.id field must be int - """ - - assert type(self.api_data['responsible_user']['id']) is int - - - def test_api_field_exists_responsible_user_display_name(self): - """ Test for existance of API Field - - responsible_user.display_name field must exist - """ - - assert 'display_name' in self.api_data['responsible_user'] - - - def test_api_field_type_responsible_user_display_name(self): - """ Test for type for API Field - - responsible_user.display_name field must be int - """ - - assert type(self.api_data['responsible_user']['display_name']) is str - - - def test_api_field_exists_responsible_user_url(self): - """ Test for existance of API Field - - responsible_user.url field must exist - """ - - assert 'url' in self.api_data['responsible_user'] - - - def test_api_field_type_responsible_user_url(self): - """ Test for type for API Field - - responsible_user.url field must be Hyperlink - """ - - assert type(self.api_data['responsible_user']['url']) is Hyperlink - - - - def test_api_field_type_responsible_teams(self): - """ Test for type for API Field - - responsible_teams field must be list - """ - - assert type(self.api_data['responsible_teams']) is list - - - def test_api_field_exists_responsible_teams_id(self): - """ Test for existance of API Field - - responsible_teams.id field must exist - """ - - assert 'id' in self.api_data['responsible_teams'][0] - - - def test_api_field_type_responsible_teams_id(self): - """ Test for type for API Field - - responsible_teams.id field must be int - """ - - assert type(self.api_data['responsible_teams'][0]['id']) is int - - - def test_api_field_exists_responsible_teams_display_name(self): - """ Test for existance of API Field - - responsible_teams.display_name field must exist - """ - - assert 'display_name' in self.api_data['responsible_teams'][0] - - - def test_api_field_type_responsible_teams_display_name(self): - """ Test for type for API Field - - responsible_teams.display_name field must be int - """ - - assert type(self.api_data['responsible_teams'][0]['display_name']) is str - - - def test_api_field_exists_responsible_teams_url(self): - """ Test for existance of API Field - - responsible_teams.url field must exist - """ - - assert 'url' in self.api_data['responsible_teams'][0] - - - def test_api_field_type_responsible_teams_url(self): - """ Test for type for API Field - - responsible_teams.url field must be str - """ - - assert type(self.api_data['responsible_teams'][0]['url']) is str diff --git a/app/assistance/tests/unit/knowledge_base/test_unit_knowledge_base_model.py b/app/assistance/tests/unit/knowledge_base/test_unit_knowledge_base_model.py index 390ec977..2237492c 100644 --- a/app/assistance/tests/unit/knowledge_base/test_unit_knowledge_base_model.py +++ b/app/assistance/tests/unit/knowledge_base/test_unit_knowledge_base_model.py @@ -88,7 +88,7 @@ class KnowledgeBaseModelTestCases( 'unique': False, }, 'responsible_user': { - 'blank': False, + 'blank': True, 'default': models.fields.NOT_PROVIDED, 'field_type': models.ForeignKey, 'null': True, @@ -128,6 +128,7 @@ class KnowledgeBaseModelInheritedCases( +@pytest.mark.module_module_assistance class KnowledgeBaseModelPyTest( KnowledgeBaseModelTestCases, ): diff --git a/app/assistance/tests/unit/knowledge_base/test_unit_knowledge_base_serializer.py b/app/assistance/tests/unit/knowledge_base/test_unit_knowledge_base_serializer.py new file mode 100644 index 00000000..1dab16cc --- /dev/null +++ b/app/assistance/tests/unit/knowledge_base/test_unit_knowledge_base_serializer.py @@ -0,0 +1,258 @@ +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_knowledgebase +class KnowledgeBaseSerializerTestCases( + 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_title(self, + kwargs_api_create, model, model_serializer, request_user): + """Serializer Validation Check + + Ensure that if creating and no title is provided a validation error occurs + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_api_create.copy() + del kwargs['title'] + + 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()['title'][0] == 'required' + + + + def test_serializer_validation_both_target_team_target_user(self, + kwargs_api_create, model, model_serializer, request_user, + model_team, kwargs_team + ): + """Serializer Validation Check + + Ensure that both target user and target team raises a validation error + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_api_create.copy() + kwargs.update({ + 'target_user': request_user.id + }) + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['non_field_errors'][0] == 'invalid_not_both_target_team_user' + + + + def test_serializer_validation_no_target_team_target_user(self, + kwargs_api_create, model, model_serializer, request_user, + model_team, kwargs_team + ): + """Serializer Validation Check + + Ensure that if either target user and target team is missing it raises validation error + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_api_create.copy() + del kwargs['target_team'] + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['non_field_errors'][0] == 'invalid_need_target_team_or_user' + + + + def test_serializer_validation_update_existing_target_user(self, + kwargs_api_create, model, model_serializer, request_user, + model_team, kwargs_team + ): + """Serializer Validation Check + + Ensure that if an existing item with target user is updated to include a target_team + it raises a validation error + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_api_create.copy() + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + self.item_has_target_user, + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data={ + "target_team": [ self.add_team.id ] + }, + partial=True, + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['non_field_errors'][0] == 'invalid_not_both_target_team_user' + + + def test_serializer_validation_update_existing_target_user(self, + created_model, + kwargs_api_create, model, model_serializer, request_user, + model_team, kwargs_team + ): + """Serializer Validation Check + + Ensure that if an existing item with target team is updated to include a target_user + it raises a validation error + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_api_create.copy() + team = model_team.objects.create( **kwargs_team ) + kwargs.update({ + 'target_team': [ team ] + }) + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + created_model, + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data={ + "target_user": request_user.id + }, + partial=True, + ) + + serializer.is_valid(raise_exception = True) + + team.delete() + + assert err.value.get_codes()['non_field_errors'][0] == 'invalid_not_both_target_team_user' + + + + +class KnowledgeBaseSerializerInheritedCases( + KnowledgeBaseSerializerTestCases +): + pass + + + +@pytest.mark.module_assistance +class KnowledgeBaseSerializerPyTest( + KnowledgeBaseSerializerTestCases +): + pass \ No newline at end of file diff --git a/app/assistance/tests/unit/knowledge_base/test_unit_knowledge_base_viewset.py b/app/assistance/tests/unit/knowledge_base/test_unit_knowledge_base_viewset.py index 99e34dcf..6a5a06c2 100644 --- a/app/assistance/tests/unit/knowledge_base/test_unit_knowledge_base_viewset.py +++ b/app/assistance/tests/unit/knowledge_base/test_unit_knowledge_base_viewset.py @@ -1,44 +1,91 @@ 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 assistance.viewsets.knowledge_base import ViewSet +from assistance.viewsets.knowledge_base import ( + KnowledgeBase, + ViewSet, +) -@pytest.mark.skip(reason = 'see #895, tests being refactored') -class KnowledgeBaseViewsetList( +@pytest.mark.model_knowledgebase +class ViewsetTestCases( ModelViewSetInheritedCases, - TestCase, ): - viewset = ViewSet - route_name = 'v2:_api_knowledgebase' + @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', + 'category', + 'target_user', + 'target_team', + 'responsible_user', + 'responsible_teams', + 'public' + ] + }, + 'model': { + 'value': KnowledgeBase + }, + 'model_documentation': { + 'type': type(None), + }, + 'queryset': { + 'type': type(None), + }, + 'serializer_class': { + 'type': type(None), + }, + 'search_fields': { + 'value': [ + 'title', + 'summary', + 'content' + ] + }, + 'view_description': { + 'value': 'Information Management Knowledge Base Article(s)' + }, + 'view_name': { + 'type': type(None), + }, + 'view_serializer_name': { + 'type': type(None), + } + } - super().setUpTestData() + +class KnowledgeBaseViewsetInheritedCases( + ViewsetTestCases, +): + pass - client = Client() - - url = reverse( - self.route_name + '-list', - kwargs = self.kwargs - ) - client.force_login(self.view_user) +@pytest.mark.module_assistance +class KnowledgeBaseViewsetPyTest( + ViewsetTestCases, +): - self.http_options_response_list = client.options(url) + pass diff --git a/app/assistance/tests/unit/knowledge_base_category/conftest.py b/app/assistance/tests/unit/knowledge_base_category/conftest.py index e13e9b42..02f54423 100644 --- a/app/assistance/tests/unit/knowledge_base_category/conftest.py +++ b/app/assistance/tests/unit/knowledge_base_category/conftest.py @@ -17,3 +17,9 @@ def model_kwargs(request, kwargs_knowledgebasecategory): if hasattr(request.cls, 'kwargs_create_item'): del request.cls.kwargs_create_item + + +@pytest.fixture( scope = 'class') +def model_serializer(serializer_knowledgebasecategory): + + yield serializer_knowledgebasecategory diff --git a/app/assistance/tests/unit/knowledge_base_category/knowledge_base_category_api_v2.py b/app/assistance/tests/unit/knowledge_base_category/knowledge_base_category_api_v2.py deleted file mode 100644 index 779db269..00000000 --- a/app/assistance/tests/unit/knowledge_base_category/knowledge_base_category_api_v2.py +++ /dev/null @@ -1,481 +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 assistance.models.knowledge_base_category import KnowledgeBaseCategory - -User = django.contrib.auth.get_user_model() - - - -class KnowledgeBaseCategoryAPI( - TestCase, - APITenancyObject -): - - model = KnowledgeBaseCategory - - app_namespace = 'v2' - - url_name = '_api_v2_knowledge_base_category' - - @classmethod - def setUpTestData(self): - """Setup Test - - 1. Create the object - 2. create view user - 4. make api request - """ - - organization = Organization.objects.create(name='test_org') - - self.organization = organization - - different_organization = Organization.objects.create(name='test_different_organization') - - - self.view_team = Team.objects.create( - organization=organization, - team_name = 'teamone', - model_notes = 'random note' - ) - - 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, - ) - ) - - self.view_team.permissions.set([view_permissions]) - - self.view_user = User.objects.create_user(username="test_user_view", password="password") - - parent_category = self.model.objects.create( - name='cat parent', - target_user = self.view_user, - organization=organization, - ) - - self.item = self.model.objects.create( - name='cat', - model_notes='dsa', - target_user = self.view_user, - organization=organization, - parent_category = parent_category, - ) - - # self.item.target_teams.set([self.view_team]) - - self.url_view_kwargs = {'pk': self.item.id} - - teamuser = TeamUsers.objects.create( - team = self.view_team, - user = self.view_user - ) - - organization.manager = self.view_user - - organization.save() - - client = Client() - url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs) - - - client.force_login(self.view_user) - response = client.get(url) - - self.api_data = response.data - - - - # def test_api_field_exists_model_notes(self): - # """ Test for existance of API Field - - # model_notes field does not exist for KB articles - # """ - - # assert 'model_notes' not in self.api_data - - - # def test_api_field_type_model_notes(self): - # """ Test for type for API Field - - # model_notes does not exist for KB articles - # """ - - # pass - - - - def test_api_field_exists_parent_category(self): - """ Test for existance of API Field - - parent_category field must exist - """ - - assert 'parent_category' in self.api_data - - - def test_api_field_type_parent_category(self): - """ Test for type for API Field - - parent_category field must be dict - """ - - assert type(self.api_data['parent_category']) is dict - - - def test_api_field_exists_parent_category_id(self): - """ Test for existance of API Field - - parent_category.id field must exist - """ - - assert 'id' in self.api_data['parent_category'] - - - def test_api_field_type_parent_category_id(self): - """ Test for type for API Field - - parent_category.id field must be int - """ - - assert type(self.api_data['parent_category']['id']) is int - - - def test_api_field_exists_parent_category_display_name(self): - """ Test for existance of API Field - - parent_category.display_name field must exist - """ - - assert 'display_name' in self.api_data['parent_category'] - - - def test_api_field_type_parent_category_display_name(self): - """ Test for type for API Field - - parent_category.display_name field must be int - """ - - assert type(self.api_data['parent_category']['display_name']) is str - - - def test_api_field_exists_parent_category_url(self): - """ Test for existance of API Field - - parent_category.url field must exist - """ - - assert 'url' in self.api_data['parent_category'] - - - def test_api_field_type_parent_category_url(self): - """ Test for type for API Field - - parent_category.url field must be int - """ - - assert type(self.api_data['parent_category']['url']) is str - - - - # def test_api_field_exists_summary(self): - # """ Test for existance of API Field - - # summary field must exist - # """ - - # assert 'summary' in self.api_data - - - # def test_api_field_type_summary(self): - # """ Test for type for API Field - - # summary field must be str - # """ - - # assert type(self.api_data['summary']) is str - - - - # def test_api_field_exists_content(self): - # """ Test for existance of API Field - - # content field must exist - # """ - - # assert 'content' in self.api_data - - - # def test_api_field_type_summary(self): - # """ Test for type for API Field - - # content field must be str - # """ - - # assert type(self.api_data['content']) is str - - - - # def test_api_field_exists_release_date(self): - # """ Test for existance of API Field - - # release_date field must exist - # """ - - # assert 'release_date' in self.api_data - - - # def test_api_field_type_release_date(self): - # """ Test for type for API Field - - # release_date field must be str - # """ - - # assert type(self.api_data['release_date']) is str - - - - # def test_api_field_exists_expiry_date(self): - # """ Test for existance of API Field - - # expiry_date field must exist - # """ - - # assert 'expiry_date' in self.api_data - - - # def test_api_field_type_expiry_date(self): - # """ Test for type for API Field - - # expiry_date field must be str - # """ - - # assert type(self.api_data['expiry_date']) is str - - - - # def test_api_field_exists_public(self): - # """ Test for existance of API Field - - # public field must exist - # """ - - # assert 'public' in self.api_data - - - # def test_api_field_type_public(self): - # """ Test for type for API Field - - # public field must be bool - # """ - - # assert type(self.api_data['public']) is bool - - - - def test_api_field_type_target_user(self): - """ Test for type for API Field - - target_user field must be dict - """ - - assert type(self.api_data['target_user']) is dict - - - def test_api_field_exists_target_user_id(self): - """ Test for existance of API Field - - target_user.id field must exist - """ - - assert 'id' in self.api_data['target_user'] - - - def test_api_field_type_target_user_id(self): - """ Test for type for API Field - - target_user.id field must be int - """ - - assert type(self.api_data['target_user']['id']) is int - - - def test_api_field_exists_target_user_display_name(self): - """ Test for existance of API Field - - target_user.display_name field must exist - """ - - assert 'display_name' in self.api_data['target_user'] - - - def test_api_field_type_target_user_display_name(self): - """ Test for type for API Field - - target_user.display_name field must be int - """ - - assert type(self.api_data['target_user']['display_name']) is str - - - def test_api_field_exists_target_user_url(self): - """ Test for existance of API Field - - target_user.url field must exist - """ - - assert 'url' in self.api_data['target_user'] - - - def test_api_field_type_target_user_url(self): - """ Test for type for API Field - - target_user.url field must be int - """ - - assert type(self.api_data['target_user']['url']) is Hyperlink - - - - # def test_api_field_type_responsible_user(self): - # """ Test for type for API Field - - # responsible_user field must be dict - # """ - - # assert type(self.api_data['responsible_user']) is dict - - - # def test_api_field_exists_responsible_user_id(self): - # """ Test for existance of API Field - - # responsible_user.id field must exist - # """ - - # assert 'id' in self.api_data['responsible_user'] - - - # def test_api_field_type_responsible_user_id(self): - # """ Test for type for API Field - - # responsible_user.id field must be int - # """ - - # assert type(self.api_data['responsible_user']['id']) is int - - - # def test_api_field_exists_responsible_user_display_name(self): - # """ Test for existance of API Field - - # responsible_user.display_name field must exist - # """ - - # assert 'display_name' in self.api_data['responsible_user'] - - - # def test_api_field_type_responsible_user_display_name(self): - # """ Test for type for API Field - - # responsible_user.display_name field must be int - # """ - - # assert type(self.api_data['responsible_user']['display_name']) is str - - - # def test_api_field_exists_responsible_user_url(self): - # """ Test for existance of API Field - - # responsible_user.url field must exist - # """ - - # assert 'url' in self.api_data['responsible_user'] - - - # def test_api_field_type_responsible_user_url(self): - # """ Test for type for API Field - - # responsible_user.url field must be Hyperlink - # """ - - # assert type(self.api_data['responsible_user']['url']) is Hyperlink - - - - # def test_api_field_type_responsible_teams(self): - # """ Test for type for API Field - - # responsible_teams field must be list - # """ - - # assert type(self.api_data['responsible_teams']) is list - - - # def test_api_field_exists_responsible_teams_id(self): - # """ Test for existance of API Field - - # responsible_teams.id field must exist - # """ - - # assert 'id' in self.api_data['responsible_teams'][0] - - - # def test_api_field_type_responsible_teams_id(self): - # """ Test for type for API Field - - # responsible_teams.id field must be int - # """ - - # assert type(self.api_data['responsible_teams'][0]['id']) is int - - - # def test_api_field_exists_responsible_teams_display_name(self): - # """ Test for existance of API Field - - # responsible_teams.display_name field must exist - # """ - - # assert 'display_name' in self.api_data['responsible_teams'][0] - - - # def test_api_field_type_responsible_teams_display_name(self): - # """ Test for type for API Field - - # responsible_teams.display_name field must be int - # """ - - # assert type(self.api_data['responsible_teams'][0]['display_name']) is str - - - # def test_api_field_exists_responsible_teams_url(self): - # """ Test for existance of API Field - - # responsible_teams.url field must exist - # """ - - # assert 'url' in self.api_data['responsible_teams'][0] - - - # def test_api_field_type_responsible_teams_url(self): - # """ Test for type for API Field - - # responsible_teams.url field must be str - # """ - - # assert type(self.api_data['responsible_teams'][0]['url']) is str diff --git a/app/assistance/tests/unit/knowledge_base_category/test_unit_knowledge_base_category_model.py b/app/assistance/tests/unit/knowledge_base_category/test_unit_knowledge_base_category_model.py index 65c03f11..3a5c9d18 100644 --- a/app/assistance/tests/unit/knowledge_base_category/test_unit_knowledge_base_category_model.py +++ b/app/assistance/tests/unit/knowledge_base_category/test_unit_knowledge_base_category_model.py @@ -77,6 +77,7 @@ class KnowledgeBaseCategoryModelInheritedCases( +@pytest.mark.module_assistance class KnowledgeBaseCategoryModelPyTest( KnowledgeBaseCategoryModelTestCases, ): diff --git a/app/assistance/tests/unit/knowledge_base_category/test_unit_knowledge_base_category_serializer.py b/app/assistance/tests/unit/knowledge_base_category/test_unit_knowledge_base_category_serializer.py new file mode 100644 index 00000000..df1d5468 --- /dev/null +++ b/app/assistance/tests/unit/knowledge_base_category/test_unit_knowledge_base_category_serializer.py @@ -0,0 +1,266 @@ +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_knowledgebasecategory +class knowledgebaseCategorySerializerTestCases( + SerializerTestCases +): + + @pytest.fixture( scope = 'function' ) + def created_model(self, django_db_blocker, model, model_kwargs): + + with django_db_blocker.unblock(): + + item = model.objects.create( **model_kwargs ) + + yield item + + item.delete() + + + + def test_serializer_validation_no_name(self, + kwargs_api_create, model, model_serializer, request_user + ): + """Serializer Validation Check + + Ensure that if creating and no name is provided a validation error occurs + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_api_create.copy() + del kwargs['name'] + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['name'][0] == 'required' + + + + def test_serializer_validation_parent_category_not_self(self, + created_model, + model, model_serializer, request_user + ): + """Serializer Validation Check + + Ensure that you cant assisgn self as parent category + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + created_model, + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data={ + "parent_category": created_model.id + }, + partial=True, + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['parent_category'][0] == 'parent_category_not_self' + + + + def test_serializer_validation_both_target_team_target_user(self, + kwargs_api_create, model, model_serializer, request_user, + model_team, kwargs_team + ): + """Serializer Validation Check + + Ensure that both target user and target team raises a validation error + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_api_create.copy() + team = model_team.objects.create( **kwargs_team ) + kwargs.update({ + 'target_team': [ team ] + }) + + 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) + + team.delete() + assert err.value.get_codes()['non_field_errors'][0] == 'invalid_not_both_target_team_user' + + + + def test_serializer_validation_no_target_team_target_user(self, + kwargs_api_create, model, model_serializer, request_user + ): + """Serializer Validation Check + + Ensure that if either target user and target team is missing it raises validation error + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + kwargs = kwargs_api_create.copy() + del kwargs['target_user'] + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data = kwargs + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['non_field_errors'][0] == 'invalid_need_target_team_or_user' + + + + def test_serializer_validation_update_existing_target_team(self, + created_model, + model, model_serializer, request_user, + model_team, kwargs_team + ): + """Serializer Validation Check + + Ensure that if an existing item with target user is updated to include a target_team + it raises a validation error + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + team = model_team.objects.create( **kwargs_team ) + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + created_model, + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data={ + "target_team": [ team.id ] + }, + partial=True, + ) + + serializer.is_valid(raise_exception = True) + + team.delete() + + assert err.value.get_codes()['non_field_errors'][0] == 'invalid_not_both_target_team_user' + + + def test_serializer_validation_update_existing_target_user(self, + created_model, + model, model_serializer, request_user, + model_team, kwargs_team + ): + """Serializer Validation Check + + Ensure that if an existing item with target team is updated to include a target_user + it raises a validation error + """ + + mock_view = MockView( + user = request_user, + model = model, + action = 'create', + ) + + team = model_team.objects.create( **kwargs_team ) + created_model.target_user = None + created_model.target_team.add( team ) + created_model.save() + + with pytest.raises(ValidationError) as err: + + serializer = model_serializer['model']( + created_model, + context = { + 'request': mock_view.request, + 'view': mock_view, + }, + data={ + "target_user": request_user.id + }, + partial=True, + ) + + serializer.is_valid(raise_exception = True) + + team.delete() + + assert err.value.get_codes()['non_field_errors'][0] == 'invalid_not_both_target_team_user' + + + + +class knowledgebaseCategorySerializerInheritedCases( + knowledgebaseCategorySerializerTestCases +): + pass + + + +@pytest.mark.module_assistance +class knowledgebaseCategorySerializerPyTest( + knowledgebaseCategorySerializerTestCases +): + pass \ No newline at end of file diff --git a/app/assistance/tests/unit/knowledge_base_category/test_unit_knowledge_base_category_viewset.py b/app/assistance/tests/unit/knowledge_base_category/test_unit_knowledge_base_category_viewset.py new file mode 100644 index 00000000..9982b27f --- /dev/null +++ b/app/assistance/tests/unit/knowledge_base_category/test_unit_knowledge_base_category_viewset.py @@ -0,0 +1,87 @@ +import pytest + +from api.tests.unit.test_unit_common_viewset import ModelViewSetInheritedCases + +from assistance.viewsets.knowledge_base_category import ( + KnowledgeBaseCategory, + ViewSet, +) + + + +@pytest.mark.model_knowledgebasecategory +class ViewsetTestCases( + ModelViewSetInheritedCases, +): + + + @pytest.fixture( scope = 'function' ) + def viewset(self): + return ViewSet + + + @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': [ + 'name', + 'organization', + 'parent_category', + 'target_user', + 'target_team' + ] + }, + 'model': { + 'value': KnowledgeBaseCategory + }, + 'model_documentation': { + 'type': type(None), + }, + 'queryset': { + 'type': type(None), + }, + 'serializer_class': { + 'type': type(None), + }, + 'search_fields': { + 'value': [ + 'name' + ] + }, + 'view_description': { + 'value': 'Settings, Knowledge Base Categories' + }, + 'view_name': { + 'type': type(None), + }, + 'view_serializer_name': { + 'type': type(None), + } + } + + + +class knowledgebaseCategoryViewsetInheritedCases( + ViewsetTestCases, +): + pass + + + +@pytest.mark.module_assistance +class knowledgebaseCategoryViewsetPyTest( + ViewsetTestCases, +): + + pass diff --git a/app/assistance/tests/unit/knowledge_base_category/unit_knowledge_base_category_viewset.py b/app/assistance/tests/unit/knowledge_base_category/unit_knowledge_base_category_viewset.py deleted file mode 100644 index 26cd3aad..00000000 --- a/app/assistance/tests/unit/knowledge_base_category/unit_knowledge_base_category_viewset.py +++ /dev/null @@ -1,42 +0,0 @@ -from django.test import Client, TestCase - -from rest_framework.reverse import reverse - -from api.tests.unit.test_unit_common_viewset import ModelViewSetInheritedCases - -from assistance.viewsets.knowledge_base_category import ViewSet - - - -@pytest.mark.skip(reason = 'see #895, tests being refactored') -class KnowledgeBaseViewsetList( - ModelViewSetInheritedCases, - TestCase, -): - - viewset = ViewSet - - route_name = 'v2:_api_v2_knowledge_base_category' - - - @classmethod - def setUpTestData(self): - """Setup Test - - 1. make list request - """ - - - super().setUpTestData() - - - client = Client() - - url = reverse( - self.route_name + '-list', - kwargs = self.kwargs - ) - - client.force_login(self.view_user) - - self.http_options_response_list = client.options(url) diff --git a/app/assistance/tests/unit/model_knowledge_base_article/test_unit_model_knowledge_base_article_viewset.py b/app/assistance/tests/unit/model_knowledge_base_article/test_unit_model_knowledge_base_article_viewset.py index b7f49a22..60e00391 100644 --- a/app/assistance/tests/unit/model_knowledge_base_article/test_unit_model_knowledge_base_article_viewset.py +++ b/app/assistance/tests/unit/model_knowledge_base_article/test_unit_model_knowledge_base_article_viewset.py @@ -11,7 +11,7 @@ from assistance.viewsets.model_knowledge_base_article import ViewSet from itam.models.device import Device -@pytest.mark.skip(reason = 'see #895, tests being refactored') +@pytest.mark.skip(reason = 'see #895 #903, tests being refactored') class ModelKnowledgeBaseArticleViewsetList( ModelViewSetInheritedCases, TestCase, diff --git a/app/assistance/viewsets/index.py b/app/assistance/viewsets/index.py index 804ffdb5..06cef2e0 100644 --- a/app/assistance/viewsets/index.py +++ b/app/assistance/viewsets/index.py @@ -25,7 +25,7 @@ class Index(IndexViewset): return Response( { - "knowledge_base": reverse('v2:_api_v2_knowledge_base-list', request=request), + "knowledge_base": reverse('v2:_api_knowledgebase-list', request=request), "request": reverse('v2:_api_v2_ticket_request-list', request=request), } ) diff --git a/app/centurion/tests/functional_models.py b/app/centurion/tests/functional_models.py index 29c80f71..53f66dba 100644 --- a/app/centurion/tests/functional_models.py +++ b/app/centurion/tests/functional_models.py @@ -1,5 +1,7 @@ import pytest +from django.db import models + @pytest.mark.models @@ -23,25 +25,70 @@ class ModelTestCases: """ - @pytest.fixture( scope = 'function' ) - def created_model(self, django_db_blocker, model, model_kwargs): + @pytest.fixture( scope = 'function') + def created_model(self, request, django_db_blocker, + model, model_kwargs, mocker, model_user, kwargs_user + ): - model_object = None + item = None if not model._meta.abstract: with django_db_blocker.unblock(): - model_object = model.objects.create( - **model_kwargs + 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 + }) + + + context_user = mocker.patch.object( + model, 'context' + ) + user = model_user.objects.create( **kwargs_user ) + context_user.__getitem__.side_effect = { + 'logger': None, + 'user': user + }.__getitem__ + + item = model.objects.create( + **kwargs ) - yield model_object + for key, value in kwargs_many_to_many.items(): - with django_db_blocker.unblock(): + field = getattr(item, key) - if model_object: - model_object.delete() + for entry in value: + + field.add(entry) + + request.cls.item = item + + yield item + + if item: + + with django_db_blocker.unblock(): + + item.delete() + + user.delete() diff --git a/app/conftest.py b/app/conftest.py index 631ad81b..d11dfcc9 100644 --- a/app/conftest.py +++ b/app/conftest.py @@ -685,7 +685,11 @@ def recursearray() -> dict[dict, str, any]: print( f'Index {keys[1]} does not exist. List had a length of {len(v)}', file = sys.stderr ) - return None + return { + 'obj': obj, + 'key': key, + 'value': '-value-does_not-exist-' + } except ValueError: diff --git a/app/core/tests/functional/centurion_abstract/test_functional_centurion_abstract_model.py b/app/core/tests/functional/centurion_abstract/test_functional_centurion_abstract_model.py index 93800602..63eb70b3 100644 --- a/app/core/tests/functional/centurion_abstract/test_functional_centurion_abstract_model.py +++ b/app/core/tests/functional/centurion_abstract/test_functional_centurion_abstract_model.py @@ -39,7 +39,7 @@ class CenturionAbstractModelTestCases( assert history_model.__name__ == model().get_history_model_name() - + @pytest.mark.skip( reason = 'test must be as part of serializer and viewset tests, not model' ) def test_model_create_has_history_entry(self, model_contenttype, created_model, model): """Model Created diff --git a/app/devops/tests/functional/git_group/test_functional_git_group_model.py b/app/devops/tests/functional/git_group/test_functional_git_group_model.py index e3d140bd..229895cc 100644 --- a/app/devops/tests/functional/git_group/test_functional_git_group_model.py +++ b/app/devops/tests/functional/git_group/test_functional_git_group_model.py @@ -16,17 +16,6 @@ class GitGroupModelTestCases( ): - @pytest.mark.skip( reason = 'test must be as part of serializer and viewset tests, not model' ) - def test_model_create_has_history_entry(self, model_contenttype, created_model, model): - """Model Created - - Ensure that the model when created, added a `create` Audit History - entry. - """ - - pass - - def test_model_create_with_parent_sets_tenancy(self, created_model, model, model_kwargs): """Model Created diff --git a/app/tests/fixtures/__init__.py b/app/tests/fixtures/__init__.py index cac1d618..3402203f 100644 --- a/app/tests/fixtures/__init__.py +++ b/app/tests/fixtures/__init__.py @@ -181,11 +181,13 @@ from .model_itamassetbase import ( from .model_knowledgebase import ( kwargs_knowledgebase, model_knowledgebase, + serializer_knowledgebase, ) from .model_knowledgebasecategory import ( kwargs_knowledgebasecategory, model_knowledgebasecategory, + serializer_knowledgebasecategory, ) from .model_kwarg_data import ( diff --git a/app/tests/fixtures/model_knowledgebase.py b/app/tests/fixtures/model_knowledgebase.py index 60721533..c18dc2fd 100644 --- a/app/tests/fixtures/model_knowledgebase.py +++ b/app/tests/fixtures/model_knowledgebase.py @@ -2,6 +2,11 @@ import datetime import pytest from assistance.models.knowledge_base import KnowledgeBase +from assistance.serializers.knowledge_base import ( + KnowledgeBaseBaseSerializer, + KnowledgeBaseModelSerializer, + KnowledgeBaseViewSerializer +) @@ -65,3 +70,13 @@ def kwargs_knowledgebase(django_db_blocker, pass category.delete() + + +@pytest.fixture( scope = 'class') +def serializer_knowledgebase(): + + yield { + 'base': KnowledgeBaseBaseSerializer, + 'model': KnowledgeBaseModelSerializer, + 'view': KnowledgeBaseViewSerializer + } diff --git a/app/tests/fixtures/model_knowledgebasecategory.py b/app/tests/fixtures/model_knowledgebasecategory.py index 4f624468..5ad10174 100644 --- a/app/tests/fixtures/model_knowledgebasecategory.py +++ b/app/tests/fixtures/model_knowledgebasecategory.py @@ -2,6 +2,11 @@ import datetime import pytest from assistance.models.knowledge_base_category import KnowledgeBaseCategory +from assistance.serializers.knowledge_base_category import ( + KnowledgeBaseCategoryBaseSerializer, + KnowledgeBaseCategoryModelSerializer, + KnowledgeBaseCategoryViewSerializer +) @@ -37,3 +42,13 @@ def kwargs_knowledgebasecategory(django_db_blocker, kwargs_centurionmodel, model with django_db_blocker.unblock(): user.delete() + + +@pytest.fixture( scope = 'class') +def serializer_knowledgebasecategory(): + + yield { + 'base': KnowledgeBaseCategoryBaseSerializer, + 'model': KnowledgeBaseCategoryModelSerializer, + 'view': KnowledgeBaseCategoryViewSerializer + }