"""Tests for the git-backed event store (SHARD-WP-0009 T1). The git backend must satisfy the same EventStore contract as the in-memory one (round-trip, ordering, determinism) while making the log git-addressable. """ import subprocess import pytest from shard_wiki.coordination import ( DecisionLog, EventType, GitEventStore, InMemoryEventStore, deserialize_event, serialize_event, ) @pytest.fixture def git_store(tmp_path): return GitEventStore(tmp_path / "coord") def test_append_git_read_round_trips(git_store): log = DecisionLog(git_store) ev = log.append("s", EventType.ALIAS_SET, {"alias": "Home", "target": "shardA:Index"}) (read,) = log.events("s") assert read.seq == ev.seq == 0 assert read.space == "s" assert read.type is EventType.ALIAS_SET assert read.payload == {"alias": "Home", "target": "shardA:Index"} def test_ordering_preserved_and_per_space_monotonic(git_store): log = DecisionLog(git_store) log.append("a", EventType.ALIAS_SET, {"alias": "X", "target": "s:1"}) log.append("a", EventType.ALIAS_SET, {"alias": "Y", "target": "s:2"}) log.append("b", EventType.ALIAS_SET, {"alias": "Z", "target": "s:3"}) assert [e.seq for e in log.events("a")] == [0, 1] assert [e.payload["alias"] for e in log.events("a")] == ["X", "Y"] assert [e.seq for e in log.events("b")] == [0] # independent ref/ordering def test_each_append_is_a_git_commit(git_store): log = DecisionLog(git_store) log.append("s", EventType.BINDING_MADE, {"members": ["a", "b"]}) log.append("s", EventType.PAGE_FORKED, {"source": "a", "fork": "c"}) ref = GitEventStore._ref("s") count = subprocess.run( ["git", "-C", str(git_store.repo_path), "rev-list", "--count", ref], capture_output=True, text=True, check=True, ).stdout.strip() assert count == "2" # one immutable commit object per append def test_deterministic_serialization_is_stable_and_sorted(): log = InMemoryEventStore() ev = log.append("s", EventType.ALIAS_SET, {"target": "z", "alias": "a"}) blob = serialize_event(ev) assert serialize_event(ev) == blob # stable across calls assert blob.index(b'"alias"') < blob.index(b'"target"') # payload keys sorted, not insertion assert deserialize_event(blob).payload == {"alias": "a", "target": "z"} def test_git_fold_matches_in_memory_fold(git_store): events = [ (EventType.ALIAS_SET, {"alias": "Home", "target": "shardA:Index"}), (EventType.BINDING_MADE, {"members": ["a", "b"]}), (EventType.BINDING_MADE, {"members": ["b", "c"]}), (EventType.ALIAS_SET, {"alias": "Home", "target": "shardB:Main"}), ] mem = DecisionLog(InMemoryEventStore()) git = DecisionLog(git_store) for typ, payload in events: mem.append("s", typ, payload) git.append("s", typ, payload) assert git.fold("s").aliases == mem.fold("s").aliases assert git.fold("s").equivalence_groups == mem.fold("s").equivalence_groups def test_default_decisionlog_is_in_memory(): assert isinstance(DecisionLog()._store, InMemoryEventStore)