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(
|
||||
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
|
||||
def parameterized_class_attributes(self):
|
||||
|
||||
|
@ -60,7 +60,7 @@ class SoftwareItemTicketAPI(
|
||||
|
||||
self.url_view_kwargs = {
|
||||
'item_class': self.item_class,
|
||||
'item_id': self.item.id,
|
||||
'item_id': self.linked_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,
|
||||
)
|
||||
|
||||
from .model_software import (
|
||||
kwargs_software,
|
||||
model_software,
|
||||
)
|
||||
|
||||
from .model_softwareenablefeatureflag import (
|
||||
model_softwareenablefeatureflag,
|
||||
)
|
||||
|
||||
from .model_team import (
|
||||
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
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
@pytest.fixture(scope = 'class')
|
||||
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_tenancyabstract,
|
||||
'model_notes': 'model notes txt',
|
||||
'created': '2025-05-23T00:00',
|
||||
'created': '2025-05-23T00:00Z',
|
||||
}
|
||||
|
||||
yield kwargs.copy()
|
||||
|
28
app/tests/fixtures/model_featureflag.py
vendored
28
app/tests/fixtures/model_featureflag.py
vendored
@ -11,17 +11,37 @@ def model_featureflag(request):
|
||||
|
||||
|
||||
@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']
|
||||
|
||||
with django_db_blocker.unblock():
|
||||
|
||||
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': None,
|
||||
'software': software,
|
||||
'name': 'a name',
|
||||
'description': ' a description',
|
||||
'enabled': True,
|
||||
}
|
||||
|
||||
yield kwargs.copy()
|
||||
|
||||
enable_feature_flag.delete()
|
||||
try:
|
||||
software.delete()
|
||||
except:
|
||||
pass
|
||||
|
3
app/tests/fixtures/model_instance.py
vendored
3
app/tests/fixtures/model_instance.py
vendored
@ -79,7 +79,10 @@ def model_instance(django_db_blocker, model_user, model, model_kwargs):
|
||||
|
||||
else:
|
||||
|
||||
try:
|
||||
model_obj.delete()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
- model `core.tests.functional.centurion_abstract.test_functional_centurion_abstract_model.CenturionAbstractModelInheritedCases`
|
||||
|
||||
- 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
|
||||
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
|
||||
|
||||
|
@ -1071,6 +1071,7 @@ markers = [
|
||||
"centurion_models: Selects Centurion models",
|
||||
"functional: Selects all Functional tests.",
|
||||
"meta_models: Selects Meta models",
|
||||
"model_featureflag: Feature Flag Model",
|
||||
"model_gitgroup: Selects tests for model `git group`",
|
||||
"models: Selects all models tests.",
|
||||
"note_models: Selects all centurion model note models",
|
||||
|
Reference in New Issue
Block a user