Spaces:
Sleeping
Sleeping
| """ | |
| AI Test Generator - Generates tests from requirements | |
| """ | |
| import os | |
| from typing import List, Optional, TYPE_CHECKING | |
| if TYPE_CHECKING: | |
| from src.types import PRDiff | |
| from src.types import Requirement, GeneratedTest | |
| from src.config import get_config, TestConfig | |
| from src.ai_client import get_ai_client, AIClient | |
| class TestGenerator: | |
| """Generates automated tests from requirements""" | |
| def __init__(self, ai_client: Optional[AIClient] = None, config: Optional[TestConfig] = None): | |
| self.config = config or get_config().tests | |
| self.ai = ai_client or get_ai_client() | |
| def generate_tests( | |
| self, | |
| requirements: List[Requirement], | |
| pr_diff: Optional["PRDiff"] = None | |
| ) -> List[GeneratedTest]: | |
| """Generate tests for each requirement""" | |
| tests = [] | |
| for requirement in requirements: | |
| if requirement.status.value == "PASS": | |
| # Generate test for passed requirements to verify | |
| test = self._generate_single_test(requirement) | |
| tests.append(test) | |
| else: | |
| # Generate test that SHOULD pass when requirement is implemented | |
| test = self._generate_single_test(requirement) | |
| test.error = "Requirement not yet implemented" | |
| tests.append(test) | |
| return tests | |
| def _generate_single_test(self, requirement: Requirement) -> GeneratedTest: | |
| """Generate a single test for a requirement""" | |
| system_prompt = f"""You are a test generation expert. Generate a {self.config.framework} test | |
| function that tests the given requirement. Return ONLY the test code, no explanations. | |
| The test should be complete and runnable.""" | |
| prompt = f"""Generate a test for this requirement: | |
| Requirement ID: {requirement.id} | |
| Text: {requirement.text} | |
| Category: {requirement.category} | |
| Priority: {requirement.priority} | |
| Acceptance Criteria: | |
| {chr(10).join(f"- {ac}" for ac in requirement.acceptance_criteria)} | |
| Generate a {self.config.framework} test function. Include: | |
| 1. Proper imports | |
| 2. Clear test name (test_{requirement.id.lower()}_{requirement.category}) | |
| 3. Assert statements for the acceptance criteria | |
| 4. Example test data if needed | |
| Return ONLY the test code, no markdown formatting.""" | |
| try: | |
| test_code = self.ai.complete(prompt, system_prompt) | |
| # Clean up the test code | |
| test_code = test_code.strip() | |
| if test_code.startswith("```python"): | |
| test_code = test_code[10:] | |
| if test_code.startswith("```"): | |
| test_code = test_code[3:] | |
| if test_code.endswith("```"): | |
| test_code = test_code[:-3] | |
| test_code = test_code.strip() | |
| return GeneratedTest( | |
| name=f"test_{requirement.id.lower()}_{requirement.category}", | |
| requirement_id=requirement.id, | |
| test_code=test_code, | |
| framework=self.config.framework, | |
| ) | |
| except Exception as e: | |
| return GeneratedTest( | |
| name=f"test_{requirement.id.lower()}_{requirement.category}", | |
| requirement_id=requirement.id, | |
| test_code=f"# Error generating test: {str(e)}", | |
| framework=self.config.framework, | |
| error=str(e), | |
| ) | |
| def save_tests(self, tests: List[GeneratedTest]) -> List[str]: | |
| """Save generated tests to files""" | |
| os.makedirs(self.config.output_dir, exist_ok=True) | |
| saved_files = [] | |
| # Group tests by requirement ID | |
| tests_by_req = {} | |
| for test in tests: | |
| if test.requirement_id not in tests_by_req: | |
| tests_by_req[test.requirement_id] = [] | |
| tests_by_req[test.requirement_id].append(test) | |
| # Save each group | |
| for req_id, req_tests in tests_by_req.items(): | |
| filename = os.path.join(self.config.output_dir, f"test_{req_id.lower()}.py") | |
| # Build test file content | |
| content = f'''""" | |
| Auto-generated tests for requirement {req_id} | |
| Generated by AI PR Reviewer | |
| """ | |
| import pytest | |
| ''' | |
| for test in req_tests: | |
| content += test.test_code + "\n\n" | |
| with open(filename, 'w') as f: | |
| f.write(content) | |
| saved_files.append(filename) | |
| return saved_files | |
| def run_tests(self, tests: List[GeneratedTest]) -> List[GeneratedTest]: | |
| """Run generated tests (if auto_run is enabled)""" | |
| if not self.config.auto_run: | |
| return tests | |
| # Save tests first | |
| self.save_tests(tests) | |
| # Try to run pytest | |
| try: | |
| import subprocess | |
| result = subprocess.run( | |
| ["pytest", self.config.output_dir, "-v"], | |
| capture_output=True, | |
| text=True, | |
| ) | |
| # Update test results | |
| for test in tests: | |
| test.passed = result.returncode == 0 | |
| if result.returncode != 0: | |
| test.error = result.stderr | |
| return tests | |
| except Exception as e: | |
| for test in tests: | |
| test.error = f"Failed to run tests: {str(e)}" | |
| return tests | |