77from typer .testing import CliRunner
88
99from metaspec .cli .main import app
10+ from metaspec .validation import ValidationCheck , ValidationResult
1011
1112runner = CliRunner ()
1213
1314
15+ def create_passing_validation_result () -> ValidationResult :
16+ """Create a passing validation result for tests."""
17+ checks = [
18+ ValidationCheck (
19+ name = "pyproject.toml" ,
20+ passed = True ,
21+ message = "Valid (name: test, version: 1.0.0)" ,
22+ ),
23+ ValidationCheck (
24+ name = "README.md" ,
25+ passed = True ,
26+ message = "Found (100 characters)" ,
27+ ),
28+ ValidationCheck (
29+ name = "LICENSE" ,
30+ passed = True ,
31+ message = "Found" ,
32+ ),
33+ ValidationCheck (
34+ name = "CLI Entry Point" ,
35+ passed = True ,
36+ message = "Found: test-command" ,
37+ ),
38+ ValidationCheck (
39+ name = "GitHub Repository" ,
40+ passed = True ,
41+ message = "Found: https://github.com/test/repo" ,
42+ ),
43+ ]
44+ return ValidationResult (checks = checks , passed = True , warnings = [])
45+
46+
1447class TestContributeCommand :
1548 """Tests for contribute command."""
1649
@@ -54,6 +87,7 @@ def test_contribute_command_description(self) -> None:
5487 assert result .exit_code == 0
5588 assert "community" in result .stdout .lower () or "registry" in result .stdout .lower ()
5689
90+ @patch ("metaspec.cli.contribute.SpeckitValidator" )
5791 @patch ("metaspec.cli.contribute.shutil.which" )
5892 @patch ("metaspec.cli.contribute.CommunityRegistry" )
5993 @patch ("metaspec.cli.contribute.Prompt.ask" )
@@ -66,8 +100,14 @@ def test_contribute_interactive_full_flow(
66100 mock_prompt : MagicMock ,
67101 mock_registry_class : MagicMock ,
68102 mock_which : MagicMock ,
103+ mock_validator_class : MagicMock ,
69104 ) -> None :
70105 """Test complete interactive contribution flow."""
106+ # Mock validation passing
107+ mock_validator = MagicMock ()
108+ mock_validator .validate .return_value = create_passing_validation_result ()
109+ mock_validator_class .return_value = mock_validator
110+
71111 # Mock command exists
72112 mock_which .return_value = "/usr/local/bin/my-spec-kit"
73113
@@ -102,6 +142,7 @@ def test_contribute_interactive_full_flow(
102142 assert result .exit_code == 0
103143 assert "Generated metadata" in result .stdout or mock_file .called
104144
145+ @patch ("metaspec.cli.contribute.SpeckitValidator" )
105146 @patch ("metaspec.cli.contribute.shutil.which" )
106147 @patch ("metaspec.cli.contribute.CommunityRegistry" )
107148 @patch ("metaspec.cli.contribute.Prompt.ask" )
@@ -114,8 +155,14 @@ def test_contribute_with_detected_commands(
114155 mock_prompt : MagicMock ,
115156 mock_registry_class : MagicMock ,
116157 mock_which : MagicMock ,
158+ mock_validator_class : MagicMock ,
117159 ) -> None :
118160 """Test contribution uses detected commands."""
161+ # Mock validation passing
162+ mock_validator = MagicMock ()
163+ mock_validator .validate .return_value = create_passing_validation_result ()
164+ mock_validator_class .return_value = mock_validator
165+
119166 # Mock command exists
120167 mock_which .return_value = "/usr/local/bin/test-kit"
121168
@@ -172,6 +219,7 @@ def test_contribute_command_not_in_path_continue(
172219 # Should exit
173220 assert result .exit_code == 1
174221
222+ @patch ("metaspec.cli.contribute.SpeckitValidator" )
175223 @patch ("metaspec.cli.contribute.shutil.which" )
176224 @patch ("metaspec.cli.contribute.CommunityRegistry" )
177225 @patch ("metaspec.cli.contribute.Prompt.ask" )
@@ -184,8 +232,14 @@ def test_contribute_custom_commands(
184232 mock_prompt : MagicMock ,
185233 mock_registry_class : MagicMock ,
186234 mock_which : MagicMock ,
235+ mock_validator_class : MagicMock ,
187236 ) -> None :
188237 """Test contribution with custom commands instead of detected ones."""
238+ # Mock validation passing
239+ mock_validator = MagicMock ()
240+ mock_validator .validate .return_value = create_passing_validation_result ()
241+ mock_validator_class .return_value = mock_validator
242+
189243 # Mock command exists
190244 mock_which .return_value = "/usr/bin/custom-kit"
191245
@@ -220,6 +274,45 @@ def test_contribute_custom_commands(
220274 # Should complete successfully
221275 assert result .exit_code == 0
222276
277+ @patch ("metaspec.cli.contribute.SpeckitValidator" )
278+ def test_contribute_check_only_passing (self , mock_validator_class : MagicMock ) -> None :
279+ """Test --check-only flag with passing validation."""
280+ # Mock validation passing
281+ mock_validator = MagicMock ()
282+ mock_validator .validate .return_value = create_passing_validation_result ()
283+ mock_validator_class .return_value = mock_validator
284+
285+ result = runner .invoke (app , ["contribute" , "--check-only" ])
286+
287+ # Should exit with 0 when validation passes
288+ assert result .exit_code == 0
289+ # Validation was called
290+ assert mock_validator .validate .called
291+ assert mock_validator .display_results .called
292+
293+ @patch ("metaspec.cli.contribute.SpeckitValidator" )
294+ def test_contribute_check_only_failing (self , mock_validator_class : MagicMock ) -> None :
295+ """Test --check-only flag with failing validation."""
296+ # Mock validation failing
297+ failing_checks = [
298+ ValidationCheck (
299+ name = "pyproject.toml" ,
300+ passed = False ,
301+ message = "pyproject.toml not found" ,
302+ fix_suggestion = "Create pyproject.toml" ,
303+ ),
304+ ]
305+ mock_validator = MagicMock ()
306+ mock_validator .validate .return_value = ValidationResult (
307+ checks = failing_checks , passed = False , warnings = ["Missing files" ]
308+ )
309+ mock_validator_class .return_value = mock_validator
310+
311+ result = runner .invoke (app , ["contribute" , "--check-only" ])
312+
313+ # Should exit with 1 when validation fails
314+ assert result .exit_code == 1
315+
223316 def test_contribute_non_interactive_fails (self ) -> None :
224317 """Test that non-interactive mode shows error."""
225318 result = runner .invoke (
0 commit comments