""" Test generation with AI assistance. """ import subprocess import tempfile from pathlib import Path from typing import Optional from .config import get_config from .workspace import WorkspaceManager from .exceptions import TestGenerationError class TestGenerator: """Generates tests using AI assistance.""" def __init__(self, config=None): self.config = config or get_config() self.workspace_manager = WorkspaceManager(config) def generate_test(self, scenario_name: str, test_description: str) -> Path: """Generate a test file for the current workspace issue.""" workspace = self.workspace_manager.get_current_workspace() if not workspace: raise TestGenerationError("No active workspace found") # Create test file name test_filename = self.config.test_file_pattern.format( issue_num=workspace.issue_number, scenario=scenario_name.lower().replace(' ', '_').replace('-', '_') ) test_file_path = workspace.tests_dir / test_filename # Generate test prompt prompt = self._create_test_prompt(workspace, scenario_name, test_description) # Use Claude Code to generate the test try: with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: f.write(prompt) prompt_file = Path(f.name) result = subprocess.run( [self.config.claude_code_command, '--file', str(prompt_file)], cwd=workspace.workspace_dir, capture_output=True, text=True ) prompt_file.unlink() # Clean up temp file if result.returncode != 0: raise TestGenerationError(f"Claude Code failed: {result.stderr}") # Extract Python code from Claude's response test_content = self._extract_test_code(result.stdout) # Write test file test_file_path.write_text(test_content) # Update test plan self._update_test_plan(workspace, scenario_name, test_filename) return test_file_path except subprocess.CalledProcessError as e: raise TestGenerationError(f"Failed to generate test: {e}") except Exception as e: raise TestGenerationError(f"Test generation error: {e}") def _create_test_prompt(self, workspace, scenario_name: str, test_description: str) -> str: """Create prompt for Claude Code to generate test.""" return f"""# Test Generation Request ## Context - Issue #{workspace.issue_number}: {workspace.issue_title} - Scenario: {scenario_name} ## Issue Description {workspace.issue_body} ## Test Requirements {test_description} ## Instructions Please generate a comprehensive Python test file that: 1. Tests the behavior described in the scenario 2. Follows pytest conventions 3. Includes proper docstrings and comments 4. Tests both positive and negative cases 5. Uses meaningful test method names 6. Includes appropriate assertions The test should focus on behavior verification rather than implementation details. ## Expected Output Please provide only the Python test code without any additional explanation. The code should be ready to save as `{self.config.test_file_pattern.format(issue_num=workspace.issue_number, scenario=scenario_name.lower().replace(' ', '_'))}` """ def _extract_test_code(self, claude_response: str) -> str: """Extract Python test code from Claude's response.""" lines = claude_response.split('\n') code_lines = [] in_code_block = False for line in lines: if line.strip().startswith('```python'): in_code_block = True continue elif line.strip() == '```' and in_code_block: break elif in_code_block: code_lines.append(line) if not code_lines: # If no code block found, assume entire response is code return claude_response.strip() return '\n'.join(code_lines) def _update_test_plan(self, workspace, scenario_name: str, test_filename: str) -> None: """Update the test plan with the new test.""" test_plan_content = workspace.test_plan_file.read_text() # Add test to the generated tests section new_entry = f"- [x] {scenario_name} (`{test_filename}`)" if "### Generated Tests" in test_plan_content: # Add to existing generated tests section lines = test_plan_content.split('\n') for i, line in enumerate(lines): if line.strip() == "Tests generated for this workspace will be listed here as they are created.": lines[i] = new_entry break elif line.startswith("- [") and "Generated Tests" in lines[max(0, i-5):i]: lines.insert(i, new_entry) break else: # Add at the end of generated tests section for i, line in enumerate(lines): if "### Generated Tests" in line: # Find next section or end j = i + 1 while j < len(lines) and not lines[j].startswith('##'): j += 1 lines.insert(j, new_entry) break workspace.test_plan_file.write_text('\n'.join(lines)) def list_generated_tests(self) -> list: """List all generated tests for the current workspace.""" workspace = self.workspace_manager.get_current_workspace() if not workspace: return [] if not workspace.tests_dir.exists(): return [] return list(workspace.tests_dir.glob("*.py"))