Skip to content

Commit f1f7599

Browse files
committed
tests(cli): Implement CLI testing framework
1 parent e8e0a94 commit f1f7599

9 files changed

+1460
-9
lines changed

notes/TODO.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@
7979
- [x] Create example-based tests
8080
- [x] Ensure examples serve as both documentation and tests
8181

82-
- [ ] **Enhanced CLI Testing**
83-
- [ ] Implement comprehensive CLI command tests
84-
- [ ] Test CLI output formats
85-
- [ ] Create mocks for CLI environment
82+
- [x] **Enhanced CLI Testing**
83+
- [x] Implement comprehensive CLI command tests
84+
- [x] Test CLI output formats
85+
- [x] Create mocks for CLI environment
8686

8787
## 4. Internal APIs
8888

@@ -220,11 +220,11 @@
220220
- [ ] Create initial API reference generation
221221
- [ ] Implement doctest integration
222222

223-
- [ ] **CLI Testing Framework**
224-
- [ ] Implement CLI testing fixtures
225-
- [ ] Create test suite for existing commands
226-
- [ ] Add coverage for error cases
227-
- [ ] Implement test validation with schema
223+
- [x] **CLI Testing Framework**
224+
- [x] Implement CLI testing fixtures
225+
- [x] Create test suite for existing commands
226+
- [x] Add coverage for error cases
227+
- [x] Implement test validation with schema
228228

229229
- [ ] **Migration Tool**
230230
- [ ] Design migration strategy

tests/cli/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""CLI testing package."""

tests/cli/commands/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Command testing package."""

