gabriel / muse public

test_plumbing_remaining.py file-level

at sha256:6 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:0 test: validate staging push and MP workflow for aaronrene · · Jul 7, 2026
1 """Comprehensive tests for the remaining 8 plumbing commands:
2 domain-info, show-ref, verify-object, symbolic-ref,
3 for-each-ref, name-rev, check-ref-format, verify-pack.
4
5 (check-ignore and check-attr are covered via attribute/ignore tests elsewhere.)
6
7 Coverage tiers
8 --------------
9 - Integration: core functionality, JSON/text formats, key flags
10 - Security: errors to stderr, no traceback on bad input
11 - Stress: 100+ object verify, 200 ref iterations
12 """
13 from __future__ import annotations
14
15 import datetime
16 import hashlib
17 import json
18 import pathlib
19
20 import msgpack
21
22 from muse.core.errors import ExitCode
23 from muse.core.object_store import write_object
24 from muse.core.snapshot import compute_commit_id, compute_snapshot_id
25 from muse.core.store import CommitRecord, SnapshotRecord, write_commit, write_snapshot
26 from muse.core._types import Manifest
27 from tests.cli_test_helper import CliRunner, InvokeResult
28
29 runner = CliRunner()
30
31
32 # ---------------------------------------------------------------------------
33 # Shared helpers
34 # ---------------------------------------------------------------------------
35
36 def _make_repo(tmp_path: pathlib.Path, domain: str = "code") -> pathlib.Path:
37 repo = tmp_path / "repo"
38 muse = repo / ".muse"
39 for sub in ("objects", "commits", "snapshots", "refs/heads"):
40 (muse / sub).mkdir(parents=True)
41 (muse / "HEAD").write_text("ref: refs/heads/main")
42 (muse / "repo.json").write_text(json.dumps({"repo_id": "test-repo", "domain": domain}))
43 return repo
44
45
46 _TS = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
47
48
49 def _snap(repo: pathlib.Path, manifest: Manifest | None = None) -> str:
50 sid = compute_snapshot_id(manifest or {})
51 write_snapshot(repo, SnapshotRecord(
52 snapshot_id=sid,
53 manifest=manifest or {},
54 created_at=_TS,
55 ))
56 return sid
57
58
59 def _commit(
60 repo: pathlib.Path,
61 snap_id: str,
62 *,
63 branch: str = "main",
64 parent: str | None = None,
65 message: str = "test",
66 ) -> str:
67 parents = [parent] if parent else []
68 cid = compute_commit_id(parents, snap_id, message, _TS.isoformat())
69 write_commit(repo, CommitRecord(
70 commit_id=cid,
71 repo_id="test-repo",
72 branch=branch,
73 snapshot_id=snap_id,
74 message=message,
75 committed_at=_TS,
76 parent_commit_id=parent,
77 ))
78 return cid
79
80
81 def _set_head(repo: pathlib.Path, branch: str, commit_id: str) -> None:
82 ref = repo / ".muse" / "refs" / "heads" / branch
83 ref.parent.mkdir(parents=True, exist_ok=True)
84 ref.write_text(commit_id)
85 (repo / ".muse" / "HEAD").write_text(f"ref: refs/heads/{branch}")
86
87
88 def _invoke(cmd: str, repo: pathlib.Path, *args: str, input: bytes | None = None) -> InvokeResult:
89 from muse.cli.app import main as cli
90 return runner.invoke(
91 cli,
92 [cmd, *args],
93 env={"MUSE_REPO_ROOT": str(repo)},
94 input=input,
95 )
96
97
98 def _fake_oid(n: int) -> str:
99 return format(n, "064x")
100
101
102 def _write_obj(repo: pathlib.Path, data: bytes) -> str:
103 """Write bytes to the object store; returns the SHA-256 object ID."""
104 oid = hashlib.sha256(data).hexdigest()
105 write_object(repo, oid, data)
106 return oid
107
108
109 # ===========================================================================
110 # domain-info
111 # ===========================================================================
112
113
114 class TestDomainInfo:
115 def test_all_domains_returns_list(self, tmp_path: pathlib.Path) -> None:
116 repo = _make_repo(tmp_path)
117 result = _invoke("domain-info", repo, "--all-domains")
118 assert result.exit_code == 0
119 data = json.loads(result.output)
120 assert "registered_domains" in data
121 assert isinstance(data["registered_domains"], list)
122
123 def test_all_domains_text_format(self, tmp_path: pathlib.Path) -> None:
124 repo = _make_repo(tmp_path)
125 result = _invoke("domain-info", repo, "--all-domains", "--format", "text")
126 assert result.exit_code == 0
127 assert len(result.output.strip()) > 0
128
129 def test_json_shorthand(self, tmp_path: pathlib.Path) -> None:
130 repo = _make_repo(tmp_path)
131 result = _invoke("domain-info", repo, "--all-domains", "--json")
132 assert result.exit_code == 0
133 assert "registered_domains" in json.loads(result.output)
134
135 def test_no_traceback_on_bad_domain(self, tmp_path: pathlib.Path) -> None:
136 repo = _make_repo(tmp_path, domain="nonexistent-domain")
137 result = _invoke("domain-info", repo)
138 assert "Traceback" not in result.output
139
140
141 # ===========================================================================
142 # show-ref
143 # ===========================================================================
144
145
146 class TestShowRef:
147 def test_shows_branches(self, tmp_path: pathlib.Path) -> None:
148 repo = _make_repo(tmp_path)
149 sid = _snap(repo)
150 cid = _commit(repo, sid)
151 _set_head(repo, "main", cid)
152 result = _invoke("show-ref", repo)
153 assert result.exit_code == 0
154 data = json.loads(result.output)
155 assert data["count"] == 1
156 assert any(r["ref"] == "refs/heads/main" for r in data["refs"])
157
158 def test_head_only_flag(self, tmp_path: pathlib.Path) -> None:
159 repo = _make_repo(tmp_path)
160 sid = _snap(repo)
161 cid = _commit(repo, sid)
162 _set_head(repo, "main", cid)
163 result = _invoke("show-ref", repo, "--head")
164 assert result.exit_code == 0
165 data = json.loads(result.output)
166 assert data["head"] is not None
167 assert data["head"]["commit_id"] == cid
168
169 def test_head_text_format(self, tmp_path: pathlib.Path) -> None:
170 repo = _make_repo(tmp_path)
171 sid = _snap(repo)
172 cid = _commit(repo, sid)
173 _set_head(repo, "main", cid)
174 result = _invoke("show-ref", repo, "--format", "text")
175 assert result.exit_code == 0
176 assert cid in result.output
177 assert "main" in result.output
178
179 def test_verify_existing_ref(self, tmp_path: pathlib.Path) -> None:
180 repo = _make_repo(tmp_path)
181 sid = _snap(repo)
182 cid = _commit(repo, sid)
183 _set_head(repo, "main", cid)
184 result = _invoke("show-ref", repo, "--verify", "refs/heads/main")
185 assert result.exit_code == 0
186
187 def test_verify_nonexistent_ref_exits_1(self, tmp_path: pathlib.Path) -> None:
188 repo = _make_repo(tmp_path)
189 result = _invoke("show-ref", repo, "--verify", "ghost")
190 assert result.exit_code == ExitCode.USER_ERROR
191
192 def test_empty_repo_no_refs(self, tmp_path: pathlib.Path) -> None:
193 repo = _make_repo(tmp_path)
194 result = _invoke("show-ref", repo)
195 assert result.exit_code == 0
196 data = json.loads(result.output)
197 assert data["count"] == 0
198
199 def test_200_sequential_calls(self, tmp_path: pathlib.Path) -> None:
200 repo = _make_repo(tmp_path)
201 sid = _snap(repo)
202 cid = _commit(repo, sid)
203 _set_head(repo, "main", cid)
204 for i in range(200):
205 result = _invoke("show-ref", repo)
206 assert result.exit_code == 0, f"failed at {i}"
207
208
209 # ===========================================================================
210 # verify-object
211 # ===========================================================================
212
213
214 class TestVerifyObject:
215 def test_valid_object_ok(self, tmp_path: pathlib.Path) -> None:
216 repo = _make_repo(tmp_path)
217 oid = _write_obj(repo, b"hello world")
218 result = _invoke("verify-object", repo, oid)
219 assert result.exit_code == 0
220 data = json.loads(result.output)
221 assert data["all_ok"] is True
222 assert data["results"][0]["ok"] is True
223
224 def test_missing_object_fails(self, tmp_path: pathlib.Path) -> None:
225 repo = _make_repo(tmp_path)
226 result = _invoke("verify-object", repo, "dead" + "beef" * 15)
227 assert result.exit_code == ExitCode.USER_ERROR
228 data = json.loads(result.output)
229 assert data["all_ok"] is False
230 assert not data["results"][0]["ok"]
231
232 def test_invalid_object_id_fails(self, tmp_path: pathlib.Path) -> None:
233 repo = _make_repo(tmp_path)
234 result = _invoke("verify-object", repo, "not-hex")
235 assert result.exit_code == ExitCode.USER_ERROR
236 data = json.loads(result.output)
237 assert data["all_ok"] is False
238
239 def test_text_format(self, tmp_path: pathlib.Path) -> None:
240 repo = _make_repo(tmp_path)
241 oid = _write_obj(repo, b"text test")
242 result = _invoke("verify-object", repo, "--format", "text", oid)
243 assert result.exit_code == 0
244 assert "OK" in result.output
245
246 def test_quiet_mode_exit_code(self, tmp_path: pathlib.Path) -> None:
247 repo = _make_repo(tmp_path)
248 oid = _write_obj(repo, b"quiet")
249 result = _invoke("verify-object", repo, "--quiet", oid)
250 assert result.exit_code == 0
251
252 def test_multiple_objects(self, tmp_path: pathlib.Path) -> None:
253 repo = _make_repo(tmp_path)
254 oid1 = _write_obj(repo, b"obj1")
255 oid2 = _write_obj(repo, b"obj2")
256 result = _invoke("verify-object", repo, oid1, oid2)
257 assert result.exit_code == 0
258 data = json.loads(result.output)
259 assert data["checked"] == 2
260 assert data["failed"] == 0
261
262 def test_100_objects(self, tmp_path: pathlib.Path) -> None:
263 repo = _make_repo(tmp_path)
264 oids = [_write_obj(repo, f"obj-{i}".encode()) for i in range(100)]
265 result = _invoke("verify-object", repo, *oids)
266 assert result.exit_code == 0
267 data = json.loads(result.output)
268 assert data["checked"] == 100
269 assert data["failed"] == 0
270
271 def test_no_traceback_on_bad_id(self, tmp_path: pathlib.Path) -> None:
272 repo = _make_repo(tmp_path)
273 result = _invoke("verify-object", repo, "bad")
274 assert "Traceback" not in result.output
275
276
277 # ===========================================================================
278 # symbolic-ref
279 # ===========================================================================
280
281
282 class TestSymbolicRef:
283 def test_reads_head_branch(self, tmp_path: pathlib.Path) -> None:
284 repo = _make_repo(tmp_path)
285 sid = _snap(repo)
286 cid = _commit(repo, sid)
287 _set_head(repo, "main", cid)
288 result = _invoke("symbolic-ref", repo, "HEAD")
289 assert result.exit_code == 0
290 data = json.loads(result.output)
291 assert data["branch"] == "main"
292 assert "refs/heads/main" in data["symbolic_target"]
293
294 def test_short_flag(self, tmp_path: pathlib.Path) -> None:
295 repo = _make_repo(tmp_path)
296 sid = _snap(repo)
297 cid = _commit(repo, sid)
298 _set_head(repo, "main", cid)
299 result = _invoke("symbolic-ref", repo, "--format", "text", "--short", "HEAD")
300 assert result.exit_code == 0
301 assert result.output.strip() == "main"
302
303 def test_set_changes_head(self, tmp_path: pathlib.Path) -> None:
304 repo = _make_repo(tmp_path)
305 sid = _snap(repo)
306 cid = _commit(repo, sid, branch="dev")
307 _set_head(repo, "dev", cid)
308 result = _invoke("symbolic-ref", repo, "--set", "dev", "HEAD")
309 assert result.exit_code == 0
310 assert (repo / ".muse" / "HEAD").read_text().strip() == "ref: refs/heads/dev"
311
312 def test_unsupported_ref_errors(self, tmp_path: pathlib.Path) -> None:
313 repo = _make_repo(tmp_path)
314 result = _invoke("symbolic-ref", repo, "refs/heads/main")
315 assert result.exit_code == ExitCode.USER_ERROR
316
317 def test_no_traceback_on_bad_ref(self, tmp_path: pathlib.Path) -> None:
318 repo = _make_repo(tmp_path)
319 result = _invoke("symbolic-ref", repo, "bad-ref")
320 assert "Traceback" not in result.output
321
322
323 # ===========================================================================
324 # for-each-ref
325 # ===========================================================================
326
327
328 class TestForEachRef:
329 def test_lists_all_refs(self, tmp_path: pathlib.Path) -> None:
330 repo = _make_repo(tmp_path)
331 sid = _snap(repo)
332 cid1 = _commit(repo, sid, branch="main", message="init-main")
333 cid2 = _commit(repo, sid, branch="dev", message="init-dev")
334 _set_head(repo, "main", cid1)
335 _set_head(repo, "dev", cid2)
336 result = _invoke("for-each-ref", repo)
337 assert result.exit_code == 0
338 data = json.loads(result.output)
339 branch_names = {r["branch"] for r in data["refs"]}
340 assert "main" in branch_names
341 assert "dev" in branch_names
342
343 def test_text_format(self, tmp_path: pathlib.Path) -> None:
344 repo = _make_repo(tmp_path)
345 sid = _snap(repo)
346 cid = _commit(repo, sid)
347 _set_head(repo, "main", cid)
348 result = _invoke("for-each-ref", repo, "--format", "text")
349 assert result.exit_code == 0
350 assert "main" in result.output
351
352 def test_count_limit(self, tmp_path: pathlib.Path) -> None:
353 repo = _make_repo(tmp_path)
354 sid = _snap(repo)
355 for i in range(5):
356 cid = _commit(repo, sid, branch=f"branch-{i}", message=f"branch-{i}")
357 _set_head(repo, f"branch-{i}", cid)
358 result = _invoke("for-each-ref", repo, "--count", "3")
359 assert result.exit_code == 0
360 data = json.loads(result.output)
361 assert len(data["refs"]) == 3
362
363 def test_empty_repo(self, tmp_path: pathlib.Path) -> None:
364 repo = _make_repo(tmp_path)
365 result = _invoke("for-each-ref", repo)
366 assert result.exit_code == 0
367 data = json.loads(result.output)
368 assert data["refs"] == []
369
370
371 # ===========================================================================
372 # name-rev
373 # ===========================================================================
374
375
376 class TestNameRev:
377 def test_tip_commit_names_to_branch(self, tmp_path: pathlib.Path) -> None:
378 repo = _make_repo(tmp_path)
379 sid = _snap(repo)
380 cid = _commit(repo, sid, branch="main")
381 _set_head(repo, "main", cid)
382 result = _invoke("name-rev", repo, cid)
383 assert result.exit_code == 0
384 data = json.loads(result.output)
385 assert len(data["results"]) == 1
386 assert data["results"][0]["commit_id"] == cid
387 assert "main" in data["results"][0]["name"]
388
389 def test_parent_commit_named_with_tilde(self, tmp_path: pathlib.Path) -> None:
390 repo = _make_repo(tmp_path)
391 sid = _snap(repo)
392 c1 = _commit(repo, sid, branch="main", message="c1-msg")
393 c2 = _commit(repo, sid, branch="main", parent=c1, message="c2-msg")
394 _set_head(repo, "main", c2)
395 result = _invoke("name-rev", repo, c1)
396 assert result.exit_code == 0
397 data = json.loads(result.output)
398 name = data["results"][0]["name"]
399 assert "~" in name or "main" in name
400
401 def test_no_commit_ids_errors(self, tmp_path: pathlib.Path) -> None:
402 repo = _make_repo(tmp_path)
403 result = _invoke("name-rev", repo)
404 assert result.exit_code == ExitCode.USER_ERROR
405
406 def test_no_traceback_on_empty_input(self, tmp_path: pathlib.Path) -> None:
407 repo = _make_repo(tmp_path)
408 result = _invoke("name-rev", repo)
409 assert "Traceback" not in result.output
410
411
412 # ===========================================================================
413 # check-ref-format
414 # ===========================================================================
415
416
417 class TestCheckRefFormat:
418 def test_valid_branch_name(self, tmp_path: pathlib.Path) -> None:
419 repo = _make_repo(tmp_path)
420 result = _invoke("check-ref-format", repo, "main")
421 assert result.exit_code == 0
422 data = json.loads(result.output)
423 assert data["all_valid"] is True
424
425 def test_valid_feature_branch(self, tmp_path: pathlib.Path) -> None:
426 repo = _make_repo(tmp_path)
427 result = _invoke("check-ref-format", repo, "feat/add-melody")
428 assert result.exit_code == 0
429 data = json.loads(result.output)
430 assert data["all_valid"] is True
431
432 def test_invalid_null_byte_rejected(self, tmp_path: pathlib.Path) -> None:
433 repo = _make_repo(tmp_path)
434 result = _invoke("check-ref-format", repo, "bad\x00branch")
435 assert result.exit_code == ExitCode.USER_ERROR
436 data = json.loads(result.output)
437 assert data["all_valid"] is False
438
439 def test_multiple_names_mixed_validity(self, tmp_path: pathlib.Path) -> None:
440 repo = _make_repo(tmp_path)
441 result = _invoke("check-ref-format", repo, "main", "bad\x00branch")
442 assert result.exit_code == ExitCode.USER_ERROR
443 data = json.loads(result.output)
444 assert data["all_valid"] is False
445 valid = {r["name"]: r["valid"] for r in data["results"]}
446 assert valid["main"] is True
447 assert valid["bad\x00branch"] is False
448
449 def test_text_format(self, tmp_path: pathlib.Path) -> None:
450 repo = _make_repo(tmp_path)
451 result = _invoke("check-ref-format", repo, "--format", "text", "main")
452 assert result.exit_code == 0
453 assert "ok" in result.output.lower()
454
455 def test_quiet_mode(self, tmp_path: pathlib.Path) -> None:
456 repo = _make_repo(tmp_path)
457 result = _invoke("check-ref-format", repo, "--quiet", "main")
458 assert result.exit_code == 0
459 assert result.output.strip() == ""
460
461 def test_no_args_errors(self, tmp_path: pathlib.Path) -> None:
462 repo = _make_repo(tmp_path)
463 result = _invoke("check-ref-format", repo)
464 assert result.exit_code == ExitCode.USER_ERROR
465
466 def test_no_traceback_on_bad_name(self, tmp_path: pathlib.Path) -> None:
467 repo = _make_repo(tmp_path)
468 result = _invoke("check-ref-format", repo, "bad\x00branch")
469 assert "Traceback" not in result.output
470
471
472 # ===========================================================================
473 # verify-pack
474 # ===========================================================================
475
476
477 class TestVerifyPack:
478 def _make_pack_bytes(self, repo: pathlib.Path, cid: str, sid: str) -> bytes:
479 from muse.cli.app import main as cli
480 result = runner.invoke(
481 cli,
482 ["pack-objects", cid],
483 env={"MUSE_REPO_ROOT": str(repo)},
484 )
485 assert result.exit_code == 0
486 return result.stdout_bytes
487
488 def test_valid_pack_from_stdin(self, tmp_path: pathlib.Path) -> None:
489 repo = _make_repo(tmp_path)
490 sid = _snap(repo)
491 cid = _commit(repo, sid, message="test-pack")
492 pack_bytes = self._make_pack_bytes(repo, cid, sid)
493 result = _invoke("verify-pack", repo, input=pack_bytes)
494 assert result.exit_code == 0
495 data = json.loads(result.output)
496 assert data["all_ok"] is True
497 assert data["commits_checked"] >= 1
498
499 def test_empty_stdin_errors(self, tmp_path: pathlib.Path) -> None:
500 repo = _make_repo(tmp_path)
501 result = _invoke("verify-pack", repo, input=b"")
502 assert result.exit_code == ExitCode.USER_ERROR
503
504 def test_corrupted_msgpack_errors(self, tmp_path: pathlib.Path) -> None:
505 repo = _make_repo(tmp_path)
506 result = _invoke("verify-pack", repo, input=b"\xff\xfe corrupted")
507 assert result.exit_code == ExitCode.USER_ERROR
508
509 def test_no_traceback_on_bad_input(self, tmp_path: pathlib.Path) -> None:
510 repo = _make_repo(tmp_path)
511 result = _invoke("verify-pack", repo, input=b"not msgpack at all")
512 assert "Traceback" not in result.output