Files
netbox-plugin-dns-bridge/netbox_dns_bridge/tests/test_signals.py
T
sven 1c51206196 Upgrade models to full NetboxModel to allow UI/API interaction:
modified:   netbox_dns_bridge/models.py
deleted:    netbox_dns_bridge/migrations/0003_catalogzone_and_per_zone_identifier.py
new file:   netbox_dns_bridge/migrations/0003_full_netbox_models.py

Adjust model access and function calls to match new models:
modified:   netbox_dns_bridge/signals/netbox_dns_zones.py
modified:   netbox_dns_bridge/management/commands/dns_transfer_endpoint/catalog_zone_manager.py
modified:   netbox_dns_bridge/management/commands/dns_transfer_endpoint/request_handler.py

Update UI elements and permission interactions:
modified:   netbox_dns_bridge/filtersets.py
modified:   netbox_dns_bridge/forms.py
modified:   netbox_dns_bridge/jobs/notify.py
modified:   netbox_dns_bridge/navigation.py
modified:   netbox_dns_bridge/tables.py
deleted:    netbox_dns_bridge/templates/netbox_dns_bridge/catalog_zone_members.html
new file:   netbox_dns_bridge/templates/netbox_dns_bridge/catalogzone.html
new file:   netbox_dns_bridge/templates/netbox_dns_bridge/catalogzonemember.html
deleted:    netbox_dns_bridge/templates/netbox_dns_bridge/notify_overview.html
new file:   netbox_dns_bridge/templates/netbox_dns_bridge/seentransferclient.html
modified:   netbox_dns_bridge/urls.py
modified:   netbox_dns_bridge/views.py

AI/LLM updated tests (Not verified):
modified:   netbox_dns_bridge/tests/conftest.py
modified:   netbox_dns_bridge/tests/test_catalog_zone_manager.py
modified:   netbox_dns_bridge/tests/test_models.py
modified:   netbox_dns_bridge/tests/test_notify.py
modified:   netbox_dns_bridge/tests/test_request_handler_integration.py
modified:   netbox_dns_bridge/tests/test_signals.py
modified:   netbox_dns_bridge/tests/test_smoke.py
modified:   netbox_dns_bridge/tests/test_views.py
2026-08-17 17:05:25 +02:00

241 lines
9.0 KiB
Python