tests/cli/commands/test_detect.py

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
"""Tests for detect command."""
2+
3+
from __future__ import annotations
4+
5+
import json
6+
from unittest.mock import patch
7+
8+
import pytest
9+
import yaml
10+
11+
12+
@pytest.mark.parametrize(
13+
"args",
14+
[
15+
["detect", "--help"],
16+
["detect", "-h"],
17+
],
18+
)
19+
def test_detect_help(cli_runner, args):
20+
"""Test detect command help output."""
21+
stdout, stderr, exit_code = cli_runner(args, expected_exit_code=0)
22+
23+
# Check for help text
24+
assert "usage:" in stdout
25+
assert "detect" in stdout
26+
assert "Detect repositories" in stdout
27+
28+
29+
@patch("vcspull.operations.detect_repositories")
30+
def test_detect_command_basic(mock_detect, cli_runner, tmp_path):
31+
"""Test detect command with basic options."""
32+
# Create a dummy directory to scan
33+
target_dir = tmp_path / "repos"
34+
target_dir.mkdir()
35+
36+
# Mock the detect_repositories function
37+
mock_detect.return_value = [
38+
{
39+
"name": "repo1",
40+
"path": str(target_dir / "repo1"),
41+
"type": "git",
42+
"url": "https://github.com/user/repo1",
43+
}
44+
]
45+
46+
# Run the command
47+
stdout, stderr, exit_code = cli_runner(
48+
["detect", str(target_dir)],
49+
expected_exit_code=0,
50+
)
51+
52+
# Check mock was called with correct path
53+
mock_detect.assert_called_once()
54+
args, _ = mock_detect.call_args
55+
assert str(target_dir) in str(args[0])
56+
57+
# Verify output
58+
assert "Detected repositories" in stdout
59+
assert "repo1" in stdout
60+
61+
62+
@patch("vcspull.operations.detect_repositories")
63+
def test_detect_command_save_config(mock_detect, cli_runner, tmp_path):
64+
"""Test detect command with save-config option."""
65+
# Create a dummy directory to scan
66+
target_dir = tmp_path / "repos"
67+
target_dir.mkdir()
68+
69+
# Output config file
70+
output_file = tmp_path / "detected_config.yaml"
71+
72+
# Mock the detect_repositories function
73+
mock_detect.return_value = [
74+
{
75+
"name": "repo1",
76+
"path": str(target_dir / "repo1"),
77+
"type": "git",
78+
"url": "https://github.com/user/repo1",
79+
}
80+
]
81+
82+
# Run the command with save-config option
83+
stdout, stderr, exit_code = cli_runner(
84+
[
85+
"detect",
86+
str(target_dir),
87+
"--save-config",
88+
str(output_file),
89+
],
90+
expected_exit_code=0,
91+
)
92+
93+
# Verify config file was created
94+
assert output_file.exists()
95+
96+
# Verify config content
97+
config = yaml.safe_load(output_file.read_text())
98+
assert "repositories" in config
99+
assert len(config["repositories"]) == 1
100+
assert config["repositories"][0]["name"] == "repo1"
101+
102+
103+
@patch("vcspull.operations.detect_repositories")
104+
def test_detect_command_json_output(mock_detect, cli_runner, tmp_path):
105+
"""Test detect command with JSON output."""
106+
# Create a dummy directory to scan
107+
target_dir = tmp_path / "repos"
108+
target_dir.mkdir()
109+
110+
# Mock the detect_repositories function
111+
mock_detect.return_value = [
112+
{
113+
"name": "repo1",
114+
"path": str(target_dir / "repo1"),
115+
"type": "git",
116+
"url": "https://github.com/user/repo1",
117+
}
118+
]
119+
120+
# Run the command with JSON output
121+
stdout, stderr, exit_code = cli_runner(
122+
["detect", str(target_dir), "--output", "json"],
123+
expected_exit_code=0,
124+
)
125+
126+
# Output should be valid JSON
127+
try:
128+
json_output = json.loads(stdout)
129+
assert isinstance(json_output, dict)
130+
assert "repositories" in json_output
131+
assert len(json_output["repositories"]) == 1
132+
except json.JSONDecodeError:
133+
pytest.fail("Output is not valid JSON")
134+
135+
136+
@patch("vcspull.operations.detect_repositories")
137+
def test_detect_command_filter_type(mock_detect, cli_runner, tmp_path):
138+
"""Test detect command with type filter."""
139+
# Create a dummy directory to scan
140+
target_dir = tmp_path / "repos"
141+
target_dir.mkdir()
142+
143+
# Mock the detect_repositories function
144+
mock_detect.return_value = [
145+
{
146+
"name": "repo1",
147+
"path": str(target_dir / "repo1"),
148+
"type": "git",
149+
"url": "https://github.com/user/repo1",
150+
}
151+
]
152+
153+
# Run the command with type filter
154+
stdout, stderr, exit_code = cli_runner(
155+
["detect", str(target_dir), "--type", "git"],
156+
expected_exit_code=0,
157+
)
158+
159+
# Check mock was called with type filter
160+
mock_detect.assert_called_once()
161+
_, kwargs = mock_detect.call_args
162+
assert "vcs_types" in kwargs
163+
assert "git" in kwargs["vcs_types"]
164+
165+
166+
@patch("vcspull.operations.detect_repositories")
167+
def test_detect_command_max_depth(mock_detect, cli_runner, tmp_path):
168+
"""Test detect command with max-depth option."""
169+
# Create a dummy directory to scan
170+
target_dir = tmp_path / "repos"
171+
target_dir.mkdir()
172+
173+
# Mock the detect_repositories function
174+
mock_detect.return_value = []
175+
176+
# Run the command with max-depth option
177+
stdout, stderr, exit_code = cli_runner(
178+
["detect", str(target_dir), "--max-depth", "3"],
179+
expected_exit_code=0,
180+
)
181+
182+
# Check mock was called with max_depth parameter
183+
mock_detect.assert_called_once()
184+
_, kwargs = mock_detect.call_args
185+
assert "max_depth" in kwargs
186+
assert kwargs["max_depth"] == 3

0 commit comments

Comments
 (0)