mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-23 12:08:43 -06:00
* Closes #19944: Add multi-scenario CSV import testing support with cleanup Enhanced BulkImportObjectsViewTestCase to support multiple CSV import scenarios via dictionary format, where each scenario runs as a separate subtest with automatic cleanup. This enables testing different import configurations (e.g., with/without optional fields) in a single test run with clear output showing which scenario is being tested. Introduces cleanupSubTest() context manager that uses database savepoints to automatically roll back changes between subtests, providing test isolation similar to separate test methods. This allows subtests to create/modify objects without affecting subsequent subtests in the same test method. Added post_import_callback parameter to bulk import tests, allowing child classes to inject custom assertions that run before database cleanup. This solves the inheritance problem where child classes need to verify imported data but the parent's cleanup would roll back the data before assertions could run. The callback approach is cleaner than conditional cleanup parameters - it makes the execution timing explicit and maintains test isolation while still allowing extensibility. * Fixup ModuleTypeTestCase bulk import test to work with callback mechamisn * Update CableTestCase to use expanded CSV scenario testing * Remove unneeded permission cleanup Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com> * Consolidate scenario name retrieval into method --------- Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
This commit is contained in:
@@ -1078,14 +1078,14 @@ class ModuleTypeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
'dcim.add_modulebaytemplate',
|
||||
)
|
||||
|
||||
def verify_module_type_profile(scenario_name):
|
||||
# TODO: remove extra regression asserts once parent test supports testing all import fields
|
||||
fan_module_type = ModuleType.objects.get(part_number='generic-fan')
|
||||
fan_module_type_profile = ModuleTypeProfile.objects.get(name='Fan')
|
||||
assert fan_module_type.profile == fan_module_type_profile
|
||||
|
||||
# run base test
|
||||
super().test_bulk_import_objects_with_permission()
|
||||
|
||||
# TODO: remove extra regression asserts once parent test supports testing all import fields
|
||||
fan_module_type = ModuleType.objects.get(part_number='generic-fan')
|
||||
fan_module_type_profile = ModuleTypeProfile.objects.get(name='Fan')
|
||||
|
||||
assert fan_module_type.profile == fan_module_type_profile
|
||||
super().test_bulk_import_objects_with_permission(post_import_callback=verify_module_type_profile)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[])
|
||||
def test_bulk_import_objects_with_constrained_permission(self):
|
||||
@@ -3290,8 +3290,10 @@ class CableTestCase(
|
||||
Device(name='Device 1', site=sites[0], device_type=devicetype, role=role),
|
||||
Device(name='Device 2', site=sites[0], device_type=devicetype, role=role),
|
||||
Device(name='Device 3', site=sites[0], device_type=devicetype, role=role),
|
||||
Device(name='Device 4', site=sites[0], device_type=devicetype, role=role),
|
||||
# Create 'Device 1' assigned to 'Site 2' (allowed since the site is different)
|
||||
Device(name='Device 1', site=sites[1], device_type=devicetype, role=role),
|
||||
Device(name='Device 5', site=sites[1], device_type=devicetype, role=role),
|
||||
)
|
||||
Device.objects.bulk_create(devices)
|
||||
|
||||
@@ -3300,22 +3302,36 @@ class CableTestCase(
|
||||
vc.save()
|
||||
|
||||
interfaces = (
|
||||
# Device 1, Site 1
|
||||
Interface(device=devices[0], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[0], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[0], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
# Device 2, Site 1
|
||||
Interface(device=devices[1], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[1], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[1], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
# Device 3, Site 1
|
||||
Interface(device=devices[2], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[2], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[2], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
# Device 3, Site 1
|
||||
Interface(device=devices[3], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[3], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[3], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
# Device 1, Site 2
|
||||
Interface(device=devices[4], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[4], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[4], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
|
||||
# Device 1, Site 2
|
||||
Interface(device=devices[5], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[5], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[5], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
|
||||
Interface(device=devices[1], name='Device 2 Interface', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[2], name='Device 3 Interface', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[3], name='Interface 4', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[3], name='Interface 5', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[4], name='Interface 4', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[4], name='Interface 5', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
)
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
|
||||
@@ -3342,16 +3358,29 @@ class CableTestCase(
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
||||
# Ensure that CSV bulk import supports assigning terminations from parent devices that share
|
||||
# the same device name, provided those devices belong to different sites.
|
||||
cls.csv_data = (
|
||||
"side_a_site,side_a_device,side_a_type,side_a_name,side_b_site,side_b_device,side_b_type,side_b_name",
|
||||
"Site 1,Device 3,dcim.interface,Interface 1,Site 2,Device 1,dcim.interface,Interface 1",
|
||||
"Site 1,Device 3,dcim.interface,Interface 2,Site 2,Device 1,dcim.interface,Interface 2",
|
||||
"Site 1,Device 3,dcim.interface,Interface 3,Site 2,Device 1,dcim.interface,Interface 3",
|
||||
"Site 1,Device 1,dcim.interface,Device 2 Interface,Site 2,Device 1,dcim.interface,Interface 4",
|
||||
"Site 1,Device 1,dcim.interface,Device 3 Interface,Site 2,Device 1,dcim.interface,Interface 5",
|
||||
)
|
||||
cls.csv_data = {
|
||||
'default': (
|
||||
"side_a_device,side_a_type,side_a_name,side_b_device,side_b_type,side_b_name",
|
||||
"Device 4,dcim.interface,Interface 1,Device 5,dcim.interface,Interface 1",
|
||||
"Device 3,dcim.interface,Interface 2,Device 4,dcim.interface,Interface 2",
|
||||
"Device 3,dcim.interface,Interface 3,Device 4,dcim.interface,Interface 3",
|
||||
|
||||
# The following is no longer possible in this scenario, because there are multiple
|
||||
# devices named "Device 1" across multiple sites. See the "site-filtering" scenario
|
||||
# below for how to specify a site for non-unique device names.
|
||||
# "Device 1,dcim.interface,Device 3 Interface,Device 4,dcim.interface,Interface 5",
|
||||
),
|
||||
'site-filtering': (
|
||||
# Ensure that CSV bulk import supports assigning terminations from parent devices
|
||||
# that share the same device name, provided those devices belong to different sites.
|
||||
"side_a_site,side_a_device,side_a_type,side_a_name,side_b_site,side_b_device,side_b_type,side_b_name",
|
||||
"Site 1,Device 3,dcim.interface,Interface 1,Site 2,Device 1,dcim.interface,Interface 1",
|
||||
"Site 1,Device 3,dcim.interface,Interface 2,Site 2,Device 1,dcim.interface,Interface 2",
|
||||
"Site 1,Device 3,dcim.interface,Interface 3,Site 2,Device 1,dcim.interface,Interface 3",
|
||||
"Site 1,Device 1,dcim.interface,Device 2 Interface,Site 2,Device 1,dcim.interface,Interface 4",
|
||||
"Site 1,Device 1,dcim.interface,Device 3 Interface,Site 2,Device 1,dcim.interface,Interface 5",
|
||||
)
|
||||
}
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,label,color",
|
||||
|
||||
Reference in New Issue
Block a user