Files
markitect-main/tddai/issue_creator.py
tegwick 6713768ea6 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>
2025-09-26 17:15:36 +02:00

211 lines
6.9 KiB
Python

"""
Issue creation using the Gitea facade.
This module now acts as an adapter to the new gitea package,
maintaining backwards compatibility while using the cleaner API.
"""
import os
from typing import Dict, Any, Optional, List
from gitea import GiteaClient, GiteaConfig, Priority
from .config import get_config
from .exceptions import IssueError
class IssueCreator:
"""Creates new issues using the Gitea facade."""
def __init__(self, config=None, auth_token=None):
self.config = config or get_config()
self.auth_token = auth_token or os.getenv('GITEA_API_TOKEN')
# Create Gitea client from tddai config
gitea_config = GiteaConfig.from_tddai_config(self.config)
if self.auth_token:
gitea_config.auth_token = self.auth_token
self.gitea_client = GiteaClient(gitea_config)
def create_issue(self, title: str, body: str, **kwargs) -> Dict[str, Any]:
"""Create a new issue via POST operation.
Args:
title: Issue title (required)
body: Issue description/body (required)
**kwargs: Optional fields (assignees, milestone, labels, etc.)
Returns:
Dict containing created issue data including issue number
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,
body=body,
assignees=kwargs.get('assignees', []),
milestone=kwargs.get('milestone'),
labels=kwargs.get('labels', [])
)
# Convert back to dict format for backwards compatibility
return {
'number': issue.number,
'title': issue.title,
'body': issue.body,
'state': issue.state,
'html_url': issue.html_url,
'created_at': issue.created_at.isoformat(),
'updated_at': issue.updated_at.isoformat(),
'assignee': {'login': issue.assignee.login} if issue.assignee else None,
'labels': [{'name': label.name} for label in issue.labels]
}
except Exception as e:
raise IssueError(f"Failed to create issue: {e}")
def create_enhancement_issue(self, title: str, use_case: str,
technical_requirements: str = "",
acceptance_criteria: List[str] = None,
dependencies: List[str] = None,
priority: str = "Medium") -> Dict[str, Any]:
"""Create an enhancement issue with structured format.
Args:
title: Issue title
use_case: UseCase description
technical_requirements: Technical implementation details
acceptance_criteria: List of acceptance criteria
dependencies: List of dependency descriptions
priority: Priority level (High, Medium, Low)
Returns:
Dict containing created issue data
"""
# Build structured body
body_parts = [f"UseCase: {use_case}"]
if technical_requirements:
body_parts.extend([
"",
"Technical Requirements:",
technical_requirements
])
if acceptance_criteria:
body_parts.extend([
"",
"Acceptance Criteria:"
])
for criterion in acceptance_criteria:
body_parts.append(f"- [ ] {criterion}")
if dependencies:
body_parts.extend([
"",
"Dependencies:"
])
for dep in dependencies:
body_parts.append(f"- {dep}")
body = "\n".join(body_parts)
# Create with enhancement label
return self.create_issue(
title=title,
body=body,
labels=[priority.lower(), "enhancement"]
)
def create_bug_issue(self, title: str, description: str,
steps_to_reproduce: List[str] = None,
expected_behavior: str = "",
actual_behavior: str = "",
environment: str = "") -> Dict[str, Any]:
"""Create a bug issue with structured format.
Args:
title: Bug title
description: Bug description
steps_to_reproduce: List of reproduction steps
expected_behavior: What should happen
actual_behavior: What actually happens
environment: Environment details
Returns:
Dict containing created issue data
"""
body_parts = [description]
if steps_to_reproduce:
body_parts.extend([
"",
"Steps to Reproduce:"
])
for i, step in enumerate(steps_to_reproduce, 1):
body_parts.append(f"{i}. {step}")
if expected_behavior:
body_parts.extend([
"",
f"Expected Behavior: {expected_behavior}"
])
if actual_behavior:
body_parts.extend([
"",
f"Actual Behavior: {actual_behavior}"
])
if environment:
body_parts.extend([
"",
f"Environment: {environment}"
])
body = "\n".join(body_parts)
# Create with bug label
return self.create_issue(
title=title,
body=body,
labels=["bug"]
)
def create_from_template(self, template_file: str, **template_vars) -> Dict[str, Any]:
"""Create issue from a template file.
Args:
template_file: Path to template file
**template_vars: Variables to substitute in template
Returns:
Dict containing created issue data
"""
try:
with open(template_file, 'r') as f:
template_content = f.read()
# Simple template variable substitution
for key, value in template_vars.items():
template_content = template_content.replace(f"{{{key}}}", str(value))
# Extract title (first line) and body (rest)
lines = template_content.strip().split('\n')
if not lines or (len(lines) == 1 and not lines[0].strip()):
raise IssueError("Template file is empty")
title = lines[0].replace('Title: ', '').strip()
body = '\n'.join(lines[1:]).strip()
return self.create_issue(title=title, body=body)
except FileNotFoundError:
raise IssueError(f"Template file not found: {template_file}")
except Exception as e:
raise IssueError(f"Failed to process template: {e}")