165
app/api/tests/functional/test_functional_meta_permissions_api.py
Normal file
165
app/api/tests/functional/test_functional_meta_permissions_api.py
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from api.tests.functional.test_functional_permissions_api import (
|
||||||
|
APIPermissionsInheritedCases
|
||||||
|
)
|
||||||
|
|
||||||
|
from core.models.centurion import CenturionModel
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_models( excludes: list[ str ] = [] ) -> list[ tuple ]:
|
||||||
|
"""Fetch models from Centurion Apps
|
||||||
|
|
||||||
|
Args:
|
||||||
|
excludes (list[ str ]): Words that may be in a models name to exclude
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[ tuple ]: Centurion ERP Only models
|
||||||
|
"""
|
||||||
|
|
||||||
|
models: list = []
|
||||||
|
|
||||||
|
model_apps: list = []
|
||||||
|
|
||||||
|
exclude_model_apps = [
|
||||||
|
'django',
|
||||||
|
'django_celery_results',
|
||||||
|
'django_filters',
|
||||||
|
'drf_spectacular',
|
||||||
|
'drf_spectacular_sidecar',
|
||||||
|
'coresheaders',
|
||||||
|
'corsheaders',
|
||||||
|
'rest_framework',
|
||||||
|
'rest_framework_json_api',
|
||||||
|
'social_django',
|
||||||
|
]
|
||||||
|
|
||||||
|
for app in settings.INSTALLED_APPS:
|
||||||
|
|
||||||
|
app = app.split('.')[0]
|
||||||
|
|
||||||
|
if app in exclude_model_apps:
|
||||||
|
continue
|
||||||
|
|
||||||
|
model_apps += [ app ]
|
||||||
|
|
||||||
|
|
||||||
|
for model in apps.get_models():
|
||||||
|
|
||||||
|
if model._meta.app_label not in model_apps:
|
||||||
|
continue
|
||||||
|
|
||||||
|
skip = False
|
||||||
|
|
||||||
|
for exclude in excludes:
|
||||||
|
|
||||||
|
if exclude in str(model._meta.model_name):
|
||||||
|
skip = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if skip:
|
||||||
|
continue
|
||||||
|
|
||||||
|
models += [ model ]
|
||||||
|
|
||||||
|
return models
|
||||||
|
|
||||||
|
|
||||||
|
def make_fixture_with_args(arg_names, func, decorator_factory=None, decorator_args=None):
|
||||||
|
args_str = ", ".join(arg_names)
|
||||||
|
|
||||||
|
src = f"""
|
||||||
|
@decorator_factory(**decorator_args)
|
||||||
|
def _generated(self, {args_str}):
|
||||||
|
yield from func(self, {args_str})
|
||||||
|
"""
|
||||||
|
|
||||||
|
local_ns = {}
|
||||||
|
global_ns = {
|
||||||
|
"func": func,
|
||||||
|
"decorator_factory": decorator_factory,
|
||||||
|
"decorator_args": decorator_args,
|
||||||
|
}
|
||||||
|
|
||||||
|
exec(src, global_ns, local_ns)
|
||||||
|
return local_ns["_generated"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def model(self, model__model_name):
|
||||||
|
|
||||||
|
yield model__model_name
|
||||||
|
|
||||||
|
|
||||||
|
def model_kwargs(self, request, kwargs__model_name):
|
||||||
|
|
||||||
|
request.cls.kwargs_create_item = kwargs__model_name.copy()
|
||||||
|
|
||||||
|
yield kwargs__model_name.copy()
|
||||||
|
|
||||||
|
if hasattr(request.cls, 'kwargs_create_item'):
|
||||||
|
del request.cls.kwargs_create_item
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class APIPermissionsTestCases(
|
||||||
|
APIPermissionsInheritedCases
|
||||||
|
):
|
||||||
|
"""API Permission Test Cases
|
||||||
|
|
||||||
|
This test suite is dynamically created for `CenturionModel` sub-classes.
|
||||||
|
Each `CenturionModel` must ensure their model fixture exists in
|
||||||
|
`tests/fixtures/model_<model_name>` with fixtures `model_<model_name>` and
|
||||||
|
`kwargs_<model_name>` defined.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
for centurion_model in get_models(
|
||||||
|
excludes = [
|
||||||
|
'centurionaudit',
|
||||||
|
'history',
|
||||||
|
'centurionmodelnote',
|
||||||
|
'notes'
|
||||||
|
]
|
||||||
|
):
|
||||||
|
|
||||||
|
if(
|
||||||
|
not issubclass(centurion_model, CenturionModel)
|
||||||
|
or centurion_model == CenturionModel
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
model_name = centurion_model._meta.model_name
|
||||||
|
cls_name: str = f"{centurion_model._meta.object_name}APIPermissionsPyTest"
|
||||||
|
|
||||||
|
dynamic_class = type(
|
||||||
|
cls_name,
|
||||||
|
(APIPermissionsTestCases,),
|
||||||
|
{
|
||||||
|
'model': make_fixture_with_args(
|
||||||
|
arg_names = ['model_' + str(centurion_model._meta.model_name) ],
|
||||||
|
func = model,
|
||||||
|
decorator_factory = pytest.fixture,
|
||||||
|
decorator_args = {'scope': 'class'}
|
||||||
|
|
||||||
|
),
|
||||||
|
'model_kwargs': make_fixture_with_args(
|
||||||
|
arg_names = ['request', f'kwargs_{model_name}' ],
|
||||||
|
func = model_kwargs,
|
||||||
|
decorator_factory = pytest.fixture,
|
||||||
|
decorator_args = {'scope': 'class', 'autouse': True}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
model_mark = f'model_{model_name}'
|
||||||
|
dynamic_class = pytest.mark.__getattr__(model_mark)(dynamic_class)
|
||||||
|
|
||||||
|
globals()[cls_name] = dynamic_class
|
@ -1,27 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from api.tests.functional.test_functional_permissions_api import (
|
|
||||||
APIPermissionsInheritedCases,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.model_gitgroup
|
|
||||||
class GitGroupPermissionsAPITestCases(
|
|
||||||
APIPermissionsInheritedCases,
|
|
||||||
):
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class GitGroupPermissionsAPIInheritedCases(
|
|
||||||
GitGroupPermissionsAPITestCases,
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class GitGroupPermissionsAPIPyTest(
|
|
||||||
GitGroupPermissionsAPITestCases,
|
|
||||||
):
|
|
||||||
|
|
||||||
pass
|
|
@ -8,45 +8,12 @@ from core.tests.unit.centurion_abstract.test_unit_centurion_abstract_model impor
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.model_feature_flag
|
@pytest.mark.model_featureflag
|
||||||
class FeatureFlagModelTestCases(
|
class FeatureFlagModelTestCases(
|
||||||
CenturionAbstractModelInheritedCases
|
CenturionAbstractModelInheritedCases
|
||||||
):
|
):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture( scope = 'class', autouse = True )
|
|
||||||
def software_setup(self, request, django_db_blocker, organization_one):
|
|
||||||
|
|
||||||
from itam.models.software import Software
|
|
||||||
|
|
||||||
with django_db_blocker.unblock():
|
|
||||||
|
|
||||||
software = Software.objects.create(
|
|
||||||
organization = organization_one,
|
|
||||||
name = 'software test'
|
|
||||||
)
|
|
||||||
|
|
||||||
request.cls.kwargs_create_item.update({
|
|
||||||
'software': software
|
|
||||||
})
|
|
||||||
|
|
||||||
yield
|
|
||||||
|
|
||||||
with django_db_blocker.unblock():
|
|
||||||
|
|
||||||
software.delete()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
kwargs_create_item = {
|
|
||||||
'software': None,
|
|
||||||
'name': 'a name',
|
|
||||||
'description': ' a description',
|
|
||||||
'enabled': True,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parameterized_class_attributes(self):
|
def parameterized_class_attributes(self):
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class SoftwareItemTicketAPI(
|
|||||||
|
|
||||||
self.url_view_kwargs = {
|
self.url_view_kwargs = {
|
||||||
'item_class': self.item_class,
|
'item_class': self.item_class,
|
||||||
'item_id': self.item.id,
|
'item_id': self.linked_item.id,
|
||||||
'pk': self.item.id,
|
'pk': self.item.id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
9
app/tests/fixtures/__init__.py
vendored
9
app/tests/fixtures/__init__.py
vendored
@ -62,6 +62,15 @@ from .model_permission import (
|
|||||||
model_permission,
|
model_permission,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .model_software import (
|
||||||
|
kwargs_software,
|
||||||
|
model_software,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .model_softwareenablefeatureflag import (
|
||||||
|
model_softwareenablefeatureflag,
|
||||||
|
)
|
||||||
|
|
||||||
from .model_team import (
|
from .model_team import (
|
||||||
model_team,
|
model_team,
|
||||||
)
|
)
|
||||||
|
2
app/tests/fixtures/kwargs_api_create.py
vendored
2
app/tests/fixtures/kwargs_api_create.py
vendored
@ -1,5 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'class')
|
@pytest.fixture(scope = 'class')
|
||||||
def kwargs_api_create(django_db_blocker, model_kwargs):
|
def kwargs_api_create(django_db_blocker, model_kwargs):
|
||||||
|
2
app/tests/fixtures/model_centurionmodel.py
vendored
2
app/tests/fixtures/model_centurionmodel.py
vendored
@ -16,7 +16,7 @@ def kwargs_centurionmodel(kwargs_tenancyabstract):
|
|||||||
kwargs = {
|
kwargs = {
|
||||||
**kwargs_tenancyabstract,
|
**kwargs_tenancyabstract,
|
||||||
'model_notes': 'model notes txt',
|
'model_notes': 'model notes txt',
|
||||||
'created': '2025-05-23T00:00',
|
'created': '2025-05-23T00:00Z',
|
||||||
}
|
}
|
||||||
|
|
||||||
yield kwargs.copy()
|
yield kwargs.copy()
|
||||||
|
42
app/tests/fixtures/model_featureflag.py
vendored
42
app/tests/fixtures/model_featureflag.py
vendored
@ -11,17 +11,37 @@ def model_featureflag(request):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture( scope = 'class')
|
@pytest.fixture( scope = 'class')
|
||||||
def kwargs_featureflag(kwargs_centurionmodel):
|
def kwargs_featureflag(django_db_blocker, kwargs_centurionmodel, model_software, kwargs_software, model_softwareenablefeatureflag):
|
||||||
|
|
||||||
# kwargs = kwargs_centurionmodel.copy()
|
|
||||||
# del kwargs['model_notes']
|
|
||||||
|
|
||||||
kwargs = {
|
with django_db_blocker.unblock():
|
||||||
**kwargs_centurionmodel.copy(),
|
|
||||||
'software': None,
|
|
||||||
'name': 'a name',
|
|
||||||
'description': ' a description',
|
|
||||||
'enabled': True,
|
|
||||||
}
|
|
||||||
|
|
||||||
yield kwargs.copy()
|
kwargs = kwargs_software
|
||||||
|
|
||||||
|
kwargs.update({'name': 'ff_enable_software'})
|
||||||
|
|
||||||
|
software = model_software.objects.create(
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
enable_feature_flag = model_softwareenablefeatureflag.objects.create(
|
||||||
|
organization = kwargs_centurionmodel['organization'],
|
||||||
|
software = software,
|
||||||
|
enabled = True
|
||||||
|
)
|
||||||
|
|
||||||
|
kwargs = {
|
||||||
|
**kwargs_centurionmodel.copy(),
|
||||||
|
'software': software,
|
||||||
|
'name': 'a name',
|
||||||
|
'description': ' a description',
|
||||||
|
'enabled': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
yield kwargs.copy()
|
||||||
|
|
||||||
|
enable_feature_flag.delete()
|
||||||
|
try:
|
||||||
|
software.delete()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
5
app/tests/fixtures/model_instance.py
vendored
5
app/tests/fixtures/model_instance.py
vendored
@ -79,7 +79,10 @@ def model_instance(django_db_blocker, model_user, model, model_kwargs):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
model_obj.delete()
|
try:
|
||||||
|
model_obj.delete()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
if 'mockmodel' in apps.all_models['core']:
|
if 'mockmodel' in apps.all_models['core']:
|
||||||
|
24
app/tests/fixtures/model_software.py
vendored
Normal file
24
app/tests/fixtures/model_software.py
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import datetime
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from itam.models.software import Software
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture( scope = 'class')
|
||||||
|
def model_software(request):
|
||||||
|
|
||||||
|
yield Software
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture( scope = 'class')
|
||||||
|
def kwargs_software(kwargs_centurionmodel):
|
||||||
|
|
||||||
|
random_str = str(datetime.datetime.now(tz=datetime.timezone.utc))
|
||||||
|
|
||||||
|
kwargs = {
|
||||||
|
**kwargs_centurionmodel.copy(),
|
||||||
|
'name': 'software_' + random_str,
|
||||||
|
}
|
||||||
|
|
||||||
|
yield kwargs.copy()
|
10
app/tests/fixtures/model_softwareenablefeatureflag.py
vendored
Normal file
10
app/tests/fixtures/model_softwareenablefeatureflag.py
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from devops.models.software_enable_feature_flag import SoftwareEnableFeatureFlag
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture( scope = 'class')
|
||||||
|
def model_softwareenablefeatureflag(request):
|
||||||
|
|
||||||
|
yield SoftwareEnableFeatureFlag
|
@ -277,18 +277,25 @@ The following Unit test suites exists for models:
|
|||||||
|
|
||||||
- Functional Tests
|
- Functional Tests
|
||||||
|
|
||||||
|
|
||||||
- model `core.tests.functional.centurion_abstract.test_functional_centurion_abstract_model.CenturionAbstractModelInheritedCases`
|
- model `core.tests.functional.centurion_abstract.test_functional_centurion_abstract_model.CenturionAbstractModelInheritedCases`
|
||||||
|
|
||||||
- API Fields Render `api.tests.functional.test_functional_api_fields.APIFieldsInheritedCases`
|
- API Fields Render `api.tests.functional.test_functional_api_fields.APIFieldsInheritedCases`
|
||||||
|
|
||||||
- API Permissions `api.tests.functional.test_functional_api_permissions.<permission type>InheriredCases`
|
|
||||||
|
|
||||||
Generally Test Cases from class `APIPermissionsInheritedCases` will be used as it covers the standard Django Permissions, `add`, `change`, `delete` and `view`.
|
|
||||||
|
|
||||||
!!! info
|
!!! info
|
||||||
If you add a feature you will have to [write the test cases](./testing.md) for that feature if they are not covered by existing test cases.
|
If you add a feature you will have to [write the test cases](./testing.md) for that feature if they are not covered by existing test cases.
|
||||||
|
|
||||||
|
Each model has the following Test Suites auto-magic created:
|
||||||
|
|
||||||
|
- API Permissions checks `api.tests.functional.test_functional_meta_permissions_api`
|
||||||
|
|
||||||
|
_Checks the CRUD permissions against the models API endpoints_
|
||||||
|
|
||||||
|
- Audit History Model checks, `core.tests.unit.centurion_audit_meta.test_unit_meta_audit_history_model`
|
||||||
|
|
||||||
|
_Confirms the model has a [`AuditHistory`](./api/models/audit_history.md) model and other checks as required for an `AuditHistory` model._
|
||||||
|
|
||||||
|
These auto-magic tests require no input and will be created on a model inheriting from [`CenturionModel`](./api/models/centurion.md) and run every time the tests are run.
|
||||||
|
|
||||||
|
|
||||||
## Knowledge Base Article linking
|
## Knowledge Base Article linking
|
||||||
|
|
||||||
|
@ -1071,6 +1071,7 @@ markers = [
|
|||||||
"centurion_models: Selects Centurion models",
|
"centurion_models: Selects Centurion models",
|
||||||
"functional: Selects all Functional tests.",
|
"functional: Selects all Functional tests.",
|
||||||
"meta_models: Selects Meta models",
|
"meta_models: Selects Meta models",
|
||||||
|
"model_featureflag: Feature Flag Model",
|
||||||
"model_gitgroup: Selects tests for model `git group`",
|
"model_gitgroup: Selects tests for model `git group`",
|
||||||
"models: Selects all models tests.",
|
"models: Selects all models tests.",
|
||||||
"note_models: Selects all centurion model note models",
|
"note_models: Selects all centurion model note models",
|
||||||
|
Reference in New Issue
Block a user