118 lines
4.5 KiB
Python
118 lines
4.5 KiB
Python
from importlib import import_module
|
|
from types import SimpleNamespace
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from django.test import SimpleTestCase
|
|
|
|
from netbox_utilities.patchpanel import (
|
|
_component_saved,
|
|
_wrap_component_container_save,
|
|
is_patchpanel,
|
|
pair_ports,
|
|
port_identifier,
|
|
retrace_patchpanel_paths,
|
|
)
|
|
|
|
|
|
def port(pk, name):
|
|
return SimpleNamespace(pk=pk, name=name)
|
|
|
|
|
|
class PatchpanelPairingTest(SimpleTestCase):
|
|
@patch("netbox_utilities.patchpanel.synchronize_patchpanel")
|
|
def test_component_edits_do_not_trigger_automation(self, synchronize):
|
|
component = SimpleNamespace(device_id=42)
|
|
|
|
_component_saved(None, component, created=False)
|
|
|
|
synchronize.assert_not_called()
|
|
|
|
@patch("netbox_utilities.patchpanel.synchronize_patchpanel")
|
|
def test_new_components_trigger_automation(self, synchronize):
|
|
component = SimpleNamespace(device_id=42)
|
|
|
|
_component_saved(None, component, created=True)
|
|
|
|
synchronize.assert_called_once_with(42)
|
|
|
|
@patch("netbox_utilities.patchpanel.synchronize_patchpanel")
|
|
def test_existing_device_save_does_not_trigger_automation(self, synchronize):
|
|
class Container:
|
|
def __init__(self, adding):
|
|
self._state = SimpleNamespace(adding=adding)
|
|
self.pk = 42
|
|
|
|
def save(self):
|
|
self._state.adding = False
|
|
return "saved"
|
|
|
|
_wrap_component_container_save(Container, lambda instance: instance.pk)
|
|
|
|
self.assertEqual(Container(adding=False).save(), "saved")
|
|
synchronize.assert_not_called()
|
|
|
|
self.assertEqual(Container(adding=True).save(), "saved")
|
|
synchronize.assert_called_once_with(42)
|
|
|
|
def test_existing_device_migrations_are_noops(self):
|
|
migration_names = (
|
|
"0004_patchpanel_port_mappings",
|
|
"0005_fix_patchpanel_port_number_mappings",
|
|
"0006_retrace_existing_patchpanel_paths",
|
|
)
|
|
|
|
for migration_name in migration_names:
|
|
with self.subTest(migration=migration_name):
|
|
module = import_module(f"netbox_utilities.migrations.{migration_name}")
|
|
self.assertEqual(module.Migration.operations, [])
|
|
|
|
def test_detects_patchpanel_role_case_insensitively(self):
|
|
self.assertTrue(is_patchpanel(SimpleNamespace(role=SimpleNamespace(name="Patchpanel"))))
|
|
self.assertTrue(is_patchpanel(SimpleNamespace(role=SimpleNamespace(name="PATCHPANEL"))))
|
|
self.assertFalse(is_patchpanel(SimpleNamespace(role=SimpleNamespace(name="Switch"))))
|
|
|
|
def test_pairs_front_and_rear_ports_by_matching_number(self):
|
|
front_ports = [port(10, "Front 10"), port(2, "Front 2"), port(1, "Front 1")]
|
|
rear_ports = [port(102, "Rear 2"), port(110, "Rear 10"), port(101, "Rear 1")]
|
|
|
|
pairs = pair_ports(front_ports, rear_ports)
|
|
|
|
self.assertEqual([(front.pk, rear.pk) for front, rear in pairs], [(1, 101), (2, 102), (10, 110)])
|
|
|
|
def test_leaves_unmatched_ports_without_a_mapping(self):
|
|
pairs = pair_ports([port(1, "Front 1"), port(2, "Front 2")], [port(102, "Rear 2")])
|
|
|
|
self.assertEqual([(front.pk, rear.pk) for front, rear in pairs], [(2, 102)])
|
|
|
|
def test_normalizes_common_side_prefixes_and_leading_zeroes(self):
|
|
self.assertEqual(port_identifier(port(1, "F01")), (1,))
|
|
self.assertEqual(port_identifier(port(2, "Rear Port 1")), (1,))
|
|
self.assertEqual(port_identifier(port(3, "FrontPort01")), (1,))
|
|
|
|
def test_does_not_pair_different_identifiers(self):
|
|
pairs = pair_ports([port(1, "Front 1")], [port(102, "Rear 2")])
|
|
|
|
self.assertEqual(pairs, [])
|
|
|
|
@patch("dcim.models.CablePath")
|
|
def test_retraces_each_affected_cable_path_once(self, cable_path_model):
|
|
first_path = MagicMock()
|
|
second_path = MagicMock()
|
|
|
|
def filter_paths(**kwargs):
|
|
queryset = MagicMock()
|
|
if "_nodes__contains" in kwargs:
|
|
port_id = kwargs["_nodes__contains"].pk
|
|
queryset.values_list.return_value = [1] if port_id == 1 else [1, 2]
|
|
else:
|
|
queryset.first.return_value = {1: first_path, 2: second_path}.get(kwargs["pk"])
|
|
return queryset
|
|
|
|
cable_path_model.objects.filter.side_effect = filter_paths
|
|
|
|
retraced = retrace_patchpanel_paths([port(1, "Front 1"), port(2, "Front 2")])
|
|
|
|
self.assertEqual(retraced, 2)
|
|
first_path.retrace.assert_called_once_with()
|
|
second_path.retrace.assert_called_once_with()
|