Initial upload: ScaffoldForge CLI tool with full codebase, tests, and CI/CD

This commit is contained in:
2026-02-04 05:37:20 +00:00
parent dba02f0756
commit 2c7323c59c

181
tests/unit/test_cli.py Normal file
View File

@@ -0,0 +1,181 @@
"""Unit tests for the CLI module."""
import pytest
from click.testing import CliRunner
from unittest.mock import Mock, patch, MagicMock
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