diff --git a/tests/unit/test_parsers.py b/tests/unit/test_parsers.py new file mode 100644 index 0000000..4429e0c --- /dev/null +++ b/tests/unit/test_parsers.py @@ -0,0 +1,255 @@ +"""Unit tests for the parsers module.""" + +import pytest +from unittest.mock import Mock, patch, MagicMock + +from scaffoldforge.parsers import IssueParser, IssueData, ChecklistItem + + +class TestChecklistItem: + """Tests for ChecklistItem dataclass.""" + + def test_checklist_item_creation(self): + """Test creating a checklist item.""" + item = ChecklistItem( + text="Implement feature", + completed=False, + line_number=5, + category="Implementation" + ) + assert item.text == "Implement feature" + assert item.completed is False + assert item.line_number == 5 + assert item.category == "Implementation" + + def test_checklist_item_completed(self): + """Test completed checklist item.""" + item = ChecklistItem( + text="Completed task", + completed=True, + line_number=10 + ) + assert item.completed is True + + +class TestIssueData: + """Tests for IssueData dataclass.""" + + def test_issue_data_creation(self): + """Test creating issue data.""" + data = IssueData( + number=123, + title="Test Issue", + body="Test body", + body_html="
Test body
", + labels=["bug", "priority"], + 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" + ) + assert data.number == 123 + assert data.title == "Test Issue" + assert data.labels == ["bug", "priority"] + + def test_get_todo_items(self): + """Test getting incomplete todo items.""" + checklist = [ + ChecklistItem(text="Task 1", completed=False), + ChecklistItem(text="Task 2", completed=True), + ChecklistItem(text="Task 3", completed=False), + ] + data = IssueData( + number=1, title="Test", body="", body_html="", + labels=[], state="open", url="", repository="", author="", + created_at="", updated_at="", checklist=checklist + ) + todos = data.get_todo_items() + assert len(todos) == 2 + assert "Task 1" in todos + assert "Task 3" in todos + assert "Task 2" not in todos + + def test_get_completed_items(self): + """Test getting completed items.""" + checklist = [ + ChecklistItem(text="Task 1", completed=False), + ChecklistItem(text="Task 2", completed=True), + ] + data = IssueData( + number=1, title="Test", body="", body_html="", + labels=[], state="open", url="", repository="", author="", + created_at="", updated_at="", checklist=checklist + ) + completed = data.get_completed_items() + assert len(completed) == 1 + assert "Task 2" in completed + + def test_generate_todo_comments(self): + """Test generating TODO comments.""" + checklist = [ + ChecklistItem(text="Implement feature", completed=False), + ChecklistItem(text="Write tests", completed=False), + ] + data = IssueData( + number=42, title="Test", body="", body_html="", + labels=[], state="open", url="", repository="", author="", + created_at="", updated_at="", checklist=checklist + ) + comments = data.generate_todo_comments() + assert "TODO #1: Implement feature" in comments + assert "TODO #2: Write tests" in comments + + +class TestIssueParser: + """Tests for IssueParser class.""" + + @patch('scaffoldforge.parsers.issue_parser.Github') + def test_parser_initialization_without_token(self, mock_github): + """Test parser initialization without token.""" + parser = IssueParser() + mock_github.assert_called_once() + + @patch('scaffoldforge.parsers.issue_parser.Github') + def test_parser_initialization_with_token(self, mock_github): + """Test parser initialization with token.""" + parser = IssueParser(token="test_token") + mock_github.assert_called_once_with("test_token") + + def test_parse_checklist_simple(self): + """Test parsing simple checklist items.""" + parser = IssueParser() + body = """ +## Tasks +- [x] Completed task +- [ ] Pending task +- [X] Also completed + """ + checklist = parser._parse_checklist(body) + assert len(checklist) == 3 + assert checklist[0].text == "Completed task" + assert checklist[0].completed is True + assert checklist[1].text == "Pending task" + assert checklist[1].completed is False + + def test_parse_checklist_with_categories(self): + """Test parsing checklist with category headers.""" + parser = IssueParser() + body = """ +## Implementation +- [ ] Task 1 +- [ ] Task 2 + +## Testing +- [ ] Test 1 + """ + checklist = parser._parse_checklist(body) + assert len(checklist) == 3 + assert checklist[0].category == "Implementation" + assert checklist[2].category == "Testing" + + def test_parse_requirements(self): + """Test parsing requirements section.""" + parser = IssueParser() + body = """ +## Requirements +- Must work +- Must be fast +- Must be secure + """ + requirements = parser._parse_requirements(body) + assert len(requirements) == 3 + assert "Must work" in requirements + + def test_parse_acceptance_criteria(self): + """Test parsing acceptance criteria.""" + parser = IssueParser() + body = """ +## Acceptance Criteria +- [x] Feature works +- [ ] Tests pass + """ + criteria = parser._parse_acceptance_criteria(body) + assert len(criteria) == 2 + assert "Feature works" in criteria + + def test_parse_file_paths(self): + """Test parsing file paths from body.""" + parser = IssueParser() + body = """ +Create a new file `src/main.py` +Also need `utils.py` + """ + files = parser._parse_file_paths(body) + assert len(files) >= 2 + + def test_detect_language_from_labels(self): + """Test language detection from labels.""" + parser = IssueParser() + issue_data = IssueData( + number=1, title="Test", body="", body_html="", + labels=["python", "enhancement"], state="open", url="", + repository="", author="", created_at="", updated_at="" + ) + lang = parser.detect_language(issue_data) + assert lang == "python" + + def test_detect_language_from_body(self): + """Test language detection from body content.""" + parser = IssueParser() + issue_data = IssueData( + number=1, title="Test", body="This is a Go project", + body_html="", labels=[], state="open", url="", + repository="", author="", created_at="", updated_at="" + ) + lang = parser.detect_language(issue_data) + assert lang == "go" + + def test_detect_language_unknown(self): + """Test language detection when unknown.""" + parser = IssueParser() + issue_data = IssueData( + number=1, title="Test", body="No language mentioned", + body_html="", labels=[], state="open", url="", + repository="", author="", created_at="", updated_at="" + ) + lang = parser.detect_language(issue_data) + assert lang is None + + def test_detect_project_type_cli(self): + """Test CLI project type detection.""" + parser = IssueParser() + issue_data = IssueData( + number=1, title="Create a CLI tool", + body="Need to build a command line interface", + body_html="", labels=[], state="open", url="", + repository="", author="", created_at="", updated_at="" + ) + proj_type = parser.detect_project_type(issue_data) + assert proj_type == "cli" + + def test_detect_project_type_api(self): + """Test API project type detection.""" + parser = IssueParser() + issue_data = IssueData( + number=1, title="Create REST API", + body="Build an API endpoint", + body_html="", labels=[], state="open", url="", + repository="", author="", created_at="", updated_at="" + ) + proj_type = parser.detect_project_type(issue_data) + assert proj_type == "api" + + def test_empty_body_handling(self): + """Test handling of empty body.""" + parser = IssueParser() + checklist = parser._parse_checklist("") + assert len(checklist) == 0 + + requirements = parser._parse_requirements("") + assert len(requirements) == 0 + + files = parser._parse_file_paths("") + assert len(files) == 0