Files
netbox-librenms-plugin/netbox_librenms_plugin/tests/test_coverage_list.py
Vlastislav Svatek 673e67106e
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
first commit
2026-06-05 10:39:05 +02:00

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