diff --git a/tests/integration/test_full_workflow.py b/tests/integration/test_full_workflow.py new file mode 100644 index 0000000..64f3465 --- /dev/null +++ b/tests/integration/test_full_workflow.py @@ -0,0 +1,296 @@ +"""Integration tests for ScaffoldForge.""" + +import pytest +import os +import tempfile +from pathlib import Path +from unittest.mock import Mock, patch, MagicMock + +from click.testing import CliRunner + +from scaffoldforge.cli import cli +from scaffoldforge.parsers import IssueParser, IssueData, ChecklistItem +from scaffoldforge.templates import TemplateEngine +from scaffoldforge.generators import StructureGenerator, CodeGenerator + + +class TestFullWorkflow: + """Integration tests for the full workflow.""" + + @patch('scaffoldforge.cli.commands.IssueParser') + @patch('scaffoldforge.cli.commands.TemplateEngine') + @patch('scaffoldforge.cli.commands.StructureGenerator') + @patch('scaffoldforge.cli.commands.CodeGenerator') + def test_full_generate_workflow( + self, mock_code_gen, mock_struct_gen, mock_tpl_engine, mock_parser + ): + """Test complete generate workflow.""" + runner = CliRunner() + + mock_issue_data = Mock() + mock_issue_data.number = 42 + mock_issue_data.title = "Implement User Authentication" + mock_issue_data.body = "Add user login and registration" + mock_issue_data.labels = ["python", "security"] + mock_issue_data.checklist = [ + ChecklistItem(text="Create login form", completed=False), + ChecklistItem(text="Add password hashing", completed=False), + ] + mock_issue_data.get_todo_items.return_value = [ + "Create login form", + "Add password hashing" + ] + mock_issue_data.requirements = ["Must be secure", "Must be fast"] + mock_issue_data.acceptance_criteria = [] + mock_issue_data.suggested_files = [] + mock_issue_data.suggested_directories = [] + mock_issue_data.repository = "user/repo" + mock_issue_data.author = "testuser" + mock_issue_data.created_at = "2024-01-01T00:00:00" + + 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 = { + "main.py": "# content", + "utils.py": "# utils" + } + mock_engine.get_template_context.return_value = { + "project_name": "implement-user-authentication", + "issue_number": 42 + } + mock_tpl_engine.return_value = mock_engine + + mock_code_gen_instance = Mock() + mock_code_gen_instance.generate_all_files.return_value = [] + mock_code_gen.return_value = mock_code_gen_instance + + mock_struct_gen_instance = Mock() + mock_struct_gen.return_value = mock_struct_gen_instance + + with runner.isolated_filesystem(): + result = runner.invoke( + cli, + [ + "generate", + "https://github.com/user/repo/issues/42", + "--language", "python", + "--output", "./test-project" + ] + ) + + assert result.exit_code == 0 + + def test_template_engine_with_real_templates(self): + """Test TemplateEngine with real built-in templates.""" + engine = TemplateEngine() + + languages = engine.list_available_languages() + assert "python" in languages + assert "javascript" in languages + assert "go" in languages + assert "rust" in languages + + python_templates = engine.list_available_templates("python") + assert "default" in python_templates + + def test_issue_data_integration(self): + """Test IssueData with real checklist items.""" + checklist = [ + ChecklistItem(text="First task", completed=False), + ChecklistItem(text="Second task", completed=True), + ChecklistItem(text="Third task", completed=False), + ] + + issue_data = IssueData( + number=100, + title="Integration Test Issue", + body="Test body for integration", + body_html="
Test body
", + labels=["test"], + state="open", + url="https://github.com/test/repo/issues/100", + repository="test/repo", + author="testuser", + created_at="2024-01-01T00:00:00", + updated_at="2024-01-02T00:00:00", + checklist=checklist + ) + + todos = issue_data.get_todo_items() + completed = issue_data.get_completed_items() + + assert len(todos) == 2 + assert len(completed) == 1 + assert "First task" in todos + assert "Second task" in completed + + def test_generator_with_real_templates(self): + """Test code generation with real templates.""" + issue_data = IssueData( + number=1, + title="Test Python Project", + body="", + body_html="", + labels=["python"], + state="open", + url="", + repository="", + author="", + created_at="", + updated_at="", + checklist=[ + ChecklistItem(text="Task 1", completed=False), + ChecklistItem(text="Task 2", completed=False), + ] + ) + + engine = TemplateEngine() + gen = CodeGenerator(engine, issue_data) + + files = gen.generate_all_files("python", issue_data) + + assert len(files) > 0 + for f in files: + assert f.path is not None + assert f.content is not None + + def test_structure_generator_preview_mode(self): + """Test structure generator in preview mode.""" + issue_data = IssueData( + number=1, + title="Preview Test", + body="", + body_html="", + labels=["python"], + state="open", + url="", + repository="", + author="", + created_at="", + updated_at="", + checklist=[] + ) + + engine = TemplateEngine() + code_gen = CodeGenerator(engine, issue_data) + + with tempfile.TemporaryDirectory() as tmpdir: + gen = StructureGenerator(output_dir=tmpdir, preview=True) + gen.generate("python", issue_data, code_gen) + + assert len(gen.created_files) == 0 + assert len(gen.created_dirs) == 0 + + def test_multiple_language_templates(self): + """Test that all language templates can be loaded.""" + engine = TemplateEngine() + + for lang in ["python", "javascript", "go", "rust"]: + templates = engine.list_available_templates(lang) + assert len(templates) >= 1 + + context = engine.get_template_context( + IssueData( + number=1, title="Test", body="", body_html="", + labels=[lang], state="open", url="", repository="", + author="", created_at="", updated_at="" + ) + ) + assert context is not None + + def test_cli_with_preview_flag(self): + """Test CLI with preview flag doesn't create files.""" + runner = CliRunner() + + with patch('scaffoldforge.cli.commands.IssueParser') as mock_parser, \ + patch('scaffoldforge.cli.commands.TemplateEngine') as mock_tpl_engine, \ + patch('scaffoldforge.cli.commands.StructureGenerator') as mock_struct_gen, \ + patch('scaffoldforge.cli.commands.CodeGenerator') as mock_code_gen: + + mock_issue_data = Mock() + mock_issue_data.number = 1 + mock_issue_data.title = "Test" + 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_issue_data.repository = "" + mock_issue_data.author = "" + mock_issue_data.created_at = "" + + mock_parser.return_value.parse_issue.return_value = mock_issue_data + mock_parser.return_value.detect_language.return_value = "python" + + with runner.isolated_filesystem(): + result = runner.invoke( + cli, + ["generate", "https://github.com/test/repo/issues/1", "--preview"] + ) + + assert result.exit_code == 0 + + def test_readme_generation(self): + """Test README generation in structure generator.""" + issue_data = IssueData( + number=123, + title="Test Project", + body="This is a test project body", + body_html="", + labels=["python"], + state="open", + url="https://github.com/user/repo/issues/123", + repository="user/repo", + author="testuser", + created_at="2024-01-01T00:00:00", + updated_at="2024-01-02T00:00:00", + checklist=[ + ChecklistItem(text="Task 1", completed=False), + ChecklistItem(text="Task 2", completed=True), + ] + ) + + engine = TemplateEngine() + code_gen = CodeGenerator(engine, issue_data) + + with tempfile.TemporaryDirectory() as tmpdir: + gen = StructureGenerator(output_dir=tmpdir, preview=False) + gen.generate("python", issue_data, code_gen) + + readme_path = Path(tmpdir) / "test-project" / "README.md" + if readme_path.exists(): + content = readme_path.read_text() + assert "Test Project" in content + assert "123" in content + + +class TestRepositoryIntegration: + """Tests for repository integration features.""" + + def test_gitignore_content_by_language(self): + """Test that gitignore content varies by language.""" + issue_data = IssueData( + number=1, title="Test", body="", body_html="", + labels=[], state="open", url="", repository="", + author="", created_at="", updated_at="" + ) + + engine = TemplateEngine() + code_gen = CodeGenerator(engine, issue_data) + + for lang in ["python", "javascript", "go", "rust"]: + files = code_gen.generate_all_files(lang, issue_data) + file_names = [f.path for f in files] + + if lang == "python": + assert any("requirements" in f or "pyproject" in f for f in file_names) + elif lang == "javascript": + assert any("package.json" in f for f in file_names) + elif lang == "go": + assert any("go.mod" in f for f in file_names) + elif lang == "rust": + assert any("Cargo.toml" in f for f in file_names)