cgcardona / muse public
test_cli_show.py python
226 lines 8.1 KB
c8984819 test: bring core VCS coverage from 60% to 91% Gabriel Cardona <gabriel@tellurstori.com> 3d ago
1 """Tests for muse show — inspect a commit's metadata, diff, and files."""
2 from __future__ import annotations
3
4 import json
5 import pathlib
6
7 import pytest
8 from typer.testing import CliRunner
9
10 from muse.cli.app import cli
11
12 runner = CliRunner()
13
14
15 @pytest.fixture
16 def repo(tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> pathlib.Path:
17 monkeypatch.chdir(tmp_path)
18 monkeypatch.setenv("MUSE_REPO_ROOT", str(tmp_path))
19 result = runner.invoke(cli, ["init"])
20 assert result.exit_code == 0, result.output
21 return tmp_path
22
23
24 def _write(repo: pathlib.Path, filename: str, content: str = "data") -> None:
25 (repo / "muse-work" / filename).write_text(content)
26
27
28 def _commit(msg: str = "initial", **flags: str) -> str:
29 args = ["commit", "-m", msg]
30 for k, v in flags.items():
31 args += [f"--{k}", v]
32 result = runner.invoke(cli, args)
33 assert result.exit_code == 0, result.output
34 # output: "[main abcd1234] msg" → strip trailing ] from token
35 return result.output.split()[1].rstrip("]")
36
37
38 class TestShowHead:
39 def test_shows_commit_id(self, repo: pathlib.Path) -> None:
40 _write(repo, "beat.mid")
41 _commit("initial commit")
42 result = runner.invoke(cli, ["show"])
43 assert result.exit_code == 0, result.output
44 assert "commit" in result.output
45
46 def test_shows_message(self, repo: pathlib.Path) -> None:
47 _write(repo, "beat.mid")
48 _commit("my special message")
49 result = runner.invoke(cli, ["show"])
50 assert result.exit_code == 0
51 assert "my special message" in result.output
52
53 def test_shows_date(self, repo: pathlib.Path) -> None:
54 _write(repo, "beat.mid")
55 _commit("dated commit")
56 result = runner.invoke(cli, ["show"])
57 assert result.exit_code == 0
58 assert "Date:" in result.output
59
60 def test_shows_author(self, repo: pathlib.Path) -> None:
61 _write(repo, "beat.mid")
62 runner.invoke(cli, ["commit", "-m", "authored", "--author", "Gabriel"])
63 result = runner.invoke(cli, ["show"])
64 assert result.exit_code == 0
65 assert "Gabriel" in result.output
66
67 def test_no_author_line_when_empty(self, repo: pathlib.Path) -> None:
68 _write(repo, "beat.mid")
69 _commit("no author")
70 result = runner.invoke(cli, ["show"])
71 assert result.exit_code == 0
72 assert "Author:" not in result.output
73
74
75 class TestShowStat:
76 def test_shows_added_file_by_default(self, repo: pathlib.Path) -> None:
77 _write(repo, "beat.mid")
78 _commit("add beat")
79 result = runner.invoke(cli, ["show"])
80 assert result.exit_code == 0
81 assert "beat.mid" in result.output
82 assert "+" in result.output
83
84 def test_no_stat_flag_hides_files(self, repo: pathlib.Path) -> None:
85 _write(repo, "beat.mid")
86 _commit("add beat")
87 result = runner.invoke(cli, ["show", "--no-stat"])
88 assert result.exit_code == 0
89 assert "beat.mid" not in result.output
90
91 def test_shows_modified_file(self, repo: pathlib.Path) -> None:
92 _write(repo, "beat.mid", "v1")
93 _commit("v1")
94 _write(repo, "beat.mid", "v2")
95 _commit("v2")
96 result = runner.invoke(cli, ["show"])
97 assert result.exit_code == 0
98 assert "beat.mid" in result.output
99
100 def test_file_change_count(self, repo: pathlib.Path) -> None:
101 _write(repo, "a.mid")
102 _write(repo, "b.mid")
103 _commit("two files")
104 result = runner.invoke(cli, ["show"])
105 assert result.exit_code == 0
106 assert "file(s) changed" in result.output
107
108 def test_no_files_changed_no_count_line(self, repo: pathlib.Path) -> None:
109 _write(repo, "beat.mid", "v1")
110 _commit("v1")
111 _write(repo, "beat.mid", "v1")
112 result = runner.invoke(cli, ["commit", "--allow-empty"])
113 # empty commit — stat block should show no files changed
114 result2 = runner.invoke(cli, ["show"])
115 assert result2.exit_code == 0
116
117
118 class TestShowMetadata:
119 def test_shows_section_metadata(self, repo: pathlib.Path) -> None:
120 _write(repo, "beat.mid")
121 runner.invoke(cli, ["commit", "-m", "verse", "--section", "verse"])
122 result = runner.invoke(cli, ["show"])
123 assert result.exit_code == 0
124 assert "section" in result.output
125 assert "verse" in result.output
126
127 def test_shows_track_and_emotion(self, repo: pathlib.Path) -> None:
128 _write(repo, "beat.mid")
129 runner.invoke(cli, ["commit", "-m", "drums", "--track", "drums", "--emotion", "joyful"])
130 result = runner.invoke(cli, ["show"])
131 assert result.exit_code == 0
132 assert "track" in result.output
133 assert "emotion" in result.output
134
135
136 class TestShowRef:
137 def test_show_specific_commit(self, repo: pathlib.Path) -> None:
138 _write(repo, "beat.mid")
139 short = _commit("first")
140 _write(repo, "lead.mid")
141 _commit("second")
142 # show the first commit by prefix
143 result = runner.invoke(cli, ["show", short])
144 assert result.exit_code == 0
145 assert "first" in result.output
146
147 def test_show_unknown_ref_errors(self, repo: pathlib.Path) -> None:
148 _write(repo, "beat.mid")
149 _commit("only")
150 result = runner.invoke(cli, ["show", "deadbeef"])
151 assert result.exit_code != 0
152 assert "not found" in result.output.lower() or "deadbeef" in result.output
153
154 def test_show_no_commits_errors(self, repo: pathlib.Path) -> None:
155 result = runner.invoke(cli, ["show"])
156 assert result.exit_code != 0
157
158
159 class TestShowParent:
160 def test_shows_parent_after_second_commit(self, repo: pathlib.Path) -> None:
161 _write(repo, "beat.mid")
162 _commit("first")
163 _write(repo, "lead.mid")
164 _commit("second")
165 result = runner.invoke(cli, ["show"])
166 assert result.exit_code == 0
167 assert "Parent:" in result.output
168
169 def test_root_commit_has_no_parent_line(self, repo: pathlib.Path) -> None:
170 _write(repo, "beat.mid")
171 short = _commit("root commit")
172 result = runner.invoke(cli, ["show", short])
173 assert result.exit_code == 0
174 assert "Parent:" not in result.output
175
176
177 class TestShowJson:
178 def test_json_output_is_valid(self, repo: pathlib.Path) -> None:
179 _write(repo, "beat.mid")
180 _commit("json test")
181 result = runner.invoke(cli, ["show", "--json"])
182 assert result.exit_code == 0
183 data = json.loads(result.output)
184 assert "commit_id" in data
185 assert "message" in data
186
187 def test_json_contains_message(self, repo: pathlib.Path) -> None:
188 _write(repo, "beat.mid")
189 _commit("the message")
190 result = runner.invoke(cli, ["show", "--json"])
191 data = json.loads(result.output)
192 assert data["message"] == "the message"
193
194 def test_json_with_stat_includes_file_lists(self, repo: pathlib.Path) -> None:
195 _write(repo, "beat.mid")
196 _commit("add beat")
197 result = runner.invoke(cli, ["show", "--json", "--stat"])
198 data = json.loads(result.output)
199 assert "files_added" in data
200 assert "beat.mid" in data["files_added"]
201
202 def test_json_no_stat_excludes_file_lists(self, repo: pathlib.Path) -> None:
203 _write(repo, "beat.mid")
204 _commit("add beat")
205 result = runner.invoke(cli, ["show", "--json", "--no-stat"])
206 data = json.loads(result.output)
207 assert "files_added" not in data
208
209 def test_json_stat_shows_removed_file(self, repo: pathlib.Path) -> None:
210 _write(repo, "beat.mid", "v1")
211 _commit("add")
212 (repo / "muse-work" / "beat.mid").unlink()
213 _write(repo, "lead.mid", "new")
214 _commit("swap")
215 result = runner.invoke(cli, ["show", "--json", "--stat"])
216 data = json.loads(result.output)
217 assert "beat.mid" in data["files_removed"]
218
219 def test_json_stat_shows_modified_file(self, repo: pathlib.Path) -> None:
220 _write(repo, "beat.mid", "v1")
221 _commit("v1")
222 _write(repo, "beat.mid", "v2")
223 _commit("v2")
224 result = runner.invoke(cli, ["show", "--json", "--stat"])
225 data = json.loads(result.output)
226 assert "beat.mid" in data["files_modified"]