Persist generic object references

This commit is contained in:
Lyncoln
2026-09-19 20:22:18 -04:00
parent d5caee8b8b
commit dc6505d165
3 changed files with 295 additions and 9 deletions
+21 -2
View File
@@ -203,7 +203,7 @@ class AppliedStatePersistenceTests(unittest.TestCase):
class SchemaMigrationTests(unittest.TestCase):
def test_version_one_database_is_migrated_to_version_two(self):
def test_version_one_database_is_migrated_to_version_three(self):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "state.sqlite3"
connection = sqlite3.connect(path)
@@ -230,10 +230,29 @@ class SchemaMigrationTests(unittest.TestCase):
"""
)
}
reference_columns = {
table: {
row[1]
for row in store.connection.execute(
f"PRAGMA table_info({table})"
)
}
for table in (
"observations",
"desired_state",
"applied_state",
)
}
self.assertEqual(version, 2)
self.assertEqual(version, 3)
self.assertIn("desired_state", tables)
self.assertIn("applied_state", tables)
self.assertTrue(
all(
"references_json" in columns
for columns in reference_columns.values()
)
)
if __name__ == "__main__":
+142
View File
@@ -0,0 +1,142 @@
import unittest
from datetime import datetime, timezone
from dns_sync.adapters import (
CERTIFICATES,
PROXY_HOSTS,
NormalizedObject,
ObjectReference,
Observation,
)
from dns_sync.persistence import SQLiteStateStore
ENDPOINT = "npm://192.168.1.4:81"
NOW = datetime(2026, 9, 20, 0, 0, tzinfo=timezone.utc)
def local_certificate_reference() -> ObjectReference:
return ObjectReference(
relation="certificate",
object_type=CERTIFICATES,
local_key="67",
)
def logical_certificate_reference() -> ObjectReference:
return ObjectReference(
relation="certificate",
object_type=CERTIFICATES,
logical_key="app.example.com",
)
def proxy_host(
*,
local_key: str | None,
reference: ObjectReference,
) -> NormalizedObject:
return NormalizedObject(
object_type=PROXY_HOSTS,
logical_key="app.example.com",
local_key=local_key,
payload={
"domain_names": ["app.example.com"],
"forward_scheme": "http",
"forward_host": "192.168.1.20",
"forward_port": 8080,
"enabled": True,
},
references=(reference,),
)
class ReferencePersistenceTests(unittest.TestCase):
def test_observed_local_reference_round_trips(self):
observed = Observation(
endpoint_id=ENDPOINT,
object=proxy_host(
local_key="12",
reference=local_certificate_reference(),
),
observed_at=NOW,
)
with SQLiteStateStore(":memory:") as store:
store.replace_observations(
ENDPOINT,
PROXY_HOSTS,
[observed],
NOW,
)
loaded = store.load_observations(PROXY_HOSTS)
self.assertEqual(loaded, [observed])
self.assertEqual(
loaded[0].object.references[0].local_key,
"67",
)
def test_desired_logical_reference_round_trips(self):
desired = proxy_host(
local_key=None,
reference=logical_certificate_reference(),
)
with SQLiteStateStore(":memory:") as store:
store.replace_desired_state(
PROXY_HOSTS,
[desired],
NOW,
)
loaded = store.load_desired_state(PROXY_HOSTS)
self.assertEqual(loaded[0].object, desired)
self.assertEqual(
loaded[0].object.references[0].logical_key,
"app.example.com",
)
def test_applied_reference_participates_in_verification(self):
applied = proxy_host(
local_key="22",
reference=logical_certificate_reference(),
)
different_reference = proxy_host(
local_key="22",
reference=ObjectReference(
relation="certificate",
object_type=CERTIFICATES,
logical_key="other.example.com",
),
)
with SQLiteStateStore(":memory:") as store:
store.record_applied(ENDPOINT, applied, NOW)
with self.assertRaisesRegex(
RuntimeError,
"does not match",
):
store.record_verification_success(
ENDPOINT,
different_reference,
NOW,
)
store.record_verification_success(
ENDPOINT,
applied,
NOW,
)
loaded = store.load_applied_state(
ENDPOINT,
PROXY_HOSTS,
)
self.assertEqual(loaded[0].object, applied)
self.assertTrue(loaded[0].is_verified)
if __name__ == "__main__":
unittest.main()