1407 lines
69 KiB
Python
1407 lines
69 KiB
Python
"""
|
|
Tests for views/imports/list.py — targeting ≥95% coverage.
|
|
|
|
Tests the LibreNMSImportView: get_required_permission, should_use_background_job,
|
|
_load_job_results, get, get_queryset, get_table, and _get_import_queryset.
|
|
|
|
Conventions:
|
|
- Plain pytest classes (no Django TestCase)
|
|
- No @pytest.mark.django_db — all DB interactions mocked
|
|
- Inline imports inside test methods
|
|
- object.__new__(ViewClass) for instantiation
|
|
- MagicMock for all external dependencies
|
|
"""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
|
|
class TestGetRequiredPermission:
|
|
"""Tests for get_required_permission()."""
|
|
|
|
def test_returns_device_view_permission(self):
|
|
"""get_required_permission returns the 'view' permission for Device model."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
with patch("netbox_librenms_plugin.views.imports.list.Device") as mock_device:
|
|
with patch("utilities.permissions.get_permission_for_model") as mock_perm:
|
|
mock_perm.return_value = "dcim.view_device"
|
|
result = view.get_required_permission()
|
|
assert result == "dcim.view_device"
|
|
mock_perm.assert_called_once_with(mock_device, "view")
|
|
|
|
|
|
class TestShouldUseBackgroundJob:
|
|
"""Tests for should_use_background_job()."""
|
|
|
|
def test_non_superuser_returns_false(self):
|
|
"""Non-superusers always get synchronous mode."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
view._filter_form_data = {"use_background_job": True}
|
|
view.request = MagicMock()
|
|
view.request.user.is_superuser = False
|
|
|
|
assert view.should_use_background_job() is False
|
|
|
|
def test_superuser_use_background_true(self):
|
|
"""Superuser with use_background_job=True returns True."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
view._filter_form_data = {"use_background_job": True}
|
|
view.request = MagicMock()
|
|
view.request.user.is_superuser = True
|
|
|
|
assert view.should_use_background_job() is True
|
|
|
|
def test_superuser_use_background_false(self):
|
|
"""Superuser with use_background_job=False returns False."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
view._filter_form_data = {"use_background_job": False}
|
|
view.request = MagicMock()
|
|
view.request.user.is_superuser = True
|
|
|
|
assert view.should_use_background_job() is False
|
|
|
|
def test_superuser_field_missing_defaults_true(self):
|
|
"""When use_background_job key absent, default is True for superusers."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
view._filter_form_data = {}
|
|
view.request = MagicMock()
|
|
view.request.user.is_superuser = True
|
|
|
|
assert view.should_use_background_job() is True
|
|
|
|
|
|
class TestLoadJobResults:
|
|
"""Tests for _load_job_results()."""
|
|
|
|
def test_job_not_found_returns_empty(self):
|
|
"""Returns [] when job doesn't exist (DoesNotExist path lines 79-81)."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
|
|
class MockDoesNotExist(Exception):
|
|
pass
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.logger") as mock_logger:
|
|
with patch("core.models.Job") as mock_job_cls:
|
|
mock_job_cls.DoesNotExist = MockDoesNotExist
|
|
mock_job_cls.objects.get.side_effect = MockDoesNotExist("not found")
|
|
|
|
result = view._load_job_results(999)
|
|
assert result == []
|
|
mock_logger.warning.assert_called()
|
|
|
|
def test_job_not_completed_returns_empty(self):
|
|
"""Returns [] when job status is not 'completed'."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.logger"):
|
|
mock_job = MagicMock()
|
|
mock_job.status = "running"
|
|
|
|
with patch("core.models.Job") as mock_job_cls:
|
|
mock_job_cls.objects.get.return_value = mock_job
|
|
|
|
result = view._load_job_results(42)
|
|
assert result == []
|
|
|
|
def test_empty_device_ids_returns_empty(self):
|
|
"""Returns [] when job data has no device_ids."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.logger"):
|
|
mock_job = MagicMock()
|
|
mock_job.status = "completed"
|
|
mock_job.data = {"device_ids": [], "filters": {}, "server_key": "default"}
|
|
|
|
with patch("core.models.Job") as mock_job_cls:
|
|
mock_job_cls.objects.get.return_value = mock_job
|
|
|
|
result = view._load_job_results(42)
|
|
assert result == []
|
|
|
|
def test_devices_loaded_from_cache(self):
|
|
"""Returns validated devices found in cache for each device_id."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
|
|
mock_device_a = {"device_id": 1, "hostname": "router1"}
|
|
mock_device_b = {"device_id": 2, "hostname": "router2"}
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.logger"):
|
|
mock_job = MagicMock()
|
|
mock_job.status = "completed"
|
|
mock_job.data = {
|
|
"device_ids": [1, 2],
|
|
"filters": {},
|
|
"server_key": "default",
|
|
"vc_detection_enabled": False,
|
|
"use_sysname": True,
|
|
"strip_domain": False,
|
|
"cached_at": "2024-01-01T00:00:00",
|
|
"cache_timeout": 300,
|
|
}
|
|
|
|
with patch("core.models.Job") as mock_job_cls:
|
|
mock_job_cls.objects.get.return_value = mock_job
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
with patch("netbox_librenms_plugin.import_utils.get_validated_device_cache_key") as mock_key:
|
|
mock_key.side_effect = lambda **kwargs: f"key_{kwargs['device_id']}"
|
|
mock_cache.get.side_effect = lambda key: mock_device_a if key == "key_1" else mock_device_b
|
|
|
|
result = view._load_job_results(42)
|
|
assert len(result) == 2
|
|
assert mock_device_a in result
|
|
assert mock_device_b in result
|
|
|
|
def test_load_job_results_sets_vc_detection_enabled_from_job_data(self):
|
|
"""vc_detection_enabled from job data is preserved on the view instance."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.logger"):
|
|
mock_job = MagicMock()
|
|
mock_job.status = "completed"
|
|
mock_job.data = {
|
|
"device_ids": [1],
|
|
"filters": {},
|
|
"server_key": "default",
|
|
"vc_detection_enabled": True,
|
|
"use_sysname": True,
|
|
"strip_domain": False,
|
|
}
|
|
|
|
with patch("core.models.Job") as mock_job_cls:
|
|
mock_job_cls.objects.get.return_value = mock_job
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
with patch("netbox_librenms_plugin.import_utils.get_validated_device_cache_key") as mock_key:
|
|
mock_key.return_value = "key_1"
|
|
mock_cache.get.return_value = {"device_id": 1}
|
|
|
|
result = view._load_job_results(42)
|
|
|
|
assert len(result) == 1
|
|
assert view._vc_detection_enabled is True
|
|
|
|
def test_cache_miss_skips_device(self):
|
|
"""Devices missing from cache are silently skipped."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
|
|
mock_device = {"device_id": 1, "hostname": "router1"}
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.logger"):
|
|
mock_job = MagicMock()
|
|
mock_job.status = "completed"
|
|
mock_job.data = {
|
|
"device_ids": [1, 2],
|
|
"filters": {},
|
|
"server_key": "default",
|
|
"vc_detection_enabled": False,
|
|
"use_sysname": True,
|
|
"strip_domain": False,
|
|
}
|
|
|
|
with patch("core.models.Job") as mock_job_cls:
|
|
mock_job_cls.objects.get.return_value = mock_job
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
with patch("netbox_librenms_plugin.import_utils.get_validated_device_cache_key") as mock_key:
|
|
mock_key.side_effect = lambda **kwargs: f"key_{kwargs['device_id']}"
|
|
# device_id=2 is missing from cache (returns None)
|
|
mock_cache.get.side_effect = lambda key: mock_device if key == "key_1" else None
|
|
|
|
result = view._load_job_results(42)
|
|
assert len(result) == 1
|
|
assert result[0] == mock_device
|
|
|
|
def test_all_cache_expired_logs_error(self):
|
|
"""When all devices missing from cache, logs error and returns []."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.logger") as mock_logger:
|
|
mock_job = MagicMock()
|
|
mock_job.status = "completed"
|
|
mock_job.data = {
|
|
"device_ids": [1, 2],
|
|
"filters": {},
|
|
"server_key": "default",
|
|
"vc_detection_enabled": False,
|
|
"use_sysname": True,
|
|
"strip_domain": False,
|
|
}
|
|
|
|
with patch("core.models.Job") as mock_job_cls:
|
|
mock_job_cls.objects.get.return_value = mock_job
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
with patch("netbox_librenms_plugin.import_utils.get_validated_device_cache_key") as mock_key:
|
|
mock_key.return_value = "some_key"
|
|
mock_cache.get.return_value = None
|
|
|
|
result = view._load_job_results(42)
|
|
assert result == []
|
|
mock_logger.error.assert_called_once()
|
|
|
|
|
|
class TestGetTable:
|
|
"""Tests for get_table()."""
|
|
|
|
def test_returns_table_with_import_data(self):
|
|
"""get_table returns a DeviceImportTable populated from _import_data."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
view._import_data = [{"device_id": 1, "hostname": "router1"}]
|
|
|
|
request = MagicMock()
|
|
request.GET.get.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable") as mock_table_cls:
|
|
mock_table = MagicMock()
|
|
mock_table_cls.return_value = mock_table
|
|
|
|
result = view.get_table([], request, bulk_actions=True)
|
|
assert result is mock_table
|
|
mock_table_cls.assert_called_once_with(
|
|
view._import_data,
|
|
order_by=None,
|
|
)
|
|
|
|
def test_get_table_loads_import_data_when_missing(self):
|
|
"""When _import_data is absent, get_table calls _get_import_queryset."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
# No _import_data set
|
|
view._job_results_loaded = False
|
|
view._filters_submitted = False
|
|
|
|
request = MagicMock()
|
|
request.GET.get.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable") as mock_table_cls:
|
|
mock_table_cls.return_value = MagicMock()
|
|
view.get_table([], request, bulk_actions=True)
|
|
assert hasattr(view, "_import_data")
|
|
|
|
|
|
class TestGetQueryset:
|
|
"""Tests for get_queryset()."""
|
|
|
|
def test_returns_empty_device_queryset(self):
|
|
"""get_queryset always returns Device.objects.none()."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
view._job_results_loaded = False
|
|
view._filters_submitted = False
|
|
|
|
request = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.Device") as mock_device:
|
|
mock_device.objects.none.return_value = []
|
|
result = view.get_queryset(request)
|
|
assert result == []
|
|
mock_device.objects.none.assert_called_once()
|
|
|
|
def test_sets_import_data(self):
|
|
"""get_queryset sets _import_data via _get_import_queryset."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
view._job_results_loaded = False
|
|
view._filters_submitted = False
|
|
|
|
request = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.Device") as mock_device:
|
|
mock_device.objects.none.return_value = []
|
|
view.get_queryset(request)
|
|
assert hasattr(view, "_import_data")
|
|
assert view._import_data == []
|
|
|
|
|
|
class TestGetImportQueryset:
|
|
"""Tests for _get_import_queryset()."""
|
|
|
|
def test_job_results_loaded_returns_existing(self):
|
|
"""When _job_results_loaded is True, returns existing _import_data."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
view._job_results_loaded = True
|
|
view._import_data = [{"device_id": 1}]
|
|
|
|
result = view._get_import_queryset()
|
|
assert result == [{"device_id": 1}]
|
|
|
|
def test_filters_not_submitted_returns_empty(self):
|
|
"""When no filters submitted, returns empty list."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
view._job_results_loaded = False
|
|
view._filters_submitted = False
|
|
|
|
result = view._get_import_queryset()
|
|
assert result == []
|
|
|
|
def test_filter_warning_returns_empty(self):
|
|
"""When _filter_warning is set, returns empty list."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
view._job_results_loaded = False
|
|
view._filters_submitted = True
|
|
view._filter_warning = "Some warning"
|
|
|
|
result = view._get_import_queryset()
|
|
assert result == []
|
|
|
|
def test_calls_process_device_filters(self):
|
|
"""When filters submitted and valid, calls process_device_filters."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
view._job_results_loaded = False
|
|
view._filters_submitted = True
|
|
view._filter_warning = None
|
|
view._filter_form_data = {
|
|
"librenms_location": "DC1",
|
|
"enable_vc_detection": False,
|
|
"clear_cache": False,
|
|
"show_disabled": False,
|
|
"exclude_existing": False,
|
|
}
|
|
view._vc_detection_enabled = False
|
|
view._cache_cleared = False
|
|
view._use_sysname = True
|
|
view._strip_domain = False
|
|
|
|
mock_request = MagicMock()
|
|
view._request = mock_request
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
view._librenms_api = mock_api
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.process_device_filters") as mock_process:
|
|
mock_process.return_value = ([{"device_id": 1}], False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings_cls:
|
|
mock_settings_cls.objects.first.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
mock_cache.get.return_value = None
|
|
|
|
result = view._get_import_queryset()
|
|
mock_process.assert_called_once()
|
|
assert result == [{"device_id": 1}]
|
|
|
|
|
|
class TestGetView:
|
|
"""Tests for the get() method of LibreNMSImportView."""
|
|
|
|
def _make_view_with_request(self, superuser=True, query_params=None):
|
|
"""Helper to set up a view instance with a mock request."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
request = MagicMock()
|
|
request.user.is_superuser = superuser
|
|
request.user.username = "testuser"
|
|
|
|
params = query_params or {}
|
|
request.GET.get = lambda key, default=None: params.get(key, default)
|
|
request.GET.__contains__ = lambda self, key: key in params
|
|
|
|
view.request = request
|
|
return view, request
|
|
|
|
def test_get_job_id_loads_results(self):
|
|
"""When job_id is in GET params, _load_job_results is called."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request(query_params={"job_id": "42"})
|
|
|
|
mock_devices = [{"device_id": 1, "hostname": "router1"}]
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(view, "_load_job_results", return_value=mock_devices) as mock_load:
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = False
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
view.get(request)
|
|
mock_load.assert_called_once_with(42)
|
|
|
|
def test_get_job_id_preserves_vc_flag_when_query_flag_missing(self):
|
|
"""job_id pages keep vc_detection_enabled from loaded job results."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request(query_params={"job_id": "42"})
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
def _load_job_side_effect(_job_id):
|
|
view._vc_detection_enabled = True
|
|
return [{"device_id": 1, "hostname": "router1"}]
|
|
|
|
with patch.object(view, "_load_job_results", side_effect=_load_job_side_effect) as mock_load:
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref", return_value=None):
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = False
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches",
|
|
return_value=[],
|
|
):
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
view.get(request)
|
|
|
|
mock_load.assert_called_once_with(42)
|
|
context = mock_render.call_args[0][2]
|
|
assert context["vc_detection_enabled"] is True
|
|
|
|
def test_get_invalid_job_id_logs_warning(self):
|
|
"""Invalid (non-integer) job_id is caught and logged."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request(query_params={"job_id": "not-an-int"})
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = False
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
with patch("netbox_librenms_plugin.views.imports.list.logger") as mock_logger:
|
|
view.get(request)
|
|
mock_logger.warning.assert_called()
|
|
|
|
def test_get_no_job_id_renders_template(self):
|
|
"""Normal GET without job_id renders the import template."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request()
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = False
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
view.get(request)
|
|
mock_render.assert_called_once()
|
|
# Verify called with correct template
|
|
call_args = mock_render.call_args
|
|
assert "librenms_import.html" in call_args[0][1]
|
|
|
|
def test_get_settings_exception_falls_back_to_none(self):
|
|
"""LibreNMSSettings exception during GET is caught and settings set to None."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request()
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.side_effect = Exception("DB error")
|
|
mock_settings.objects.get_or_create.side_effect = Exception("DB error")
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = False
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
# Should not raise
|
|
view.get(request)
|
|
mock_render.assert_called_once()
|
|
ctx = mock_render.call_args[0][2]
|
|
assert ctx["settings"] is None
|
|
|
|
def test_get_filters_submitted_with_valid_form(self):
|
|
"""When filters are submitted and form is valid, processes filters."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request(query_params={"apply_filters": "1", "librenms_location": "DC1"})
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = True
|
|
mock_form.cleaned_data = {
|
|
"enable_vc_detection": False,
|
|
"clear_cache": False,
|
|
"use_background_job": False,
|
|
}
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
with patch(
|
|
"netbox_librenms_plugin.import_utils.get_cache_metadata_key"
|
|
) as mock_meta_key:
|
|
mock_meta_key.return_value = "meta_key"
|
|
with patch(
|
|
"netbox_librenms_plugin.import_utils.get_device_count_for_filters"
|
|
) as mock_count:
|
|
mock_count.return_value = 5
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
mock_cache.get.return_value = None
|
|
with patch.object(view, "_get_import_queryset", return_value=[]):
|
|
view.get(request)
|
|
mock_render.assert_called_once()
|
|
|
|
def test_get_background_job_enqueued_for_superuser(self):
|
|
"""Superuser with workers available triggers background job enqueue."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request(
|
|
superuser=True,
|
|
query_params={"apply_filters": "1", "librenms_location": "DC1"},
|
|
)
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = True
|
|
mock_form.cleaned_data = {
|
|
"enable_vc_detection": False,
|
|
"clear_cache": False,
|
|
"use_background_job": True, # Background mode
|
|
}
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_workers_for_queue") as mock_workers:
|
|
mock_workers.return_value = 1 # Workers available
|
|
|
|
with patch("netbox_librenms_plugin.import_utils.get_cache_metadata_key") as mock_meta:
|
|
mock_meta.return_value = "meta_key"
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
mock_cache.get.return_value = None # No cached results
|
|
|
|
with patch(
|
|
"netbox_librenms_plugin.import_utils.get_device_count_for_filters"
|
|
) as mock_count:
|
|
mock_count.return_value = 10
|
|
|
|
with patch("netbox_librenms_plugin.jobs.FilterDevicesJob") as mock_job_cls:
|
|
mock_job = MagicMock()
|
|
mock_job.pk = 123
|
|
mock_job.job_id = "uuid-123"
|
|
mock_job_cls.enqueue.return_value = mock_job
|
|
|
|
import json
|
|
from django.http import JsonResponse
|
|
|
|
result = view.get(request)
|
|
assert isinstance(result, JsonResponse)
|
|
data = json.loads(result.content)
|
|
assert "job_pk" in data
|
|
assert "poll_url" in data
|
|
|
|
def test_get_no_workers_falls_back_to_sync(self):
|
|
"""With no RQ workers, falls back to synchronous processing."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request(
|
|
superuser=True,
|
|
query_params={"apply_filters": "1", "librenms_location": "DC1"},
|
|
)
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = True
|
|
mock_form.cleaned_data = {
|
|
"enable_vc_detection": False,
|
|
"clear_cache": False,
|
|
"use_background_job": True,
|
|
}
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_workers_for_queue") as mock_workers:
|
|
mock_workers.return_value = 0 # No workers
|
|
|
|
with patch("netbox_librenms_plugin.import_utils.get_cache_metadata_key") as mock_meta:
|
|
mock_meta.return_value = "meta_key"
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
mock_cache.get.return_value = None
|
|
|
|
with patch(
|
|
"netbox_librenms_plugin.import_utils.get_device_count_for_filters"
|
|
) as mock_count:
|
|
mock_count.return_value = 5
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.messages"
|
|
) as mock_messages:
|
|
with patch.object(
|
|
view, "_get_import_queryset", return_value=[]
|
|
):
|
|
view.get(request)
|
|
# Should render page (synchronous fallback)
|
|
mock_render.assert_called_once()
|
|
mock_messages.warning.assert_called_once()
|
|
|
|
def test_get_job_results_expired_shows_warning(self):
|
|
"""When job results are empty, shows warning message to user."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request(query_params={"job_id": "42"})
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = False
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch.object(view, "_load_job_results", return_value=[]):
|
|
with patch("netbox_librenms_plugin.views.imports.list.messages") as mock_messages:
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
view.get(request)
|
|
mock_messages.warning.assert_called_once()
|
|
|
|
def test_get_legacy_skip_vc_detection_flag(self):
|
|
"""Legacy skip_vc_detection=true sets vc_detection_enabled=False."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request(query_params={"skip_vc_detection": "true"})
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = False
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
view.get(request)
|
|
assert view._vc_detection_enabled is False
|
|
|
|
def test_get_enable_vc_detection_flag(self):
|
|
"""enable_vc_detection=1 sets vc_detection_enabled=True."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request(query_params={"enable_vc_detection": "1"})
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = False
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
view.get(request)
|
|
assert view._vc_detection_enabled is True
|
|
|
|
def test_get_context_includes_can_use_background_jobs(self):
|
|
"""Rendered context includes can_use_background_jobs keyed on is_superuser."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request(superuser=True)
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = False
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
view.get(request)
|
|
ctx = mock_render.call_args[0][2]
|
|
assert ctx["can_use_background_jobs"] is True
|
|
|
|
def test_get_form_non_field_errors_set_warning(self):
|
|
"""Non-field form validation errors are stored as _filter_warning."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view_with_request(query_params={"apply_filters": "1"})
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
expected_warning = "At least one filter is required."
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = False
|
|
mock_form.non_field_errors.return_value = [expected_warning]
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
view.get(request)
|
|
assert view._filter_warning == expected_warning
|
|
|
|
|
|
class TestGetViewFilterFields:
|
|
"""Tests that exercise individual filter field extraction paths in get()."""
|
|
|
|
def _make_view(self, query_params=None):
|
|
"""Helper to create a view with a mock request containing specific params."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
request = MagicMock()
|
|
request.user.is_superuser = False
|
|
request.user.username = "testuser"
|
|
params = query_params or {}
|
|
request.GET.get = lambda key, default=None: params.get(key, default)
|
|
request.GET.__contains__ = lambda self, key: key in params
|
|
view.request = request
|
|
return view, request
|
|
|
|
def test_get_all_filter_fields_extracted(self):
|
|
"""All six filter fields are extracted into libre_filters for background jobs."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view(
|
|
query_params={
|
|
"apply_filters": "1",
|
|
"librenms_location": "DC1",
|
|
"librenms_type": "network",
|
|
"librenms_os": "ios",
|
|
"librenms_hostname": "router",
|
|
"librenms_sysname": "sw1",
|
|
"librenms_hardware": "Cisco C9300",
|
|
}
|
|
)
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = True
|
|
mock_form.cleaned_data = {
|
|
"enable_vc_detection": False,
|
|
"clear_cache": False,
|
|
"use_background_job": False,
|
|
}
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
with patch(
|
|
"netbox_librenms_plugin.import_utils.get_cache_metadata_key"
|
|
) as mock_meta:
|
|
mock_meta.return_value = "meta_key"
|
|
with patch(
|
|
"netbox_librenms_plugin.import_utils.get_device_count_for_filters"
|
|
) as mock_count:
|
|
mock_count.return_value = 5
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
mock_cache.get.return_value = None
|
|
with patch.object(view, "_get_import_queryset", return_value=[]):
|
|
view.get(request)
|
|
# All filters were submitted — filter count passed to device count
|
|
mock_count.assert_called_once()
|
|
args, kwargs = mock_count.call_args
|
|
filters = (
|
|
kwargs.get("filters")
|
|
if kwargs.get("filters") is not None
|
|
else (args[0] if args else None)
|
|
)
|
|
assert filters.get("location") == "DC1"
|
|
assert filters.get("type") == "network"
|
|
assert filters.get("os") == "ios"
|
|
assert filters.get("hostname") == "router"
|
|
assert filters.get("sysname") == "sw1"
|
|
assert filters.get("hardware") == "Cisco C9300"
|
|
|
|
def test_get_settings_exception_is_caught(self):
|
|
"""LibreNMSSettings exception at the top of get() is caught and settings set to None."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view(query_params={"apply_filters": "1", "librenms_location": "DC1"})
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.side_effect = Exception("DB error")
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = True
|
|
mock_form.cleaned_data = {
|
|
"enable_vc_detection": False,
|
|
"clear_cache": False,
|
|
"use_background_job": False,
|
|
}
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
with patch(
|
|
"netbox_librenms_plugin.import_utils.get_cache_metadata_key"
|
|
) as mock_meta:
|
|
mock_meta.return_value = "meta_key"
|
|
with patch(
|
|
"netbox_librenms_plugin.import_utils.get_device_count_for_filters"
|
|
) as mock_count:
|
|
mock_count.return_value = 3
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
mock_cache.get.return_value = None
|
|
# Should not raise despite the settings exception
|
|
with patch.object(view, "_get_import_queryset", return_value=[]):
|
|
view.get(request)
|
|
mock_render.assert_called_once()
|
|
|
|
def test_get_device_count_exception_defaults_zero(self):
|
|
"""Device count exception falls back to 0 (lines 304-306)."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view(query_params={"apply_filters": "1", "librenms_location": "DC1"})
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = True
|
|
mock_form.cleaned_data = {
|
|
"enable_vc_detection": False,
|
|
"clear_cache": False,
|
|
"use_background_job": False,
|
|
}
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
with patch(
|
|
"netbox_librenms_plugin.import_utils.get_cache_metadata_key"
|
|
) as mock_meta:
|
|
mock_meta.return_value = "meta_key"
|
|
with patch(
|
|
"netbox_librenms_plugin.import_utils.get_device_count_for_filters"
|
|
) as mock_count:
|
|
mock_count.side_effect = Exception("API error")
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
mock_cache.get.return_value = None
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.logger"
|
|
) as mock_logger:
|
|
with patch.object(view, "_get_import_queryset", return_value=[]):
|
|
view.get(request)
|
|
mock_render.assert_called_once()
|
|
mock_logger.error.assert_called()
|
|
context = mock_render.call_args[0][2]
|
|
assert context["device_count"] == 0
|
|
|
|
def test_get_cache_check_exception_continues(self):
|
|
"""Cache check exception is logged and processing continues (lines 293-294)."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view, request = self._make_view(query_params={"apply_filters": "1", "librenms_location": "DC1"})
|
|
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
|
|
with patch.object(LibreNMSImportView, "librenms_api", new_callable=lambda: property(lambda self: mock_api)):
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
mock_settings.objects.get_or_create.return_value = (None, False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
mock_form_cls = MagicMock()
|
|
mock_form = MagicMock()
|
|
mock_form.is_valid.return_value = True
|
|
mock_form.cleaned_data = {
|
|
"enable_vc_detection": False,
|
|
"clear_cache": False,
|
|
"use_background_job": False,
|
|
}
|
|
mock_form_cls.return_value = mock_form
|
|
view.filterset_form = mock_form_cls
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.render") as mock_render:
|
|
mock_render.return_value = MagicMock()
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.DeviceImportTable"):
|
|
with patch(
|
|
"netbox_librenms_plugin.views.imports.list.get_active_cached_searches"
|
|
) as mock_searches:
|
|
mock_searches.return_value = []
|
|
|
|
with patch.object(view, "get_server_info", return_value={}):
|
|
with patch(
|
|
"netbox_librenms_plugin.import_utils.get_cache_metadata_key"
|
|
) as mock_meta:
|
|
# Cache check raises exception
|
|
mock_meta.side_effect = Exception("cache error")
|
|
with patch(
|
|
"netbox_librenms_plugin.import_utils.get_device_count_for_filters"
|
|
) as mock_count:
|
|
mock_count.return_value = 5
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
mock_cache.get.return_value = None
|
|
# Should not raise
|
|
with patch.object(view, "_get_import_queryset", return_value=[]):
|
|
view.get(request)
|
|
mock_render.assert_called_once()
|
|
|
|
|
|
class TestGetImportQuerysetFilterFields:
|
|
"""Tests for individual filter field branches in _get_import_queryset()."""
|
|
|
|
def _make_view(self, filter_data=None):
|
|
"""Helper to create a configured view instance."""
|
|
from netbox_librenms_plugin.views.imports.list import LibreNMSImportView
|
|
|
|
view = object.__new__(LibreNMSImportView)
|
|
view._job_results_loaded = False
|
|
view._filters_submitted = True
|
|
view._filter_warning = None
|
|
view._filter_form_data = filter_data or {}
|
|
view._vc_detection_enabled = False
|
|
view._cache_cleared = False
|
|
view._request = MagicMock()
|
|
mock_api = MagicMock()
|
|
mock_api.server_key = "default"
|
|
view._librenms_api = mock_api
|
|
view._use_sysname = True
|
|
view._strip_domain = False
|
|
return view
|
|
|
|
def test_all_filter_fields_passed_to_process(self):
|
|
"""All 6 filter fields present in filter_data are forwarded to process_device_filters."""
|
|
view = self._make_view(
|
|
filter_data={
|
|
"librenms_location": "DC1",
|
|
"librenms_type": "network",
|
|
"librenms_os": "ios",
|
|
"librenms_hostname": "router01",
|
|
"librenms_sysname": "sw1",
|
|
"librenms_hardware": "Cisco",
|
|
"enable_vc_detection": False,
|
|
"clear_cache": False,
|
|
"show_disabled": False,
|
|
"exclude_existing": False,
|
|
}
|
|
)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.process_device_filters") as mock_process:
|
|
mock_process.return_value = ([], False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
mock_cache.get.return_value = None
|
|
|
|
view._get_import_queryset()
|
|
|
|
call_kwargs = mock_process.call_args[1]
|
|
filters = call_kwargs["filters"]
|
|
assert filters["location"] == "DC1"
|
|
assert filters["type"] == "network"
|
|
assert filters["os"] == "ios"
|
|
assert filters["hostname"] == "router01"
|
|
assert filters["sysname"] == "sw1"
|
|
assert filters["hardware"] == "Cisco"
|
|
|
|
def test_get_import_queryset_returns_empty_on_no_results(self):
|
|
"""_get_import_queryset returns [] when process_device_filters returns empty."""
|
|
view = self._make_view(
|
|
filter_data={
|
|
"librenms_location": "DC1",
|
|
"enable_vc_detection": False,
|
|
"clear_cache": False,
|
|
}
|
|
)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.process_device_filters") as mock_process:
|
|
mock_process.return_value = ([], False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
mock_cache.get.return_value = None
|
|
result = view._get_import_queryset()
|
|
assert result == []
|
|
|
|
def test_cache_metadata_found_sets_timestamps(self):
|
|
"""When cache metadata is found, timestamps are set (lines 523-527)."""
|
|
mock_device = {"device_id": 1, "_validation": {}}
|
|
view = self._make_view(
|
|
filter_data={
|
|
"librenms_location": "DC1",
|
|
"enable_vc_detection": True,
|
|
"clear_cache": False,
|
|
}
|
|
)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.process_device_filters") as mock_process:
|
|
mock_process.return_value = ([mock_device], False)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
mock_cache.get.return_value = {
|
|
"cached_at": "2024-01-01T00:00:00",
|
|
"cache_timeout": 600,
|
|
}
|
|
|
|
with patch("netbox_librenms_plugin.import_utils.get_cache_metadata_key") as mock_meta_key:
|
|
mock_meta_key.return_value = "meta_key"
|
|
|
|
result = view._get_import_queryset()
|
|
assert len(result) == 1
|
|
assert view._cache_timestamp == "2024-01-01T00:00:00"
|
|
assert view._cache_timeout == 600
|
|
# _vc_detection_enabled is propagated to device validation
|
|
assert result[0]["_validation"]["_vc_detection_enabled"] is True
|
|
|
|
def test_cache_metadata_missing_sets_flag(self):
|
|
"""When cache metadata is absent, _cache_metadata_missing is set True."""
|
|
mock_device = {"device_id": 1, "_validation": {}}
|
|
view = self._make_view(
|
|
filter_data={
|
|
"librenms_location": "DC1",
|
|
"enable_vc_detection": False,
|
|
"clear_cache": False,
|
|
}
|
|
)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.process_device_filters") as mock_process:
|
|
mock_process.return_value = ([mock_device], True)
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.get_user_pref") as mock_pref:
|
|
mock_pref.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.LibreNMSSettings") as mock_settings:
|
|
mock_settings.objects.first.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.views.imports.list.cache") as mock_cache:
|
|
# Cache metadata not found
|
|
mock_cache.get.return_value = None
|
|
|
|
with patch("netbox_librenms_plugin.import_utils.get_cache_metadata_key") as mock_meta_key:
|
|
mock_meta_key.return_value = "meta_key"
|
|
|
|
view._get_import_queryset()
|
|
assert view._cache_metadata_missing is True
|