mirror of
https://github.com/netbox-community/netbox.git
synced 2026-04-14 05:00:13 +02:00
Compare commits
4 Commits
post-raw-s
...
21361-test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c69c6c4ca1 | ||
|
|
b1d98643ea | ||
|
|
9b734bac93 | ||
|
|
0f277894b2 |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -56,9 +56,9 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Check Python linting & PEP8 compliance
|
||||
uses: astral-sh/ruff-action@4919ec5cf1f49eff0871dbcea0da843445b837e6 # v3.6.1
|
||||
uses: astral-sh/ruff-action@0ce1b0bf8b818ef400413f810f8a11cdbda0034b # v4.0.0
|
||||
with:
|
||||
version: "0.15.2"
|
||||
version: "0.15.10"
|
||||
args: "check --output-format=github"
|
||||
src: "netbox/"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Create app token
|
||||
uses: actions/create-github-app-token@v1
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: 1076524
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
run: python netbox/manage.py makemessages -l ${{ env.LOCALE }}
|
||||
|
||||
- name: Commit changes
|
||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
||||
uses: EndBug/add-and-commit@290ea2c423ad77ca9c62ae0f5b224379612c0321 # v10.0.0
|
||||
with:
|
||||
add: 'netbox/translations/'
|
||||
default_author: github_actions
|
||||
|
||||
@@ -6,7 +6,6 @@ from django.dispatch import receiver
|
||||
|
||||
from dcim.choices import CableEndChoices, LinkStatusChoices
|
||||
from ipam.models import Prefix
|
||||
from netbox.signals import post_raw_create
|
||||
from virtualization.models import Cluster, VMInterface
|
||||
from wireless.models import WirelessLAN
|
||||
|
||||
@@ -167,27 +166,6 @@ def retrace_cable_paths(instance, **kwargs):
|
||||
cablepath.retrace()
|
||||
|
||||
|
||||
@receiver(post_raw_create, sender=Cable)
|
||||
def retrace_cable_paths_after_raw_create(sender, pks, **kwargs):
|
||||
"""
|
||||
When Cables are created via a raw save, the normal Cable.save() path is bypassed,
|
||||
so trace_paths is never sent. Retrace paths for all newly created cables.
|
||||
|
||||
Callers must only send this signal after all CableTerminations for the given cables
|
||||
have been applied. If a cable has no terminations, update_connected_endpoints will
|
||||
find empty termination lists and skip path creation — so this is safe to call even
|
||||
if terminations are absent, but path tracing will have no effect.
|
||||
|
||||
Note: raw=False (the default) is intentional here — we explicitly want
|
||||
update_connected_endpoints to run, unlike during fixture loading (raw=True).
|
||||
"""
|
||||
logger = logging.getLogger('netbox.dcim.cable')
|
||||
for cable in Cable.objects.filter(pk__in=pks):
|
||||
cable._terminations_modified = True
|
||||
trace_paths.send(Cable, instance=cable, created=True)
|
||||
logger.debug(f"Retraced cable paths for Cable {cable.pk}")
|
||||
|
||||
|
||||
@receiver((post_delete, post_save), sender=PortMapping)
|
||||
def update_passthrough_port_paths(instance, **kwargs):
|
||||
"""
|
||||
|
||||
@@ -2,10 +2,3 @@ from django.dispatch import Signal
|
||||
|
||||
# Signals that a model has completed its clean() method
|
||||
post_clean = Signal()
|
||||
|
||||
# Sent after objects of a given model are created via raw save.
|
||||
# Expected call signature: post_raw_create.send(sender=MyModel, pks=[...])
|
||||
# Provides: pks (list) - PKs of the newly created objects.
|
||||
# Callers must ensure all related objects (e.g. M2M, dependent rows) are in place
|
||||
# before sending, as receivers may query related data to perform post-create work.
|
||||
post_raw_create = Signal()
|
||||
|
||||
@@ -254,6 +254,7 @@ class APIViewTestCases:
|
||||
action=ObjectChangeActionChoices.ACTION_CREATE,
|
||||
)
|
||||
self.assertEqual(objectchange.message, data['changelog_message'])
|
||||
self.assertObjectChangeData(objectchange, prechange_is_none=True, postchange_is_none=False)
|
||||
|
||||
def test_bulk_create_objects(self):
|
||||
"""
|
||||
@@ -307,6 +308,7 @@ class APIViewTestCases:
|
||||
self.assertEqual(len(objectchanges), len(self.create_data))
|
||||
for oc in objectchanges:
|
||||
self.assertEqual(oc.message, changelog_message)
|
||||
self.assertObjectChangeData(oc, prechange_is_none=True, postchange_is_none=False)
|
||||
|
||||
class UpdateObjectViewTestCase(APITestCase):
|
||||
update_data = {}
|
||||
@@ -366,6 +368,8 @@ class APIViewTestCases:
|
||||
)
|
||||
self.assertEqual(objectchange.action, ObjectChangeActionChoices.ACTION_UPDATE)
|
||||
self.assertEqual(objectchange.message, data['changelog_message'])
|
||||
self.assertObjectChangeData(objectchange, prechange_is_none=False, postchange_is_none=False)
|
||||
self.assertNotEqual(objectchange.prechange_data, objectchange.postchange_data)
|
||||
|
||||
def test_bulk_update_objects(self):
|
||||
"""
|
||||
@@ -416,6 +420,8 @@ class APIViewTestCases:
|
||||
for oc in objectchanges:
|
||||
self.assertEqual(oc.action, ObjectChangeActionChoices.ACTION_UPDATE)
|
||||
self.assertEqual(oc.message, changelog_message)
|
||||
self.assertObjectChangeData(oc, prechange_is_none=False, postchange_is_none=False)
|
||||
self.assertNotEqual(oc.prechange_data, oc.postchange_data)
|
||||
|
||||
class DeleteObjectViewTestCase(APITestCase):
|
||||
|
||||
@@ -464,6 +470,7 @@ class APIViewTestCases:
|
||||
)
|
||||
self.assertEqual(objectchange.action, ObjectChangeActionChoices.ACTION_DELETE)
|
||||
self.assertEqual(objectchange.message, data['changelog_message'])
|
||||
self.assertObjectChangeData(objectchange, prechange_is_none=False, postchange_is_none=True)
|
||||
|
||||
def test_bulk_delete_objects(self):
|
||||
"""
|
||||
@@ -505,6 +512,7 @@ class APIViewTestCases:
|
||||
for oc in objectchanges:
|
||||
self.assertEqual(oc.action, ObjectChangeActionChoices.ACTION_DELETE)
|
||||
self.assertEqual(oc.message, changelog_message)
|
||||
self.assertObjectChangeData(oc, prechange_is_none=False, postchange_is_none=True)
|
||||
|
||||
class GraphQLTestCase(APITestCase):
|
||||
|
||||
|
||||
@@ -83,6 +83,20 @@ class TestCase(_TestCase):
|
||||
# Custom assertions
|
||||
#
|
||||
|
||||
def assertObjectChangeData(self, objectchange, *, prechange_is_none: bool, postchange_is_none: bool):
|
||||
"""
|
||||
Assert that an ObjectChange record has the expected prechange_data and postchange_data.
|
||||
Set prechange_is_none=True to assert the field is null, False to assert it is populated.
|
||||
"""
|
||||
if prechange_is_none:
|
||||
self.assertIsNone(objectchange.prechange_data, "Expected prechange_data to be None")
|
||||
else:
|
||||
self.assertIsNotNone(objectchange.prechange_data, "Expected prechange_data to be populated")
|
||||
if postchange_is_none:
|
||||
self.assertIsNone(objectchange.postchange_data, "Expected postchange_data to be None")
|
||||
else:
|
||||
self.assertIsNotNone(objectchange.postchange_data, "Expected postchange_data to be populated")
|
||||
|
||||
def assertHttpStatus(self, response, expected_status):
|
||||
"""
|
||||
TestCase method. Provide more detail in the event of an unexpected HTTP response.
|
||||
|
||||
@@ -194,6 +194,7 @@ class ViewTestCases:
|
||||
self.assertEqual(len(objectchanges), 1)
|
||||
self.assertEqual(objectchanges[0].action, ObjectChangeActionChoices.ACTION_CREATE)
|
||||
self.assertEqual(objectchanges[0].message, self.form_data['changelog_message'])
|
||||
self.assertObjectChangeData(objectchanges[0], prechange_is_none=True, postchange_is_none=False)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[])
|
||||
def test_create_object_with_constrained_permission(self):
|
||||
@@ -301,6 +302,8 @@ class ViewTestCases:
|
||||
self.assertEqual(len(objectchanges), 1)
|
||||
self.assertEqual(objectchanges[0].action, ObjectChangeActionChoices.ACTION_UPDATE)
|
||||
self.assertEqual(objectchanges[0].message, self.form_data['changelog_message'])
|
||||
self.assertObjectChangeData(objectchanges[0], prechange_is_none=False, postchange_is_none=False)
|
||||
self.assertNotEqual(objectchanges[0].prechange_data, objectchanges[0].postchange_data)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[])
|
||||
def test_edit_object_with_constrained_permission(self):
|
||||
@@ -396,6 +399,7 @@ class ViewTestCases:
|
||||
self.assertEqual(len(objectchanges), 1)
|
||||
self.assertEqual(objectchanges[0].action, ObjectChangeActionChoices.ACTION_DELETE)
|
||||
self.assertEqual(objectchanges[0].message, form_data['changelog_message'])
|
||||
self.assertObjectChangeData(objectchanges[0], prechange_is_none=False, postchange_is_none=True)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_delete_object_with_constrained_permission(self):
|
||||
@@ -717,6 +721,7 @@ class ViewTestCases:
|
||||
|
||||
for oc in objectchanges:
|
||||
self.assertEqual(oc.message, data['changelog_message'])
|
||||
self.assertObjectChangeData(oc, prechange_is_none=True, postchange_is_none=False)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_bulk_update_objects_with_permission(self):
|
||||
@@ -870,6 +875,8 @@ class ViewTestCases:
|
||||
for oc in objectchanges:
|
||||
self.assertEqual(oc.action, ObjectChangeActionChoices.ACTION_UPDATE)
|
||||
self.assertEqual(oc.message, data['changelog_message'])
|
||||
self.assertObjectChangeData(oc, prechange_is_none=False, postchange_is_none=False)
|
||||
self.assertNotEqual(oc.prechange_data, oc.postchange_data)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[])
|
||||
def test_bulk_edit_objects_with_constrained_permission(self):
|
||||
@@ -966,6 +973,7 @@ class ViewTestCases:
|
||||
for oc in objectchanges:
|
||||
self.assertEqual(oc.action, ObjectChangeActionChoices.ACTION_DELETE)
|
||||
self.assertEqual(oc.message, data['changelog_message'])
|
||||
self.assertObjectChangeData(oc, prechange_is_none=False, postchange_is_none=True)
|
||||
|
||||
def test_bulk_delete_objects_with_constrained_permission(self):
|
||||
pk_list = self._get_queryset().values_list('pk', flat=True)
|
||||
|
||||
Reference in New Issue
Block a user