feat: add personalized navigation and tenant filtering
This commit is contained in:
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.test import RequestFactory, SimpleTestCase
|
||||
|
||||
from netbox_utilities.middleware import GlobalTenantFilterMiddleware
|
||||
|
||||
|
||||
class TenantFilterSet:
|
||||
base_filters = {"tenant_id": object()}
|
||||
|
||||
|
||||
class SharedFilterSet:
|
||||
base_filters = {"status": object()}
|
||||
|
||||
|
||||
def resolver_match(filterset=None, view_name="dcim:device_list"):
|
||||
view_class = SimpleNamespace(filterset=filterset)
|
||||
return SimpleNamespace(
|
||||
func=SimpleNamespace(view_class=view_class),
|
||||
namespaces=["dcim"],
|
||||
view_name=view_name,
|
||||
)
|
||||
|
||||
|
||||
class GlobalTenantFilterMiddlewareTest(SimpleTestCase):
|
||||
def setUp(self):
|
||||
self.factory = RequestFactory()
|
||||
|
||||
@patch("netbox_utilities.middleware.resolve")
|
||||
def test_injects_and_overrides_tenant_id(self, mocked_resolve):
|
||||
mocked_resolve.return_value = resolver_match(TenantFilterSet)
|
||||
request = self.factory.get("/dcim/devices/?tenant_id=7&status=active")
|
||||
|
||||
GlobalTenantFilterMiddleware._inject_filter_parameter(request, 42)
|
||||
|
||||
self.assertEqual(request.GET.getlist("tenant_id"), ["42"])
|
||||
self.assertEqual(request.GET["status"], "active")
|
||||
|
||||
@patch("netbox_utilities.middleware.resolve")
|
||||
def test_does_not_modify_shared_list(self, mocked_resolve):
|
||||
mocked_resolve.return_value = resolver_match(SharedFilterSet)
|
||||
request = self.factory.get("/dcim/manufacturers/?status=active")
|
||||
|
||||
GlobalTenantFilterMiddleware._inject_filter_parameter(request, 42)
|
||||
|
||||
self.assertNotIn("tenant_id", request.GET)
|
||||
self.assertEqual(request.GET["status"], "active")
|
||||
|
||||
@patch("netbox_utilities.middleware.resolve")
|
||||
def test_filters_tenant_list_by_primary_key(self, mocked_resolve):
|
||||
mocked_resolve.return_value = resolver_match(None, "tenancy:tenant_list")
|
||||
request = self.factory.get("/tenancy/tenants/")
|
||||
|
||||
GlobalTenantFilterMiddleware._inject_filter_parameter(request, 42)
|
||||
|
||||
self.assertEqual(request.GET.getlist("id"), ["42"])
|
||||
@@ -0,0 +1,21 @@
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
from netbox_utilities.navigation_helpers import normalize_preferences
|
||||
|
||||
|
||||
class NormalizePreferencesTest(SimpleTestCase):
|
||||
def test_removes_unknown_and_duplicate_keys(self):
|
||||
order, hidden = normalize_preferences(
|
||||
["devices", "unknown", "devices"],
|
||||
["ipam", "unknown", "ipam"],
|
||||
["organization", "devices", "ipam"],
|
||||
)
|
||||
|
||||
self.assertEqual(order, ["devices", "organization", "ipam"])
|
||||
self.assertEqual(hidden, ["ipam"])
|
||||
|
||||
def test_new_menus_are_appended(self):
|
||||
order, hidden = normalize_preferences(["devices"], [], ["devices", "vpn"])
|
||||
|
||||
self.assertEqual(order, ["devices", "vpn"])
|
||||
self.assertEqual(hidden, [])
|
||||
@@ -0,0 +1,34 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
from netbox_utilities.tenant_scope import object_matches_tenant
|
||||
|
||||
|
||||
class FakeMeta:
|
||||
def __init__(self, *field_names):
|
||||
self.fields = [SimpleNamespace(name=name) for name in field_names]
|
||||
|
||||
def get_fields(self):
|
||||
return self.fields
|
||||
|
||||
|
||||
class TenantScopeTest(SimpleTestCase):
|
||||
def test_direct_tenant_match(self):
|
||||
obj = SimpleNamespace(_meta=FakeMeta("tenant"), tenant_id=42)
|
||||
self.assertTrue(object_matches_tenant(obj, 42))
|
||||
self.assertFalse(object_matches_tenant(obj, 7))
|
||||
|
||||
def test_child_inherits_tenant_from_device(self):
|
||||
device = SimpleNamespace(_meta=FakeMeta("tenant"), tenant_id=42)
|
||||
interface = SimpleNamespace(_meta=FakeMeta("device"), device=device)
|
||||
self.assertTrue(object_matches_tenant(interface, 42))
|
||||
self.assertFalse(object_matches_tenant(interface, 7))
|
||||
|
||||
def test_shared_reference_data_remains_visible(self):
|
||||
manufacturer = SimpleNamespace(_meta=FakeMeta("name"))
|
||||
self.assertTrue(object_matches_tenant(manufacturer, 42))
|
||||
|
||||
def test_unassigned_tenant_object_is_hidden(self):
|
||||
obj = SimpleNamespace(_meta=FakeMeta("tenant"), tenant_id=None)
|
||||
self.assertFalse(object_matches_tenant(obj, 42))
|
||||
Reference in New Issue
Block a user