gabriel / muse public
test_cli_inspect.py python
749 lines 26.3 KB
Raw
sha256:b636f72dcba9e190afb980bece906fa5b717fbde014b76ef023df8cb96e01eb9 docs: expand cache plan with all seven testing tiers and do… Sonnet 4.6 131 days ago
1 """Tests for all Low-level commands under ``muse …``.
2
3 Each command is tested via the Typer CliRunner so tests exercise the
4 full CLI stack including argument parsing, error handling, and JSON output
5 format. Commands are accessed directly at the top level.
6
7 The ``MUSE_REPO_ROOT`` env-var is used to point repo-discovery at the test
8 fixture without requiring ``os.chdir``.
9 """
10
11 from __future__ import annotations
12
13 import datetime
14 import json
15 import pathlib
16
17 import msgpack
18 import pytest
19 from tests.cli_test_helper import CliRunner
20
21 cli = None # argparse migration — CliRunner ignores this arg
22 from muse.core.errors import ExitCode
23 from muse.core.object_store import write_object
24 from muse.core.pack import build_mpack
25 from muse.core.snapshot import compute_commit_id, compute_snapshot_id
26
27 from muse.core._types import Manifest, blob_id, long_id
28 from muse.core.store import (
29 CommitRecord,
30 SnapshotRecord,
31 write_commit,
32 write_snapshot,
33 )
34
35 runner = CliRunner()
36
37 # ---------------------------------------------------------------------------
38 # Helpers
39 # ---------------------------------------------------------------------------
40
41
42 def _init_repo(path: pathlib.Path) -> pathlib.Path:
43 """Create a minimal .muse/ directory structure."""
44 muse = path / ".muse"
45 (muse / "commits").mkdir(parents=True)
46 (muse / "snapshots").mkdir(parents=True)
47 (muse / "objects").mkdir(parents=True)
48 (muse / "refs" / "heads").mkdir(parents=True)
49 muse.joinpath("HEAD").write_text("ref: refs/heads/main")
50 muse.joinpath("repo.json").write_text(
51 json.dumps({"repo_id": "test-repo-id", "domain": "generic"})
52 )
53 return path
54
55
56 def _make_object(repo: pathlib.Path, content: bytes) -> str:
57 oid = blob_id(content)
58 write_object(repo, oid, content)
59 return oid
60
61
62 def _make_snapshot(
63 repo: pathlib.Path, manifest: Manifest
64 ) -> SnapshotRecord:
65 """Write a SnapshotRecord whose ID is content-addressed from *manifest*."""
66 snap_id = compute_snapshot_id(manifest)
67 snap = SnapshotRecord(
68 snapshot_id=snap_id,
69 manifest=manifest,
70 created_at=datetime.datetime(2026, 3, 18, tzinfo=datetime.timezone.utc),
71 )
72 write_snapshot(repo, snap)
73 return snap
74
75
76 def _make_commit(
77 repo: pathlib.Path,
78 snapshot_id: str,
79 *,
80 branch: str = "main",
81 parent_commit_id: str | None = None,
82 message: str = "test commit",
83 ) -> CommitRecord:
84 """Write a CommitRecord whose ID is content-addressed from its fields."""
85 committed_at = datetime.datetime(2026, 3, 18, tzinfo=datetime.timezone.utc)
86 parent_ids = [parent_commit_id] if parent_commit_id else []
87 commit_id = compute_commit_id(
88 repo_id="test-repo-id",
89 parent_ids=parent_ids,
90 snapshot_id=snapshot_id,
91 message=message,
92 committed_at_iso=committed_at.isoformat(),
93 author="tester",
94 )
95 rec = CommitRecord(
96 commit_id=commit_id,
97 repo_id="test-repo-id",
98 created_on_branch=branch,
99 snapshot_id=snapshot_id,
100 message=message,
101 committed_at=committed_at,
102 author="tester",
103 parent_commit_id=parent_commit_id,
104 )
105 write_commit(repo, rec)
106 return rec
107
108
109 def _set_head(repo: pathlib.Path, branch: str, commit_id: str) -> None:
110 ref = repo / ".muse" / "refs" / "heads" / branch
111 ref.parent.mkdir(parents=True, exist_ok=True)
112 ref.write_text(commit_id)
113
114
115 def _repo_env(repo: pathlib.Path) -> Manifest:
116 """Return env dict that sets MUSE_REPO_ROOT to the given path."""
117 return {"MUSE_REPO_ROOT": str(repo)}
118
119
120 # ---------------------------------------------------------------------------
121 # hash-object
122 # ---------------------------------------------------------------------------
123
124
125 class TestHashObject:
126 def test_hash_file_json_output(self, tmp_path: pathlib.Path) -> None:
127 f = tmp_path / "test.txt"
128 f.write_bytes(b"hello world")
129 result = runner.invoke(cli, ["hash-object", "--json", str(f)])
130 assert result.exit_code == 0, result.output
131 data = json.loads(result.stdout)
132 assert "object_id" in data
133 assert data["object_id"].startswith("sha256:")
134 assert len(data["object_id"]) == 71
135 assert data["stored"] is False
136
137 def test_hash_file_text_format(self, tmp_path: pathlib.Path) -> None:
138 f = tmp_path / "data.bin"
139 f.write_bytes(b"test bytes")
140 result = runner.invoke(
141 cli, ["hash-object", str(f)]
142 )
143 assert result.exit_code == 0, result.output
144 assert result.stdout.strip().startswith("sha256:")
145 assert len(result.stdout.strip()) == 71
146
147 def test_hash_and_write(self, tmp_path: pathlib.Path) -> None:
148 repo = _init_repo(tmp_path / "repo")
149 f = repo / "sample.txt"
150 f.write_bytes(b"write me")
151 result = runner.invoke(
152 cli,
153 ["hash-object", "--write", "--json", str(f)],
154 env=_repo_env(repo),
155 catch_exceptions=False,
156 )
157 assert result.exit_code == 0, result.output
158 data = json.loads(result.stdout)
159 assert data["stored"] is True
160
161 def test_missing_file_errors(self, tmp_path: pathlib.Path) -> None:
162 result = runner.invoke(cli, ["hash-object", str(tmp_path / "no.txt")])
163 assert result.exit_code == ExitCode.USER_ERROR
164
165 def test_directory_errors(self, tmp_path: pathlib.Path) -> None:
166 result = runner.invoke(cli, ["hash-object", str(tmp_path)])
167 assert result.exit_code == ExitCode.USER_ERROR
168
169
170 # ---------------------------------------------------------------------------
171 # cat-object
172 # ---------------------------------------------------------------------------
173
174
175 class TestCatObject:
176 def test_cat_raw_bytes(self, tmp_path: pathlib.Path) -> None:
177 repo = _init_repo(tmp_path)
178 content = b"raw content data"
179 oid = _make_object(repo, content)
180 result = runner.invoke(
181 cli, ["cat-object", oid],
182 env=_repo_env(repo),
183 catch_exceptions=False,
184 )
185 assert result.exit_code == 0, result.output
186 assert result.stdout_bytes == content
187
188 def test_cat_info_format(self, tmp_path: pathlib.Path) -> None:
189 repo = _init_repo(tmp_path)
190 content = b"info content"
191 oid = _make_object(repo, content)
192 result = runner.invoke(
193 cli, ["cat-object", "--json", oid],
194 env=_repo_env(repo),
195 )
196 assert result.exit_code == 0, result.output
197 data = json.loads(result.stdout)
198 assert data["object_id"] == oid
199 assert data["present"] is True
200 assert data["size_bytes"] == len(content)
201
202 def test_missing_object_errors(self, tmp_path: pathlib.Path) -> None:
203 repo = _init_repo(tmp_path)
204 result = runner.invoke(
205 cli, ["cat-object", "a" * 64],
206 env=_repo_env(repo),
207 )
208 assert result.exit_code == ExitCode.USER_ERROR
209
210 def test_missing_object_info_format(self, tmp_path: pathlib.Path) -> None:
211 repo = _init_repo(tmp_path)
212 oid = long_id("b" * 64)
213 result = runner.invoke(
214 cli, ["cat-object", "--json", oid],
215 env=_repo_env(repo),
216 )
217 assert result.exit_code == ExitCode.USER_ERROR
218 data = json.loads(result.stdout)
219 assert data["present"] is False
220
221
222 # ---------------------------------------------------------------------------
223 # rev-parse
224 # ---------------------------------------------------------------------------
225
226
227 class TestRevParse:
228 def test_resolve_branch(self, tmp_path: pathlib.Path) -> None:
229 repo = _init_repo(tmp_path)
230 oid = _make_object(repo, b"data")
231 snap = _make_snapshot(repo, {"f": oid})
232 commit = _make_commit(repo, snap.snapshot_id)
233 _set_head(repo, "main", commit.commit_id)
234
235 result = runner.invoke(
236 cli, ["rev-parse", "main", "--json"],
237 env=_repo_env(repo),
238 )
239 assert result.exit_code == 0, result.output
240 data = json.loads(result.stdout)
241 assert data["commit_id"] == commit.commit_id
242 assert data["ref"] == "main"
243
244 def test_resolve_head(self, tmp_path: pathlib.Path) -> None:
245 repo = _init_repo(tmp_path)
246 oid = _make_object(repo, b"data")
247 snap = _make_snapshot(repo, {"f": oid})
248 commit = _make_commit(repo, snap.snapshot_id, message="resolve head")
249 _set_head(repo, "main", commit.commit_id)
250
251 result = runner.invoke(
252 cli, ["rev-parse", "HEAD", "--json"],
253 env=_repo_env(repo),
254 )
255 assert result.exit_code == 0, result.output
256 data = json.loads(result.stdout)
257 assert data["commit_id"] == commit.commit_id
258
259 def test_resolve_text_format(self, tmp_path: pathlib.Path) -> None:
260 repo = _init_repo(tmp_path)
261 oid = _make_object(repo, b"data")
262 snap = _make_snapshot(repo, {"f": oid})
263 commit = _make_commit(repo, snap.snapshot_id, message="text format")
264 _set_head(repo, "main", commit.commit_id)
265
266 result = runner.invoke(
267 cli, ["rev-parse", "main"],
268 env=_repo_env(repo),
269 )
270 assert result.exit_code == 0, result.output
271 assert result.stdout.strip() == commit.commit_id
272
273 def test_unknown_ref_errors(self, tmp_path: pathlib.Path) -> None:
274 repo = _init_repo(tmp_path)
275 result = runner.invoke(
276 cli, ["rev-parse", "nonexistent", "--json"],
277 env=_repo_env(repo),
278 )
279 assert result.exit_code == ExitCode.USER_ERROR
280 data = json.loads(result.stdout)
281 assert data["commit_id"] is None
282
283
284 # ---------------------------------------------------------------------------
285 # ls-files
286 # ---------------------------------------------------------------------------
287
288
289 class TestLsFiles:
290 def test_lists_files_json(self, tmp_path: pathlib.Path) -> None:
291 repo = _init_repo(tmp_path)
292 oid = _make_object(repo, b"track data")
293 snap = _make_snapshot(repo, {"tracks/drums.mid": oid})
294 commit = _make_commit(repo, snap.snapshot_id)
295 _set_head(repo, "main", commit.commit_id)
296
297 result = runner.invoke(
298 cli, ["ls-files", "--json"],
299 env=_repo_env(repo),
300 )
301 assert result.exit_code == 0, result.output
302 data = json.loads(result.stdout)
303 assert data["file_count"] == 1
304 assert data["files"][0]["path"] == "tracks/drums.mid"
305 assert data["files"][0]["object_id"] == oid
306
307 def test_lists_files_text(self, tmp_path: pathlib.Path) -> None:
308 repo = _init_repo(tmp_path)
309 oid = _make_object(repo, b"data")
310 snap = _make_snapshot(repo, {"a.txt": oid})
311 commit = _make_commit(repo, snap.snapshot_id, message="ls files text")
312 _set_head(repo, "main", commit.commit_id)
313
314 result = runner.invoke(
315 cli, ["ls-files"],
316 env=_repo_env(repo),
317 )
318 assert result.exit_code == 0, result.output
319 assert "a.txt" in result.stdout
320
321 def test_with_explicit_commit(self, tmp_path: pathlib.Path) -> None:
322 repo = _init_repo(tmp_path)
323 oid = _make_object(repo, b"data")
324 snap = _make_snapshot(repo, {"x.mid": oid})
325 commit = _make_commit(repo, snap.snapshot_id, message="explicit commit")
326
327 result = runner.invoke(
328 cli, ["ls-files", "--commit", commit.commit_id, "--json"],
329 env=_repo_env(repo),
330 )
331 assert result.exit_code == 0, result.output
332 data = json.loads(result.stdout)
333 assert data["commit_id"] == commit.commit_id
334
335 def test_no_commits_errors(self, tmp_path: pathlib.Path) -> None:
336 repo = _init_repo(tmp_path)
337 result = runner.invoke(
338 cli, ["ls-files"],
339 env=_repo_env(repo),
340 )
341 assert result.exit_code == ExitCode.USER_ERROR
342
343
344 # ---------------------------------------------------------------------------
345 # read-commit
346 # ---------------------------------------------------------------------------
347
348
349 class TestReadCommit:
350 def test_reads_commit_json(self, tmp_path: pathlib.Path) -> None:
351 repo = _init_repo(tmp_path)
352 oid = _make_object(repo, b"data")
353 snap = _make_snapshot(repo, {"f": oid})
354 commit = _make_commit(repo, snap.snapshot_id, message="my message")
355
356 result = runner.invoke(
357 cli, ["read-commit", commit.commit_id, "--json"],
358 env=_repo_env(repo),
359 catch_exceptions=False,
360 )
361 assert result.exit_code == 0, result.output
362 data = json.loads(result.stdout)
363 assert data["commit_id"] == commit.commit_id
364 assert data["message"] == "my message"
365 assert data["snapshot_id"] == snap.snapshot_id
366
367 def test_missing_commit_errors(self, tmp_path: pathlib.Path) -> None:
368 repo = _init_repo(tmp_path)
369 result = runner.invoke(
370 cli, ["read-commit", "z" * 64],
371 env=_repo_env(repo),
372 )
373 assert result.exit_code == ExitCode.USER_ERROR
374 data = json.loads(result.stdout)
375 assert "error" in data
376
377
378 # ---------------------------------------------------------------------------
379 # read-snapshot
380 # ---------------------------------------------------------------------------
381
382
383 class TestReadSnapshot:
384 def test_reads_snapshot_json(self, tmp_path: pathlib.Path) -> None:
385 repo = _init_repo(tmp_path)
386 oid = _make_object(repo, b"snap data")
387 snap = _make_snapshot(repo, {"track.mid": oid})
388
389 result = runner.invoke(
390 cli, ["read-snapshot", snap.snapshot_id, "--json"],
391 env=_repo_env(repo),
392 catch_exceptions=False,
393 )
394 assert result.exit_code == 0, result.output
395 data = json.loads(result.stdout)
396 assert data["snapshot_id"] == snap.snapshot_id
397 assert data["file_count"] == 1
398 assert "track.mid" in data["manifest"]
399
400 def test_missing_snapshot_errors(self, tmp_path: pathlib.Path) -> None:
401 repo = _init_repo(tmp_path)
402 result = runner.invoke(
403 cli, ["read-snapshot", "nothere"],
404 env=_repo_env(repo),
405 )
406 assert result.exit_code == ExitCode.USER_ERROR
407
408
409 # ---------------------------------------------------------------------------
410 # commit-tree
411 # ---------------------------------------------------------------------------
412
413
414 class TestCommitTree:
415 def test_creates_commit_from_snapshot(self, tmp_path: pathlib.Path) -> None:
416 repo = _init_repo(tmp_path)
417 oid = _make_object(repo, b"content")
418 snap = _make_snapshot(repo, {"file.txt": oid})
419
420 result = runner.invoke(
421 cli,
422 [
423 "commit-tree",
424 "--snapshot", snap.snapshot_id,
425 "--message", "plumbing commit",
426 "--author", "bot",
427 "--json",
428 ],
429 env=_repo_env(repo),
430 catch_exceptions=False,
431 )
432 assert result.exit_code == 0, result.output
433 data = json.loads(result.stdout)
434 assert "commit_id" in data
435 assert data["commit_id"].startswith("sha256:")
436 assert len(data["commit_id"]) == 71
437
438 def test_with_parent(self, tmp_path: pathlib.Path) -> None:
439 repo = _init_repo(tmp_path)
440 oid = _make_object(repo, b"data")
441 snap1 = _make_snapshot(repo, {"a": oid})
442 parent = _make_commit(repo, snap1.snapshot_id, message="parent commit")
443
444 snap2 = _make_snapshot(repo, {"b": oid})
445 result = runner.invoke(
446 cli,
447 [
448 "commit-tree",
449 "--snapshot", snap2.snapshot_id,
450 "--parent", parent.commit_id,
451 "--message", "child",
452 "--json",
453 ],
454 env=_repo_env(repo),
455 catch_exceptions=False,
456 )
457 assert result.exit_code == 0, result.output
458 data = json.loads(result.stdout)
459 assert "commit_id" in data
460
461 def test_missing_snapshot_errors(self, tmp_path: pathlib.Path) -> None:
462 repo = _init_repo(tmp_path)
463 result = runner.invoke(
464 cli,
465 ["commit-tree", "--snapshot", "nosuch"],
466 env=_repo_env(repo),
467 )
468 assert result.exit_code == ExitCode.USER_ERROR
469
470
471 # ---------------------------------------------------------------------------
472 # update-ref
473 # ---------------------------------------------------------------------------
474
475
476 class TestUpdateRef:
477 def test_creates_branch_ref(self, tmp_path: pathlib.Path) -> None:
478 repo = _init_repo(tmp_path)
479 oid = _make_object(repo, b"x")
480 snap = _make_snapshot(repo, {"x": oid})
481 commit = _make_commit(repo, snap.snapshot_id, message="create ref")
482
483 result = runner.invoke(
484 cli,
485 ["update-ref", "feature", commit.commit_id, "--json"],
486 env=_repo_env(repo),
487 catch_exceptions=False,
488 )
489 assert result.exit_code == 0, result.output
490 data = json.loads(result.stdout)
491 assert data["branch"] == "feature"
492 assert data["commit_id"] == commit.commit_id
493 ref = repo / ".muse" / "refs" / "heads" / "feature"
494 assert ref.read_text() == commit.commit_id
495
496 def test_updates_existing_ref(self, tmp_path: pathlib.Path) -> None:
497 repo = _init_repo(tmp_path)
498 oid = _make_object(repo, b"y")
499 snap = _make_snapshot(repo, {"y": oid})
500 commit1 = _make_commit(repo, snap.snapshot_id, message="first")
501 commit2 = _make_commit(repo, snap.snapshot_id, message="second", parent_commit_id=commit1.commit_id)
502 _set_head(repo, "main", commit1.commit_id)
503
504 result = runner.invoke(
505 cli,
506 ["update-ref", "main", commit2.commit_id, "--json"],
507 env=_repo_env(repo),
508 )
509 assert result.exit_code == 0, result.output
510 data = json.loads(result.stdout)
511 assert data["previous"] == commit1.commit_id
512 assert data["commit_id"] == commit2.commit_id
513
514 def test_delete_ref(self, tmp_path: pathlib.Path) -> None:
515 repo = _init_repo(tmp_path)
516 _set_head(repo, "todelete", "x" * 64)
517 result = runner.invoke(
518 cli,
519 ["update-ref", "--delete", "todelete", "--json"],
520 env=_repo_env(repo),
521 )
522 assert result.exit_code == 0, result.output
523 data = json.loads(result.stdout)
524 assert data["deleted"] is True
525 ref = repo / ".muse" / "refs" / "heads" / "todelete"
526 assert not ref.exists()
527
528 def test_verify_commit_not_found_errors(self, tmp_path: pathlib.Path) -> None:
529 repo = _init_repo(tmp_path)
530 result = runner.invoke(
531 cli,
532 ["update-ref", "main", "0" * 64],
533 env=_repo_env(repo),
534 )
535 assert result.exit_code == ExitCode.USER_ERROR
536
537 def test_no_verify_skips_commit_check(self, tmp_path: pathlib.Path) -> None:
538 repo = _init_repo(tmp_path)
539 result = runner.invoke(
540 cli,
541 ["update-ref", "--no-verify", "feature", long_id("9" * 64)],
542 env=_repo_env(repo),
543 )
544 assert result.exit_code == 0, result.output
545 ref = repo / ".muse" / "refs" / "heads" / "feature"
546 assert ref.read_text() == long_id("9" * 64)
547
548
549 # ---------------------------------------------------------------------------
550 # commit-graph
551 # ---------------------------------------------------------------------------
552
553
554 class TestCommitGraph:
555 def test_linear_graph(self, tmp_path: pathlib.Path) -> None:
556 repo = _init_repo(tmp_path)
557 oid = _make_object(repo, b"data")
558 snap = _make_snapshot(repo, {"f": oid})
559 c1 = _make_commit(repo, snap.snapshot_id, message="first")
560 c2 = _make_commit(repo, snap.snapshot_id, message="second", parent_commit_id=c1.commit_id)
561 _set_head(repo, "main", c2.commit_id)
562
563 result = runner.invoke(
564 cli, ["commit-graph", "--json"],
565 env=_repo_env(repo),
566 catch_exceptions=False,
567 )
568 assert result.exit_code == 0, result.output
569 data = json.loads(result.stdout)
570 assert data["count"] == 2
571 commit_ids = [c["commit_id"] for c in data["commits"]]
572 assert c2.commit_id in commit_ids
573 assert c1.commit_id in commit_ids
574
575 def test_text_format(self, tmp_path: pathlib.Path) -> None:
576 repo = _init_repo(tmp_path)
577 oid = _make_object(repo, b"data")
578 snap = _make_snapshot(repo, {"f": oid})
579 commit = _make_commit(repo, snap.snapshot_id, message="text format commit")
580 _set_head(repo, "main", commit.commit_id)
581
582 result = runner.invoke(
583 cli, ["commit-graph"],
584 env=_repo_env(repo),
585 )
586 assert result.exit_code == 0, result.output
587 assert commit.commit_id in result.stdout
588
589 def test_explicit_tip(self, tmp_path: pathlib.Path) -> None:
590 repo = _init_repo(tmp_path)
591 oid = _make_object(repo, b"data")
592 snap = _make_snapshot(repo, {"f": oid})
593 commit = _make_commit(repo, snap.snapshot_id, message="explicit tip commit")
594
595 result = runner.invoke(
596 cli, ["commit-graph", "--tip", commit.commit_id, "--json"],
597 env=_repo_env(repo),
598 )
599 assert result.exit_code == 0, result.output
600 data = json.loads(result.stdout)
601 assert data["tip"] == commit.commit_id
602
603 def test_no_commits_errors(self, tmp_path: pathlib.Path) -> None:
604 repo = _init_repo(tmp_path)
605 result = runner.invoke(
606 cli, ["commit-graph"],
607 env=_repo_env(repo),
608 )
609 assert result.exit_code == ExitCode.USER_ERROR
610
611
612 # ---------------------------------------------------------------------------
613 # pack-objects
614 # ---------------------------------------------------------------------------
615
616
617 class TestPackObjects:
618 def test_packs_head(self, tmp_path: pathlib.Path) -> None:
619 repo = _init_repo(tmp_path)
620 oid = _make_object(repo, b"pack me")
621 snap = _make_snapshot(repo, {"f.mid": oid})
622 commit = _make_commit(repo, snap.snapshot_id, message="pack head")
623 _set_head(repo, "main", commit.commit_id)
624
625 result = runner.invoke(
626 cli, ["pack-objects", "HEAD"],
627 env=_repo_env(repo),
628 catch_exceptions=False,
629 )
630 assert result.exit_code == 0, result.output
631 data = msgpack.unpackb(result.stdout_bytes, raw=False)
632 assert "commits" in data
633 assert len(data["commits"]) >= 1
634
635 def test_packs_explicit_commit(self, tmp_path: pathlib.Path) -> None:
636 repo = _init_repo(tmp_path)
637 oid = _make_object(repo, b"explicit")
638 snap = _make_snapshot(repo, {"g": oid})
639 commit = _make_commit(repo, snap.snapshot_id, message="pack explicit")
640
641 result = runner.invoke(
642 cli, ["pack-objects", commit.commit_id],
643 env=_repo_env(repo),
644 catch_exceptions=False,
645 )
646 assert result.exit_code == 0, result.output
647 data = msgpack.unpackb(result.stdout_bytes, raw=False)
648 commit_ids = [c["commit_id"] for c in data["commits"]]
649 assert commit.commit_id in commit_ids
650
651 def test_no_commits_on_head_errors(self, tmp_path: pathlib.Path) -> None:
652 repo = _init_repo(tmp_path)
653 result = runner.invoke(
654 cli, ["pack-objects", "HEAD"],
655 env=_repo_env(repo),
656 )
657 assert result.exit_code == ExitCode.USER_ERROR
658
659
660 # ---------------------------------------------------------------------------
661 # unpack-objects
662 # ---------------------------------------------------------------------------
663
664
665 class TestUnpackObjects:
666 def test_unpacks_valid_bundle(self, tmp_path: pathlib.Path) -> None:
667 source = _init_repo(tmp_path / "src")
668 dest = _init_repo(tmp_path / "dst")
669
670 oid = _make_object(source, b"unpack me")
671 snap = _make_snapshot(source, {"h.mid": oid})
672 commit = _make_commit(source, snap.snapshot_id, message="unpack bundle")
673
674 bundle = build_mpack(source, [commit.commit_id])
675 bundle_bytes = msgpack.packb(bundle, use_bin_type=True)
676
677 result = runner.invoke(
678 cli,
679 ["unpack-objects", "--json"],
680 input=bundle_bytes,
681 env=_repo_env(dest),
682 catch_exceptions=False,
683 )
684 assert result.exit_code == 0, result.output
685 data = json.loads(result.stdout)
686 assert "objects_written" in data
687 assert data["commits_written"] == 1
688 assert data["objects_written"] == 1
689
690 def test_invalid_msgpack_errors(self, tmp_path: pathlib.Path) -> None:
691 repo = _init_repo(tmp_path)
692 result = runner.invoke(
693 cli,
694 ["unpack-objects"],
695 input=b"\xff\xff NOT VALID MSGPACK",
696 env=_repo_env(repo),
697 )
698 assert result.exit_code == ExitCode.USER_ERROR
699
700 def test_idempotent_unpack(self, tmp_path: pathlib.Path) -> None:
701 repo = _init_repo(tmp_path)
702 oid = _make_object(repo, b"idempotent")
703 snap = _make_snapshot(repo, {"i.txt": oid})
704 commit = _make_commit(repo, snap.snapshot_id, message="idempotent unpack")
705
706 bundle = build_mpack(repo, [commit.commit_id])
707 bundle_bytes = msgpack.packb(bundle, use_bin_type=True)
708
709 result1 = runner.invoke(
710 cli, ["unpack-objects"],
711 input=bundle_bytes,
712 env=_repo_env(repo),
713 )
714 assert result1.exit_code == 0, result1.output
715
716 result2 = runner.invoke(
717 cli, ["unpack-objects", "--json"],
718 input=bundle_bytes,
719 env=_repo_env(repo),
720 )
721 assert result2.exit_code == 0, result2.output
722 data = json.loads(result2.stdout)
723 assert data["objects_written"] == 0
724 assert data["objects_skipped"] == 1
725
726
727 # ---------------------------------------------------------------------------
728 # ls-remote
729 # ---------------------------------------------------------------------------
730
731
732 class TestLsRemote:
733 def test_bare_url_transport_error(self) -> None:
734 """Bare URL to a non-existent server produces exit code INTERNAL_ERROR."""
735 result = runner.invoke(
736 cli,
737 ["ls-remote", "https://localhost:0/no-such-server"],
738 )
739 assert result.exit_code == ExitCode.INTERNAL_ERROR
740
741 def test_non_url_non_remote_errors(self, tmp_path: pathlib.Path) -> None:
742 """A non-URL, non-configured remote name exits with code USER_ERROR."""
743 repo = _init_repo(tmp_path)
744 result = runner.invoke(
745 cli,
746 ["ls-remote", "not-a-url-or-remote"],
747 env=_repo_env(repo),
748 )
749 assert result.exit_code == ExitCode.USER_ERROR
File History 3 commits
sha256:b636f72dcba9e190afb980bece906fa5b717fbde014b76ef023df8cb96e01eb9 docs: expand cache plan with all seven testing tiers and do… Sonnet 4.6 131 days ago
sha256:7f9e2ef5286aedad9c1e6011b4c46ca27f39dbdad6e3409357e36b26e46b3b7c docs: docstring sprint for-each-ref→hotspots — idiomatic ru… Sonnet 4.6 patch 137 days ago
sha256:a09b1b4f6838754495547f200aa0ce88e2f56ffc5b20b900f6f0cff2c3cdede9 fix(cursorignore): remove git-ism (.git/worktrees) Human minor 140 days ago