aipr / src /test_generator.py
jarvisemitra's picture
Upload src/test_generator.py with huggingface_hub
a640428 verified
Raw
History Blame Contribute Delete
5.7 kB
"""
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