mirror of
https://github.com/netbox-community/netbox.git
synced 2026-02-23 17:24:55 +01:00
NetBox now accepts case-insensitive model identifiers in configuration, allowing both lowercase (e.g. "dcim.site") and PascalCase (e.g. "dcim.Site") for DEFAULT_DASHBOARD, CUSTOM_VALIDATORS, and PROTECTION_RULES. This makes model name handling consistent with FIELD_CHOICES. - Add a shared case-insensitive config lookup helper (get_config_value_ci()) - Use the helper in extras/signals.py and core/signals.py - Update FIELD_CHOICES ChoiceSetMeta to support case-insensitive replace/extend (only compute extend choices if no replacement is defined) - Add unit tests for get_config_value_ci() - Add integration tests for case-insensitive FIELD_CHOICES replacement/extension - Update documentation examples to use PascalCase consistently
122 lines
4.0 KiB
Python
122 lines
4.0 KiB
Python
from django.db.backends.postgresql.psycopg_any import NumericRange
|
|
from django.test import TestCase
|
|
from utilities.data import (
|
|
check_ranges_overlap,
|
|
get_config_value_ci,
|
|
ranges_to_string,
|
|
ranges_to_string_list,
|
|
string_to_ranges,
|
|
)
|
|
|
|
|
|
class RangeFunctionsTestCase(TestCase):
|
|
|
|
def test_check_ranges_overlap(self):
|
|
# Non-overlapping ranges
|
|
self.assertFalse(
|
|
check_ranges_overlap([
|
|
NumericRange(9, 19, bounds='(]'), # 10-19
|
|
NumericRange(19, 30, bounds='(]'), # 20-29
|
|
])
|
|
)
|
|
self.assertFalse(
|
|
check_ranges_overlap([
|
|
NumericRange(10, 19, bounds='[]'), # 10-19
|
|
NumericRange(20, 29, bounds='[]'), # 20-29
|
|
])
|
|
)
|
|
self.assertFalse(
|
|
check_ranges_overlap([
|
|
NumericRange(10, 20, bounds='[)'), # 10-19
|
|
NumericRange(20, 30, bounds='[)'), # 20-29
|
|
])
|
|
)
|
|
|
|
# Overlapping ranges
|
|
self.assertTrue(
|
|
check_ranges_overlap([
|
|
NumericRange(9, 20, bounds='(]'), # 10-20
|
|
NumericRange(19, 30, bounds='(]'), # 20-30
|
|
])
|
|
)
|
|
self.assertTrue(
|
|
check_ranges_overlap([
|
|
NumericRange(10, 20, bounds='[]'), # 10-20
|
|
NumericRange(20, 30, bounds='[]'), # 20-30
|
|
])
|
|
)
|
|
self.assertTrue(
|
|
check_ranges_overlap([
|
|
NumericRange(10, 21, bounds='[)'), # 10-20
|
|
NumericRange(20, 31, bounds='[)'), # 10-30
|
|
])
|
|
)
|
|
|
|
def test_ranges_to_string_list(self):
|
|
self.assertEqual(
|
|
ranges_to_string_list([
|
|
NumericRange(10, 20), # 10-19
|
|
NumericRange(30, 40), # 30-39
|
|
NumericRange(50, 51), # 50-50
|
|
NumericRange(100, 200), # 100-199
|
|
]),
|
|
['10-19', '30-39', '50', '100-199']
|
|
)
|
|
|
|
def test_ranges_to_string(self):
|
|
self.assertEqual(
|
|
ranges_to_string([
|
|
NumericRange(10, 20), # 10-19
|
|
NumericRange(30, 40), # 30-39
|
|
NumericRange(50, 51), # 50-50
|
|
NumericRange(100, 200), # 100-199
|
|
]),
|
|
'10-19,30-39,50,100-199'
|
|
)
|
|
|
|
def test_string_to_ranges(self):
|
|
self.assertEqual(
|
|
string_to_ranges('10-19, 30-39, 100-199'),
|
|
[
|
|
NumericRange(10, 20, bounds='[)'), # 10-20
|
|
NumericRange(30, 40, bounds='[)'), # 30-40
|
|
NumericRange(100, 200, bounds='[)'), # 100-200
|
|
]
|
|
)
|
|
|
|
self.assertEqual(
|
|
string_to_ranges('1-2, 5, 10-12'),
|
|
[
|
|
NumericRange(1, 3, bounds='[)'), # 1-3
|
|
NumericRange(5, 6, bounds='[)'), # 5-6
|
|
NumericRange(10, 13, bounds='[)'), # 10-13
|
|
]
|
|
)
|
|
|
|
self.assertEqual(
|
|
string_to_ranges('2-10, a-b'),
|
|
None # Fails to convert
|
|
)
|
|
|
|
|
|
class GetConfigValueCITestCase(TestCase):
|
|
|
|
def test_exact_match(self):
|
|
config = {'dcim.site': 'value1', 'dcim.Device': 'value2'}
|
|
self.assertEqual(get_config_value_ci(config, 'dcim.site'), 'value1')
|
|
self.assertEqual(get_config_value_ci(config, 'dcim.Device'), 'value2')
|
|
|
|
def test_case_insensitive_match(self):
|
|
config = {'dcim.Site': 'value1', 'ipam.IPAddress': 'value2'}
|
|
self.assertEqual(get_config_value_ci(config, 'dcim.site'), 'value1')
|
|
self.assertEqual(get_config_value_ci(config, 'ipam.ipaddress'), 'value2')
|
|
|
|
def test_default_value(self):
|
|
config = {'dcim.site': 'value1'}
|
|
self.assertIsNone(get_config_value_ci(config, 'nonexistent'))
|
|
self.assertEqual(get_config_value_ci(config, 'nonexistent', default=[]), [])
|
|
|
|
def test_empty_dict(self):
|
|
self.assertIsNone(get_config_value_ci({}, 'any.key'))
|
|
self.assertEqual(get_config_value_ci({}, 'any.key', default=[]), [])
|