gabriel / muse public
test_plumbing_rev_parse.py python
221 lines 8.3 KB
86000da9 fix: replace typer CliRunner with argparse-compatible test helper Gabriel Cardona <gabriel@tellurstori.com> 1d ago
1 """Tests for ``muse plumbing rev-parse``.
2
3 Covers: HEAD resolution, branch-name resolution, full commit-ID passthrough,
4 abbreviated-prefix resolution, ambiguous-prefix detection, not-found errors,
5 text-format output, bad-format handling, and an empty-branch edge case.
6 """
7
8 from __future__ import annotations
9
10 import datetime
11 import hashlib
12 import json
13 import pathlib
14
15 from tests.cli_test_helper import CliRunner
16
17 cli = None # argparse migration — CliRunner ignores this arg
18 from muse.core.errors import ExitCode
19 from muse.core.store import CommitRecord, SnapshotRecord, write_commit, write_snapshot
20
21 runner = CliRunner()
22
23
24 # ---------------------------------------------------------------------------
25 # Helpers
26 # ---------------------------------------------------------------------------
27
28
29 def _sha(tag: str) -> str:
30 return hashlib.sha256(tag.encode()).hexdigest()
31
32
33 def _init_repo(path: pathlib.Path) -> pathlib.Path:
34 muse = path / ".muse"
35 (muse / "commits").mkdir(parents=True)
36 (muse / "snapshots").mkdir(parents=True)
37 (muse / "objects").mkdir(parents=True)
38 (muse / "refs" / "heads").mkdir(parents=True)
39 (muse / "HEAD").write_text("ref: refs/heads/main", encoding="utf-8")
40 (muse / "repo.json").write_text(
41 json.dumps({"repo_id": "test-repo", "domain": "midi"}), encoding="utf-8"
42 )
43 return path
44
45
46 def _env(repo: pathlib.Path) -> dict[str, str]:
47 return {"MUSE_REPO_ROOT": str(repo)}
48
49
50 def _snap(repo: pathlib.Path, tag: str = "snap") -> str:
51 sid = _sha(f"snap-{tag}")
52 write_snapshot(
53 repo,
54 SnapshotRecord(
55 snapshot_id=sid,
56 manifest={},
57 created_at=datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc),
58 ),
59 )
60 return sid
61
62
63 def _commit(
64 repo: pathlib.Path, tag: str, sid: str, branch: str = "main", parent: str | None = None
65 ) -> str:
66 cid = _sha(tag)
67 write_commit(
68 repo,
69 CommitRecord(
70 commit_id=cid,
71 repo_id="test-repo",
72 branch=branch,
73 snapshot_id=sid,
74 message=tag,
75 committed_at=datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc),
76 author="tester",
77 parent_commit_id=parent,
78 ),
79 )
80 ref = repo / ".muse" / "refs" / "heads" / branch
81 ref.parent.mkdir(parents=True, exist_ok=True)
82 ref.write_text(cid, encoding="utf-8")
83 return cid
84
85
86 # ---------------------------------------------------------------------------
87 # Unit: HEAD resolution
88 # ---------------------------------------------------------------------------
89
90
91 class TestRevParseHead:
92 def test_head_resolves_to_current_branch_commit(self, tmp_path: pathlib.Path) -> None:
93 repo = _init_repo(tmp_path)
94 sid = _snap(repo)
95 cid = _commit(repo, "first", sid)
96 result = runner.invoke(cli, ["plumbing", "rev-parse", "HEAD"], env=_env(repo))
97 assert result.exit_code == 0, result.output
98 data = json.loads(result.stdout)
99 assert data["commit_id"] == cid
100 assert data["ref"] == "HEAD"
101
102 def test_head_case_insensitive(self, tmp_path: pathlib.Path) -> None:
103 repo = _init_repo(tmp_path)
104 sid = _snap(repo)
105 cid = _commit(repo, "c1", sid)
106 for variant in ("head", "Head", "hEaD"):
107 result = runner.invoke(cli, ["plumbing", "rev-parse", variant], env=_env(repo))
108 assert result.exit_code == 0
109 assert json.loads(result.stdout)["commit_id"] == cid
110
111 def test_head_on_empty_branch_exits_user_error(self, tmp_path: pathlib.Path) -> None:
112 repo = _init_repo(tmp_path) # No commits yet.
113 result = runner.invoke(cli, ["plumbing", "rev-parse", "HEAD"], env=_env(repo))
114 assert result.exit_code == ExitCode.USER_ERROR
115 assert "error" in json.loads(result.stdout)
116
117
118 # ---------------------------------------------------------------------------
119 # Integration: branch-name resolution
120 # ---------------------------------------------------------------------------
121
122
123 class TestRevParseBranch:
124 def test_branch_name_resolves_to_tip_commit(self, tmp_path: pathlib.Path) -> None:
125 repo = _init_repo(tmp_path)
126 sid = _snap(repo)
127 cid = _commit(repo, "tip", sid, branch="feature")
128 (repo / ".muse" / "HEAD").write_text("ref: refs/heads/main", encoding="utf-8")
129 result = runner.invoke(cli, ["plumbing", "rev-parse", "feature"], env=_env(repo))
130 assert result.exit_code == 0
131 assert json.loads(result.stdout)["commit_id"] == cid
132
133 def test_nonexistent_branch_not_found(self, tmp_path: pathlib.Path) -> None:
134 repo = _init_repo(tmp_path)
135 result = runner.invoke(cli, ["plumbing", "rev-parse", "no-such-branch"], env=_env(repo))
136 assert result.exit_code == ExitCode.USER_ERROR
137 assert json.loads(result.stdout)["error"] == "not found"
138
139 def test_multiple_branches_each_resolve_independently(self, tmp_path: pathlib.Path) -> None:
140 repo = _init_repo(tmp_path)
141 sid = _snap(repo)
142 cid_main = _commit(repo, "main-c", sid, branch="main")
143 cid_dev = _commit(repo, "dev-c", sid, branch="dev")
144 r_main = runner.invoke(cli, ["plumbing", "rev-parse", "main"], env=_env(repo))
145 r_dev = runner.invoke(cli, ["plumbing", "rev-parse", "dev"], env=_env(repo))
146 assert json.loads(r_main.stdout)["commit_id"] == cid_main
147 assert json.loads(r_dev.stdout)["commit_id"] == cid_dev
148
149
150 # ---------------------------------------------------------------------------
151 # Integration: full & abbreviated commit ID
152 # ---------------------------------------------------------------------------
153
154
155 class TestRevParseCommitId:
156 def test_full_64_char_id_resolves(self, tmp_path: pathlib.Path) -> None:
157 repo = _init_repo(tmp_path)
158 sid = _snap(repo)
159 cid = _commit(repo, "commit", sid)
160 result = runner.invoke(cli, ["plumbing", "rev-parse", cid], env=_env(repo))
161 assert result.exit_code == 0
162 assert json.loads(result.stdout)["commit_id"] == cid
163
164 def test_prefix_resolves_when_unique(self, tmp_path: pathlib.Path) -> None:
165 repo = _init_repo(tmp_path)
166 sid = _snap(repo)
167 cid = _commit(repo, "unique-prefix", sid)
168 prefix = cid[:12]
169 result = runner.invoke(cli, ["plumbing", "rev-parse", prefix], env=_env(repo))
170 assert result.exit_code == 0
171 assert json.loads(result.stdout)["commit_id"] == cid
172
173 def test_unknown_full_id_exits_not_found(self, tmp_path: pathlib.Path) -> None:
174 repo = _init_repo(tmp_path)
175 ghost = _sha("ghost-commit")
176 result = runner.invoke(cli, ["plumbing", "rev-parse", ghost], env=_env(repo))
177 assert result.exit_code == ExitCode.USER_ERROR
178
179
180 # ---------------------------------------------------------------------------
181 # Integration: format flags
182 # ---------------------------------------------------------------------------
183
184
185 class TestRevParseFormats:
186 def test_text_format_emits_bare_commit_id(self, tmp_path: pathlib.Path) -> None:
187 repo = _init_repo(tmp_path)
188 sid = _snap(repo)
189 cid = _commit(repo, "text", sid)
190 result = runner.invoke(
191 cli, ["plumbing", "rev-parse", "--format", "text", "main"], env=_env(repo)
192 )
193 assert result.exit_code == 0
194 assert result.stdout.strip() == cid
195
196 def test_short_format_flag(self, tmp_path: pathlib.Path) -> None:
197 repo = _init_repo(tmp_path)
198 sid = _snap(repo)
199 cid = _commit(repo, "short", sid)
200 result = runner.invoke(cli, ["plumbing", "rev-parse", "-f", "text", "main"], env=_env(repo))
201 assert result.exit_code == 0
202 assert result.stdout.strip() == cid
203
204 def test_bad_format_exits_user_error(self, tmp_path: pathlib.Path) -> None:
205 repo = _init_repo(tmp_path)
206 sid = _snap(repo)
207 _commit(repo, "x", sid)
208 result = runner.invoke(
209 cli, ["plumbing", "rev-parse", "--format", "toml", "main"], env=_env(repo)
210 )
211 assert result.exit_code == ExitCode.USER_ERROR
212
213 def test_json_output_has_ref_and_commit_id_keys(self, tmp_path: pathlib.Path) -> None:
214 repo = _init_repo(tmp_path)
215 sid = _snap(repo)
216 _commit(repo, "kv", sid)
217 result = runner.invoke(cli, ["plumbing", "rev-parse", "main"], env=_env(repo))
218 assert result.exit_code == 0
219 data = json.loads(result.stdout)
220 assert "ref" in data
221 assert "commit_id" in data