From d5caee8b8b41e8ade8c059c9a50b3f99b6810472 Mon Sep 17 00:00:00 2001 From: Lyncoln Date: Sat, 19 Sep 2026 19:57:03 -0400 Subject: [PATCH] Attach generic references to normalized objects --- dns_sync/adapters.py | 32 +++++++++++ dns_sync/object_normalizer.py | 3 ++ tests/test_reference_normalization.py | 76 +++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 tests/test_reference_normalization.py diff --git a/dns_sync/adapters.py b/dns_sync/adapters.py index f3b9106..63fe686 100644 --- a/dns_sync/adapters.py +++ b/dns_sync/adapters.py @@ -110,11 +110,35 @@ class ObjectReference: object.__setattr__(self, "logical_key", logical_key) +def _normalized_references( + references, +) -> tuple[ObjectReference, ...]: + normalized = tuple(references) + if any( + not isinstance(reference, ObjectReference) + for reference in normalized + ): + raise ValueError( + "Object references must be ObjectReference instances" + ) + if len(set(normalized)) != len(normalized): + raise ValueError("Object references cannot contain duplicates") + return normalized + + @dataclass(frozen=True) class DiscoveredObject: object_type: str local_key: str payload: Mapping[str, Any] + references: tuple[ObjectReference, ...] = () + + def __post_init__(self) -> None: + object.__setattr__( + self, + "references", + _normalized_references(self.references), + ) @dataclass(frozen=True) @@ -123,6 +147,14 @@ class NormalizedObject: logical_key: str payload: Mapping[str, Any] local_key: str | None = None + references: tuple[ObjectReference, ...] = () + + def __post_init__(self) -> None: + object.__setattr__( + self, + "references", + _normalized_references(self.references), + ) @dataclass(frozen=True) diff --git a/dns_sync/object_normalizer.py b/dns_sync/object_normalizer.py index 858a3e2..0bc2e5d 100644 --- a/dns_sync/object_normalizer.py +++ b/dns_sync/object_normalizer.py @@ -95,6 +95,7 @@ def _normalize_dns_record( logical_key=hostname, local_key=discovered.local_key, payload=normalized_payload, + references=discovered.references, ) @@ -146,6 +147,7 @@ def _normalize_proxy_host( logical_key=",".join(domains), local_key=discovered.local_key, payload=normalized_payload, + references=discovered.references, ) @@ -175,6 +177,7 @@ def _normalize_certificate( logical_key=identity.key, local_key=discovered.local_key, payload=normalized_payload, + references=discovered.references, ) diff --git a/tests/test_reference_normalization.py b/tests/test_reference_normalization.py new file mode 100644 index 0000000..b5ccfac --- /dev/null +++ b/tests/test_reference_normalization.py @@ -0,0 +1,76 @@ +import unittest + +from dns_sync.adapters import ( + CERTIFICATES, + PROXY_HOSTS, + DiscoveredObject, + ObjectReference, +) +from dns_sync.object_normalizer import normalize_discovered_object + + +class ReferenceNormalizationTests(unittest.TestCase): + def test_adapter_reference_survives_central_normalization(self): + certificate = ObjectReference( + relation="certificate", + object_type=CERTIFICATES, + local_key="67", + ) + discovered = DiscoveredObject( + object_type=PROXY_HOSTS, + local_key="12", + payload={ + "domain_names": ["app.example.com"], + "forward_scheme": "http", + "forward_host": "192.168.1.20", + "forward_port": 8080, + "enabled": True, + }, + references=[certificate], + ) + + normalized = normalize_discovered_object(discovered) + + self.assertIsInstance(discovered.references, tuple) + self.assertEqual(normalized.references, (certificate,)) + self.assertEqual( + normalized.references[0].local_key, + "67", + ) + self.assertIsNone( + normalized.references[0].logical_key, + ) + + def test_duplicate_references_are_rejected(self): + certificate = ObjectReference( + relation="certificate", + object_type=CERTIFICATES, + local_key="67", + ) + + with self.assertRaisesRegex( + ValueError, + "cannot contain duplicates", + ): + DiscoveredObject( + object_type=PROXY_HOSTS, + local_key="12", + payload={}, + references=(certificate, certificate), + ) + + def test_non_reference_values_are_rejected(self): + with self.assertRaisesRegex( + ValueError, + "ObjectReference instances", + ): + DiscoveredObject( + object_type=PROXY_HOSTS, + local_key="12", + payload={}, + references=("certificate:67",), + ) + + +if __name__ == "__main__": + unittest.main()