Merge pull request #872 from nofusscomputing/2025-07-21

This commit is contained in:
Jon
2025-07-23 06:35:17 +09:30
committed by GitHub
19 changed files with 432 additions and 15 deletions

View File

@ -39,7 +39,7 @@ class TenantModelTestCases(
'name': {
'blank': False,
'default': models.fields.NOT_PROVIDED,
'field_type': models.TextField,
'field_type': models.CharField,
'max_length': 50,
'null': False,
'unique': True,

View File

@ -0,0 +1,24 @@
# Generated by Django 5.1.10 on 2025-07-21 03:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("api", "0003_alter_authtoken_options"),
]
operations = [
migrations.AlterField(
model_name="authtoken",
name="note",
field=models.CharField(
blank=True,
help_text="A note about this token",
max_length=50,
null=True,
verbose_name="Note",
),
),
]

View File

@ -80,7 +80,6 @@ class AuthToken(
note = models.CharField(
blank = True,
default = None,
help_text = 'A note about this token',
max_length = 50,
null= True,
@ -118,10 +117,6 @@ class AuthToken(
modified = AutoLastModifiedField()
history_app_label: str = None
history_model_name: str = None
kb_model_name: str = None
note_basename: str = None
@property
def generate(self) -> str:
@ -145,6 +140,7 @@ class AuthToken(
return self.token
page_layout = []
table_fields: list = [
'note',

View File

@ -0,0 +1,146 @@
import pytest
# import random
from django.test import Client
from django.urls.exceptions import NoReverseMatch
# from rest_framework.permissions import (
# IsAuthenticatedOrReadOnly
# )
class AdditionalTestCases:
def test_permission_add(self, mocker,
model_instance, api_request_permissions,
model_kwargs, kwargs_api_create
):
""" Check correct permission for add
Attempt to add as user with permission
"""
client = Client()
client.force_login( api_request_permissions['user']['add'] )
the_model = model_instance( kwargs_create = self.kwargs_create_item )
context_user = mocker.patch.object(
the_model, 'context'
)
context_user.__getitem__.side_effect = {
'logger': None,
'user': api_request_permissions['user']['add']
}.__getitem__
the_model.user = api_request_permissions['user']['add']
url = the_model.get_url( many = True )
# the_model.delete()
response = client.post(
path = url,
data = kwargs_api_create,
content_type = 'application/json'
)
assert response.status_code == 201, response.content
def test_permission_delete(self, mocker, model_instance, api_request_permissions):
""" Check correct permission for delete
Delete item as user with delete permission
"""
client = Client()
client.force_login( api_request_permissions['user']['delete'] )
kwargs = self.kwargs_create_item
kwargs.update({
'organization': api_request_permissions['tenancy']['user']
})
delete_item = model_instance(
kwargs_create = kwargs
)
context_user = mocker.patch.object(
delete_item, 'context'
)
context_user.__getitem__.side_effect = {
'logger': None,
'user': api_request_permissions['user']['delete']
}.__getitem__
delete_item.user = api_request_permissions['user']['delete']
response = client.delete(
path = delete_item.get_url( many = False ),
)
if response.status_code == 405:
pytest.xfail( reason = 'ViewSet does not have this request method.' )
assert response.status_code == 204, response.content
def test_permission_view(self, mocker, model_instance, api_request_permissions):
""" Check correct permission for view
Attempt to view as user with view permission
"""
client = Client()
client.force_login( api_request_permissions['user']['view'] )
kwargs = self.kwargs_create_item
kwargs.update({
'organization': api_request_permissions['tenancy']['user']
})
view_item = model_instance(
kwargs_create = kwargs
)
context_user = mocker.patch.object(
view_item, 'context'
)
context_user.__getitem__.side_effect = {
'logger': None,
'user': api_request_permissions['user']['view']
}.__getitem__
view_item.user = api_request_permissions['user']['view']
response = client.get(
path = view_item.get_url( many = False )
)
if response.status_code == 405:
pytest.xfail( reason = 'ViewSet does not have this request method.' )
assert response.status_code == 200, response.content
def test_returned_results_only_user_orgs(self):
pytest.mark.xfail( reason = 'This model is not tenancy based. It is user based.' )
def test_returned_data_from_user_and_global_organizations_only(self):
pytest.mark.xfail( reason = 'This model is not tenancy based. It is user based.' )

View File

@ -0,0 +1,19 @@
import pytest
@pytest.fixture( scope = 'class')
def model(model_authtoken):
yield model_authtoken
@pytest.fixture( scope = 'class', autouse = True)
def model_kwargs(request, kwargs_authtoken):
request.cls.kwargs_create_item = kwargs_authtoken.copy()
yield kwargs_authtoken.copy()
if hasattr(request.cls, 'kwargs_create_item'):
del request.cls.kwargs_create_item

View File

@ -0,0 +1,145 @@
import pytest
from django.db import models
from django.utils.timezone import now
from api.models.tokens import AuthToken
from core.tests.unit.mixin_centurion.test_unit_centurion_mixin import CenturionMixnInheritedCases
@pytest.mark.unit
@pytest.mark.centurion_models
class AuthTokenModelTestCases(
CenturionMixnInheritedCases,
):
@property
def parameterized_class_attributes(self):
return {
'_audit_enabled': {
'type': bool,
'value': False,
},
'_is_submodel': {
'type': bool,
'value': False,
},
'_notes_enabled': {
'type': bool,
'value': False,
},
'model_tag': {
'type': models.fields.NOT_PROVIDED,
'value': models.fields.NOT_PROVIDED,
},
'url_model_name': {
'type': type(None),
'value': None,
}
}
@property
def parameterized_model_fields(self):
return {
'id': {
'blank': True,
'default': models.fields.NOT_PROVIDED,
'field_type': models.IntegerField,
'null': False,
'unique': True,
},
'note': {
'blank': True,
'default': models.fields.NOT_PROVIDED,
'field_type': models.CharField,
'length': 50,
'null': True,
'unique': False,
},
'token': {
'blank': False,
'default': models.fields.NOT_PROVIDED,
'field_type': models.CharField,
'length': 64,
'null': False,
'unique': True,
},
'user': {
'blank': False,
'default': models.fields.NOT_PROVIDED,
'field_type': models.ForeignKey,
'null': False,
'unique': False,
},
'expires': {
'blank': False,
'default': models.fields.NOT_PROVIDED,
'field_type': models.DateTimeField,
'null': False,
'unique': False,
},
'created': {
'blank': False,
'default': now,
'field_type': models.DateTimeField,
'null': False,
'unique': False,
},
'modified': {
'blank': False,
'default': now,
'field_type': models.DateTimeField,
'null': False,
'unique': False,
},
}
def test_class_inherits_centurion_model(self, model):
""" Class Check
Ensure this model inherits from `AuthToken`
"""
assert issubclass(model, AuthToken)
def test_method_get_url_kwargs(self, mocker, model_instance, settings):
"""Test Class Method
Ensure method `get_url_kwargs` returns the correct value.
"""
url = model_instance.get_url_kwargs()
assert model_instance.get_url_kwargs() == { 'model_id': model_instance.user.id, 'pk': model_instance.id }
def test_model_tag_defined(self, model):
pytest.mark.xfail( reason = 'model does not use tag' )
class AuthTokenModelInheritedCases(
AuthTokenModelTestCases,
):
pass
class AuthTokenModelPyTest(
AuthTokenModelTestCases,
):
pass

View File

@ -1,3 +1,4 @@
import django
import pytest
from django.apps import apps
@ -153,6 +154,7 @@ class ModelTestCases(
@pytest.mark.regression
def test_model_field_parameter_value_blank(self,
model_instance,
parameterized, param_key_model_fields, param_field_name, param_blank
@ -172,6 +174,7 @@ class ModelTestCases(
@pytest.mark.regression
def test_model_field_parameter_value_default(self,
model_instance,
parameterized, param_key_model_fields, param_field_name, param_default
@ -192,6 +195,7 @@ class ModelTestCases(
@pytest.mark.regression
def test_model_field_parameter_value_null(self,
model_instance,
parameterized, param_key_model_fields, param_field_name, param_null
@ -212,6 +216,7 @@ class ModelTestCases(
@pytest.mark.regression
def test_model_field_parameter_value_unique(self,
model_instance,
parameterized, param_key_model_fields, param_field_name, param_unique
@ -232,6 +237,30 @@ class ModelTestCases(
@pytest.mark.regression
def test_model_field_type(self,
model_instance,
parameterized, param_key_model_fields, param_field_name, param_field_type
):
"""Test Model Field
Ensure field `param_field_type` is of the correct type
"""
if param_field_type is models.fields.NOT_PROVIDED:
pytest.xfail( reason = 'Field not used for model.' )
try:
the_field = model_instance._meta.get_field(param_field_name)
except django.core.exceptions.FieldDoesNotExist:
pytest.mark.xfail( reason = 'Field does not exist for model.' )
assert isinstance(the_field, param_field_type), type(the_field)
@pytest.mark.regression
def test_method_type___str__(self, model, model_instance ):
"""Test Method

View File

@ -117,7 +117,7 @@ class ConfigGroupModelTestCases(
'name': {
'blank': False,
'default': models.fields.NOT_PROVIDED,
'field_type': models.TextField,
'field_type': models.CharField,
'max_length': 50,
'null': False,
'unique': False,

View File

@ -67,7 +67,7 @@ class CenturionAbstractModelTestCases(
'created': {
'blank': False,
'default': now,
'field_type': models.IntegerField,
'field_type': models.DateTimeField,
'null': False,
'unique': False,
},

View File

@ -49,7 +49,7 @@ class CenturionAuditModelTestCases(
'content_type': {
'blank': True,
'default': models.fields.NOT_PROVIDED,
'field_type': models.IntegerField,
'field_type': models.ForeignKey,
'null': False,
'unique': False,
},

View File

@ -93,7 +93,7 @@ class AuditHistoryMetaModelTestCases(
model_kwargs = kwargs_centurionauditmeta.copy()
with django_db_blocker.unblock():
audit_model_kwargs = request.getfixturevalue('kwargs_' + audit_model._meta.model_name)
kwargs = {}

View File

@ -33,7 +33,7 @@ class ManufacturerModelTestCases(
'name': {
'blank': False,
'default': models.fields.NOT_PROVIDED,
'field_type': models.TextField,
'field_type': models.CharField,
'max_length': 50,
'null': False,
'unique': True,

View File

@ -142,7 +142,7 @@ CREATE TABLE IF NOT EXISTS "accounting_assetbase" ("id" integer NOT NULL PRIMARY
CREATE TABLE IF NOT EXISTS "accounting_assetbase_audithistory" ("centurionaudit_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_audithistory" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "accounting_assetbase" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE TABLE IF NOT EXISTS "accounting_assetbase_centurionmodelnote" ("centurionmodelnote_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_centurionmodelnote" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "accounting_assetbase" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE TABLE IF NOT EXISTS "django_admin_log" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "object_id" text NULL, "object_repr" varchar(200) NOT NULL, "action_flag" smallint unsigned NOT NULL CHECK ("action_flag" >= 0), "change_message" text NOT NULL, "content_type_id" integer NULL REFERENCES "django_content_type" ("id") DEFERRABLE INITIALLY DEFERRED, "user_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "action_time" datetime NOT NULL);
CREATE TABLE IF NOT EXISTS "api_authtoken" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "note" varchar(50) NULL, "token" varchar(64) NOT NULL UNIQUE, "expires" datetime NOT NULL, "created" datetime NOT NULL, "modified" datetime NOT NULL, "user_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE TABLE IF NOT EXISTS "api_authtoken" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "token" varchar(64) NOT NULL UNIQUE, "expires" datetime NOT NULL, "created" datetime NOT NULL, "modified" datetime NOT NULL, "user_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "note" varchar(50) NULL);
CREATE TABLE IF NOT EXISTS "itam_itamassetbase" ("assetbase_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "accounting_assetbase" ("id") DEFERRABLE INITIALLY DEFERRED, "itam_type" varchar(30) NOT NULL);
CREATE TABLE IF NOT EXISTS "itam_devicemodel_audithistory" ("centurionaudit_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_audithistory" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "itam_devicemodel" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE TABLE IF NOT EXISTS "itam_devicemodel_centurionmodelnote" ("centurionmodelnote_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_centurionmodelnote" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "itam_devicemodel" ("id") DEFERRABLE INITIALLY DEFERRED);
@ -239,7 +239,7 @@ CREATE TABLE IF NOT EXISTS "social_auth_nonce" ("id" integer NOT NULL PRIMARY KE
CREATE TABLE IF NOT EXISTS "social_auth_usersocialauth" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "provider" varchar(32) NOT NULL, "uid" varchar(255) NOT NULL, "user_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "created" datetime NOT NULL, "modified" datetime NOT NULL, "extra_data" text NOT NULL CHECK ((JSON_VALID("extra_data") OR "extra_data" IS NULL)));
CREATE TABLE IF NOT EXISTS "social_auth_partial" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "token" varchar(32) NOT NULL, "next_step" smallint unsigned NOT NULL CHECK ("next_step" >= 0), "backend" varchar(32) NOT NULL, "timestamp" datetime NOT NULL, "data" text NOT NULL CHECK ((JSON_VALID("data") OR "data" IS NULL)));
DELETE FROM sqlite_sequence;
INSERT INTO sqlite_sequence VALUES('django_migrations',230);
INSERT INTO sqlite_sequence VALUES('django_migrations',231);
INSERT INTO sqlite_sequence VALUES('django_content_type',218);
INSERT INTO sqlite_sequence VALUES('auth_permission',917);
INSERT INTO sqlite_sequence VALUES('auth_group',0);
@ -265,6 +265,7 @@ INSERT INTO sqlite_sequence VALUES('access_entity',0);
INSERT INTO sqlite_sequence VALUES('access_role',0);
INSERT INTO sqlite_sequence VALUES('accounting_assetbase',0);
INSERT INTO sqlite_sequence VALUES('django_admin_log',0);
INSERT INTO sqlite_sequence VALUES('api_authtoken',0);
INSERT INTO sqlite_sequence VALUES('itam_devicetype',0);
INSERT INTO sqlite_sequence VALUES('config_management_configgrouphosts',0);
INSERT INTO sqlite_sequence VALUES('config_management_configgroups',0);

View File

@ -23,6 +23,11 @@ from .model_assetbase import (
model_assetbase,
)
from .model_authtoken import (
kwargs_authtoken,
model_authtoken,
)
from .model_centurionaudit import (
kwargs_centurionaudit,
model_centurionaudit,

42
app/tests/fixtures/model_authtoken.py vendored Normal file
View File

@ -0,0 +1,42 @@
import datetime
import pytest
import random
from api.models.tokens import AuthToken
@pytest.fixture( scope = 'class')
def model_authtoken():
yield AuthToken
@pytest.fixture( scope = 'class')
def kwargs_authtoken(django_db_blocker,
model_authtoken, model_user, kwargs_user
):
random_str = str(datetime.datetime.now(tz=datetime.timezone.utc))
random_str = str(random_str).replace(
' ', '').replace(':', '').replace('+', '').replace('.', '')
with django_db_blocker.unblock():
kwargs = kwargs_user.copy()
kwargs['username'] = 'at_' + str(random.randint(9999,99999))
user = model_user.objects.create( **kwargs )
kwargs = {
'note': 'a note',
'token': model_authtoken().generate,
'user': user,
'expires': '2025-02-25T23:14Z'
}
yield kwargs
with django_db_blocker.unblock():
user.delete()

View File

@ -82,7 +82,13 @@ def model_instance(django_db_blocker, model_kwarg_data, model, model_kwargs):
if(
model is not Tenant
and org is not None
and (
org is not None
or (
'organization' not in model_kwargs
and 'organization' not in kwargs_create
)
)
):
obj = model_kwarg_data(

View File

@ -24,6 +24,9 @@ def model_kwarg_data():
for field, value in model_kwargs.items():
is_unique_together_field = False
if not hasattr(model, field):
continue
if not hasattr(getattr(model, field), 'field'):
continue

View File

@ -22,7 +22,7 @@ def kwargs_ticketbase(django_db_blocker, kwargs_centurionmodel,
with django_db_blocker.unblock():
user = model_user.objects.create( **kwargs_user )
user = model_user.objects.create( **kwargs_user.copy() )
kwargs = kwargs_centurionmodel.copy()
del kwargs['model_notes']

View File

@ -1171,6 +1171,7 @@ markers = [
"module_settings: Selects all tests from module settings.",
"note_models: Selects all centurion model note models",
"permissions: selects all permission related tests.",
"regressopm: selects all tests for regression.",
"serializer: Selects all serializer tests",
"tenancy_models: Selects Tenancy models.",
"unit: Selects all Unit Tests.",