From 9a7fd2daae51cbc8ff6d9118f38672f270b3b45d Mon Sep 17 00:00:00 2001 From: Krzysztof Rudnicki Date: Sun, 5 Jul 2026 20:55:03 +0200 Subject: [PATCH] Add cross-language wire-format fixture test and wrong-shape sync coverage The Python and Dart test suites each only verified round-tripping through their own encoder/decoder, so a Record.to_dict()/toJson() key mismatch between the two (deleted_hlc vs deletedHlc) would have gone undetected despite being the exact thing this shared library exists to prevent. Pin one canonical fixture in both suites so a future divergence fails loudly on at least one side. Also add a test for a valid-JSON-but-wrong-shape remote push, exercising the full (ValueError, KeyError, TypeError) skip path, not just JSON syntax errors. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_017McuA1nAi6jBT1EaS5BrVf --- crdt-sync/crdt_sync/tests/test_record.py | 29 ++++++++++++++++++++++++ crdt-sync/crdt_sync/tests/test_sync.py | 22 ++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/crdt-sync/crdt_sync/tests/test_record.py b/crdt-sync/crdt_sync/tests/test_record.py index c372963..787c950 100644 --- a/crdt-sync/crdt_sync/tests/test_record.py +++ b/crdt-sync/crdt_sync/tests/test_record.py @@ -109,3 +109,32 @@ class TestRecordDictRoundTrip: def test_round_trips_a_record_with_no_delete_clock(self) -> None: record = Record(id="x", fields={}, deleted=False, deleted_hlc=None) assert Record.from_dict(record.to_dict()) == record + + +class TestCrossLanguageWireFormat: + """Pins the exact wire shape shared with crdt_sync_dart's Record. + + Values chosen here are duplicated verbatim in crdt_sync_dart's + `test/record_test.dart` (see its ``TestCrossLanguageWireFormat`` + group). If this test and that one both pass, the two languages agree + on the wire format; if only one changes, the two suites diverge and + at least one of them fails -- catching exactly the kind of + key-naming mismatch (``deleted_hlc`` vs ``deletedHlc``) that neither + language's own round-trip test can see on its own. + """ + + def test_matches_the_fixture_shared_with_the_dart_package(self) -> None: + record = Record( + id="abc123", + fields={"text": ("hello", Hlc(wall_time_ms=100, counter=0, node_id="pc"))}, + deleted=True, + deleted_hlc=Hlc(wall_time_ms=1000, counter=0, node_id="node-a"), + ) + expected = { + "id": "abc123", + "fields": {"text": ["hello", "1970-01-01T00:00:00.100Z-0000-pc"]}, + "deleted": True, + "deleted_hlc": "1970-01-01T00:00:01.000Z-0000-node-a", + } + assert record.to_dict() == expected + assert Record.from_dict(expected) == record diff --git a/crdt-sync/crdt_sync/tests/test_sync.py b/crdt-sync/crdt_sync/tests/test_sync.py index 593ad18..45fdbb4 100644 --- a/crdt-sync/crdt_sync/tests/test_sync.py +++ b/crdt-sync/crdt_sync/tests/test_sync.py @@ -117,6 +117,28 @@ class TestSyncLog: assert merged == {} assert "Unparsable log" in caplog.text + def test_skips_a_device_whose_pushed_json_has_the_wrong_shape(self) -> None: + """Valid JSON that isn't a record map (e.g. from an incompatible + writer) must be skipped like corrupt JSON, not crash the whole + sync -- this is what the broad ``(ValueError, KeyError, TypeError)`` + catch in ``_pull_remote_logs`` is for, not just JSON syntax errors. + """ + client = _mock_client( + devices=("phone",), + files={"devices/phone/log.json": '{"a": 5}'}, + ) + + merged = sync_log( + client=client, + device_id="pc", + path_prefix="devices", + local_log={}, + encode=_encode, + decode=_decode, + ) + + assert merged == {} + def test_merges_in_a_remote_devices_entries( self, make_hlc: Callable[..., Hlc] ) -> None: