gabriel / muse public
test_plumbing_show_ref.py python
196 lines 7.2 KB
86000da9 fix: replace typer CliRunner with argparse-compatible test helper Gabriel Cardona <gabriel@tellurstori.com> 1d ago
1 """Tests for ``muse plumbing show-ref``.
2
3 Verifies enumeration of branch refs, HEAD resolution, glob-pattern filtering,
4 verify mode (silent existence check), and text-format output.
5 """
6
7 from __future__ import annotations
8
9 import datetime
10 import hashlib
11 import json
12 import pathlib
13
14 from tests.cli_test_helper import CliRunner
15
16 cli = None # argparse migration — CliRunner ignores this arg
17 from muse.core.errors import ExitCode
18 from muse.core.store import CommitRecord, SnapshotRecord, write_commit, write_snapshot
19
20 runner = CliRunner()
21
22
23 # ---------------------------------------------------------------------------
24 # Helpers
25 # ---------------------------------------------------------------------------
26
27
28 def _sha(tag: str) -> str:
29 return hashlib.sha256(tag.encode()).hexdigest()
30
31
32 def _init_repo(path: pathlib.Path) -> pathlib.Path:
33 muse = path / ".muse"
34 (muse / "commits").mkdir(parents=True)
35 (muse / "snapshots").mkdir(parents=True)
36 (muse / "objects").mkdir(parents=True)
37 (muse / "refs" / "heads").mkdir(parents=True)
38 (muse / "HEAD").write_text("ref: refs/heads/main", encoding="utf-8")
39 (muse / "repo.json").write_text(
40 json.dumps({"repo_id": "test-repo", "domain": "midi"}), encoding="utf-8"
41 )
42 return path
43
44
45 def _env(repo: pathlib.Path) -> dict[str, str]:
46 return {"MUSE_REPO_ROOT": str(repo)}
47
48
49 def _snap(repo: pathlib.Path) -> str:
50 sid = _sha("snap")
51 write_snapshot(
52 repo,
53 SnapshotRecord(
54 snapshot_id=sid,
55 manifest={},
56 created_at=datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc),
57 ),
58 )
59 return sid
60
61
62 def _make_branch(repo: pathlib.Path, branch: str, tag: str, snap_id: str) -> str:
63 cid = _sha(tag)
64 write_commit(
65 repo,
66 CommitRecord(
67 commit_id=cid,
68 repo_id="test-repo",
69 branch=branch,
70 snapshot_id=snap_id,
71 message=tag,
72 committed_at=datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc),
73 author="tester",
74 parent_commit_id=None,
75 ),
76 )
77 ref = repo / ".muse" / "refs" / "heads" / branch
78 ref.write_text(cid, encoding="utf-8")
79 return cid
80
81
82 # ---------------------------------------------------------------------------
83 # Tests
84 # ---------------------------------------------------------------------------
85
86
87 class TestShowRef:
88 def test_lists_all_branch_refs(self, tmp_path: pathlib.Path) -> None:
89 repo = _init_repo(tmp_path)
90 sid = _snap(repo)
91 _make_branch(repo, "main", "main-commit", sid)
92 _make_branch(repo, "dev", "dev-commit", sid)
93 result = runner.invoke(cli, ["plumbing", "show-ref"], env=_env(repo))
94 assert result.exit_code == 0, result.output
95 data = json.loads(result.stdout)
96 assert data["count"] == 2
97 ref_names = {r["ref"] for r in data["refs"]}
98 assert "refs/heads/main" in ref_names
99 assert "refs/heads/dev" in ref_names
100
101 def test_commit_ids_match_branch_heads(self, tmp_path: pathlib.Path) -> None:
102 repo = _init_repo(tmp_path)
103 sid = _snap(repo)
104 cid_main = _make_branch(repo, "main", "main-c", sid)
105 result = runner.invoke(cli, ["plumbing", "show-ref"], env=_env(repo))
106 assert result.exit_code == 0, result.output
107 data = json.loads(result.stdout)
108 main_ref = next(r for r in data["refs"] if r["ref"] == "refs/heads/main")
109 assert main_ref["commit_id"] == cid_main
110
111 def test_head_info_included(self, tmp_path: pathlib.Path) -> None:
112 repo = _init_repo(tmp_path)
113 sid = _snap(repo)
114 cid = _make_branch(repo, "main", "main-c", sid)
115 result = runner.invoke(cli, ["plumbing", "show-ref"], env=_env(repo))
116 assert result.exit_code == 0, result.output
117 data = json.loads(result.stdout)
118 assert data["head"]["branch"] == "main"
119 assert data["head"]["commit_id"] == cid
120
121 def test_empty_repo_returns_empty_list(self, tmp_path: pathlib.Path) -> None:
122 repo = _init_repo(tmp_path)
123 result = runner.invoke(cli, ["plumbing", "show-ref"], env=_env(repo))
124 assert result.exit_code == 0, result.output
125 data = json.loads(result.stdout)
126 assert data["count"] == 0
127 assert data["refs"] == []
128
129 def test_pattern_filter_restricts_output(self, tmp_path: pathlib.Path) -> None:
130 repo = _init_repo(tmp_path)
131 sid = _snap(repo)
132 _make_branch(repo, "main", "main-c", sid)
133 _make_branch(repo, "dev", "dev-c", sid)
134 result = runner.invoke(
135 cli, ["plumbing", "show-ref", "--pattern", "refs/heads/main"], env=_env(repo)
136 )
137 assert result.exit_code == 0, result.output
138 data = json.loads(result.stdout)
139 assert data["count"] == 1
140 assert data["refs"][0]["ref"] == "refs/heads/main"
141
142 def test_head_flag_returns_only_head(self, tmp_path: pathlib.Path) -> None:
143 repo = _init_repo(tmp_path)
144 sid = _snap(repo)
145 cid = _make_branch(repo, "main", "main-c", sid)
146 _make_branch(repo, "dev", "dev-c", sid)
147 result = runner.invoke(cli, ["plumbing", "show-ref", "--head"], env=_env(repo))
148 assert result.exit_code == 0, result.output
149 data = json.loads(result.stdout)
150 assert data["head"]["commit_id"] == cid
151
152 def test_verify_existing_ref_exits_zero(self, tmp_path: pathlib.Path) -> None:
153 repo = _init_repo(tmp_path)
154 sid = _snap(repo)
155 _make_branch(repo, "main", "main-c", sid)
156 result = runner.invoke(
157 cli, ["plumbing", "show-ref", "--verify", "refs/heads/main"], env=_env(repo)
158 )
159 assert result.exit_code == 0
160
161 def test_verify_missing_ref_exits_user_error(self, tmp_path: pathlib.Path) -> None:
162 repo = _init_repo(tmp_path)
163 result = runner.invoke(
164 cli, ["plumbing", "show-ref", "--verify", "refs/heads/nonexistent"], env=_env(repo)
165 )
166 assert result.exit_code == ExitCode.USER_ERROR
167
168 def test_verify_produces_no_stdout(self, tmp_path: pathlib.Path) -> None:
169 repo = _init_repo(tmp_path)
170 sid = _snap(repo)
171 _make_branch(repo, "main", "main-c", sid)
172 result = runner.invoke(
173 cli, ["plumbing", "show-ref", "--verify", "refs/heads/main"], env=_env(repo)
174 )
175 assert result.stdout.strip() == ""
176
177 def test_text_format_output(self, tmp_path: pathlib.Path) -> None:
178 repo = _init_repo(tmp_path)
179 sid = _snap(repo)
180 _make_branch(repo, "main", "main-c", sid)
181 result = runner.invoke(
182 cli, ["plumbing", "show-ref", "--format", "text"], env=_env(repo)
183 )
184 assert result.exit_code == 0, result.output
185 assert "refs/heads/main" in result.stdout
186
187 def test_refs_sorted_lexicographically(self, tmp_path: pathlib.Path) -> None:
188 repo = _init_repo(tmp_path)
189 sid = _snap(repo)
190 _make_branch(repo, "zzz", "z-c", sid)
191 _make_branch(repo, "aaa", "a-c", sid)
192 _make_branch(repo, "mmm", "m-c", sid)
193 result = runner.invoke(cli, ["plumbing", "show-ref"], env=_env(repo))
194 assert result.exit_code == 0, result.output
195 ref_names = [r["ref"] for r in json.loads(result.stdout)["refs"]]
196 assert ref_names == sorted(ref_names)