"""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}/"