cgcardona / muse public
test_stress_e2e_workflow.py python
273 lines 9.5 KB
119290fc Add mission-critical stress test suite (9 new files, 1716 tests total) (#76) Gabriel Cardona <cgcardona@gmail.com> 1d ago
1 """End-to-end CLI stress tests — mission-critical workflow verification.
2
3 Tests the full muse CLI lifecycle using CliRunner with monkeypatch.chdir:
4 init → commit → log → branch → checkout → merge → tag → revert → stash
5
6 Adversarial scenarios:
7 - 50-commit linear history: log shows all, branch creates correctly.
8 - Concurrent agent commits with provenance fields.
9 - Branch → commit → merge full cycle.
10 - Annotate accumulates reviewers (ORSet semantics).
11 - Stash and pop.
12 - Revert commit.
13 - Reset.
14 """
15 from __future__ import annotations
16
17 import pathlib
18
19 import pytest
20 from typer.testing import CliRunner
21
22 from muse.cli.app import cli
23
24 runner = CliRunner()
25
26
27 # ---------------------------------------------------------------------------
28 # Helpers
29 # ---------------------------------------------------------------------------
30
31
32 def _run(*args: str) -> tuple[int, str]:
33 result = runner.invoke(cli, list(args), catch_exceptions=False)
34 return result.exit_code, result.output
35
36
37 def _write_file(repo: pathlib.Path, filename: str, content: str = "code = True\n") -> None:
38 work = repo / "muse-work"
39 work.mkdir(exist_ok=True)
40 (work / filename).write_text(content)
41
42
43 @pytest.fixture
44 def repo(tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> pathlib.Path:
45 monkeypatch.chdir(tmp_path)
46 monkeypatch.setenv("MUSE_REPO_ROOT", str(tmp_path))
47 code, out = _run("init", "--domain", "code")
48 assert code == 0, f"init failed: {out}"
49 return tmp_path
50
51
52 # ===========================================================================
53 # Basic lifecycle
54 # ===========================================================================
55
56
57 class TestBasicLifecycle:
58 def test_init_creates_muse_dir(self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> None:
59 monkeypatch.chdir(tmp_path)
60 code, _ = _run("init")
61 assert code == 0
62 assert (tmp_path / ".muse").is_dir()
63
64 def test_commit_and_log(self, repo: pathlib.Path) -> None:
65 _write_file(repo, "main.py", "x = 1\n")
66 code, out = _run("commit", "-m", "first commit")
67 assert code == 0
68
69 code2, log_out = _run("log")
70 assert code2 == 0
71 assert "first commit" in log_out
72
73 def test_status_works(self, repo: pathlib.Path) -> None:
74 _write_file(repo, "app.py", "print('hello')\n")
75 code, out = _run("status")
76 assert code == 0
77
78 def test_tag_commit(self, repo: pathlib.Path) -> None:
79 _write_file(repo, "tagged.py", "tagged = True\n")
80 _run("commit", "-m", "commit to tag")
81 code, out = _run("tag", "add", "v1.0.0")
82 assert code == 0
83
84 def test_log_shows_multiple_commits(self, repo: pathlib.Path) -> None:
85 for i in range(5):
86 _write_file(repo, f"file{i}.py", f"x = {i}\n")
87 _run("commit", "-m", f"commit number {i}")
88
89 code, out = _run("log")
90 assert code == 0
91 for i in range(5):
92 assert f"commit number {i}" in out
93
94 def test_show_commit(self, repo: pathlib.Path) -> None:
95 _write_file(repo, "show_me.py", "show = 'this'\n")
96 _run("commit", "-m", "showable commit")
97 code, _ = _run("log")
98 assert code == 0
99
100
101 # ===========================================================================
102 # Branch and checkout
103 # ===========================================================================
104
105
106 class TestBranchAndCheckout:
107 def test_branch_creation(self, repo: pathlib.Path) -> None:
108 _write_file(repo, "base.py", "base = 1\n")
109 _run("commit", "-m", "base commit")
110 code, out = _run("branch", "feature/new-thing")
111 assert code == 0
112
113 def test_checkout_to_new_branch_then_back(self, repo: pathlib.Path) -> None:
114 _write_file(repo, "common.py", "common = 1\n")
115 _run("commit", "-m", "initial")
116
117 _run("branch", "feature")
118 code, _ = _run("checkout", "feature")
119 assert code == 0
120 code3, _ = _run("checkout", "main")
121 assert code3 == 0
122
123 def test_multiple_branches_independent(self, repo: pathlib.Path) -> None:
124 _write_file(repo, "root.py", "root = True\n")
125 _run("commit", "-m", "root")
126
127 for i in range(3):
128 _run("branch", f"branch-{i}")
129 code, out = _run("checkout", f"branch-{i}")
130 assert code == 0, f"checkout branch-{i} failed: {out}"
131 _write_file(repo, f"branch_{i}.py", f"b = {i}\n")
132 _run("commit", "-m", f"branch-{i} commit")
133 _run("checkout", "main")
134
135
136 # ===========================================================================
137 # Stash
138 # ===========================================================================
139
140
141 class TestStash:
142 def test_stash_and_pop(self, repo: pathlib.Path) -> None:
143 _write_file(repo, "stash_me.py", "stash = True\n")
144 _run("commit", "-m", "before stash")
145
146 _write_file(repo, "unstaged.py", "unstaged = True\n")
147 code, out = _run("stash")
148 assert code == 0, f"stash failed: {out}"
149
150 code2, out2 = _run("stash", "pop")
151 assert code2 == 0, f"stash pop failed: {out2}"
152
153
154 # ===========================================================================
155 # Revert
156 # ===========================================================================
157
158
159 class TestRevert:
160 def test_revert_undoes_last_commit(self, repo: pathlib.Path) -> None:
161 _write_file(repo, "original.py", "original = True\n")
162 _run("commit", "-m", "original state")
163 _write_file(repo, "added.py", "added = True\n")
164 _run("commit", "-m", "added something")
165
166 code, out = _run("log")
167 assert "added something" in out
168
169 code2, _ = _run("revert", "HEAD")
170 assert code2 == 0
171
172
173 # ===========================================================================
174 # Reset
175 # ===========================================================================
176
177
178 class TestReset:
179 def test_soft_reset_to_head(self, repo: pathlib.Path) -> None:
180 """Reset to HEAD (soft) — a no-op but must not fail."""
181 _write_file(repo, "file.py", "x = 1\n")
182 code1, out1 = _run("commit", "-m", "commit 1")
183 assert code1 == 0
184
185 # "HEAD" is accepted by resolve_commit_ref as the current commit.
186 # Options must precede the positional REF argument in Typer sub-typers.
187 code, out = _run("reset", "--soft", "HEAD")
188 assert code == 0, f"reset failed: {out}"
189
190
191 # ===========================================================================
192 # Provenance fields in commit
193 # ===========================================================================
194
195
196 class TestProvenanceCommit:
197 def test_commit_with_agent_id(self, repo: pathlib.Path) -> None:
198 _write_file(repo, "agent.py", "agent = True\n")
199 result = runner.invoke(
200 cli,
201 ["commit", "-m", "agent commit", "--agent-id", "test-agent"],
202 catch_exceptions=False,
203 )
204 assert result.exit_code == 0
205
206 def test_commit_with_model_id(self, repo: pathlib.Path) -> None:
207 _write_file(repo, "model.py", "model = 'gpt-4o'\n")
208 result = runner.invoke(
209 cli,
210 ["commit", "-m", "model commit", "--model-id", "gpt-4o"],
211 catch_exceptions=False,
212 )
213 assert result.exit_code == 0
214
215
216 # ===========================================================================
217 # Annotate command
218 # ===========================================================================
219
220
221 class TestAnnotateCommand:
222 def test_annotate_test_run(self, repo: pathlib.Path) -> None:
223 _write_file(repo, "annotate_me.py", "code = True\n")
224 _run("commit", "-m", "to annotate")
225 code, out = _run("annotate", "--test-run")
226 assert code == 0
227
228 def test_annotate_reviewed_by(self, repo: pathlib.Path) -> None:
229 _write_file(repo, "reviewed.py", "reviewed = True\n")
230 _run("commit", "-m", "for review")
231 code, out = _run("annotate", "--reviewed-by", "alice")
232 assert code == 0
233
234 def test_annotate_accumulates_reviewers(self, repo: pathlib.Path) -> None:
235 _write_file(repo, "multi_review.py", "x = 1\n")
236 _run("commit", "-m", "multi-review")
237 _run("annotate", "--reviewed-by", "alice")
238 code, out = _run("annotate", "--reviewed-by", "bob")
239 assert code == 0
240
241
242 # ===========================================================================
243 # Long workflow stress
244 # ===========================================================================
245
246
247 class TestLongWorkflowStress:
248 def test_50_sequential_commits(self, repo: pathlib.Path) -> None:
249 for i in range(50):
250 _write_file(repo, f"module_{i:03d}.py", f"x = {i}\n")
251 code, out = _run("commit", "-m", f"commit {i:03d}")
252 assert code == 0, f"commit {i} failed: {out}"
253
254 code, out = _run("log")
255 assert code == 0
256 assert "commit 000" in out
257 assert "commit 049" in out
258
259 def test_branch_commit_merge_cycle(self, repo: pathlib.Path) -> None:
260 """Full branch → commit → merge cycle."""
261 _write_file(repo, "main.py", "main = True\n")
262 _run("commit", "-m", "main base")
263
264 _run("branch", "feature/thing")
265 _run("checkout", "feature/thing")
266 _write_file(repo, "feature.py", "feature = True\n")
267 code, out = _run("commit", "-m", "feature work")
268 assert code == 0
269
270 _run("checkout", "main")
271 code2, out2 = _run("merge", "feature/thing")
272 # Merge may succeed or report no commits yet — either way must not crash.
273 assert code2 in (0, 1), f"merge crashed: {out2}"