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
215 lines
7.8 KiB
Python
215 lines
7.8 KiB
Python
"""Tests for plugin models: constraints, __str__, cascade behaviours."""
|
|
import pytest
|
|
|
|
from netbox_dns_bridge.models import (
|
|
CatalogZone,
|
|
CatalogZoneMember,
|
|
SeenTransferClient,
|
|
)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestCatalogZone:
|
|
def test_str_representation(self, make_view):
|
|
view = make_view(name="cz-str-view")
|
|
cz = CatalogZone.objects.create(view=view, soa_serial=42)
|
|
assert str(cz) == "cz-str-view: 42"
|
|
|
|
def test_default_soa_serial(self, make_view):
|
|
view = make_view(name="cz-default-view")
|
|
cz = CatalogZone.objects.create(view=view)
|
|
assert cz.soa_serial == 1
|
|
|
|
def test_default_soa_parameters(self, make_view):
|
|
view = make_view(name="cz-soa-default-view")
|
|
cz = CatalogZone.objects.create(view=view)
|
|
assert cz.soa_refresh == 60
|
|
assert cz.soa_retry == 10
|
|
assert cz.soa_expire == 1209600
|
|
assert cz.soa_minimum == 0
|
|
|
|
def test_custom_soa_parameters(self, make_view):
|
|
view = make_view(name="cz-soa-custom-view")
|
|
cz = CatalogZone.objects.create(
|
|
view=view,
|
|
soa_refresh=300,
|
|
soa_retry=100,
|
|
soa_expire=86400,
|
|
soa_minimum=3600,
|
|
)
|
|
assert cz.soa_refresh == 300
|
|
assert cz.soa_retry == 100
|
|
assert cz.soa_expire == 86400
|
|
assert cz.soa_minimum == 3600
|
|
|
|
def test_is_full_netbox_model(self, make_view):
|
|
view = make_view(name="cz-netbox-model-view")
|
|
cz = CatalogZone.objects.create(view=view)
|
|
assert cz.created is not None
|
|
assert cz.last_updated is not None
|
|
assert cz.custom_field_data == {}
|
|
assert hasattr(cz, "tags")
|
|
assert cz.get_absolute_url() == f"/plugins/dns-bridge/catalog-zones/{cz.pk}/"
|
|
|
|
def test_one_to_one_relationship(self, make_view):
|
|
view = make_view(name="cz-oto-view")
|
|
cz = CatalogZone.objects.create(view=view, soa_serial=7)
|
|
assert view.catalog_zone == cz
|
|
|
|
def test_create_and_retrieve(self, make_view):
|
|
view = make_view(name="cz-retrieve-view")
|
|
CatalogZone.objects.create(view=view, soa_serial=99)
|
|
cz = CatalogZone.objects.get(view=view)
|
|
assert cz.soa_serial == 99
|
|
|
|
def test_ordering_by_view_name(self, make_view):
|
|
v1 = make_view(name="z-view")
|
|
v2 = make_view(name="a-view")
|
|
v3 = make_view(name="m-view")
|
|
CatalogZone.objects.create(view=v1, soa_serial=1)
|
|
CatalogZone.objects.create(view=v2, soa_serial=2)
|
|
CatalogZone.objects.create(view=v3, soa_serial=3)
|
|
names = list(
|
|
CatalogZone.objects.values_list("view__name", flat=True)
|
|
)
|
|
assert names == ["a-view", "m-view", "z-view"]
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestCatalogZoneMember:
|
|
def test_str_representation(self, make_zone):
|
|
zone = make_zone(name="str.test")
|
|
m = CatalogZoneMember.objects.get(zone=zone)
|
|
assert str(m) == m.name
|
|
|
|
def test_unique_name_per_catalog_zone(self, make_zone, make_view):
|
|
view = make_view(name="shared-view")
|
|
zone1 = make_zone(name="uniq1.test", view=view)
|
|
zone2 = make_zone(name="uniq2.test", view=view)
|
|
|
|
ident1 = CatalogZoneMember.objects.get(zone=zone1)
|
|
ident1.name = "shared-name"
|
|
ident1.save()
|
|
|
|
ident2 = CatalogZoneMember.objects.get(zone=zone2)
|
|
from django.db import IntegrityError
|
|
|
|
with pytest.raises(IntegrityError):
|
|
ident2.name = "shared-name"
|
|
ident2.save()
|
|
|
|
def test_same_name_allowed_in_different_catalog_zones(self, make_zone, make_view):
|
|
view1 = make_view(name="view-a")
|
|
view2 = make_view(name="view-b")
|
|
zone1 = make_zone(name="diff1.test", view=view1)
|
|
zone2 = make_zone(name="diff2.test", view=view2)
|
|
|
|
ident1 = CatalogZoneMember.objects.get(zone=zone1)
|
|
ident1.name = "shared-name"
|
|
ident1.save()
|
|
|
|
ident2 = CatalogZoneMember.objects.get(zone=zone2)
|
|
ident2.name = "shared-name"
|
|
ident2.save()
|
|
|
|
def test_cascade_delete_when_zone_deleted(self, make_zone):
|
|
zone = make_zone(name="cascade.test")
|
|
m = CatalogZoneMember.objects.get(zone=zone)
|
|
assert CatalogZoneMember.objects.filter(pk=m.pk).exists()
|
|
zone.delete()
|
|
assert not CatalogZoneMember.objects.filter(pk=m.pk).exists()
|
|
|
|
def test_one_to_one_relationship(self, make_zone):
|
|
zone = make_zone(name="oto.test")
|
|
m = CatalogZoneMember.objects.get(zone=zone)
|
|
assert zone.catalog_zone_member == m
|
|
|
|
def test_ordering_by_name(self, make_zone, make_view):
|
|
view = make_view(name="ord-view")
|
|
z1 = make_zone(name="z1.test", view=view)
|
|
z2 = make_zone(name="z2.test", view=view)
|
|
z3 = make_zone(name="z3.test", view=view)
|
|
CatalogZoneMember.objects.filter(zone=z3).update(name="gamma")
|
|
CatalogZoneMember.objects.filter(zone=z1).update(name="alpha")
|
|
CatalogZoneMember.objects.filter(zone=z2).update(name="beta")
|
|
names = list(
|
|
CatalogZoneMember.objects.filter(
|
|
zone__in=[z1, z2, z3]
|
|
).values_list("name", flat=True)
|
|
)
|
|
assert names == ["alpha", "beta", "gamma"]
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestSeenTransferClient:
|
|
def test_str_representation(self, make_view):
|
|
view = make_view(name="str-view")
|
|
client = SeenTransferClient.objects.create(
|
|
source_ip="192.0.2.1",
|
|
view=view,
|
|
last_seen="2026-01-01T00:00:00Z",
|
|
)
|
|
assert str(client) == f"192.0.2.1 ({view.name})"
|
|
|
|
def test_unique_ip_view_constraint(self, make_view):
|
|
from django.db import IntegrityError
|
|
|
|
view = make_view(name="uniq-view")
|
|
SeenTransferClient.objects.create(
|
|
source_ip="10.0.0.1",
|
|
view=view,
|
|
last_seen="2026-01-01T00:00:00Z",
|
|
)
|
|
with pytest.raises(IntegrityError):
|
|
SeenTransferClient.objects.create(
|
|
source_ip="10.0.0.1",
|
|
view=view,
|
|
last_seen="2026-01-02T00:00:00Z",
|
|
)
|
|
|
|
def test_same_ip_different_views_allowed(self, make_view):
|
|
v1 = make_view(name="v1")
|
|
v2 = make_view(name="v2")
|
|
SeenTransferClient.objects.create(
|
|
source_ip="10.0.0.5", view=v1, last_seen="2026-01-01T00:00:00Z"
|
|
)
|
|
SeenTransferClient.objects.create(
|
|
source_ip="10.0.0.5", view=v2, last_seen="2026-01-01T00:00:00Z"
|
|
)
|
|
assert SeenTransferClient.objects.filter(source_ip="10.0.0.5").count() == 2
|
|
|
|
def test_protect_on_view_delete(self, make_view):
|
|
from django.db.models import ProtectedError
|
|
|
|
view = make_view(name="protected-view")
|
|
SeenTransferClient.objects.create(
|
|
source_ip="10.0.0.9",
|
|
view=view,
|
|
last_seen="2026-01-01T00:00:00Z",
|
|
)
|
|
with pytest.raises(ProtectedError):
|
|
view.delete()
|
|
|
|
def test_ordering_by_source_ip(self, make_view):
|
|
from django.utils import timezone
|
|
|
|
v = make_view(name="ord-view")
|
|
now = timezone.now()
|
|
for ip in ["10.0.0.3", "10.0.0.1", "10.0.0.2"]:
|
|
SeenTransferClient.objects.create(source_ip=ip, view=v, last_seen=now)
|
|
ips = list(SeenTransferClient.objects.values_list("source_ip", flat=True))
|
|
assert ips == ["10.0.0.1", "10.0.0.2", "10.0.0.3"]
|
|
|
|
def test_is_full_netbox_model(self, make_view):
|
|
from django.utils import timezone
|
|
|
|
view = make_view(name="stc-netbox-model-view")
|
|
c = SeenTransferClient.objects.create(
|
|
source_ip="192.0.2.1", view=view, last_seen=timezone.now()
|
|
)
|
|
assert c.created is not None
|
|
assert c.last_updated is not None
|
|
assert c.custom_field_data == {}
|
|
assert hasattr(c, "tags")
|
|
assert c.get_absolute_url() == f"/plugins/dns-bridge/seen-transfer-clients/{c.pk}/"
|