test(access): Unit Model Tests for TenancyAbstractModel

ref: #770 #767 #445
This commit is contained in:
2025-05-24 00:35:29 +09:30
parent 28117bb2bb
commit 6f0ef8ab31
4 changed files with 979 additions and 14 deletions

View File

@ -1,5 +1,7 @@
import pytest
from django.apps import apps
from centurion.tests.unit_class import ClassTestCases
@ -74,6 +76,7 @@ class ModelTestCases(
class Meta:
app_label = 'core'
verbose_name = 'mock instance'
managed = False
instance = MockModel()
@ -83,6 +86,10 @@ class ModelTestCases(
yield instance
del instance
del apps.all_models['core']['mockmodel']
@pytest.fixture( scope = 'class', autouse = True)

View File

@ -1,14 +1,14 @@
# import pytest
import pytest
# from core.models.centurion import CenturionModel
from core.models.centurion import CenturionModel
# @pytest.fixture( scope = 'class')
# def model(request):
@pytest.fixture( scope = 'class')
def model(request):
# request.cls.model = CenturionModel
request.cls.model = CenturionModel
# yield request.cls.model
yield request.cls.model
# del request.cls.model
del request.cls.model

View File

@ -1,13 +1,208 @@
import inspect
import pytest
from django.db import models
from django.core.exceptions import (
ValidationError
)
from django.utils.timezone import now
from access.tests.unit.tenancy_abstract.test_unit_tenancy_abstract_model import (
TenancyAbstractModelInheritedCases
)
from core.models.centurion import CenturionModel
@pytest.mark.models
class CenturionAbstractModelTestCases:
@pytest.mark.unit
class CenturionAbstractModelTestCases(
TenancyAbstractModelInheritedCases
):
# @pytest.fixture( scope = 'class', autouse = True)
# def setup_class(cls, model):
# pass
pass
kwargs_create_item = {
'model_notes': 'model notes txt',
'created': '2025-05-23T00:00',
}
parameterized_class_attributes = {
'_audit_enabled': {
'type': bool,
'value': True,
},
'_is_submodel': {
'type': bool,
'value': False,
},
'_notes_enabled': {
'type': bool,
'value': True,
},
'context': {
'type': dict,
'value': {
'logger': None,
'user': None,
}
}
}
parameterized_model_fields = {
'id': {
'blank': True,
'default': models.fields.NOT_PROVIDED,
'field_type': models.IntegerField,
'null': False,
'unique': True,
},
'model_notes': {
'blank': True,
'default': models.fields.NOT_PROVIDED,
'field_type': models.TextField,
'null': True,
'unique': False,
},
'created': {
'blank': False,
'default': now,
'field_type': models.IntegerField,
'null': False,
'unique': False,
},
}
def test_class_inherits_centurion_model(self, model):
""" Class Check
Ensure this model inherits from `CenturionModel`
"""
assert issubclass(model, CenturionModel)
def test_method_centurion_delete_called(self, mocker, model_instance):
"""Test Class Method
Ensure method `core.models.centurion.CenturionModel.delete()` is called
when `model.delete()` is called.
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
delete = mocker.patch('core.models.centurion.CenturionModel.delete', return_value = None)
model_instance.delete()
delete.assert_called_once()
def test_method_centurion_save_called(self, mocker, model_instance):
"""Test Class Method
Ensure method `core.models.centurion.CenturionModel.save()` is called
when `model.save()` is called.
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
save = mocker.patch('core.models.centurion.CenturionModel.save', return_value = None)
model_instance.save()
save.assert_called_once()
def test_method_delete_calls_super_keep_parent_matches_is_sub_model(self, mocker, model_instance):
"""Test Class Method
Ensure when method `delete` calls `super().delete` attribute
`keep_parents` is the same value as `model._is_submodel`
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
super_delete = mocker.patch('django.db.models.base.Model.delete', return_value = None)
mocker.patch('core.models.centurion.CenturionModel.get_audit_values', return_value = {'key': 'value'})
model_instance.delete()
super_delete.assert_called_with(using = None, keep_parents = model_instance._is_submodel)
def test_method_get_history_model_name_returns__AuditModelName(self, mocker, model_instance):
"""Test Class Method
Ensure method `get_history_model_name` returns the value of the models
audit name `<Model Class name>AuditHistory`
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
test_value = f'{model_instance._meta.object_name}AuditHistory'
assert model_instance.get_history_model_name() == test_value
def test_method_validate_field_not_none_raises_exception(self, model):
""" Test Class Method
Ensure that method `validate_field_not_none` raises a validation error
when it's passed a value of `None`
"""
with pytest.raises(ValidationError) as e:
model.validate_field_not_none(None)
assert e.value.code == 'field_value_not_none'
def test_method_validate_field_not_none_no_exception(self, model):
""" Test Class Method
Ensure that method `validate_field_not_none` does not raise an
exception when the value is not `None`.
"""
assert model.validate_field_not_none('a value') == None
@ -15,11 +210,769 @@ class CenturionAbstractModelInheritedCases(
CenturionAbstractModelTestCases,
):
pass
def test_model_creation(self, model):
model_object = model.objects.create(
**self.kwargs_create_item
)
assert type(model_object.id) is int
class CenturionAbstractModelPyTest(
CenturionAbstractModelTestCases,
):
pass
def test_model_is_abstract(self, model):
assert model._meta.abstract
def test_method_clean_calls_super_clean(self, mocker, model_instance):
"""Test Class Method
Ensure method `clean` calls `super().clean`
"""
super_clean = mocker.patch('django.db.models.base.Model.clean', return_value = None)
model_instance.clean()
super_clean.assert_called_once()
def test_method_clean_fields_calls_super_clean_fields(self, mocker, model_instance):
"""Test Class Method
Ensure method `clean_fields` calls `super().clean_fields`
"""
super_clean_fields = mocker.patch('django.db.models.base.Model.clean_fields', return_value = None)
model_instance.clean_fields()
super_clean_fields.assert_called_once()
def test_method_delete_audit_disabled_no_data_collected(self, mocker, model_instance):
"""Test Class Method
Ensure method `delete` does not call `self.get_audit_values` when
auditing is disabled.
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
model_instance._audit_enabled = False
mocker.patch('django.db.models.base.Model.delete', return_value = None)
get_audit_values = mocker.spy(CenturionModel, 'get_audit_values')
model_instance.delete()
get_audit_values.assert_not_called()
def test_method_delete_audit_enabled_data_collected(self, mocker, model_instance):
"""Test Class Method
Ensure method `delete` calls `self.get_audit_values` when auditing
is enabled.
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
model_instance._audit_enabled = True
mocker.patch('django.db.models.base.Model.delete', return_value = None)
get_audit_values = mocker.patch('core.models.centurion.CenturionModel.get_audit_values', return_value = {'key': 'value'})
model_instance.delete()
get_audit_values.assert_called_once()
def test_method_delete_default_attributes(self, mocker, model_instance):
"""Test Class Method
Ensure method `delete` has the defined default attributes.
"""
sig = inspect.signature(model_instance.delete)
default_keep_parents = sig.parameters['keep_parents'].default
using = sig.parameters['using'].default
assert(
default_keep_parents is None
and using is None
)
def test_method_delete_calls_super_is_sub_model(self, mocker, model_instance):
"""Test Class Method
Ensure method `delete` calls `super().delete` with correct parameters
when model is not a sub-model
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
model_instance._is_submodel = True
super_delete = mocker.patch('django.db.models.base.Model.delete', return_value = None)
mocker.patch('core.models.centurion.CenturionModel.get_audit_values', return_value = {'key': 'value'})
model_instance.delete()
super_delete.assert_called_with(using = None, keep_parents = True)
def test_method_delete_calls_super_not_sub_model(self, mocker, model_instance):
"""Test Class Method
Ensure method `delete` calls `super().delete` with correct parameters
when model is not a sub-model
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
model_instance._is_submodel = False
super_delete = mocker.patch('django.db.models.base.Model.delete', return_value = None)
mocker.patch('core.models.centurion.CenturionModel.get_audit_values', return_value = {'key': 'value'})
model_instance.delete()
super_delete.assert_called_with(using = None, keep_parents = False)
def test_method_delete_is_called_get_audit_values(self, mocker, model_instance):
"""Test Class Method
Ensure that if `model.get_audit_values = True`, method
`self.get_audit_values()` is called.
In the alternate, if `model.get_audit_values = False` method
`self.get_audit_values()` must not be called.
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
super_delete = mocker.patch('django.db.models.base.Model.delete', return_value = None)
get_audit_values = mocker.patch('core.models.centurion.CenturionModel.get_audit_values', return_value = {'key': 'value'})
model_instance.delete()
if model_instance._audit_enabled:
get_audit_values.assert_called_once()
else:
get_audit_values.assert_not_called()
def test_method_full_clean_default_attributes(self, mocker, model_instance):
"""Test Class Method
Ensure method `full_clean` has the defined default attributes.
"""
sig = inspect.signature(model_instance.full_clean)
exclude = sig.parameters['exclude'].default
validate_unique = sig.parameters['validate_unique'].default
validate_constraints = sig.parameters['validate_constraints'].default
assert(
exclude is None
and validate_unique is True
and validate_constraints is True
)
def test_method_full_clean_calls_super_full_clean(self, mocker, model_instance):
"""Test Class Method
Ensure method `full_clean` calls `super().full_clean` with specified
parameters.
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
super_full_clean = mocker.patch('django.db.models.base.Model.full_clean', return_value = None)
model_instance.full_clean()
super_full_clean.assert_called_with(
exclude = None,
validate_unique = True,
validate_constraints = True
)
def test_method_full_clean_calls_clean(self, mocker, model_instance):
"""Test Class Method
Ensure method `full_clean` calls `self.clean`.
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
for field in self.kwargs_create_item:
setattr(model_instance, field, self.kwargs_create_item[field])
clean = mocker.patch('core.models.centurion.CenturionModel.clean', return_value = None)
model_instance.full_clean()
clean.assert_called_once()
def test_method_full_clean_calls_clean_fields(self, mocker, model_instance):
"""Test Class Method
Ensure method `full_clean` calls `self.clean_fields()`.
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
for field in self.kwargs_create_item:
setattr(model_instance, field, self.kwargs_create_item[field])
clean_fields = mocker.patch('core.models.centurion.CenturionModel.clean_fields', return_value = None)
model_instance.full_clean()
clean_fields.assert_called_once()
def test_method_full_clean_calls_validate_constraints(self, mocker, model_instance):
"""Test Class Method
Ensure method `full_clean` calls `self.validate_constraints()`
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
for field in self.kwargs_create_item:
setattr(model_instance, field, self.kwargs_create_item[field])
validate_constraints = mocker.patch('core.models.centurion.CenturionModel.validate_constraints', return_value = None)
model_instance.full_clean()
validate_constraints.assert_called_once()
def test_method_full_clean_calls_validate_unique(self, mocker, model_instance):
"""Test Class Method
Ensure method `full_clean` calls `self.validate_unique()`
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
for field in self.kwargs_create_item:
setattr(model_instance, field, self.kwargs_create_item[field])
validate_unique = mocker.patch('core.models.centurion.CenturionModel.validate_unique', return_value = None)
model_instance.full_clean()
validate_unique.assert_called_once()
def test_method_get_audit_values_clean_model_returns_empty_dict(self, mocker, model_instance):
"""Test Class Method
Ensure method `get_audit_values` returns an empty dict if `id = None`.
(is an empty model)
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
assert model_instance.get_audit_values() == {}
def test_method_get_audit_values_clean_model_returns_fields_only(self, mocker, model_instance):
"""Test Class Method
Ensure method `get_audit_values` returns All model fields as a dict
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
model_instance.id = 1
for field in self.kwargs_create_item:
setattr(model_instance, field, self.kwargs_create_item[field])
method_values = model_instance.get_audit_values()
assert method_values == {
'id': model_instance.id,
**self.kwargs_create_item,
} # Correct Values Returned
assert len(method_values) == len(model_instance._meta.fields) # Fail-Safe to ensure test writer fills all fields
def test_method_get_after_returns__after(self, mocker, model_instance):
"""Test Class Method
Ensure method `get_after` returns the value of `model._after`
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
test_value = {
'key_after': 'value_after'
}
model_instance._after = test_value
assert model_instance.get_after() == test_value
def test_method_get_before_returns__before(self, mocker, model_instance):
"""Test Class Method
Ensure method `get_after` returns the value of `model._before`
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
test_value = {
'key_before': 'value_before'
}
model_instance._before = test_value
assert model_instance.get_before() == test_value
def test_method_get_url_default_attributes(self, mocker, model_instance):
"""Test Class Method
Ensure method `get_url` has the defined default attributes.
"""
sig = inspect.signature(model_instance.get_url)
relative = sig.parameters['relative'].default
api_version = sig.parameters['api_version'].default
assert(
relative == False
and api_version == 2
)
def test_method_get_url_called_reverse(self, mocker, model_instance):
"""Test Class Method
Ensure method `get_url` calls reverse
"""
reverse = mocker.patch('rest_framework.reverse._reverse', return_value = None)
model_instance.id = 1
url_basename = f'v2:_api_{model_instance._meta.model_name}-detail'
url = model_instance.get_url()
reverse.assert_called_with( url_basename, None, { 'pk': model_instance.id }, None, None )
def test_method_save_audit_enabled_sets__after_create_model(self, mocker, model_instance):
"""Test Class Method
Ensure method `save` sets attribute `self._after` to the value of the
`self.get_audit_values()`.
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
model_instance._audit_enabled = True
mocker.patch('django.db.models.base.Model.save', return_value = None)
mocker.patch('core.models.centurion.CenturionModel.full_clean', return_value = None)
test_value = {
'save_before_key': 'save_before_value'
}
mocker.patch('core.models.centurion.CenturionModel.get_audit_values', return_value = test_value)
model_instance.save()
assert model_instance._after == test_value
def test_method_save_audit_enabled_sets__before_create_model(self, mocker, model_instance):
"""Test Class Method
Ensure method `save` sets attribute `self._before` with an empty dict for new model
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
model_instance._audit_enabled = True
mocker.patch('django.db.models.base.Model.save', return_value = None)
mocker.patch('core.models.centurion.CenturionModel.full_clean', return_value = None)
mocker.patch('core.models.centurion.CenturionModel.get_audit_values', return_value = None)
model_instance.save()
assert model_instance._before == {}
def test_method_save_audit_enabled_sets__after_update_model(self, mocker, model_instance):
"""Test Class Method
Ensure method `save` sets attribute `self._after` to the value of the
`self.get_audit_values()`.
"""
class MockManager:
class MockObj:
def get_audit_values(self):
return {}
def get(self, *args, **kwargs):
return self.MockObj()
model_instance.objects = MockManager()
model_instance.id = 1
model_instance._audit_enabled = True
mocker.patch('django.db.models.base.Model.save', return_value = None)
mocker.patch('core.models.centurion.CenturionModel.full_clean', return_value = None)
test_value = {
'save_before_key': 'save_before_value'
}
mocker.patch('core.models.centurion.CenturionModel.get_audit_values', return_value = test_value)
model_instance.save()
assert model_instance._after == test_value
def test_method_save_audit_enabled_sets__before_update_model(self, mocker, model_instance):
"""Test Class Method
Ensure method `save` sets attribute `self._before` to field values
before using method `model.get_audit_values()`.
"""
test_value = {
'save_before_key': 'save_before_value'
}
class MockManager:
class MockObj:
def get_audit_values(self):
return test_value
def get(self, *args, **kwargs):
return self.MockObj()
model_instance.objects = MockManager()
model_instance.id = 1
model_instance._audit_enabled = True
mocker.patch('django.db.models.base.Model.save', return_value = None)
mocker.patch('core.models.centurion.CenturionModel.full_clean', return_value = None)
mocker.patch('core.models.centurion.CenturionModel.get_audit_values', return_value = None)
model_instance.save()
assert model_instance._before == test_value
def test_method_save_default_attributes(self, mocker, model_instance):
"""Test Class Method
Ensure method `get_url` has the defined default attributes.
"""
sig = inspect.signature(model_instance.save)
force_insert = sig.parameters['force_insert'].default
force_update = sig.parameters['force_update'].default
using = sig.parameters['using'].default
update_fields = sig.parameters['update_fields'].default
assert(
force_insert == False
and force_update == False
and using == None
and update_fields == None
)
def test_method_save_calls_full_clean(self, mocker, model_instance):
"""Test Class Method
Ensure method `save` calls `self.full_clean` with the defined attributes.
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
model_instance._audit_enabled = False
mocker.patch('django.db.models.base.Model.save', return_value = None)
full_clean = mocker.patch('core.models.centurion.CenturionModel.full_clean', return_value = None)
model_instance.save()
full_clean.assert_called_with(
exclude = None,
validate_unique = True,
validate_constraints = True
)
def test_method_save_calls_super_save(self, mocker, model_instance):
"""Test Class Method
Ensure method `save` calls `super().save` with the defined attributes.
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
model_instance._audit_enabled = False
super_save = mocker.patch('django.db.models.base.Model.save', return_value = None)
mocker.patch('core.models.centurion.CenturionModel.full_clean', return_value = None)
model_instance.save()
super_save.assert_called_with(
force_insert = False,
force_update = False,
using = None,
update_fields = None
)
def test_method_save_audit_enabled_calls_get_audit_values_create_model(self, mocker, model_instance):
"""Test Class Method
Ensure method `save` calls `self.get_audit_values()` with the defined attributes.
"""
class MockManager:
def get(*args, **kwargs):
return model_instance
model_instance.objects = MockManager()
model_instance._audit_enabled = True
mocker.patch('django.db.models.base.Model.save', return_value = None)
mocker.patch('core.models.centurion.CenturionModel.full_clean', return_value = None)
get_audit_values = mocker.patch('core.models.centurion.CenturionModel.get_audit_values', return_value = None)
model_instance.save()
get_audit_values.assert_called_with()
def test_method_validate_constraints_calls_super_validate_constraints(self, mocker, model_instance):
"""Test Class Method
Ensure method `validate_constraints` calls `super().validate_constraints`
"""
super_validate_constraints = mocker.patch('django.db.models.base.Model.validate_constraints', return_value = None)
model_instance.validate_constraints()
super_validate_constraints.assert_called_once()
def test_method_validate_unique_fields_calls_super_validate_unique(self, mocker, model_instance):
"""Test Class Method
Ensure method `validate_unique` calls `super().validate_unique`
"""
super_validate_unique = mocker.patch('django.db.models.base.Model.validate_unique', return_value = None)
model_instance.validate_unique()
super_validate_unique.assert_called_once()

View File

@ -1040,6 +1040,11 @@ log_cli_level = 'INFO'
log_cli_format = '%(asctime)s %(levelname)s %(message)s'
log_cli_date_format = '%Y-%m-%d %H:%M:%S'
markers = [
"models: Selects tests for all models",
"unit: All Unit Tests",
]
# testpaths = [
# 'tests/*.py',
# # 'tests/functional/**/*.py',