cgcardona / muse public
test_stress_store_provenance.py python
417 lines 14.8 KB
e6786943 feat: upgrade to Python 3.14, drop from __future__ import annotations Gabriel Cardona <cgcardona@gmail.com> 1d ago
1 """Stress tests for CommitRecord, SnapshotRecord, TagRecord, and provenance fields.
2
3 Covers:
4 - CommitRecord round-trip through to_dict/from_dict for all format versions.
5 - format_version evolution: missing fields default correctly when reading old records.
6 - reviewed_by (ORSet semantics): list preserved, sorted, deduplicated via overwrite_commit.
7 - test_runs (GCounter semantics): monotonically increases via overwrite_commit.
8 - agent_id / model_id / toolchain_id / prompt_hash / signature fields.
9 - SnapshotRecord round-trip with large manifests.
10 - TagRecord round-trip.
11 - get_head_commit_id on empty branch returns None.
12 - write_commit is idempotent (won't overwrite).
13 - overwrite_commit updates the persisted record correctly.
14 - read_commit for absent commit returns None.
15 - list_commits and list_branches.
16 - list_tags returns all tags.
17 """
18
19 import datetime
20 import pathlib
21
22 import pytest
23
24 from muse.core.crdts.or_set import ORSet
25 from muse.domain import SemVerBump
26 from muse.core.store import (
27 CommitDict,
28 CommitRecord,
29 SnapshotRecord,
30 TagRecord,
31 get_all_commits,
32 get_all_tags,
33 get_head_commit_id,
34 overwrite_commit,
35 read_commit,
36 read_snapshot,
37 write_commit,
38 write_snapshot,
39 write_tag,
40 )
41
42
43 # ---------------------------------------------------------------------------
44 # Fixtures
45 # ---------------------------------------------------------------------------
46
47
48 @pytest.fixture
49 def repo(tmp_path: pathlib.Path) -> pathlib.Path:
50 muse = tmp_path / ".muse"
51 (muse / "commits").mkdir(parents=True)
52 (muse / "snapshots").mkdir(parents=True)
53 (muse / "tags").mkdir(parents=True)
54 (muse / "refs" / "heads").mkdir(parents=True)
55 return tmp_path
56
57
58 def _now() -> datetime.datetime:
59 return datetime.datetime.now(datetime.timezone.utc)
60
61
62 def _commit(
63 cid: str = "abc1234",
64 branch: str = "main",
65 parent: str | None = None,
66 ) -> CommitRecord:
67 return CommitRecord(
68 commit_id=cid,
69 repo_id="test-repo",
70 branch=branch,
71 snapshot_id=f"snap-{cid}",
72 message=f"commit {cid}",
73 committed_at=_now(),
74 parent_commit_id=parent,
75 )
76
77
78 # ===========================================================================
79 # CommitRecord round-trip
80 # ===========================================================================
81
82
83 class TestCommitRecordRoundTrip:
84 def test_minimal_round_trip(self) -> None:
85 c = _commit()
86 restored = CommitRecord.from_dict(c.to_dict())
87 assert restored.commit_id == c.commit_id
88 assert restored.branch == c.branch
89 assert restored.message == c.message
90
91 def test_all_provenance_fields_preserved(self) -> None:
92 c = CommitRecord(
93 commit_id="prov123",
94 repo_id="repo",
95 branch="main",
96 snapshot_id="snap",
97 message="provenance commit",
98 committed_at=_now(),
99 agent_id="claude-v4",
100 model_id="claude-3-5-sonnet",
101 toolchain_id="muse-cli-1.0",
102 prompt_hash="abc" * 10 + "ab",
103 signature="sig-" + "x" * 60,
104 signer_key_id="key-001",
105 )
106 d = c.to_dict()
107 restored = CommitRecord.from_dict(d)
108 assert restored.agent_id == "claude-v4"
109 assert restored.model_id == "claude-3-5-sonnet"
110 assert restored.toolchain_id == "muse-cli-1.0"
111 assert restored.signature == c.signature
112 assert restored.signer_key_id == "key-001"
113
114 def test_crdt_fields_preserved(self) -> None:
115 c = CommitRecord(
116 commit_id="crdt123",
117 repo_id="repo",
118 branch="main",
119 snapshot_id="snap",
120 message="crdt",
121 committed_at=_now(),
122 reviewed_by=["alice", "bob", "charlie"],
123 test_runs=42,
124 )
125 d = c.to_dict()
126 restored = CommitRecord.from_dict(d)
127 assert sorted(restored.reviewed_by) == ["alice", "bob", "charlie"]
128 assert restored.test_runs == 42
129
130 def test_format_version_5_is_default(self) -> None:
131 c = _commit()
132 assert c.format_version == 5
133
134 def test_format_version_persisted(self) -> None:
135 c = _commit()
136 assert CommitRecord.from_dict(c.to_dict()).format_version == 5
137
138 def test_sem_ver_bump_preserved(self) -> None:
139 bumps: tuple[SemVerBump, ...] = ("none", "patch", "minor", "major")
140 for bump in bumps:
141 c = CommitRecord(
142 commit_id="sv",
143 repo_id="r",
144 branch="main",
145 snapshot_id="s",
146 message="m",
147 committed_at=_now(),
148 sem_ver_bump=bump,
149 )
150 assert CommitRecord.from_dict(c.to_dict()).sem_ver_bump == bump
151
152 def test_breaking_changes_preserved(self) -> None:
153 c = CommitRecord(
154 commit_id="bc",
155 repo_id="r",
156 branch="main",
157 snapshot_id="s",
158 message="m",
159 committed_at=_now(),
160 breaking_changes=["removed `old_api`", "renamed `foo` → `bar`"],
161 )
162 restored = CommitRecord.from_dict(c.to_dict())
163 assert restored.breaking_changes == ["removed `old_api`", "renamed `foo` → `bar`"]
164
165 def test_parent_ids_preserved(self) -> None:
166 c = CommitRecord(
167 commit_id="merge",
168 repo_id="r",
169 branch="main",
170 snapshot_id="s",
171 message="m",
172 committed_at=_now(),
173 parent_commit_id="parent-1",
174 parent2_commit_id="parent-2",
175 )
176 restored = CommitRecord.from_dict(c.to_dict())
177 assert restored.parent_commit_id == "parent-1"
178 assert restored.parent2_commit_id == "parent-2"
179
180 def test_missing_crdt_fields_default_correctly(self) -> None:
181 """Simulates reading an older commit that lacks reviewed_by / test_runs."""
182 minimal: CommitDict = {
183 "commit_id": "old",
184 "repo_id": "r",
185 "branch": "main",
186 "snapshot_id": "snap",
187 "message": "old commit",
188 "committed_at": _now().isoformat(),
189 }
190 restored = CommitRecord.from_dict(minimal)
191 assert restored.reviewed_by == []
192 assert restored.test_runs == 0
193 assert restored.format_version == 1
194
195 def test_committed_at_timezone_aware(self) -> None:
196 c = _commit()
197 restored = CommitRecord.from_dict(c.to_dict())
198 assert restored.committed_at.tzinfo is not None
199
200
201 # ===========================================================================
202 # CommitRecord persistence
203 # ===========================================================================
204
205
206 class TestCommitPersistence:
207 def test_write_and_read_back(self, repo: pathlib.Path) -> None:
208 c = _commit("id001")
209 write_commit(repo, c)
210 restored = read_commit(repo, "id001")
211 assert restored is not None
212 assert restored.commit_id == "id001"
213
214 def test_write_is_idempotent(self, repo: pathlib.Path) -> None:
215 c = CommitRecord(
216 commit_id="id002", repo_id="test-repo", branch="main",
217 snapshot_id="snap-id002", message="original", committed_at=_now(),
218 )
219 write_commit(repo, c)
220 # Second write with a different message — original must be kept.
221 c2 = CommitRecord(
222 commit_id="id002", repo_id="test-repo", branch="main",
223 snapshot_id="snap-id002", message="overwritten-attempt", committed_at=_now(),
224 )
225 write_commit(repo, c2)
226 restored = read_commit(repo, "id002")
227 assert restored is not None
228 assert restored.message == "original"
229
230 def test_read_absent_commit_returns_none(self, repo: pathlib.Path) -> None:
231 assert read_commit(repo, "does-not-exist") is None
232
233 def test_overwrite_commit_updates_reviewed_by(self, repo: pathlib.Path) -> None:
234 c = _commit("id003")
235 write_commit(repo, c)
236 # Simulate ORSet merge: add reviewer.
237 updated = read_commit(repo, "id003")
238 assert updated is not None
239 updated.reviewed_by = ["agent-x", "human-bob"]
240 overwrite_commit(repo, updated)
241 restored = read_commit(repo, "id003")
242 assert restored is not None
243 assert "agent-x" in restored.reviewed_by
244 assert "human-bob" in restored.reviewed_by
245
246 def test_overwrite_commit_updates_test_runs(self, repo: pathlib.Path) -> None:
247 c = _commit("id004")
248 write_commit(repo, c)
249 for expected in range(1, 6):
250 rec = read_commit(repo, "id004")
251 assert rec is not None
252 rec.test_runs += 1
253 overwrite_commit(repo, rec)
254 after = read_commit(repo, "id004")
255 assert after is not None
256 assert after.test_runs == expected
257
258 def test_list_commits_returns_all_written(self, repo: pathlib.Path) -> None:
259 ids = [f"c{i:04d}" for i in range(20)]
260 for cid in ids:
261 write_commit(repo, _commit(cid))
262 found = {c.commit_id for c in get_all_commits(repo)}
263 for cid in ids:
264 assert cid in found
265
266 def test_many_commits_all_retrievable(self, repo: pathlib.Path) -> None:
267 for i in range(100):
268 write_commit(repo, _commit(f"stress-{i:04d}"))
269 for i in range(100):
270 assert read_commit(repo, f"stress-{i:04d}") is not None
271
272
273 # ===========================================================================
274 # SnapshotRecord
275 # ===========================================================================
276
277
278 class TestSnapshotRecordRoundTrip:
279 def test_minimal_round_trip(self) -> None:
280 s = SnapshotRecord(snapshot_id="snap-1", manifest={"f.mid": "hash1"})
281 restored = SnapshotRecord.from_dict(s.to_dict())
282 assert restored.snapshot_id == "snap-1"
283 assert restored.manifest == {"f.mid": "hash1"}
284
285 def test_large_manifest(self) -> None:
286 manifest = {f"track_{i:04d}.mid": f"hash-{i:064d}" for i in range(500)}
287 s = SnapshotRecord(snapshot_id="big-snap", manifest=manifest)
288 restored = SnapshotRecord.from_dict(s.to_dict())
289 assert len(restored.manifest) == 500
290 assert restored.manifest["track_0000.mid"] == f"hash-{0:064d}"
291
292 def test_write_and_read_back(self, repo: pathlib.Path) -> None:
293 s = SnapshotRecord(snapshot_id="snap-rw", manifest={"a.mid": "h1", "b.mid": "h2"})
294 write_snapshot(repo, s)
295 restored = read_snapshot(repo, "snap-rw")
296 assert restored is not None
297 assert restored.manifest == {"a.mid": "h1", "b.mid": "h2"}
298
299 def test_empty_manifest_round_trip(self) -> None:
300 s = SnapshotRecord(snapshot_id="empty-snap", manifest={})
301 restored = SnapshotRecord.from_dict(s.to_dict())
302 assert restored.manifest == {}
303
304
305 # ===========================================================================
306 # TagRecord
307 # ===========================================================================
308
309
310 class TestTagRecord:
311 def test_round_trip(self) -> None:
312 t = TagRecord(
313 tag_id="tag-001",
314 repo_id="repo",
315 commit_id="abc123",
316 tag="v1.0.0",
317 )
318 restored = TagRecord.from_dict(t.to_dict())
319 assert restored.tag_id == "tag-001"
320 assert restored.tag == "v1.0.0"
321 assert restored.commit_id == "abc123"
322
323 def test_write_and_list(self, repo: pathlib.Path) -> None:
324 for i in range(10):
325 write_tag(repo, TagRecord(
326 tag_id=f"tag-{i:04d}",
327 repo_id="repo",
328 commit_id=f"commit-{i:04d}",
329 tag=f"v{i}.0.0",
330 ))
331 tags = get_all_tags(repo, "repo")
332 assert len(tags) == 10
333
334 def test_created_at_preserved(self) -> None:
335 ts = datetime.datetime(2025, 6, 15, 12, 0, 0, tzinfo=datetime.timezone.utc)
336 t = TagRecord(tag_id="t", repo_id="r", commit_id="c", tag="v1", created_at=ts)
337 restored = TagRecord.from_dict(t.to_dict())
338 assert abs((restored.created_at - ts).total_seconds()) < 1.0
339
340
341 # ===========================================================================
342 # get_head_commit_id
343 # ===========================================================================
344
345
346 class TestGetHeadCommitId:
347 def test_empty_branch_returns_none(self, repo: pathlib.Path) -> None:
348 assert get_head_commit_id(repo, "nonexistent-branch") is None
349
350 def test_returns_id_after_writing_head_ref(self, repo: pathlib.Path) -> None:
351 ref_path = repo / ".muse" / "refs" / "heads" / "main"
352 ref_path.write_text("abc1234\n")
353 assert get_head_commit_id(repo, "main") == "abc1234"
354
355 def test_strips_whitespace(self, repo: pathlib.Path) -> None:
356 ref_path = repo / ".muse" / "refs" / "heads" / "feature"
357 ref_path.write_text(" deadbeef \n")
358 assert get_head_commit_id(repo, "feature") == "deadbeef"
359
360
361 # ===========================================================================
362 # CRDT semantics on CommitRecord fields
363 # ===========================================================================
364
365
366 class TestCRDTAnnotationSemantics:
367 def test_reviewed_by_orset_union_semantics(self, repo: pathlib.Path) -> None:
368 """ORSet union: multiple overwrite_commit calls accumulate reviewers."""
369 c = _commit("crdt-or-001")
370 write_commit(repo, c)
371
372 # Agent 1 adds their name.
373 rec = read_commit(repo, "crdt-or-001")
374 assert rec is not None
375 s, tok1 = ORSet().add("agent-alpha")
376 rec.reviewed_by = list(s.elements())
377 overwrite_commit(repo, rec)
378
379 # Agent 2 independently adds their name.
380 rec2 = read_commit(repo, "crdt-or-001")
381 assert rec2 is not None
382 s2 = ORSet()
383 for name in rec2.reviewed_by:
384 s2, _ = s2.add(name)
385 s2, tok2 = s2.add("agent-beta")
386 rec2.reviewed_by = sorted(s2.elements())
387 overwrite_commit(repo, rec2)
388
389 final = read_commit(repo, "crdt-or-001")
390 assert final is not None
391 assert "agent-alpha" in final.reviewed_by
392 assert "agent-beta" in final.reviewed_by
393
394 def test_test_runs_gcounter_monotone(self, repo: pathlib.Path) -> None:
395 """GCounter: test_runs must never decrease."""
396 c = _commit("crdt-gc-001")
397 write_commit(repo, c)
398 prev = 0
399 for _ in range(50):
400 rec = read_commit(repo, "crdt-gc-001")
401 assert rec is not None
402 rec.test_runs += 1
403 overwrite_commit(repo, rec)
404 current = read_commit(repo, "crdt-gc-001")
405 assert current is not None
406 assert current.test_runs >= prev
407 prev = current.test_runs
408 assert prev == 50
409
410 def test_all_provenance_fields_default_to_empty_string(self) -> None:
411 c = _commit()
412 assert c.agent_id == ""
413 assert c.model_id == ""
414 assert c.toolchain_id == ""
415 assert c.prompt_hash == ""
416 assert c.signature == ""
417 assert c.signer_key_id == ""