import unittest from datetime import datetime, timezone from dns_sync.adapters import ( CERTIFICATES, DNS_RECORDS, NormalizedObject, Observation, ) from dns_sync.reconciliation import ( ReconciliationConflict, group_observations, reconcile_observations, ) NOW = datetime(2026, 9, 19, 22, 0, tzinfo=timezone.utc) def dns_observation( endpoint_id: str, hostname: str, ip: str, local_key: str, ) -> Observation: return Observation( endpoint_id=endpoint_id, object=NormalizedObject( object_type=DNS_RECORDS, logical_key=hostname, local_key=local_key, payload={ "hostname": hostname, "ip": ip, "record_type": "A", "wildcard": False, }, ), observed_at=NOW, ) class ObservationGroupingTests(unittest.TestCase): def test_groups_by_type_and_logical_identity(self): observations = [ dns_observation( "npm://192.168.1.4:81", "app.example.com", "192.168.1.101", "22", ), dns_observation( "npm://192.168.1.3:81", "app.example.com", "192.168.1.101", "12", ), dns_observation( "npm://192.168.1.3:81", "other.example.com", "192.168.1.101", "13", ), ] grouped = group_observations(observations) self.assertEqual( tuple(grouped), ( (DNS_RECORDS, "app.example.com"), (DNS_RECORDS, "other.example.com"), ), ) self.assertEqual( tuple( item.endpoint_id for item in grouped[ (DNS_RECORDS, "app.example.com") ] ), ( "npm://192.168.1.3:81", "npm://192.168.1.4:81", ), ) class DnsUnionPolicyTests(unittest.TestCase): def test_union_keeps_every_unique_hostname(self): observations = [ dns_observation( "npm://192.168.1.3:81", "one.example.com", "192.168.1.101", "12", ), dns_observation( "npm://192.168.1.4:81", "two.example.com", "192.168.1.101", "22", ), ] desired = reconcile_observations(observations) self.assertEqual( tuple(item.logical_key for item in desired), ("one.example.com", "two.example.com"), ) self.assertTrue( all(item.local_key is None for item in desired) ) def test_identical_observations_converge_to_one_object(self): observations = [ dns_observation( "npm://192.168.1.3:81", "app.example.com", "192.168.1.101", "12", ), dns_observation( "npm://192.168.1.4:81", "app.example.com", "192.168.1.101", "22", ), ] desired = reconcile_observations(observations) self.assertEqual(len(desired), 1) self.assertEqual( desired[0].payload["ip"], "192.168.1.101", ) self.assertIsNone(desired[0].local_key) def test_conflicting_values_fail_without_exposing_payload(self): observations = [ dns_observation( "npm://node-one:81", "app.example.com", "192.168.1.101", "12", ), dns_observation( "npm://node-two:81", "app.example.com", "203.0.113.77", "22", ), ] with self.assertRaises(ReconciliationConflict) as raised: reconcile_observations(observations) message = str(raised.exception) self.assertIn("app.example.com", message) self.assertIn("npm://node-one:81", message) self.assertIn("npm://node-two:81", message) self.assertNotIn("192.168.1.101", message) self.assertNotIn("203.0.113.77", message) def test_missing_policy_fails_explicitly(self): observation = Observation( endpoint_id="npm://node-one:81", object=NormalizedObject( object_type=CERTIFICATES, logical_key="app.example.com", local_key="67", payload={"expires_on": "2026-12-15"}, ), observed_at=NOW, ) with self.assertRaisesRegex( RuntimeError, "No reconciliation policy", ): reconcile_observations([observation]) def test_explicit_empty_policy_registry_uses_no_defaults(self): observation = dns_observation( "npm://node-one:81", "app.example.com", "192.168.1.101", "12", ) with self.assertRaisesRegex(RuntimeError, "No reconciliation policy"): reconcile_observations([observation], policies={}) if __name__ == "__main__": unittest.main()