gabriel / muse public
test_worktree_reflog.py python
125 lines 4.8 KB
Raw
sha256:2b0e51e0e3e2002ffcf41dc5a1349af31d3eee4751d379cc6a8f100a20f9bd9c feat: reflog entries for worktree add/remove (muse#54 item 9) Sonnet 5 patch 14 hours ago
1 """muse#54 item 9 -- `muse worktree add` / `muse worktree remove` must
2 append reflog entries, so the full ref/worktree lifecycle is visible via
3 `muse reflog`, not silently invisible.
4 """
5 from __future__ import annotations
6
7 import datetime
8 import json
9 import pathlib
10
11 from tests.cli_test_helper import CliRunner
12 from muse.core.types import fake_id, Manifest
13 from muse.core.paths import heads_dir, muse_dir
14
15 cli = None
16 runner = CliRunner()
17
18
19 def _env(root: pathlib.Path) -> Manifest:
20 return {"MUSE_REPO_ROOT": str(root)}
21
22
23 def _init_repo(tmp_path: pathlib.Path) -> tuple[pathlib.Path, str]:
24 dot_muse = muse_dir(tmp_path)
25 dot_muse.mkdir()
26 repo_id = fake_id("repo")
27 (dot_muse / "repo.json").write_text(json.dumps({
28 "repo_id": repo_id, "domain": "code", "default_branch": "main",
29 "created_at": "2025-01-01T00:00:00+00:00",
30 }), encoding="utf-8")
31 (dot_muse / "HEAD").write_text("ref: refs/heads/main", encoding="utf-8")
32 (dot_muse / "refs" / "heads").mkdir(parents=True)
33 (dot_muse / "snapshots").mkdir()
34 (dot_muse / "commits").mkdir()
35 (dot_muse / "objects").mkdir()
36 return tmp_path, repo_id
37
38
39 def _make_commit(root: pathlib.Path, message: str = "test") -> str:
40 from muse.core.commits import CommitRecord, write_commit
41 from muse.core.snapshots import SnapshotRecord, write_snapshot
42 from muse.core.ids import hash_commit as compute_commit_id, hash_snapshot as compute_snapshot_id
43
44 ref_file = heads_dir(root) / "main"
45 parent_id = ref_file.read_text().strip() if ref_file.exists() else None
46 manifest: Manifest = {}
47 snap_id = compute_snapshot_id(manifest)
48 committed_at = datetime.datetime.now(datetime.timezone.utc)
49 commit_id = compute_commit_id(
50 parent_ids=[parent_id] if parent_id else [],
51 snapshot_id=snap_id, message=message,
52 committed_at_iso=committed_at.isoformat(),
53 )
54 write_snapshot(root, SnapshotRecord(snapshot_id=snap_id, manifest=manifest))
55 write_commit(root, CommitRecord(
56 commit_id=commit_id, branch="main", snapshot_id=snap_id,
57 message=message, committed_at=committed_at, parent_commit_id=parent_id,
58 ))
59 ref_file.parent.mkdir(parents=True, exist_ok=True)
60 ref_file.write_text(commit_id, encoding="utf-8")
61 return commit_id
62
63
64 class TestWorktreeAddReflog:
65 def test_add_on_existing_branch_appends_entry(self, tmp_path: pathlib.Path) -> None:
66 root, _ = _init_repo(tmp_path)
67 commit_id = _make_commit(root)
68
69 result = runner.invoke(
70 cli, ["worktree", "add", "wt1", "main", "--json"], env=_env(root)
71 )
72 assert result.exit_code == 0, result.output
73
74 from muse.core.reflog import read_reflog
75 entries = read_reflog(root, branch="main")
76 assert any("worktree: created 'wt1'" in e.operation for e in entries)
77
78 def test_add_with_new_branch_logs_branch_creation(self, tmp_path: pathlib.Path) -> None:
79 root, _ = _init_repo(tmp_path)
80 commit_id = _make_commit(root)
81
82 result = runner.invoke(
83 cli, ["worktree", "add", "wt2", "main", "-b", "feat/new", "--json"],
84 env=_env(root),
85 )
86 assert result.exit_code == 0, result.output
87
88 from muse.core.reflog import read_reflog
89 entries = read_reflog(root, branch="feat/new")
90 # Two entries land on feat/new's own log: the branch-creation entry
91 # (written first, so it's oldest / last in this newest-first list)
92 # and the worktree-created audit entry (written second, newest).
93 assert len(entries) == 2
94 assert "worktree: created 'wt2'" in entries[0].operation
95 assert "branch: created from main via worktree add" in entries[-1].operation
96 assert entries[-1].new_id == commit_id
97
98
99 class TestWorktreeRemoveReflog:
100 def test_remove_appends_entry_to_branch_log(self, tmp_path: pathlib.Path) -> None:
101 root, _ = _init_repo(tmp_path)
102 _make_commit(root)
103 add_result = runner.invoke(
104 cli, ["worktree", "add", "wt3", "main", "--json"], env=_env(root)
105 )
106 assert add_result.exit_code == 0, add_result.output
107
108 remove_result = runner.invoke(
109 cli, ["worktree", "remove", "wt3", "--json"], env=_env(root)
110 )
111 assert remove_result.exit_code == 0, remove_result.output
112
113 from muse.core.reflog import read_reflog
114 entries = read_reflog(root, branch="main")
115 assert any("worktree: removed 'wt3'" in e.operation for e in entries)
116
117 def test_remove_nonexistent_worktree_does_not_crash(
118 self, tmp_path: pathlib.Path
119 ) -> None:
120 root, _ = _init_repo(tmp_path)
121 _make_commit(root)
122 result = runner.invoke(
123 cli, ["worktree", "remove", "ghost", "--json"], env=_env(root)
124 )
125 assert result.exit_code == 1 # existing "not found" behavior unaffected
File History 1 commit
sha256:2b0e51e0e3e2002ffcf41dc5a1349af31d3eee4751d379cc6a8f100a20f9bd9c feat: reflog entries for worktree add/remove (muse#54 item 9) Sonnet 5 patch 14 hours ago