diff --git a/netbox/utilities/testing/base.py b/netbox/utilities/testing/base.py index aa2093a9a..52e5d66ca 100644 --- a/netbox/utilities/testing/base.py +++ b/netbox/utilities/testing/base.py @@ -10,10 +10,11 @@ from django.test import Client, TestCase as _TestCase from netaddr import IPNetwork from taggit.managers import TaggableManager +from netbox.models.features import CustomFieldsMixin from users.models import ObjectPermission from utilities.permissions import resolve_permission_ct from utilities.utils import content_type_identifier -from .utils import extract_form_failures +from .utils import DUMMY_CF_DATA, extract_form_failures __all__ = ( 'ModelTestCase', @@ -166,8 +167,12 @@ class ModelTestCase(TestCase): model_dict = self.model_to_dict(instance, fields=fields, api=api) # Omit any dictionary keys which are not instance attributes or have been excluded - relevant_data = { + model_data = { k: v for k, v in data.items() if hasattr(instance, k) and k not in exclude } - self.assertDictEqual(model_dict, relevant_data) + self.assertDictEqual(model_dict, model_data) + + # Validate any custom field data, if present + if getattr(instance, 'custom_field_data', None): + self.assertDictEqual(instance.custom_field_data, DUMMY_CF_DATA) diff --git a/netbox/utilities/testing/utils.py b/netbox/utilities/testing/utils.py index fd6d72e27..238f8170e 100644 --- a/netbox/utilities/testing/utils.py +++ b/netbox/utilities/testing/utils.py @@ -1,13 +1,16 @@ +import json import logging import re from contextlib import contextmanager from django.contrib.auth import get_user_model from django.contrib.auth.models import Permission +from django.contrib.contenttypes.models import ContentType from django.utils.text import slugify from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site -from extras.models import Tag +from extras.choices import CustomFieldTypeChoices +from extras.models import CustomField, Tag from virtualization.models import Cluster, ClusterType, VirtualMachine @@ -102,3 +105,42 @@ def disable_warnings(logger_name): logger.setLevel(logging.ERROR) yield logger.setLevel(current_level) + + +# +# Custom field testing +# + +DUMMY_CF_DATA = { + 'text_field': 'foo123', + 'integer_field': 456, + 'decimal_field': 456.12, + 'boolean_field': True, + 'json_field': {'abc': 123}, +} + + +def add_custom_field_data(form_data, model): + """ + Create some custom fields for the model and add a value for each to the form data. + + Args: + form_data: The dictionary of form data to be updated + model: The model of the object the form seeks to create or modify + """ + content_type = ContentType.objects.get_for_model(model) + custom_fields = ( + CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='text_field', default='foo'), + CustomField(type=CustomFieldTypeChoices.TYPE_INTEGER, name='integer_field', default=123), + CustomField(type=CustomFieldTypeChoices.TYPE_DECIMAL, name='decimal_field', default=123.45), + CustomField(type=CustomFieldTypeChoices.TYPE_BOOLEAN, name='boolean_field', default=False), + CustomField(type=CustomFieldTypeChoices.TYPE_JSON, name='json_field', default='{"x": "y"}'), + ) + CustomField.objects.bulk_create(custom_fields) + for cf in custom_fields: + cf.content_types.set([content_type]) + + form_data.update({ + f'cf_{k}': v if type(v) is str else json.dumps(v) + for k, v in DUMMY_CF_DATA.items() + }) diff --git a/netbox/utilities/testing/views.py b/netbox/utilities/testing/views.py index daa44b905..22371db3c 100644 --- a/netbox/utilities/testing/views.py +++ b/netbox/utilities/testing/views.py @@ -10,11 +10,11 @@ from django.utils.translation import gettext as _ from extras.choices import ObjectChangeActionChoices from extras.models import ObjectChange -from netbox.models.features import ChangeLoggingMixin +from netbox.models.features import ChangeLoggingMixin, CustomFieldsMixin from users.models import ObjectPermission from utilities.choices import CSVDelimiterChoices, ImportFormatChoices from .base import ModelTestCase -from .utils import disable_warnings, post_data +from .utils import add_custom_field_data, disable_warnings, post_data __all__ = ( 'ModelViewTestCase', @@ -26,7 +26,6 @@ __all__ = ( # UI Tests # - class ModelViewTestCase(ModelTestCase): """ Base TestCase for model views. Subclass to test individual views. @@ -166,6 +165,10 @@ class ViewTestCases: # Try GET with model-level permission self.assertHttpStatus(self.client.get(self._get_url('add')), 200) + # Add custom field data if the model supports it + if issubclass(self.model, CustomFieldsMixin): + add_custom_field_data(self.form_data, self.model) + # Try POST with model-level permission initial_count = self._get_queryset().count() request = { @@ -265,6 +268,10 @@ class ViewTestCases: # Try GET with model-level permission self.assertHttpStatus(self.client.get(self._get_url('edit', instance)), 200) + # Add custom field data if the model supports it + if issubclass(self.model, CustomFieldsMixin): + add_custom_field_data(self.form_data, self.model) + # Try POST with model-level permission request = { 'path': self._get_url('edit', instance),