first commit
Some checks failed
ci / deploy (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled

This commit is contained in:
Vlastislav Svatek
2026-06-05 10:39:05 +02:00
commit 673e67106e
217 changed files with 76612 additions and 0 deletions

View File

@@ -0,0 +1,692 @@
"""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