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,367 @@
"""
Tests for multi-server librenms_id helpers.
Covers get_librenms_device_id, set_librenms_device_id, find_by_librenms_id,
and migrate_legacy_librenms_id.
"""
from unittest.mock import MagicMock
class TestGetLibreNMSDeviceId:
"""Tests for get_librenms_device_id()."""
def test_returns_none_when_cf_missing(self):
from netbox_librenms_plugin.utils import get_librenms_device_id
obj = MagicMock()
obj.cf = {}
result = get_librenms_device_id(obj, "default")
assert result is None
def test_returns_int_for_legacy_bare_integer(self):
from netbox_librenms_plugin.utils import get_librenms_device_id
obj = MagicMock()
obj.cf = {"librenms_id": 42}
result = get_librenms_device_id(obj, "default")
assert result == 42
def test_legacy_bare_int_returned_for_any_server_key(self):
"""
Legacy bare integers are returned as a universal fallback for any server_key.
Devices imported before multi-server support store a bare integer in
librenms_id. These must remain discoverable regardless of which server is
active, so the bare-int is returned as-is for any server_key.
"""
from netbox_librenms_plugin.utils import get_librenms_device_id
obj = MagicMock()
obj.cf = {"librenms_id": 99}
assert get_librenms_device_id(obj, "default") == 99
assert get_librenms_device_id(obj, "production") == 99
assert get_librenms_device_id(obj, "secondary") == 99
def test_returns_value_for_matching_server_key(self):
from netbox_librenms_plugin.utils import get_librenms_device_id
obj = MagicMock()
obj.cf = {"librenms_id": {"production": 7, "secondary": 12}}
assert get_librenms_device_id(obj, "production") == 7
def test_returns_none_for_missing_server_key_in_dict(self):
from netbox_librenms_plugin.utils import get_librenms_device_id
obj = MagicMock()
obj.cf = {"librenms_id": {"production": 7}}
result = get_librenms_device_id(obj, "secondary")
assert result is None
def test_returns_none_for_unexpected_type(self):
from netbox_librenms_plugin.utils import get_librenms_device_id
obj = MagicMock()
obj.cf = {"librenms_id": "not-an-int-or-dict"}
result = get_librenms_device_id(obj, "default")
assert result is None
def test_legacy_string_int_returned_for_any_server_key(self):
"""A bare string integer ('42') is coerced and returned for any server_key."""
from netbox_librenms_plugin.utils import get_librenms_device_id
obj = MagicMock()
obj.cf = {"librenms_id": "42"}
assert get_librenms_device_id(obj, "default") == 42
assert get_librenms_device_id(obj, "production") == 42
def test_returns_none_for_bare_boolean(self):
"""bool is a subclass of int; bare True/False must not be treated as a valid ID."""
from netbox_librenms_plugin.utils import get_librenms_device_id
obj = MagicMock()
obj.cf = {"librenms_id": True}
assert get_librenms_device_id(obj, "default") is None
obj.cf = {"librenms_id": False}
assert get_librenms_device_id(obj, "default") is None
def test_returns_none_for_boolean_inside_dict(self):
"""Boolean values inside the JSON dict must be rejected."""
from netbox_librenms_plugin.utils import get_librenms_device_id
obj = MagicMock()
obj.cf = {"librenms_id": {"default": True}}
assert get_librenms_device_id(obj, "default") is None
def test_default_server_key_is_default(self):
from netbox_librenms_plugin.utils import get_librenms_device_id
obj = MagicMock()
obj.cf = {"librenms_id": {"default": 5}}
assert get_librenms_device_id(obj) == 5
class TestFindByLibreNMSId:
"""Tests for find_by_librenms_id()."""
def test_queries_server_key_and_legacy_integer(self):
"""
find_by_librenms_id() issues a Q that covers both the JSON server-key branch
and the legacy bare-int branch in a single filter() call.
We inspect the Q object's children directly because the two branches must
coexist — matching only one would silently miss devices stored in the other
format.
"""
from unittest.mock import MagicMock
from django.db.models import Q
from netbox_librenms_plugin.utils import find_by_librenms_id
mock_model = MagicMock()
mock_qs = MagicMock()
mock_model.objects.filter.return_value = mock_qs
mock_qs.first.return_value = None
find_by_librenms_id(mock_model, 42, "default")
mock_model.objects.filter.assert_called_once()
# Verify the Q predicate covers both the server-key JSON branch and legacy bare-int/string branches
call_args = mock_model.objects.filter.call_args
q_arg = call_args[0][0]
assert isinstance(q_arg, Q)
assert q_arg.connector == "OR"
# The combined Q should contain four children: JSON key (int), JSON key (str), bare-int, bare-string
q_str = str(q_arg)
assert "librenms_id__default" in q_str
assert "custom_field_data__librenms_id__default" in q_str
assert "custom_field_data__librenms_id" in q_str
assert "42" in q_str
def test_returns_first_matching_object(self):
from netbox_librenms_plugin.utils import find_by_librenms_id
expected = MagicMock()
mock_model = MagicMock()
mock_qs = MagicMock()
mock_model.objects.filter.return_value = mock_qs
mock_qs.first.return_value = expected
result = find_by_librenms_id(mock_model, 42, "default")
assert result is expected
def test_returns_none_when_not_found(self):
from unittest.mock import MagicMock
from django.db.models import Q
from netbox_librenms_plugin.utils import find_by_librenms_id
mock_model = MagicMock()
mock_qs = MagicMock()
mock_model.objects.filter.return_value = mock_qs
mock_qs.first.return_value = None
result = find_by_librenms_id(mock_model, 999, "production")
assert result is None
# Any server_key must include legacy bare-int/string fallback conditions
# so that devices imported before multi-server support are still found.
call_args = mock_model.objects.filter.call_args
q_arg = call_args[0][0]
assert isinstance(q_arg, Q)
q_str = str(q_arg)
assert "custom_field_data__librenms_id__production" in q_str
assert "custom_field_data__librenms_id" in q_str
def test_default_server_key_is_default(self):
"""find_by_librenms_id() uses "default" as the server key when no key is passed.
We inspect the Q predicate's children to confirm the key embedded in the
JSON path is exactly "default", not some other fallback value.
"""
from unittest.mock import MagicMock
from django.db.models import Q
from netbox_librenms_plugin.utils import find_by_librenms_id
mock_model = MagicMock()
mock_qs = MagicMock()
mock_model.objects.filter.return_value = mock_qs
mock_qs.first.return_value = None
find_by_librenms_id(mock_model, 42)
mock_model.objects.filter.assert_called_once()
call_args = mock_model.objects.filter.call_args
q_arg = call_args[0][0]
assert isinstance(q_arg, Q)
assert q_arg.connector == "OR"
q_str = str(q_arg)
assert "custom_field_data__librenms_id__default" in q_str
class TestMigrateLegacyLibreNMSId:
"""Tests for migrate_legacy_librenms_id()."""
def test_returns_true_when_migrated(self):
from netbox_librenms_plugin.utils import migrate_legacy_librenms_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": 42}
result = migrate_legacy_librenms_id(obj, "default")
assert result is True
def test_migrates_integer_to_dict_format(self):
from netbox_librenms_plugin.utils import migrate_legacy_librenms_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": 42}
migrate_legacy_librenms_id(obj, "production")
assert obj.custom_field_data["librenms_id"] == {"production": 42}
def test_returns_false_when_already_dict(self):
from netbox_librenms_plugin.utils import migrate_legacy_librenms_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": {"default": 42}}
result = migrate_legacy_librenms_id(obj, "default")
assert result is False
def test_returns_false_when_value_is_none(self):
from netbox_librenms_plugin.utils import migrate_legacy_librenms_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": None}
result = migrate_legacy_librenms_id(obj, "default")
assert result is False
def test_returns_false_for_boolean_value(self):
"""bool is a subclass of int; True/False must not be migrated."""
from netbox_librenms_plugin.utils import migrate_legacy_librenms_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": True}
assert migrate_legacy_librenms_id(obj, "default") is False
assert obj.custom_field_data["librenms_id"] is True # unchanged
def test_does_not_call_save(self):
"""migrate_legacy_librenms_id must NOT call obj.save() — caller is responsible."""
from netbox_librenms_plugin.utils import migrate_legacy_librenms_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": 7}
migrate_legacy_librenms_id(obj, "default")
obj.save.assert_not_called()
def test_preserves_value_in_migrated_dict(self):
from netbox_librenms_plugin.utils import migrate_legacy_librenms_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": 99}
migrate_legacy_librenms_id(obj, "secondary")
assert obj.custom_field_data["librenms_id"]["secondary"] == 99
class TestLibreNMSIdRoundtrip:
"""get_librenms_device_id should see the value set by set_librenms_device_id."""
def test_set_then_get_returns_same_value(self):
from netbox_librenms_plugin.utils import get_librenms_device_id, set_librenms_device_id
obj = MagicMock()
obj.custom_field_data = {}
obj.cf = obj.custom_field_data # make cf a live view of custom_field_data
set_librenms_device_id(obj, 42, "production")
result = get_librenms_device_id(obj, "production")
assert result == 42
def test_set_multiple_servers_get_correct_each(self):
from netbox_librenms_plugin.utils import get_librenms_device_id, set_librenms_device_id
obj = MagicMock()
obj.custom_field_data = {}
obj.cf = obj.custom_field_data
set_librenms_device_id(obj, 10, "primary")
set_librenms_device_id(obj, 20, "secondary")
assert get_librenms_device_id(obj, "primary") == 10
assert get_librenms_device_id(obj, "secondary") == 20
def test_migrate_then_get_returns_value(self):
from netbox_librenms_plugin.utils import get_librenms_device_id, migrate_legacy_librenms_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": 55}
obj.cf = obj.custom_field_data
migrate_legacy_librenms_id(obj, "default")
result = get_librenms_device_id(obj, "default")
assert result == 55
class TestSetLibreNMSDeviceId:
"""Tests for set_librenms_device_id in utils.py."""
def test_stores_int_for_valid_device_id(self):
"""Valid integer device_id is stored under server_key."""
from netbox_librenms_plugin.utils import set_librenms_device_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": None}
set_librenms_device_id(obj, 42, server_key="primary")
assert obj.custom_field_data["librenms_id"] == {"primary": 42}
def test_invalid_device_id_not_stored(self):
"""Non-integer device_id is rejected and nothing is written."""
from netbox_librenms_plugin.utils import set_librenms_device_id
obj = MagicMock()
obj.custom_field_data = {}
set_librenms_device_id(obj, "not-an-int", server_key="primary")
assert "librenms_id" not in obj.custom_field_data
def test_invalid_device_id_does_not_overwrite_existing(self):
"""Existing valid value is preserved when new device_id is invalid."""
from netbox_librenms_plugin.utils import set_librenms_device_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": {"primary": 10}}
set_librenms_device_id(obj, None, server_key="primary")
assert obj.custom_field_data["librenms_id"] == {"primary": 10}
def test_legacy_bare_int_blocks_write(self):
"""Legacy bare-integer value blocks the write (no silent migration)."""
from netbox_librenms_plugin.utils import set_librenms_device_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": 7}
set_librenms_device_id(obj, 99, server_key="secondary")
# Write must be skipped; user must use the migration workflow.
assert obj.custom_field_data["librenms_id"] == 7
def test_adds_new_server_key_to_existing_dict(self):
"""Adding a new server key preserves existing keys."""
from netbox_librenms_plugin.utils import set_librenms_device_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": {"primary": 5}}
set_librenms_device_id(obj, 20, server_key="secondary")
assert obj.custom_field_data["librenms_id"] == {"primary": 5, "secondary": 20}
def test_string_integer_is_coerced(self):
"""String '42' is coerced to int 42."""
from netbox_librenms_plugin.utils import set_librenms_device_id
obj = MagicMock()
obj.custom_field_data = {}
set_librenms_device_id(obj, "42", server_key="primary")
assert obj.custom_field_data["librenms_id"] == {"primary": 42}
def test_unexpected_cf_type_reset_to_empty(self):
"""If custom_field_data has unexpected type for librenms_id, it is reset."""
from netbox_librenms_plugin.utils import set_librenms_device_id
obj = MagicMock()
obj.custom_field_data = {"librenms_id": "unexpected-string"}
set_librenms_device_id(obj, 5, server_key="primary")
assert obj.custom_field_data["librenms_id"] == {"primary": 5}