new file: netbox_dns_bridge/tests/__init__.py new file: netbox_dns_bridge/tests/_helpers.py new file: netbox_dns_bridge/tests/conftest.py new file: netbox_dns_bridge/tests/test_bootstrap.py new file: netbox_dns_bridge/tests/test_catalog_zone_manager.py new file: netbox_dns_bridge/tests/test_dns_server.py new file: netbox_dns_bridge/tests/test_management_commands.py new file: netbox_dns_bridge/tests/test_models.py new file: netbox_dns_bridge/tests/test_notify_handler.py new file: netbox_dns_bridge/tests/test_request_handler_integration.py new file: netbox_dns_bridge/tests/test_request_handler_unit.py new file: netbox_dns_bridge/tests/test_signals.py new file: netbox_dns_bridge/tests/test_smoke.py new file: netbox_dns_bridge/tests/test_utils.py new file: netbox_dns_bridge/tests/test_views.py modified: pyproject.toml Add some tests. #AI generated
198 lines
7.5 KiB
Python
198 lines
7.5 KiB
Python
"""Tests for management commands: dns-settings, dns-export-zone, dns-export-zones,
|
|
dns-transfer-endpoint."""
|
|
import os
|
|
import tempfile
|
|
from io import StringIO
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
import pytest
|
|
from django.core.management import call_command
|
|
from django.core.management.base import CommandError
|
|
|
|
from netbox_dns_bridge.models import IntegerKeyValueSetting
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestDnsSettingsCommand:
|
|
def test_list_empty(self):
|
|
IntegerKeyValueSetting.objects.all().delete()
|
|
out = StringIO()
|
|
call_command("dns-settings", "list", stdout=out)
|
|
assert "No settings found" in out.getvalue()
|
|
|
|
def test_list_with_entries(self):
|
|
IntegerKeyValueSetting.objects.create(key="k1", value=10)
|
|
IntegerKeyValueSetting.objects.create(key="k2", value=20)
|
|
out = StringIO()
|
|
call_command("dns-settings", "list", stdout=out)
|
|
output = out.getvalue()
|
|
assert "k1: 10" in output
|
|
assert "k2: 20" in output
|
|
|
|
def test_get_existing(self):
|
|
IntegerKeyValueSetting.objects.create(key="serial", value=42)
|
|
out = StringIO()
|
|
call_command("dns-settings", "get", "serial", stdout=out)
|
|
assert "serial: 42" in out.getvalue()
|
|
|
|
def test_get_missing_raises(self):
|
|
with pytest.raises(CommandError, match="No setting"):
|
|
call_command("dns-settings", "get", "nonexistent")
|
|
|
|
def test_get_without_key_raises(self):
|
|
with pytest.raises(CommandError, match="key is required"):
|
|
call_command("dns-settings", "get")
|
|
|
|
def test_set_existing(self):
|
|
IntegerKeyValueSetting.objects.create(key="counter", value=5)
|
|
out = StringIO()
|
|
call_command("dns-settings", "set", "counter", 99, stdout=out)
|
|
assert "Updated counter from 5 to 99" in out.getvalue()
|
|
assert IntegerKeyValueSetting.objects.get(key="counter").value == 99
|
|
|
|
def test_set_missing_key_raises(self):
|
|
with pytest.raises(CommandError, match="setting is required"):
|
|
call_command("dns-settings", "set")
|
|
|
|
def test_set_without_value_raises(self):
|
|
IntegerKeyValueSetting.objects.create(key="needval", value=1)
|
|
with pytest.raises(CommandError, match="value is required"):
|
|
call_command("dns-settings", "set", "needval")
|
|
|
|
def test_set_nonexistent_setting_raises(self):
|
|
with pytest.raises(CommandError, match="No setting found"):
|
|
call_command("dns-settings", "set", "ghost", 1)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestDnsExportZoneCommand:
|
|
def test_exports_zone_file(self, make_zone, make_record):
|
|
zone = make_zone(name="export-cmd.test")
|
|
make_record(zone, name="www", type_="A", value="192.0.2.1")
|
|
with tempfile.TemporaryDirectory() as td:
|
|
path = os.path.join(td, "zone.db")
|
|
out = StringIO()
|
|
call_command(
|
|
"dns-export-zone",
|
|
"--view", zone.view.name,
|
|
"--zone", "export-cmd.test",
|
|
"--file", path,
|
|
stdout=out,
|
|
)
|
|
assert os.path.exists(path)
|
|
with open(path) as f:
|
|
content = f.read()
|
|
assert "SOA" in content
|
|
assert "192.0.2.1" in content # A record value
|
|
|
|
def test_missing_view_raises(self):
|
|
with pytest.raises(CommandError, match="--view"):
|
|
call_command("dns-export-zone", "--zone", "z.test", "--file", "/tmp/x")
|
|
|
|
def test_missing_zone_raises(self):
|
|
with pytest.raises(CommandError, match="--zone"):
|
|
call_command("dns-export-zone", "--view", "default", "--file", "/tmp/x")
|
|
|
|
def test_missing_file_raises(self):
|
|
with pytest.raises(CommandError, match="--file"):
|
|
call_command("dns-export-zone", "--view", "default", "--zone", "z.test")
|
|
|
|
def test_nonexistent_zone_raises(self, make_view):
|
|
make_view(name="default")
|
|
with pytest.raises(CommandError, match="Failed to export zone"):
|
|
call_command(
|
|
"dns-export-zone",
|
|
"--view", "default",
|
|
"--zone", "does-not-exist.test",
|
|
"--file", "/tmp/x.zone",
|
|
)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestDnsExportZonesCommand:
|
|
def test_exports_all_zones(self, make_zone, make_view):
|
|
make_zone(name="batch1.test")
|
|
make_zone(name="batch2.test")
|
|
with tempfile.TemporaryDirectory() as td:
|
|
out = StringIO()
|
|
call_command("dns-export-zones", "--path", td, stdout=out)
|
|
output = out.getvalue()
|
|
assert "batch1.test" in output
|
|
assert "batch2.test" in output
|
|
for zone_name in ("batch1.test", "batch2.test"):
|
|
for root, dirs, files in os.walk(td):
|
|
if zone_name in files:
|
|
break
|
|
else:
|
|
pytest.fail(f"Zone file for {zone_name} not found")
|
|
|
|
def test_creates_view_subdirectories(self, make_zone, make_view):
|
|
make_zone(name="subdir.test")
|
|
with tempfile.TemporaryDirectory() as td:
|
|
out = StringIO()
|
|
call_command("dns-export-zones", "--path", td, stdout=out)
|
|
view_dirs = os.listdir(td)
|
|
assert len(view_dirs) > 0
|
|
|
|
def test_missing_path_raises(self):
|
|
with pytest.raises(CommandError, match="--path"):
|
|
call_command("dns-export-zones")
|
|
|
|
def test_no_zones_warning(self):
|
|
from netbox_dns.models import Zone
|
|
|
|
Zone.objects.all().delete()
|
|
with tempfile.TemporaryDirectory() as td:
|
|
out = StringIO()
|
|
call_command("dns-export-zones", "--path", td, stdout=out)
|
|
assert "No zones found" in out.getvalue()
|
|
|
|
def test_creates_path_if_not_exists(self, make_zone):
|
|
make_zone(name="mkpath.test")
|
|
with tempfile.TemporaryDirectory() as td:
|
|
path = os.path.join(td, "subdir1", "subdir2")
|
|
out = StringIO()
|
|
call_command("dns-export-zones", "--path", path, stdout=out)
|
|
assert os.path.isdir(path)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestDnsTransferEndpointCommand:
|
|
def _get_cmd_module(self):
|
|
"""Load the dns-transfer-endpoint command module (hyphen in filename)."""
|
|
import sys
|
|
from django.core.management import load_command_class
|
|
|
|
cmd = load_command_class("netbox_dns_bridge", "dns-transfer-endpoint")
|
|
return sys.modules[cmd.__module__]
|
|
|
|
def test_missing_tsig_keys_raises(self, monkeypatch):
|
|
cmd_mod = self._get_cmd_module()
|
|
monkeypatch.setattr(cmd_mod, "SETTINGS", {})
|
|
with pytest.raises(RuntimeError, match="tsig_keys variable not set"):
|
|
call_command("dns-transfer-endpoint")
|
|
|
|
def test_loads_tsig_keys_and_starts_servers(
|
|
self, monkeypatch, make_view
|
|
):
|
|
cmd_mod = self._get_cmd_module()
|
|
|
|
view = make_view(name="ep-view")
|
|
monkeypatch.setattr(cmd_mod, "SETTINGS", {
|
|
"tsig_keys": {
|
|
"ep-view": {
|
|
"keyname": "epkey.",
|
|
"secret": "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE=",
|
|
"algorithm": "hmac-sha256",
|
|
}
|
|
}
|
|
})
|
|
|
|
with patch.object(cmd_mod, "TCPDNSServer") as mock_tcp, \
|
|
patch.object(cmd_mod, "UDPDNSServer") as mock_udp:
|
|
mock_tcp.return_value.serve_forever = MagicMock()
|
|
mock_udp.return_value.serve_forever = MagicMock()
|
|
call_command("dns-transfer-endpoint", "--port", "0", "--address", "127.0.0.1")
|
|
assert mock_tcp.called
|
|
assert mock_udp.called
|