"""Tests for signal handlers: zone_pre_save, zone_post_save, zone_post_delete.
Uses ``captureOnCommitCallbacks(execute=True)`` to fire ``transaction.on_commit``
callbacks inside the transaction-wrapped ``@pytest.mark.django_db`` test scope.
"""
from unittest.mock import patch, MagicMock
import pytest
from django.test.testcases import TestCase as _DjangoTestCase
# captureOnCommitCallbacks is a classmethod on TestCase in Django 6.x
captureOnCommitCallbacks = _DjangoTestCase.captureOnCommitCallbacks
from netbox_dns_bridge.models import (
CatalogZone,
CatalogZoneMember,
)
@pytest.mark.django_db
class TestZonePreSave:
def test_stores_old_name_and_serial_on_existing_zone(self, make_zone):
zone = make_zone(name="presave.test", soa_serial=100)
zone.refresh_from_db()
zone.soa_serial = 200
zone.save()
assert getattr(zone, "_old_name", None) == "presave.test"
assert getattr(zone, "_old_soa_serial", None) == 100
def test_old_attrs_none_on_new_zone(self, make_zone):
zone = make_zone(name="newzone.test")
assert getattr(zone, "_old_name", None) is None
assert getattr(zone, "_old_soa_serial", None) is None
@pytest.mark.django_db
class TestZonePostSaveCreate:
def test_creates_catalog_zone_member_on_new_zone(self, make_zone):
zone = make_zone(name="postcreate.test")
assert CatalogZoneMember.objects.filter(zone=zone).exists()
def test_increments_soa_serial_on_commit(self, make_zone, catz_serial):
with captureOnCommitCallbacks(execute=True):
zone = make_zone(name="serial-inc.test")
obj = CatalogZone.objects.get(view=zone.view)
assert obj.soa_serial == catz_serial + 1
def test_does_not_increment_without_on_commit(self, make_zone, catz_serial):
zone = make_zone(name="no-commit.test")
obj = CatalogZone.objects.get(view=zone.view)
assert obj.soa_serial == catz_serial
def test_schedule_client_notify_always_called(self, make_zone, patch_settings):
patch_settings({"notify_clients": True})
with patch(
"netbox_dns_bridge.jobs.notify.schedule_client_notify"
) as mock_notify:
with patch("netbox_dns_bridge.jobs.notify.schedule_ns_notify"):
with patch(
"netbox_dns_bridge.management.commands.dns_transfer_endpoint."
"catalog_zone_manager.notify.schedule_catalog_zone_notify"
):
with captureOnCommitCallbacks(execute=True):
make_zone(name="notify.test")
mock_notify.assert_called_once()
def test_schedule_ns_notify_called_when_serial_changed(
self, make_zone, patch_settings
):
patch_settings({
"notify_clients": False,
"notify_ns_all_zones": True,
})
with patch(
"netbox_dns_bridge.jobs.notify.schedule_ns_notify"
) as mock_ns_notify:
with patch("netbox_dns_bridge.jobs.notify.schedule_client_notify"):
with patch(
"netbox_dns_bridge.management.commands.dns_transfer_endpoint."
"catalog_zone_manager.notify.schedule_catalog_zone_notify"
):
with captureOnCommitCallbacks(execute=True):
make_zone(name="nsnotify.test")
mock_ns_notify.assert_called_once()
def test_schedule_ns_notify_not_called_when_serial_unchanged(
self, make_zone, patch_settings
):
patch_settings({"notify_ns_all_zones": True})
with patch(
"netbox_dns_bridge.jobs.notify.schedule_ns_notify"
) as mock_ns_notify:
with patch("netbox_dns_bridge.jobs.notify.schedule_client_notify"):
with patch(
"netbox_dns_bridge.management.commands.dns_transfer_endpoint."
"catalog_zone_manager.notify.schedule_catalog_zone_notify"
):
zone = make_zone(name="noserialchange.test")
with captureOnCommitCallbacks(execute=True):
zone.save()
mock_ns_notify.assert_not_called()
@pytest.mark.django_db
class TestZonePostSaveUpdate:
def test_updates_member_on_name_change(self, make_zone):
zone = make_zone(name="rename-old.test")
old_name = CatalogZoneMember.objects.get(zone=zone).name
zone.name = "rename-new.test"
zone.save()
new_name = CatalogZoneMember.objects.get(zone=zone).name
assert old_name != new_name
def test_no_identifier_update_when_name_unchanged(self, make_zone):
zone = make_zone(name="nochange.test")
ident = CatalogZoneMember.objects.get(zone=zone)
original_name = ident.name
zone.soa_serial = zone.soa_serial + 1
zone.save()
ident.refresh_from_db()
assert ident.name == original_name
def test_increments_serial_on_commit_on_update(self, make_zone, catz_serial):
zone = make_zone(name="update-serial.test")
with captureOnCommitCallbacks(execute=True):
zone.soa_serial = zone.soa_serial + 1
zone.save()
obj = CatalogZone.objects.get(view=zone.view)
assert obj.soa_serial == catz_serial + 1
@pytest.mark.django_db
class TestZonePostDelete:
def test_increments_soa_serial_on_commit_on_delete(self, make_zone, catz_serial):
zone = make_zone(name="delete-serial.test")
view = zone.view
with captureOnCommitCallbacks(execute=True):
zone.delete()
obj = CatalogZone.objects.get(view=view)
assert obj.soa_serial == catz_serial + 1
def test_does_not_increment_without_on_commit(self, make_zone, catz_serial):
zone = make_zone(name="delete-no-commit.test")
view = zone.view
zone.delete()
obj = CatalogZone.objects.get(view=view)
assert obj.soa_serial == catz_serial
@pytest.mark.django_db
class TestZoneViewChange:
def test_increments_both_catalog_zone_serials(self, make_zone, make_view, catz_serial):
view_a = make_view(name="view-a")
view_b = make_view(name="view-b")
# view_b has no zones yet, so no CatalogZone was auto-created for it.
CatalogZone.objects.create(view=view_b, soa_serial=5)
zone = make_zone(name="move.test", view=view_a)
serial_a_before = CatalogZone.objects.get(view=view_a).soa_serial
serial_b_before = CatalogZone.objects.get(view=view_b).soa_serial
with captureOnCommitCallbacks(execute=True):
zone.view = view_b
zone.save()
serial_a_after = CatalogZone.objects.get(view=view_a).soa_serial
serial_b_after = CatalogZone.objects.get(view=view_b).soa_serial
assert serial_a_after == serial_a_before + 1
assert serial_b_after == serial_b_before + 1
def test_updates_identifier_catalog_zone_fk(self, make_zone, make_view):
view_a = make_view(name="fk-view-a")
view_b = make_view(name="fk-view-b")
zone = make_zone(name="fk-move.test", view=view_a)
ident = CatalogZoneMember.objects.get(zone=zone)
assert ident.catalog_zone == CatalogZone.objects.get(view=view_a)
zone.view = view_b
zone.save()
ident.refresh_from_db()
assert ident.catalog_zone == CatalogZone.objects.get(view=view_b)
@pytest.mark.django_db
class TestSignalDedup:
def test_dedup_skips_second_save_in_same_request(self, make_zone):
from netbox.context import current_request
from django.contrib.auth import get_user_model
zone = make_zone(name="dedup.test")
original_serial = CatalogZone.objects.get(
view=zone.view
).soa_serial
User = get_user_model()
user = User(username="dedup-user", is_superuser=True)
user.save()
request = MagicMock()
request._handled_zones = set()
request.user = user
import uuid as _uuid
request.id = _uuid.uuid4()
with current_request.set(request):
with captureOnCommitCallbacks(execute=True):
zone.soa_serial = zone.soa_serial + 1
zone.save()
with captureOnCommitCallbacks(execute=True):
zone.soa_serial = zone.soa_serial + 2
zone.save()
new_serial = CatalogZone.objects.get(
view=zone.view
).soa_serial
assert new_serial == original_serial + 1
def test_no_dedup_without_request(self, make_zone):
zone = make_zone(name="nodedup.test")
original_serial = CatalogZone.objects.get(
view=zone.view
).soa_serial
with captureOnCommitCallbacks(execute=True):
zone.soa_serial = zone.soa_serial + 1
zone.save()
with captureOnCommitCallbacks(execute=True):
zone.soa_serial = zone.soa_serial + 2
zone.save()
new_serial = CatalogZone.objects.get(
view=zone.view
).soa_serial
assert new_serial == original_serial + 2