fix: bind rack integrations to rendered devices
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.context_processors import PermWrapper
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
@@ -9,75 +7,17 @@ from netbox_utilities.template_content import UtilitiesGlobalContent
|
||||
|
||||
|
||||
class OptionalRackIntegrationHeadTest(SimpleTestCase):
|
||||
@staticmethod
|
||||
def _context(path):
|
||||
request = RequestFactory().get(path)
|
||||
def test_foreign_rack_adapters_are_not_loaded_by_the_global_head(self):
|
||||
request = RequestFactory().get("/dcim/racks/3/reorder/")
|
||||
request.user = AnonymousUser()
|
||||
return {
|
||||
"request": request,
|
||||
"settings": settings,
|
||||
"csrf_token": "",
|
||||
"perms": PermWrapper(request.user),
|
||||
}
|
||||
html = UtilitiesGlobalContent(
|
||||
{
|
||||
"request": request,
|
||||
"settings": settings,
|
||||
"csrf_token": "",
|
||||
"perms": PermWrapper(request.user),
|
||||
}
|
||||
).head()
|
||||
|
||||
def test_reorder_adapter_is_emitted_for_an_empty_enabled_payload(self):
|
||||
data = {
|
||||
"columns": 12,
|
||||
"unit_width": 220,
|
||||
"images": True,
|
||||
"labels": True,
|
||||
"devices": [],
|
||||
"status": "ready",
|
||||
"schema_version": 3,
|
||||
"complete": True,
|
||||
}
|
||||
with (
|
||||
patch("netbox_utilities.template_content.get_reorder_rack_width_data", return_value=data),
|
||||
patch("netbox_utilities.template_content.get_topology_rack_width_data", return_value=None),
|
||||
):
|
||||
html = UtilitiesGlobalContent(self._context("/dcim/racks/3/reorder/")).head()
|
||||
|
||||
self.assertIn('id="netbox-utilities-reorder-rack-width-data"', html)
|
||||
self.assertIn("netbox_utilities/reorder-rack-width.js", html)
|
||||
self.assertIn('"status": "ready"', html)
|
||||
|
||||
def test_topology_adapter_is_emitted_for_an_empty_enabled_payload(self):
|
||||
data = {"devices": [], "status": "ready", "schema_version": 3, "complete": True}
|
||||
with (
|
||||
patch("netbox_utilities.template_content.get_reorder_rack_width_data", return_value=None),
|
||||
patch("netbox_utilities.template_content.get_topology_rack_width_data", return_value=data),
|
||||
):
|
||||
html = UtilitiesGlobalContent(
|
||||
self._context("/plugins/netbox_topology_views/rack-elevation/?rack_id=3")
|
||||
).head()
|
||||
|
||||
self.assertIn('id="netbox-utilities-topology-rack-width-data"', html)
|
||||
self.assertIn("netbox_utilities/topology-rack-width.js", html)
|
||||
self.assertIn('"status": "ready"', html)
|
||||
|
||||
def test_topology_widths_are_rendered_server_side_without_javascript(self):
|
||||
data = {
|
||||
"devices": [
|
||||
{
|
||||
"device_id": 17,
|
||||
"url": "/dcim/devices/17/",
|
||||
"left_percent": 50,
|
||||
"width_percent": 50,
|
||||
}
|
||||
],
|
||||
"status": "ready",
|
||||
"schema_version": 3,
|
||||
"complete": True,
|
||||
}
|
||||
with (
|
||||
patch("netbox_utilities.template_content.get_reorder_rack_width_data", return_value=None),
|
||||
patch("netbox_utilities.template_content.get_topology_rack_width_data", return_value=data),
|
||||
):
|
||||
html = UtilitiesGlobalContent(
|
||||
self._context("/plugins/netbox_topology_views/rack-elevation/?rack_id=3")
|
||||
).head()
|
||||
|
||||
self.assertIn('id="netbox-utilities-topology-rack-width-styles"', html)
|
||||
self.assertIn('.rack-device[href="/dcim/devices/17/"]', html)
|
||||
self.assertIn("left: calc(50% + 3px) !important", html)
|
||||
self.assertIn("width: calc(50% - 6px) !important", html)
|
||||
self.assertNotIn("netbox-utilities-reorder-rack-width-data", html)
|
||||
self.assertNotIn("netbox-utilities-topology-rack-width-data", html)
|
||||
|
||||
@@ -310,6 +310,8 @@ class ReorderRackFrontendTest(SimpleTestCase):
|
||||
self.assertIn("{% for device in reorder_devices %}", grid)
|
||||
self.assertIn('gs-w="{{ device.grid_width }}"', grid)
|
||||
self.assertIn('gs-x="{{ device.grid_x }}"', grid)
|
||||
self.assertIn('json_script:"netbox-utilities-reorder-rack-width-data"', page)
|
||||
self.assertIn("netbox_utilities/reorder-rack-width.js", page)
|
||||
|
||||
get_template("netbox_utilities/reorder_rack.html")
|
||||
rendered_grid = render_to_string(
|
||||
@@ -403,6 +405,45 @@ class ReorderRackFrontendTest(SimpleTestCase):
|
||||
self.assertEqual(template_name, "netbox_utilities/reorder_rack.html")
|
||||
self.assertEqual(context["reorder_devices"][0]["label"], "LEO-Fritzbox")
|
||||
self.assertEqual(context["reorder_devices"][0]["grid_width"], 6)
|
||||
self.assertEqual(context["asset_version"], "0.9.6")
|
||||
self.assertIs(context["reorder_rack_width_data"], get_width_data.return_value)
|
||||
get_width_data.assert_called_once()
|
||||
self.assertIs(get_width_data.call_args.kwargs["rack"], rack)
|
||||
self.assertIsNotNone(get_width_data.call_args.kwargs["devices"])
|
||||
|
||||
@patch("netbox_utilities.reorder_rack.get_permission_for_model", return_value="dcim.change_device")
|
||||
def test_view_owned_rack_data_does_not_repeat_route_or_object_filtering(self, _get_permission):
|
||||
request = RequestFactory().get("/dcim/racks/3/reorder/")
|
||||
request.user = MagicMock()
|
||||
request.user.has_perm.return_value = True
|
||||
rack = SimpleNamespace(pk=3, u_height=42, desc_units=False)
|
||||
device = SimpleNamespace(
|
||||
pk=334,
|
||||
rack_id=3,
|
||||
label="LEO-Fritzbox",
|
||||
device_type=SimpleNamespace(
|
||||
u_height=1,
|
||||
is_full_depth=False,
|
||||
front_image=None,
|
||||
rear_image=None,
|
||||
),
|
||||
role=SimpleNamespace(color="f0a000"),
|
||||
face="front",
|
||||
position=Decimal(11),
|
||||
netbox_utilities_rack_placement=SimpleNamespace(width=2, horizontal_position=1),
|
||||
)
|
||||
|
||||
with (
|
||||
patch("netbox_utilities.reorder_rack.reorder_rack_width_enabled") as enabled,
|
||||
patch("netbox.config.get_config", return_value=SimpleNamespace(RACK_ELEVATION_DEFAULT_UNIT_WIDTH=220)),
|
||||
patch("utilities.html.foreground_color", return_value="000000"),
|
||||
):
|
||||
result = get_reorder_rack_width_data(request, rack=rack, devices=[device])
|
||||
|
||||
enabled.assert_not_called()
|
||||
self.assertTrue(result["complete"])
|
||||
self.assertEqual(result["devices"][0]["id"], 334)
|
||||
self.assertEqual(result["devices"][0]["grid_width"], 6)
|
||||
|
||||
@patch("netbox_utilities.reorder_rack.reorder_rack_width_enabled", return_value=True)
|
||||
@patch("netbox_utilities.reorder_rack.Rack")
|
||||
|
||||
@@ -2,10 +2,15 @@ from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.test import RequestFactory, SimpleTestCase
|
||||
|
||||
from netbox_utilities.topology_views import (
|
||||
_inject_topology_widths,
|
||||
_topology_data_for_visible_devices,
|
||||
_visible_topology_device_urls,
|
||||
get_topology_rack_width_data,
|
||||
install_topology_rack_width_support,
|
||||
is_topology_rack_elevation_request,
|
||||
serialize_topology_placement,
|
||||
topology_rack_width_enabled,
|
||||
@@ -156,3 +161,108 @@ class TopologyViewsRackWidthTest(SimpleTestCase):
|
||||
self.assertIn(".rack-device.netbox-utilities-partial-width", stylesheet)
|
||||
self.assertIn("--netbox-utilities-rack-device-left", stylesheet)
|
||||
self.assertIn("--netbox-utilities-rack-device-width", stylesheet)
|
||||
|
||||
def test_extracts_only_devices_rendered_by_the_authorized_topology_view(self):
|
||||
html = """
|
||||
<a class="rack-device" href="/dcim/devices/334/">Fritzbox</a>
|
||||
<a class="rack-device active" href="/dcim/devices/340/">Grandstream</a>
|
||||
<a href="/dcim/devices/999/">Not a rack device</a>
|
||||
"""
|
||||
|
||||
self.assertEqual(
|
||||
_visible_topology_device_urls(html),
|
||||
{334: "/dcim/devices/334/", 340: "/dcim/devices/340/"},
|
||||
)
|
||||
|
||||
@patch("dcim.models.Device")
|
||||
def test_builds_both_stored_half_widths_from_visible_he11_devices(self, device_model):
|
||||
devices = [
|
||||
SimpleNamespace(
|
||||
pk=334,
|
||||
rack_id=3,
|
||||
face="front",
|
||||
position=11,
|
||||
device_type=SimpleNamespace(u_height=1),
|
||||
netbox_utilities_rack_placement=SimpleNamespace(width=2, horizontal_position=1),
|
||||
get_absolute_url=lambda: "/dcim/devices/334/",
|
||||
),
|
||||
SimpleNamespace(
|
||||
pk=340,
|
||||
rack_id=3,
|
||||
face="front",
|
||||
position=11,
|
||||
device_type=SimpleNamespace(u_height=1),
|
||||
netbox_utilities_rack_placement=SimpleNamespace(width=2, horizontal_position=2),
|
||||
get_absolute_url=lambda: "/dcim/devices/340/",
|
||||
),
|
||||
]
|
||||
device_model.objects.filter.return_value.select_related.return_value.order_by.return_value = devices
|
||||
|
||||
result = _topology_data_for_visible_devices({334: "/dcim/devices/334/", 340: "/dcim/devices/340/"})
|
||||
|
||||
self.assertTrue(result["complete"])
|
||||
self.assertEqual(
|
||||
[(item["device_id"], item["width"], item["horizontal_position"]) for item in result["devices"]],
|
||||
[(334, 2, 1), (340, 2, 2)],
|
||||
)
|
||||
|
||||
@patch("netbox_utilities.topology_views._topology_data_for_visible_devices")
|
||||
def test_injects_stored_widths_into_the_completed_foreign_response(self, get_data):
|
||||
get_data.return_value = {
|
||||
"devices": [
|
||||
{
|
||||
"device_id": 334,
|
||||
"url": "/dcim/devices/334/",
|
||||
"width": 2,
|
||||
"horizontal_position": 1,
|
||||
"width_source": "stored",
|
||||
"left_percent": 0,
|
||||
"width_percent": 50,
|
||||
},
|
||||
{
|
||||
"device_id": 340,
|
||||
"url": "/dcim/devices/340/",
|
||||
"width": 2,
|
||||
"horizontal_position": 2,
|
||||
"width_source": "stored",
|
||||
"left_percent": 50,
|
||||
"width_percent": 50,
|
||||
},
|
||||
],
|
||||
"status": "ready",
|
||||
"schema_version": 3,
|
||||
"complete": True,
|
||||
}
|
||||
response = HttpResponse(
|
||||
'<html><head></head><body><a class="rack-device" href="/dcim/devices/334/">A</a>'
|
||||
'<a class="rack-device" href="/dcim/devices/340/">B</a></body></html>'
|
||||
)
|
||||
|
||||
result = _inject_topology_widths(response)
|
||||
html = result.content.decode()
|
||||
|
||||
get_data.assert_called_once_with({334: "/dcim/devices/334/", 340: "/dcim/devices/340/"})
|
||||
self.assertIn('id="netbox-utilities-topology-rack-width-styles"', html)
|
||||
self.assertIn('.rack-device[href="/dcim/devices/334/"]', html)
|
||||
self.assertIn("left: calc(50% + 3px) !important", html)
|
||||
self.assertIn("netbox_utilities/topology-rack-width.js?v=0.9.6", html)
|
||||
|
||||
def test_patches_the_optional_topology_view_only_once(self):
|
||||
def original_get(self, request):
|
||||
return self, request
|
||||
|
||||
class RackElevationView:
|
||||
get = original_get
|
||||
|
||||
module = SimpleNamespace(RackElevationView=RackElevationView)
|
||||
with (
|
||||
patch("netbox_utilities.topology_views.apps.is_installed", return_value=True),
|
||||
patch("netbox_utilities.topology_views.get_plugin_config", return_value=True),
|
||||
patch("netbox_utilities.topology_views.import_module", return_value=module),
|
||||
):
|
||||
self.assertTrue(install_topology_rack_width_support())
|
||||
patched_get = RackElevationView.get
|
||||
self.assertTrue(install_topology_rack_width_support())
|
||||
|
||||
self.assertIs(RackElevationView.get, patched_get)
|
||||
self.assertIs(RackElevationView._netbox_utilities_original_get, original_get)
|
||||
|
||||
Reference in New Issue
Block a user