fix: Resolve failing tests after CLI and error handling refactoring

Fix all test failures introduced by recent architectural changes:

• Issue Creator Tests:
  - Fixed mock API responses to include required fields (created_at, updated_at, html_url)
  - Added input validation for empty titles back to issue creator
  - Updated test expectations to match new error handling patterns
  - Created helper function for complete mock responses

• Issue Fetcher Test:
  - Updated mock target from tddai.issue_fetcher.subprocess to gitea.http_client.subprocess
  - Fixed test assertions to match new error handling with specific exception chaining
  - Test now properly validates API error translation

• Makefile Integration Test:
  - Implemented lazy initialization in tddai_cli.py to prevent import-time configuration errors
  - Replaced eager CLI framework initialization with _get_cli() lazy pattern
  - Preserves normal CLI functionality while fixing test environment compatibility

• Result: All 171 tests now pass (169 passed, 2 skipped)
• Maintains backward compatibility of CLI interface
• Validates that refactored error handling works correctly

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-26 17:15:36 +02:00
parent 235e6831ed
commit 6713768ea6
4 changed files with 71 additions and 33 deletions

View File

@@ -40,6 +40,10 @@ class IssueCreator:
Raises:
IssueError: If creation fails
"""
# Validate input
if not title or not title.strip():
raise IssueError("Issue title cannot be empty")
try:
issue = self.gitea_client.issues.create(
title=title,

View File

@@ -15,48 +15,55 @@ sys.path.insert(0, str(Path(__file__).parent))
from cli import CLIFramework
# Initialize CLI framework
cli = CLIFramework()
# Lazy initialization of CLI framework
_cli_framework = None
def _get_cli():
"""Get CLI framework instance (lazy initialization)."""
global _cli_framework
if _cli_framework is None:
_cli_framework = CLIFramework()
return _cli_framework
def workspace_status():
"""Show current workspace status."""
cli.workspace_status()
_get_cli().workspace_status()
def start_issue(issue_number: int):
"""Start working on an issue."""
cli.start_issue(issue_number)
_get_cli().start_issue(issue_number)
def finish_issue():
"""Finish current issue workspace."""
cli.finish_issue()
_get_cli().finish_issue()
def add_test_guidance():
"""Show guidance for adding tests."""
cli.add_test_guidance()
_get_cli().add_test_guidance()
def list_issues():
"""List all issues."""
cli.list_issues()
_get_cli().list_issues()
def list_open_issues():
"""List only open issues."""
cli.list_open_issues()
_get_cli().list_open_issues()
def show_issue(issue_number: int):
"""Show detailed issue information."""
cli.show_issue(issue_number)
_get_cli().show_issue(issue_number)
def create_issue(title: str, body: str, issue_type: str = "enhancement"):
"""Create a new issue."""
cli.create_issue(title, body, issue_type)
_get_cli().create_issue(title, body, issue_type)
def create_enhancement_issue(title: str, use_case: str, technical_requirements: str = "",
@@ -73,7 +80,7 @@ def create_enhancement_issue(title: str, use_case: str, technical_requirements:
if dependencies:
deps_list = [line.strip() for line in dependencies.split('\n') if line.strip()]
cli.create_enhancement_issue(
_get_cli().create_enhancement_issue(
title=title,
use_case=use_case,
technical_requirements=technical_requirements,
@@ -85,52 +92,52 @@ def create_enhancement_issue(title: str, use_case: str, technical_requirements:
def create_from_template(template_file: str, **kwargs):
"""Create issue from template file."""
cli.create_from_template(template_file, **kwargs)
_get_cli().create_from_template(template_file, **kwargs)
def analyze_coverage(issue_number: int):
"""Analyze test coverage for a specific issue."""
cli.analyze_coverage(issue_number)
_get_cli().analyze_coverage(issue_number)
def setup_project_management():
"""Setup project management labels and milestones."""
cli.setup_project_management()
_get_cli().setup_project_management()
def move_issue_to_state(issue_number: int, state: str):
"""Move issue to a specific project state."""
cli.move_issue_to_state(issue_number, state)
_get_cli().move_issue_to_state(issue_number, state)
def set_issue_priority(issue_number: int, priority: str):
"""Set issue priority."""
cli.set_issue_priority(issue_number, priority)
_get_cli().set_issue_priority(issue_number, priority)
def create_milestone(title: str, description: str = ""):
"""Create a new milestone (project)."""
cli.create_milestone(title, description)
_get_cli().create_milestone(title, description)
def list_milestones():
"""List all milestones."""
cli.list_milestones()
_get_cli().list_milestones()
def assign_issue_to_milestone(issue_number: int, milestone_id: int):
"""Assign issue to a milestone."""
cli.assign_issue_to_milestone(issue_number, milestone_id)
_get_cli().assign_issue_to_milestone(issue_number, milestone_id)
def project_overview():
"""Show project management overview."""
cli.project_overview()
_get_cli().project_overview()
def issue_index(format_type="tsv", sort_by="number", filter_state=None, filter_priority=None, include_state=False):
"""Output compact index of all issues for Unix processing."""
cli.issue_index(
_get_cli().issue_index(
format_type=format_type,
sort_by=sort_by,
filter_state=filter_state,

View File

@@ -108,16 +108,16 @@ class TestWorkspaceCreation:
with pytest.raises(WorkspaceError, match="Workspace already active"):
manager.create_workspace(second_issue_data)
@patch('tddai.issue_fetcher.subprocess.run')
@patch('gitea.http_client.subprocess.run')
def test_issue_fetcher_handles_invalid_issue(self, mock_run, temp_workspace):
"""Test error handling for invalid issue numbers."""
# Mock curl response for non-existent issue
mock_run.return_value.returncode = 0
mock_run.return_value.stdout = '{"message": "404 Not Found"}'
# Mock curl response for non-existent issue (404 error)
from subprocess import CalledProcessError
mock_run.side_effect = CalledProcessError(22, 'curl') # HTTP 404 error
fetcher = IssueFetcher(temp_workspace)
with pytest.raises(IssueError, match="not found"):
with pytest.raises(IssueError, match="API error fetching issue.*HTTP request failed"):
fetcher.fetch_issue(999)
def test_workspace_cleanup(self, temp_workspace, mock_issue_data):

View File

@@ -25,6 +25,18 @@ class TestIssueCreator:
repo_name="test_repo"
)
def _get_complete_mock_response(self, number: int, title: str = "Test Issue", body: str = "Test description"):
"""Get a complete mock API response with all required fields."""
return {
"number": number,
"title": title,
"body": body,
"state": "open",
"created_at": "2025-09-26T10:00:00Z",
"updated_at": "2025-09-26T10:00:00Z",
"html_url": f"http://gitea.example.com/repo/issues/{number}"
}
def test_init_with_auth_token(self):
"""Test IssueCreator initialization with auth token."""
config = self._get_test_config()
@@ -62,7 +74,10 @@ class TestIssueCreator:
"number": 123,
"title": "Test Issue",
"body": "Test description",
"state": "open"
"state": "open",
"created_at": "2025-09-26T10:00:00Z",
"updated_at": "2025-09-26T10:00:00Z",
"html_url": "http://gitea.example.com/repo/issues/123"
}
mock_run.return_value = MagicMock(
@@ -73,7 +88,19 @@ class TestIssueCreator:
result = creator.create_issue("Test Issue", "Test description")
assert result == mock_response
# Verify the result has the expected structure (transformed by issue creator)
expected_result = {
'number': 123,
'title': "Test Issue",
'body': "Test description",
'state': "open",
'html_url': "http://gitea.example.com/repo/issues/123",
'created_at': "2025-09-26T10:00:00", # ISO format from datetime parsing
'updated_at': "2025-09-26T10:00:00",
'assignee': None,
'labels': []
}
assert result == expected_result
mock_run.assert_called_once()
# Check curl command structure
@@ -144,7 +171,7 @@ class TestIssueCreator:
stderr=""
)
with pytest.raises(IssueError, match="Failed to parse response data"):
with pytest.raises(IssueError, match="Failed to create issue.*parse.*response"):
creator.create_issue("Test Issue", "Test description")
@patch('subprocess.run')
@@ -153,7 +180,7 @@ class TestIssueCreator:
config = self._get_test_config()
creator = IssueCreator(config=config, auth_token="test-token")
mock_response = {"number": 124}
mock_response = self._get_complete_mock_response(124)
mock_run.return_value = MagicMock(
returncode=0,
stdout=json.dumps(mock_response),
@@ -183,7 +210,7 @@ class TestIssueCreator:
config = self._get_test_config()
creator = IssueCreator(config=config, auth_token="test-token")
mock_response = {"number": 125}
mock_response = self._get_complete_mock_response(125)
mock_run.return_value = MagicMock(
returncode=0,
stdout=json.dumps(mock_response),
@@ -218,7 +245,7 @@ class TestIssueCreator:
config = self._get_test_config()
creator = IssueCreator(config=config, auth_token="test-token")
mock_response = {"number": 126}
mock_response = self._get_complete_mock_response(126)
mock_run.return_value = MagicMock(
returncode=0,
stdout=json.dumps(mock_response),
@@ -255,7 +282,7 @@ class TestIssueCreator:
config = self._get_test_config()
creator = IssueCreator(config=config, auth_token="test-token")
mock_response = {"number": 127}
mock_response = self._get_complete_mock_response(127)
mock_run.return_value = MagicMock(
returncode=0,
stdout=json.dumps(mock_response),