Skip to content

Commit 19f4690

Browse files
test: enhance CLI command tests with additional cases and refactor execution method
- refactor CLI command execution to use a unified run_cmd function - add tests for extract-branch, extract-sub-issue, extract-parent-issue, get-attempt-count, parse-plot-path, status-transition, and quality-label commands - include edge cases and different input formats for comprehensive coverage
1 parent 369587b commit 19f4690

File tree

1 file changed

+206
-21
lines changed

1 file changed

+206
-21
lines changed
Lines changed: 206 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,60 @@
11
"""Tests for automation.scripts.workflow_cli module."""
22

3+
import argparse
34
import json
4-
import subprocess
55
import sys
6+
from io import StringIO
7+
from unittest.mock import patch
68

9+
import pytest
710

8-
def run_cli(*args: str) -> tuple[int, str]:
9-
"""Run the workflow CLI with given arguments."""
10-
result = subprocess.run(
11-
[sys.executable, "-m", "automation.scripts.workflow_cli", *args], capture_output=True, text=True
12-
)
13-
return result.returncode, result.stdout.strip()
11+
from automation.scripts.workflow_cli import (
12+
cmd_extract_branch,
13+
cmd_extract_parent_issue,
14+
cmd_extract_sub_issue,
15+
cmd_get_attempt_count,
16+
cmd_parse_plot_path,
17+
cmd_quality_label,
18+
cmd_status_transition,
19+
main,
20+
)
21+
22+
23+
def run_cmd(func, **kwargs) -> tuple[int, str]:
24+
"""Run a CLI command function and capture its output."""
25+
args = argparse.Namespace(**kwargs)
26+
captured = StringIO()
27+
with patch("sys.stdout", captured):
28+
code = func(args)
29+
return code, captured.getvalue().strip()
1430

1531

1632
class TestExtractBranch:
1733
"""Tests for extract-branch command."""
1834

1935
def test_valid_auto_branch(self):
20-
code, output = run_cli("extract-branch", "auto/scatter-basic/matplotlib")
36+
code, output = run_cmd(cmd_extract_branch, branch="auto/scatter-basic/matplotlib")
2137
assert code == 0
2238
data = json.loads(output)
2339
assert data["spec_id"] == "scatter-basic"
2440
assert data["library"] == "matplotlib"
2541
assert data["is_auto_branch"] is True
2642

43+
def test_valid_auto_branch_different_spec(self):
44+
code, output = run_cmd(cmd_extract_branch, branch="auto/heatmap-basic/seaborn")
45+
assert code == 0
46+
data = json.loads(output)
47+
assert data["spec_id"] == "heatmap-basic"
48+
assert data["library"] == "seaborn"
49+
assert data["is_auto_branch"] is True
50+
2751
def test_invalid_branch(self):
28-
code, output = run_cli("extract-branch", "feature/something")
52+
code, output = run_cmd(cmd_extract_branch, branch="feature/something")
53+
assert code == 1
54+
assert output == "null"
55+
56+
def test_main_branch(self):
57+
code, output = run_cmd(cmd_extract_branch, branch="main")
2958
assert code == 1
3059
assert output == "null"
3160

@@ -34,12 +63,46 @@ class TestExtractSubIssue:
3463
"""Tests for extract-sub-issue command."""
3564

3665
def test_with_sub_issue(self):
37-
code, output = run_cli("extract-sub-issue", "Sub-Issue: #42")
66+
code, output = run_cmd(cmd_extract_sub_issue, pr_body="Sub-Issue: #42")
3867
assert code == 0
3968
assert output == "42"
4069

70+
def test_with_sub_issue_different_format(self):
71+
code, output = run_cmd(cmd_extract_sub_issue, pr_body="Sub-Issue: #123\nMore text")
72+
assert code == 0
73+
assert output == "123"
74+
4175
def test_without_sub_issue(self):
42-
code, output = run_cli("extract-sub-issue", "No sub-issue here")
76+
code, output = run_cmd(cmd_extract_sub_issue, pr_body="No sub-issue here")
77+
assert code == 1
78+
assert output == ""
79+
80+
def test_empty_body(self):
81+
code, output = run_cmd(cmd_extract_sub_issue, pr_body="")
82+
assert code == 1
83+
assert output == ""
84+
85+
86+
class TestExtractParentIssue:
87+
"""Tests for extract-parent-issue command."""
88+
89+
def test_from_pr_body(self):
90+
code, output = run_cmd(cmd_extract_parent_issue, pr_body="Parent Issue: #99\nSome text", issue_body=None)
91+
assert code == 0
92+
assert output == "99"
93+
94+
def test_from_issue_body_fallback(self):
95+
code, output = run_cmd(cmd_extract_parent_issue, pr_body="No parent here", issue_body="Parent Issue: #77")
96+
assert code == 0
97+
assert output == "77"
98+
99+
def test_not_found(self):
100+
code, output = run_cmd(cmd_extract_parent_issue, pr_body="Nothing", issue_body="Also nothing")
101+
assert code == 1
102+
assert output == ""
103+
104+
def test_both_none(self):
105+
code, output = run_cmd(cmd_extract_parent_issue, pr_body="", issue_body=None)
43106
assert code == 1
44107
assert output == ""
45108

