"""Coverage tests for views/base/librenms_sync_view.py missing lines.""" from unittest.mock import MagicMock, patch def _make_view(): """Create a BaseLibreNMSSyncView instance bypassing __init__.""" from netbox_librenms_plugin.views.base.librenms_sync_view import BaseLibreNMSSyncView view = object.__new__(BaseLibreNMSSyncView) view.request = MagicMock() view.tab = "librenms_sync" view.model = MagicMock() view.queryset = MagicMock() view.kwargs = {} view._librenms_api = MagicMock() view._librenms_api.server_key = "default" view._librenms_api.librenms_url = "https://x.example.com" view._librenms_api.cache_timeout = 300 return view class TestBaseLibreNMSSyncViewGet: """Tests for get() method (lines 29-53).""" @patch("netbox_librenms_plugin.views.base.librenms_sync_view.render") @patch("netbox_librenms_plugin.views.base.librenms_sync_view.get_object_or_404") def test_get_non_vc_device(self, mock_get_obj, mock_render): """Non-VC device: librenms_lookup_device stays as obj.""" view = _make_view() obj = MagicMock() obj.virtual_chassis = None mock_get_obj.return_value = obj view._librenms_api = MagicMock() view._librenms_api.server_key = "default" view._librenms_api.get_librenms_id.return_value = 42 view.get_context_data = MagicMock(return_value={"test": "ctx"}) mock_render.return_value = MagicMock() request = MagicMock() view.get(request, pk=1) # lookup device should be obj assert view._librenms_lookup_device is obj @patch("netbox_librenms_plugin.views.base.librenms_sync_view.render") @patch("netbox_librenms_plugin.views.base.librenms_sync_view.get_object_or_404") @patch("netbox_librenms_plugin.views.base.librenms_sync_view.get_librenms_sync_device") def test_get_vc_member_always_delegates_to_sync_device(self, mock_get_sync, mock_get_obj, mock_render): """VC member: no own librenms_id - get_librenms_sync_device returns VC primary.""" view = _make_view() obj = MagicMock() obj.virtual_chassis = MagicMock() mock_get_obj.return_value = obj vc_primary = MagicMock() # Represents the VC primary device mock_get_sync.return_value = vc_primary view._librenms_api = MagicMock() view._librenms_api.server_key = "default" view._librenms_api.get_librenms_id.return_value = 99 view.get_context_data = MagicMock(return_value={}) mock_render.return_value = MagicMock() request = MagicMock() view.get(request, pk=1) mock_get_sync.assert_called_once_with(obj, server_key="default") # When member has no own ID, lookup uses the VC primary returned by get_librenms_sync_device assert view._librenms_lookup_device is vc_primary @patch("netbox_librenms_plugin.views.base.librenms_sync_view.render") @patch("netbox_librenms_plugin.views.base.librenms_sync_view.get_object_or_404") @patch("netbox_librenms_plugin.views.base.librenms_sync_view.get_librenms_sync_device") def test_get_vc_member_with_own_librenms_id_uses_itself(self, mock_get_sync, mock_get_obj, mock_render): """VC member: has own librenms_id - get_librenms_sync_device still called, returns member itself.""" view = _make_view() obj = MagicMock() obj.virtual_chassis = MagicMock() mock_get_obj.return_value = obj # get_librenms_sync_device returns obj itself (member has own librenms_id, priority 1) mock_get_sync.return_value = obj view._librenms_api = MagicMock() view._librenms_api.server_key = "default" view._librenms_api.get_librenms_id.return_value = 55 view.get_context_data = MagicMock(return_value={}) mock_render.return_value = MagicMock() request = MagicMock() view.get(request, pk=1) mock_get_sync.assert_called_once_with(obj, server_key="default") # When member has its own ID, get_librenms_sync_device returns the member itself assert view._librenms_lookup_device is obj @patch("netbox_librenms_plugin.views.base.librenms_sync_view.render") @patch("netbox_librenms_plugin.views.base.librenms_sync_view.get_object_or_404") @patch("netbox_librenms_plugin.views.base.librenms_sync_view.get_librenms_sync_device") def test_get_vc_member_no_sync_device_falls_back_to_obj(self, mock_get_sync, mock_get_obj, mock_render): """VC member: when get_librenms_sync_device returns None, keeps obj.""" view = _make_view() obj = MagicMock() obj.virtual_chassis = MagicMock() mock_get_obj.return_value = obj mock_get_sync.return_value = None view._librenms_api = MagicMock() view._librenms_api.server_key = "default" view._librenms_api.get_librenms_id.return_value = 55 view.get_context_data = MagicMock(return_value={}) mock_render.return_value = MagicMock() request = MagicMock() view.get(request, pk=1) mock_get_sync.assert_called_once_with(obj, server_key="default") assert view._librenms_lookup_device is obj class TestGetContextDataVC: """Tests for get_context_data() VC context (lines 69-91).""" def test_vc_context_sync_device_has_id_and_ip(self): """VC device: sync_device_has_librenms_id and sync_device_has_primary_ip set.""" view = _make_view() view.librenms_id = 42 view._librenms_lookup_device = MagicMock() obj = MagicMock() obj.virtual_chassis = MagicMock() obj._meta = MagicMock() obj._meta.model_name = "device" sync_device = MagicMock() sync_device.primary_ip = MagicMock() sync_device._meta.model_name = "device" sync_device.pk = 10 view._librenms_api = MagicMock() view._librenms_api.server_key = "default" view._librenms_api.librenms_url = "https://x.example.com" view.get_librenms_device_info = MagicMock( return_value={ "found_in_librenms": True, "librenms_device_details": { "librenms_device_serial": "SN001", "librenms_device_hardware": "Cisco", "librenms_device_os": "ios", "librenms_device_version": "16.9", "librenms_device_features": "-", "librenms_device_location": "NYC", "librenms_device_hardware_match": None, "vc_inventory_serials": [], }, "mismatched_device": False, } ) view.get_interface_context = MagicMock(return_value=None) view.get_cable_context = MagicMock(return_value=None) view.get_ip_context = MagicMock(return_value=None) view.get_vlan_context = MagicMock(return_value=None) with patch("netbox_librenms_plugin.views.base.librenms_sync_view.get_librenms_sync_device") as mock_sync: mock_sync.return_value = sync_device with patch("netbox_librenms_plugin.views.base.librenms_sync_view.get_librenms_device_id") as mock_id: mock_id.return_value = 42 with patch( "netbox_librenms_plugin.views.base.librenms_sync_view.get_interface_name_field", return_value="ifName", ): with patch( "netbox_librenms_plugin.views.base.librenms_sync_view.BaseLibreNMSSyncView._build_all_server_mappings", return_value=None, ): with patch( "netbox_librenms_plugin.views.base.librenms_sync_view.BaseLibreNMSSyncView._get_platform_info", return_value={}, ): with patch("netbox_librenms_plugin.views.base.librenms_sync_view.AddToLIbreSNMPV1V2"): with patch("netbox_librenms_plugin.views.base.librenms_sync_view.AddToLIbreSNMPV3"): with patch("dcim.models.Manufacturer") as MockMfr: MockMfr.objects.all.return_value.order_by.return_value = [] with patch.object(view, "get_context_data", wraps=view.get_context_data): # Call parent get_context_data via a mock of super() with patch( "netbox_librenms_plugin.views.base.librenms_sync_view.LibreNMSAPIMixin.get_context_data", return_value={}, ): ctx = view.get_context_data(MagicMock(), obj) assert ctx.get("is_vc_member") is True assert ctx.get("sync_device_has_librenms_id") is True assert ctx.get("sync_device_has_primary_ip") is True class TestBuildAllServerMappings: """Tests for _build_all_server_mappings (lines 181, 193, 200, 207-208).""" def test_returns_none_for_non_dict_cf(self): from netbox_librenms_plugin.views.base.librenms_sync_view import BaseLibreNMSSyncView obj = MagicMock() obj.custom_field_data = {"librenms_id": 42} # legacy bare int result = BaseLibreNMSSyncView._build_all_server_mappings(obj, "default") assert result is None def test_returns_none_for_empty_dict_cf(self): from netbox_librenms_plugin.views.base.librenms_sync_view import BaseLibreNMSSyncView obj = MagicMock() obj.custom_field_data = {"librenms_id": {}} result = BaseLibreNMSSyncView._build_all_server_mappings(obj, "default") assert result is None def test_valid_dict_cf_returns_list(self): from netbox_librenms_plugin.views.base.librenms_sync_view import BaseLibreNMSSyncView obj = MagicMock() obj.custom_field_data = {"librenms_id": {"default": 42, "secondary": 99}} with patch("netbox_librenms_plugin.views.base.librenms_sync_view.django_settings") as mock_settings: mock_settings.PLUGINS_CONFIG = { "netbox_librenms_plugin": { "servers": { "default": {"librenms_url": "https://x.example.com", "display_name": "Default"}, "secondary": {"librenms_url": "https://y.example.com", "display_name": "Secondary"}, } } } result = BaseLibreNMSSyncView._build_all_server_mappings(obj, "default") assert result is not None assert len(result) == 2 # Active server should be first assert result[0]["is_active"] is True assert result[0]["server_key"] == "default" def test_bool_value_skipped(self): from netbox_librenms_plugin.views.base.librenms_sync_view import BaseLibreNMSSyncView obj = MagicMock() obj.custom_field_data = {"librenms_id": {"default": True, "other": 42}} with patch("netbox_librenms_plugin.views.base.librenms_sync_view.django_settings") as mock_settings: mock_settings.PLUGINS_CONFIG = { "netbox_librenms_plugin": { "servers": {"other": {"librenms_url": "https://x.example.com", "display_name": "Other"}} } } result = BaseLibreNMSSyncView._build_all_server_mappings(obj, "default") assert result is not None assert len(result) == 1 assert result[0]["server_key"] == "other" def test_string_device_id_converted_to_int(self): from netbox_librenms_plugin.views.base.librenms_sync_view import BaseLibreNMSSyncView obj = MagicMock() obj.custom_field_data = {"librenms_id": {"default": "77"}} with patch("netbox_librenms_plugin.views.base.librenms_sync_view.django_settings") as mock_settings: mock_settings.PLUGINS_CONFIG = { "netbox_librenms_plugin": { "servers": {"default": {"librenms_url": "https://x.example.com", "display_name": "Default"}} } } result = BaseLibreNMSSyncView._build_all_server_mappings(obj, "default") assert result[0]["device_id"] == 77 def test_non_digit_string_skipped(self): from netbox_librenms_plugin.views.base.librenms_sync_view import BaseLibreNMSSyncView obj = MagicMock() obj.custom_field_data = {"librenms_id": {"default": "not-a-number"}} with patch("netbox_librenms_plugin.views.base.librenms_sync_view.django_settings") as mock_settings: mock_settings.PLUGINS_CONFIG = {"netbox_librenms_plugin": {"servers": {}}} result = BaseLibreNMSSyncView._build_all_server_mappings(obj, "default") assert result is None def test_legacy_default_key_falls_back_to_root_librenms_url(self): """'default' key with no matching servers entry uses root librenms_url.""" from netbox_librenms_plugin.views.base.librenms_sync_view import BaseLibreNMSSyncView obj = MagicMock() obj.custom_field_data = {"librenms_id": {"default": 42}} with patch("netbox_librenms_plugin.views.base.librenms_sync_view.django_settings") as mock_settings: mock_settings.PLUGINS_CONFIG = { "netbox_librenms_plugin": { "librenms_url": "https://legacy.example.com", "display_name": "Legacy Server", "servers": {}, } } result = BaseLibreNMSSyncView._build_all_server_mappings(obj, "default") assert result is not None assert result[0]["librenms_url"] == "https://legacy.example.com" def test_malformed_server_config_treated_as_unconfigured(self): """Non-dict server config entry → is_configured=False.""" from netbox_librenms_plugin.views.base.librenms_sync_view import BaseLibreNMSSyncView obj = MagicMock() obj.custom_field_data = {"librenms_id": {"default": 42}} with patch("netbox_librenms_plugin.views.base.librenms_sync_view.django_settings") as mock_settings: mock_settings.PLUGINS_CONFIG = {"netbox_librenms_plugin": {"servers": {"default": "this-is-not-a-dict"}}} result = BaseLibreNMSSyncView._build_all_server_mappings(obj, "default") assert result is not None assert result[0]["is_configured"] is False class TestGetLibreNMSDeviceInfo: """Tests for get_librenms_device_info (lines 228+).""" def test_no_librenms_id_returns_defaults(self): view = _make_view() view.librenms_id = None view._librenms_api = MagicMock() obj = MagicMock() result = view.get_librenms_device_info(obj) assert result["found_in_librenms"] is False assert result["mismatched_device"] is False def test_librenms_id_success_sets_found(self): view = _make_view() view.librenms_id = 42 view._librenms_api = MagicMock() view._librenms_api.librenms_url = "https://x.example.com" obj = MagicMock() obj.primary_ip = None obj.name = "mydevice" obj.virtual_chassis = None obj.serial = "SN001" obj.platform = None device_info = { "hardware": "Cisco C9300", "serial": "SN001", "os": "ios", "version": "16.9", "features": "-", "sysName": "mydevice", "hostname": "mydevice.example.com", "ip": "10.0.0.1", "location": "NYC", } view._librenms_api.get_device_info.return_value = (True, device_info) with patch( "netbox_librenms_plugin.views.base.librenms_sync_view.match_librenms_hardware_to_device_type" ) as mock_match: mock_match.return_value = {"matched": False, "device_type": None, "match_type": None} result = view.get_librenms_device_info(obj) assert result["found_in_librenms"] is True def test_mismatched_device_when_names_differ(self): view = _make_view() view.librenms_id = 42 view._librenms_api = MagicMock() view._librenms_api.librenms_url = "https://x.example.com" obj = MagicMock() obj.primary_ip = None obj.name = "device-netbox" obj.virtual_chassis = None obj.serial = "" obj.platform = None device_info = { "hardware": "-", "serial": "-", "os": "-", "version": "-", "features": "-", "sysName": "completely-different", "hostname": "also-different.example.com", "ip": "192.168.0.1", "location": "-", } view._librenms_api.get_device_info.return_value = (True, device_info) with patch( "netbox_librenms_plugin.views.base.librenms_sync_view.match_librenms_hardware_to_device_type" ) as mock_match: mock_match.return_value = {"matched": False, "device_type": None, "match_type": None} with patch( "netbox_librenms_plugin.views.base.librenms_sync_view.BaseLibreNMSSyncView._strip_vc_pattern", return_value=None, ): result = view.get_librenms_device_info(obj) assert result["mismatched_device"] is True class TestStripVcPattern: """Tests for _strip_vc_pattern (lines 378+).""" def test_strips_default_pattern(self): from netbox_librenms_plugin.views.base.librenms_sync_view import BaseLibreNMSSyncView mock_settings_cls = MagicMock() settings_obj = MagicMock() settings_obj.vc_member_name_pattern = "-M{position}" mock_settings_cls.objects.first.return_value = settings_obj with patch("netbox_librenms_plugin.models.LibreNMSSettings", mock_settings_cls, create=True): result = BaseLibreNMSSyncView._strip_vc_pattern("switch01-m2") # The suffix -m2 should be stripped, returning "switch01" assert result == "switch01" # suffix -m2 must be stripped def test_returns_none_on_exception(self): from netbox_librenms_plugin.views.base.librenms_sync_view import BaseLibreNMSSyncView mock_settings_cls = MagicMock() mock_settings_cls.objects.first.side_effect = Exception("DB error") with patch("netbox_librenms_plugin.models.LibreNMSSettings", mock_settings_cls, create=True): result = BaseLibreNMSSyncView._strip_vc_pattern("some-device") assert result is None class TestLibreNMSIdLegacyDetection: """Tests for librenms_id_is_legacy detection (lines 113-115).""" def test_bare_int_cf_detected_as_legacy(self): """bare int CF → librenms_id_is_legacy = True.""" view = _make_view() view.librenms_id = 42 view._librenms_lookup_device = MagicMock() view._librenms_lookup_device.cf = {"librenms_id": 42} obj = MagicMock() obj.virtual_chassis = None obj._meta = MagicMock() obj._meta.model_name = "device" obj.pk = 1 obj.serial = "SN" obj.platform = None view._librenms_api = MagicMock() view._librenms_api.server_key = "default" view._librenms_api.librenms_url = "https://x.example.com" view.get_librenms_device_info = MagicMock( return_value={ "found_in_librenms": True, "librenms_device_details": { "librenms_device_serial": "SN", "librenms_device_hardware": "-", "librenms_device_os": "-", "librenms_device_version": "-", "librenms_device_features": "-", "librenms_device_location": "-", "librenms_device_hardware_match": None, "vc_inventory_serials": [], }, "mismatched_device": False, } ) view.get_interface_context = MagicMock(return_value=None) view.get_cable_context = MagicMock(return_value=None) view.get_ip_context = MagicMock(return_value=None) view.get_vlan_context = MagicMock(return_value=None) with patch( "netbox_librenms_plugin.views.base.librenms_sync_view.get_interface_name_field", return_value="ifName" ): with patch( "netbox_librenms_plugin.views.base.librenms_sync_view.BaseLibreNMSSyncView._build_all_server_mappings", return_value=None, ): with patch( "netbox_librenms_plugin.views.base.librenms_sync_view.BaseLibreNMSSyncView._get_platform_info", return_value={}, ): with patch("netbox_librenms_plugin.views.base.librenms_sync_view.AddToLIbreSNMPV1V2"): with patch("netbox_librenms_plugin.views.base.librenms_sync_view.AddToLIbreSNMPV3"): with patch("dcim.models.Manufacturer") as MockMfr: MockMfr.objects.all.return_value.order_by.return_value = [] with patch( "netbox_librenms_plugin.views.base.librenms_sync_view.LibreNMSAPIMixin.get_context_data", return_value={}, ): ctx = view.get_context_data(MagicMock(), obj) assert ctx.get("librenms_id_is_legacy") is True class TestAbstractMethods: """Tests for abstract get_*_context methods (lines 349-376).""" def test_get_interface_context_returns_none(self): view = _make_view() result = view.get_interface_context(MagicMock(), MagicMock()) assert result is None def test_get_cable_context_returns_none(self): view = _make_view() result = view.get_cable_context(MagicMock(), MagicMock()) assert result is None def test_get_ip_context_returns_none(self): view = _make_view() result = view.get_ip_context(MagicMock(), MagicMock()) assert result is None def test_get_vlan_context_returns_none(self): view = _make_view() result = view.get_vlan_context(MagicMock(), MagicMock()) assert result is None class TestGetVCInventorySerials: """Tests for _get_vc_inventory_serials (lines 412-452).""" def test_no_inventory_returns_empty(self): view = _make_view() view.librenms_id = 42 view._librenms_api.get_device_inventory.return_value = (False, []) obj = MagicMock() obj.virtual_chassis = MagicMock() obj.virtual_chassis.members.all.return_value = [] result = view._get_vc_inventory_serials(obj) assert result == [] def test_chassis_components_matched(self): view = _make_view() view.librenms_id = 42 inventory = [ { "entPhysicalClass": "chassis", "entPhysicalSerialNum": "SN001", "entPhysicalDescr": "Chassis", "entPhysicalModelName": "C9300", }, { "entPhysicalClass": "module", "entPhysicalSerialNum": "SN002", "entPhysicalDescr": "Module", "entPhysicalModelName": "", }, ] view._librenms_api.get_device_inventory.return_value = (True, inventory) member = MagicMock() member.serial = "SN001" obj = MagicMock() obj.virtual_chassis = MagicMock() obj.virtual_chassis.members.all.return_value = [member] result = view._get_vc_inventory_serials(obj) assert len(result) == 1 assert result[0]["serial"] == "SN001" assert result[0]["assigned_member"] is member def test_unassigned_serial_returns_none_member(self): view = _make_view() view.librenms_id = 42 inventory = [ { "entPhysicalClass": "chassis", "entPhysicalSerialNum": "UNKNOWN_SN", "entPhysicalDescr": "Chassis", "entPhysicalModelName": "MX480", }, ] view._librenms_api.get_device_inventory.return_value = (True, inventory) member = MagicMock() member.serial = "SN001" # Different serial obj = MagicMock() obj.virtual_chassis = MagicMock() obj.virtual_chassis.members.all.return_value = [member] result = view._get_vc_inventory_serials(obj) assert len(result) == 1 assert result[0]["assigned_member"] is None def test_empty_serial_skipped(self): view = _make_view() view.librenms_id = 42 inventory = [ { "entPhysicalClass": "chassis", "entPhysicalSerialNum": "-", "entPhysicalDescr": "Chassis", "entPhysicalModelName": "", }, ] view._librenms_api.get_device_inventory.return_value = (True, inventory) obj = MagicMock() obj.virtual_chassis = MagicMock() obj.virtual_chassis.members.all.return_value = [] result = view._get_vc_inventory_serials(obj) assert result == [] class TestGetPlatformInfo: """Tests for _get_platform_info (lines 463-502).""" def test_no_os_returns_no_platform(self): view = _make_view() obj = MagicMock() obj.platform = None librenms_info = { "librenms_device_details": { "librenms_device_os": "-", "librenms_device_version": "-", } } with patch("dcim.models.Platform") as MockPlatform: MockPlatform.DoesNotExist = type("DoesNotExist", (Exception,), {}) MockPlatform.objects.get.side_effect = MockPlatform.DoesNotExist() result = view._get_platform_info(librenms_info, obj) assert result["platform_exists"] is False assert result["platform_name"] is None def test_matching_platform_found(self): view = _make_view() obj = MagicMock() mock_platform = MagicMock() librenms_info = { "librenms_device_details": { "librenms_device_os": "ios", "librenms_device_version": "16.9", } } with patch("dcim.models.Platform") as MockPlatform: MockPlatform.DoesNotExist = type("DoesNotExist", (Exception,), {}) MockPlatform.objects.get.return_value = mock_platform result = view._get_platform_info(librenms_info, obj) assert result["platform_exists"] is True assert result["matching_platform"] is mock_platform def test_platform_does_not_exist(self): view = _make_view() obj = MagicMock() obj.platform = None librenms_info = { "librenms_device_details": { "librenms_device_os": "eos", "librenms_device_version": "4.28", } } with patch("dcim.models.Platform") as MockPlatform: MockPlatform.DoesNotExist = type("DoesNotExist", (Exception,), {}) MockPlatform.objects.get.side_effect = MockPlatform.DoesNotExist() result = view._get_platform_info(librenms_info, obj) assert result["platform_exists"] is False assert result["matching_platform"] is None