Pārlūkot izejas kodu

Fix medium issue from re-review: missing add_macaddress in view tests

The clean() permission check fires in tests (current_request ContextVar is
set via middleware for all Django test client requests), so view tests that
submit a mac_address without dcim.add_macaddress now correctly get a 200
with a form validation error instead of a 302 redirect.

- Add 'dcim.add_macaddress' to add_permissions in test_mac_address_shortcut_create
  and test_mac_address_shortcut_edit in both dcim and virtualization test_views
- Fix unnecessary instance.save() in InterfaceCommonForm.save() clear branch
  when primary_mac_address_id is already None (no-op save generated a
  spurious changelog entry)
- Add test_mac_address_find_or_create to VMInterfaceTestCase in
  virtualization/tests/test_api.py, mirroring the dcim equivalent
- Move lazy import of redirect_to_login to module level in dcim/views.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Brian Tiemann 1 nedēļu atpakaļ
vecāks
revīzija
42260e5f0e

+ 2 - 2
netbox/dcim/forms/common.py

@@ -116,8 +116,8 @@ class InterfaceCommonForm(forms.Form):
             else:
                 if instance.primary_mac_address_id is not None:
                     instance.snapshot()
-                instance.primary_mac_address = None
-                instance.save()
+                    instance.primary_mac_address = None
+                    instance.save()
 
         instance.__dict__.pop('mac_address', None)
         return instance

+ 2 - 2
netbox/dcim/tests/test_views.py

@@ -3387,7 +3387,7 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
         Submitting the Interface form with a mac_address string creates a MACAddress
         and sets it as primary in one request.
         """
-        self.add_permissions('dcim.add_interface')
+        self.add_permissions('dcim.add_interface', 'dcim.add_macaddress')
 
         data = {**self.form_data, 'mac_address': 'AA:BB:CC:DD:EE:FF', 'changelog_message': 'test'}
         response = self.client.post(self._get_url('add'), data=post_data(data))
@@ -3403,7 +3403,7 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
         Submitting the Interface edit form with a mac_address string creates a MACAddress
         and assigns it as primary when none existed before.
         """
-        self.add_permissions('dcim.change_interface')
+        self.add_permissions('dcim.change_interface', 'dcim.add_macaddress')
 
         instance = Interface.objects.filter(device_id=self.form_data['device']).first()
         self.assertIsNone(instance.primary_mac_address)

+ 1 - 1
netbox/dcim/views.py

@@ -1,5 +1,6 @@
 from django.conf import settings
 from django.contrib import messages
+from django.contrib.auth.views import redirect_to_login
 from django.contrib.contenttypes.models import ContentType
 from django.core.paginator import EmptyPage, PageNotAnInteger
 from django.db import router, transaction
@@ -5036,7 +5037,6 @@ class MACAddressSetPrimaryView(View):
 
     def post(self, request, pk):
         if not request.user.is_authenticated:
-            from django.contrib.auth.views import redirect_to_login
             return redirect_to_login(request.get_full_path())
 
         mac = get_object_or_404(self.queryset.restrict(request.user, 'view'), pk=pk)

+ 24 - 0
netbox/virtualization/tests/test_api.py

@@ -787,6 +787,30 @@ class VMInterfaceTestCase(APIViewTestCases.APIViewTestCase):
         self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
         self.assertIn('mac_address', response.data)
 
+    def test_mac_address_find_or_create(self):
+        """
+        Patching mac_address with a MAC that already exists on the VMInterface promotes it to
+        primary without creating a duplicate MACAddress record.
+        """
+        from dcim.models import MACAddress
+        self.add_permissions('virtualization.change_vminterface', 'dcim.add_macaddress', 'dcim.change_macaddress')
+        iface = VMInterface.objects.first()
+
+        mac1 = MACAddress.objects.create(mac_address='CC:DD:EE:FF:00:01', assigned_object=iface)
+        mac2 = MACAddress.objects.create(mac_address='CC:DD:EE:FF:00:02', assigned_object=iface)
+        iface.primary_mac_address = mac1
+        iface.save()
+
+        mac_count_before = iface.mac_addresses.count()
+        url = self._get_detail_url(iface)
+
+        response = self.client.patch(url, {'mac_address': 'CC:DD:EE:FF:00:02'}, format='json', **self.header)
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+
+        iface.refresh_from_db()
+        self.assertEqual(iface.primary_mac_address.pk, mac2.pk)
+        self.assertEqual(iface.mac_addresses.count(), mac_count_before)
+
 
 class VirtualDiskTestCase(APIViewTestCases.APIViewTestCase):
     model = VirtualDisk

+ 2 - 2
netbox/virtualization/tests/test_views.py

@@ -693,7 +693,7 @@ class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
         Submitting the VMInterface form with a mac_address string creates a MACAddress
         and sets it as primary in one request.
         """
-        self.add_permissions('virtualization.add_vminterface')
+        self.add_permissions('virtualization.add_vminterface', 'dcim.add_macaddress')
 
         data = {**self.form_data, 'mac_address': 'AA:BB:CC:DD:EE:FF', 'changelog_message': 'test'}
         response = self.client.post(self._get_url('add'), data=post_data(data))
@@ -709,7 +709,7 @@ class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
         Submitting the VMInterface edit form with a mac_address string creates a MACAddress
         and assigns it as primary when none existed before.
         """
-        self.add_permissions('virtualization.change_vminterface')
+        self.add_permissions('virtualization.change_vminterface', 'dcim.add_macaddress')
 
         instance = VMInterface.objects.filter(virtual_machine_id=self.form_data['virtual_machine']).first()
         self.assertIsNone(instance.primary_mac_address)