@@ -48,17 +111,27 @@ class TestGetAttemptCount:
48111
"""Tests for get-attempt-count command."""
49112

50113
def test_with_attempt_labels(self):
51-
code, output = run_cli("get-attempt-count", "ai-attempt-2,library:matplotlib")
114+
code, output = run_cmd(cmd_get_attempt_count, labels="ai-attempt-2,library:matplotlib")
52115
assert code == 0
53116
assert output == "2"
54117

118+
def test_with_attempt_1(self):
119+
code, output = run_cmd(cmd_get_attempt_count, labels="ai-attempt-1")
120+
assert code == 0
121+
assert output == "1"
122+
123+
def test_with_attempt_3(self):
124+
code, output = run_cmd(cmd_get_attempt_count, labels="testing,ai-attempt-3,done")
125+
assert code == 0
126+
assert output == "3"
127+
55128
def test_without_attempt_labels(self):
56-
code, output = run_cli("get-attempt-count", "library:matplotlib,testing")
129+
code, output = run_cmd(cmd_get_attempt_count, labels="library:matplotlib,testing")
57130
assert code == 0
58131
assert output == "0"
59132

60133
def test_empty_labels(self):
61-
code, output = run_cli("get-attempt-count", "")
134+
code, output = run_cmd(cmd_get_attempt_count, labels="")
62135
assert code == 0
63136
assert output == "0"
64137

@@ -67,15 +140,27 @@ class TestParsePlotPath:
67140
"""Tests for parse-plot-path command."""
68141

69142
def test_valid_path(self):
70-
code, output = run_cli("parse-plot-path", "plots/matplotlib/scatter/scatter-basic/default.py")
143+
code, output = run_cmd(cmd_parse_plot_path, path="plots/matplotlib/scatter/scatter-basic/default.py")
71144
assert code == 0
72145
data = json.loads(output)
73146
assert data["library"] == "matplotlib"
74147
assert data["spec_id"] == "scatter-basic"
75148
assert data["variant"] == "default"
76149

150+
def test_valid_path_different_library(self):
151+
code, output = run_cmd(cmd_parse_plot_path, path="plots/seaborn/heatmap/heatmap-correlation/default.py")
152+
assert code == 0
153+
data = json.loads(output)
154+
assert data["library"] == "seaborn"
155+
assert data["spec_id"] == "heatmap-correlation"
156+
77157
def test_invalid_path(self):
78-
code, output = run_cli("parse-plot-path", "invalid/path.py")
158+
code, output = run_cmd(cmd_parse_plot_path, path="invalid/path.py")
159+
assert code == 1
160+
assert output == "null"
161+
162+
def test_empty_path(self):
163+
code, output = run_cmd(cmd_parse_plot_path, path="")
79164
assert code == 1
80165
assert output == "null"
81166

@@ -84,31 +169,131 @@ class TestStatusTransition:
84169
"""Tests for status-transition command."""
85170

86171
def test_simple_transition(self):
87-
code, output = run_cli("status-transition", "generating,library:matplotlib", "testing")
172+
code, output = run_cmd(
173+
cmd_status_transition, current_labels="generating,library:matplotlib", to_status="testing"
174+
)
88175
assert code == 0
89176
assert '--remove-label "generating"' in output
90177
assert '--add-label "testing"' in output
91178

92179
def test_no_change_needed(self):
93-
code, output = run_cli("status-transition", "testing", "testing")
180+
code, output = run_cmd(cmd_status_transition, current_labels="testing", to_status="testing")
94181
assert code == 0
95182
assert output == ""
96183

184+
def test_empty_current_labels(self):
185+
code, output = run_cmd(cmd_status_transition, current_labels="", to_status="reviewing")
186+
assert code == 0
187+
assert '--add-label "reviewing"' in output
188+
189+
def test_multiple_status_labels(self):
190+
code, output = run_cmd(cmd_status_transition, current_labels="generating,testing", to_status="done")
191+
assert code == 0
192+
assert "done" in output
193+
97194

