"""Unit tests for the CLI module.""" from unittest.mock import Mock, patch import pytest from click.testing import CliRunner from scaffoldforge.cli import cli from scaffoldforge.cli.commands import parse_github_url class TestParseGitHubUrl: """Tests for GitHub URL parsing.""" def test_parse_valid_url(self): """Test parsing a valid GitHub issue URL.""" owner, repo, issue_number = parse_github_url( "https://github.com/owner/repo/issues/123" ) assert owner == "owner" assert repo == "repo" assert issue_number == 123 def test_parse_url_with_www(self): """Test parsing URL with www prefix.""" owner, repo, issue_number = parse_github_url( "https://www.github.com/owner/repo/issues/456" ) assert owner == "owner" assert repo == "repo" assert issue_number == 456 def test_parse_url_with_trailing_slash(self): """Test parsing URL with trailing slash.""" owner, repo, issue_number = parse_github_url( "https://github.com/owner/repo/issues/789/" ) assert owner == "owner" assert repo == "repo" def test_parse_invalid_url(self): """Test parsing invalid URL raises exception.""" from click import ClickException with pytest.raises(ClickException): parse_github_url("https://invalid.com/owner/repo/issues/123") def test_parse_url_without_issue_number(self): """Test parsing URL without issue number.""" from click import ClickException with pytest.raises(ClickException): parse_github_url("https://github.com/owner/repo") class TestCLI: """Tests for CLI commands.""" @patch('scaffoldforge.cli.commands.IssueParser') @patch('scaffoldforge.cli.commands.TemplateEngine') @patch('scaffoldforge.cli.commands.StructureGenerator') @patch('scaffoldforge.cli.commands.CodeGenerator') def test_generate_command_preview_mode( self, mock_code_gen, mock_struct_gen, mock_tpl_engine, mock_parser ): """Test generate command in preview mode.""" runner = CliRunner() mock_issue_data = Mock() mock_issue_data.number = 1 mock_issue_data.title = "Test Issue" mock_issue_data.labels = ["python"] mock_issue_data.checklist = [] mock_issue_data.get_todo_items.return_value = [] mock_issue_data.requirements = [] mock_issue_data.body = "" mock_parser_instance = Mock() mock_parser_instance.parse_issue.return_value = mock_issue_data mock_parser_instance.detect_language.return_value = "python" mock_parser.return_value = mock_parser_instance mock_engine = Mock() mock_engine.load_templates.return_value = {} mock_engine.get_template_context.return_value = {} mock_tpl_engine.return_value = mock_engine result = runner.invoke( cli, ["generate", "https://github.com/owner/repo/issues/1", "--preview"] ) assert result.exit_code == 0 assert "PREVIEW" in result.output or "preview" in result.output.lower() @patch('scaffoldforge.cli.commands.TemplateEngine') def test_list_templates_command(self, mock_tpl_engine): """Test list-templates command.""" runner = CliRunner() mock_engine = Mock() mock_engine.list_available_templates.return_value = ["default", "cli"] mock_tpl_engine.return_value = mock_engine result = runner.invoke(cli, ["list-templates", "--language", "python"]) assert result.exit_code == 0 assert "python" in result.output.lower() @patch('scaffoldforge.cli.commands.TemplateEngine') def test_list_templates_all_languages(self, mock_tpl_engine): """Test list-templates without language filter.""" runner = CliRunner() mock_engine = Mock() mock_engine.list_available_templates.side_effect = [ ["default"], ["default"], ["default"], ["default"] ] mock_tpl_engine.return_value = mock_engine result = runner.invoke(cli, ["list-templates"]) assert result.exit_code == 0 def test_cli_help(self): """Test CLI help output.""" runner = CliRunner() result = runner.invoke(cli, ["--help"]) assert result.exit_code == 0 assert "ScaffoldForge" in result.output def test_generate_help(self): """Test generate command help.""" runner = CliRunner() result = runner.invoke(cli, ["generate", "--help"]) assert result.exit_code == 0 assert "--output" in result.output or "-o" in result.output assert "--language" in result.output or "-l" in result.output assert "--preview" in result.output or "-p" in result.output class TestCLIErrorHandling: """Tests for CLI error handling.""" def test_generate_invalid_url(self): """Test generate with invalid URL.""" runner = CliRunner() result = runner.invoke( cli, ["generate", "https://invalid.com/issue/123"] ) assert result.exit_code != 0 assert "Invalid" in result.output or "error" in result.output.lower() @patch('scaffoldforge.cli.commands.IssueParser') def test_generate_missing_language( self, mock_parser ): """Test generate without specifying language when it can't be detected.""" runner = CliRunner() mock_issue_data = Mock() mock_issue_data.number = 1 mock_issue_data.title = "Test Issue" mock_issue_data.labels = [] mock_issue_data.checklist = [] mock_issue_data.get_todo_items.return_value = [] mock_issue_data.requirements = [] mock_issue_data.body = "" mock_parser_instance = Mock() mock_parser_instance.parse_issue.return_value = mock_issue_data mock_parser_instance.detect_language.return_value = None mock_parser.return_value = mock_parser_instance result = runner.invoke( cli, ["generate", "https://github.com/owner/repo/issues/1"] ) assert result.exit_code != 0