98195
class TestQualityLabel:
99196
"""Tests for quality-label command."""
100197

101198
def test_excellent(self):
102-
code, output = run_cli("quality-label", "95")
199+
code, output = run_cmd(cmd_quality_label, score=95)
103200
assert code == 0
104201
assert output == "quality:excellent"
105202

106203
def test_good(self):
107-
code, output = run_cli("quality-label", "87")
204+
code, output = run_cmd(cmd_quality_label, score=87)
108205
assert code == 0
109206
assert output == "quality:good"
110207

208+
def test_needs_work(self):
209+
code, output = run_cmd(cmd_quality_label, score=78)
210+
assert code == 0
211+
assert output == "quality:needs-work"
212+
111213
def test_poor(self):
112-
code, output = run_cli("quality-label", "50")
214+
code, output = run_cmd(cmd_quality_label, score=50)
113215
assert code == 0
114216
assert output == "quality:poor"
217+
218+
def test_boundary_excellent(self):
219+
code, output = run_cmd(cmd_quality_label, score=90)
220+
assert code == 0
221+
assert output == "quality:excellent"
222+
223+
def test_boundary_good(self):
224+
code, output = run_cmd(cmd_quality_label, score=85)
225+
assert code == 0
226+
assert output == "quality:good"
227+
228+
def test_boundary_needs_work(self):
229+
code, output = run_cmd(cmd_quality_label, score=75)
230+
assert code == 0
231+
assert output == "quality:needs-work"
232+
233+
234+
class TestMain:
235+
"""Tests for main() CLI entry point."""
236+
237+
def test_extract_branch_via_main(self, monkeypatch, capsys):
238+
monkeypatch.setattr(sys, "argv", ["workflow_cli", "extract-branch", "auto/test-spec/plotly"])
239+
code = main()
240+
assert code == 0
241+
captured = capsys.readouterr()
242+
data = json.loads(captured.out.strip())
243+
assert data["spec_id"] == "test-spec"
244+
assert data["library"] == "plotly"
245+
246+
def test_extract_sub_issue_via_main(self, monkeypatch, capsys):
247+
monkeypatch.setattr(sys, "argv", ["workflow_cli", "extract-sub-issue", "Sub-Issue: #55"])
248+
code = main()
249+
assert code == 0
250+
captured = capsys.readouterr()
251+
assert captured.out.strip() == "55"
252+
253+
def test_extract_parent_issue_via_main(self, monkeypatch, capsys):
254+
monkeypatch.setattr(
255+
sys, "argv", ["workflow_cli", "extract-parent-issue", "Parent Issue: #88", "--issue-body", ""]
256+
)
257+
code = main()
258+
assert code == 0
259+
captured = capsys.readouterr()
260+
assert captured.out.strip() == "88"
261+
262+
def test_get_attempt_count_via_main(self, monkeypatch, capsys):
263+
monkeypatch.setattr(sys, "argv", ["workflow_cli", "get-attempt-count", "ai-attempt-3,done"])
264+
code = main()
265+
assert code == 0
266+
captured = capsys.readouterr()
267+
assert captured.out.strip() == "3"
268+
269+
def test_parse_plot_path_via_main(self, monkeypatch, capsys):
270+
monkeypatch.setattr(sys, "argv", ["workflow_cli", "parse-plot-path", "plots/bokeh/line/line-basic/default.py"])
271+
code = main()
272+
assert code == 0
273+
captured = capsys.readouterr()
274+
data = json.loads(captured.out.strip())
275+
assert data["library"] == "bokeh"
276+
277+
def test_status_transition_via_main(self, monkeypatch, capsys):
278+
monkeypatch.setattr(sys, "argv", ["workflow_cli", "status-transition", "generating", "done"])
279+
code = main()
280+
assert code == 0
281+
captured = capsys.readouterr()
282+
assert "done" in captured.out
283+
284+
def test_quality_label_via_main(self, monkeypatch, capsys):
285+
monkeypatch.setattr(sys, "argv", ["workflow_cli", "quality-label", "92"])
286+
code = main()
287+
assert code == 0
288+
captured = capsys.readouterr()
289+
assert captured.out.strip() == "quality:excellent"
290+
291+
def test_missing_command(self, monkeypatch):
292+
monkeypatch.setattr(sys, "argv", ["workflow_cli"])
293+
with pytest.raises(SystemExit):
294+
main()
295+
296+
def test_unknown_command(self, monkeypatch):
297+
monkeypatch.setattr(sys, "argv", ["workflow_cli", "unknown-command"])
298+
with pytest.raises(SystemExit):
299+
main()

0 commit comments

Comments
 (0)