feat: Add comprehensive test coverage assessment system
- Add CoverageAnalyzer class for analyzing functional test coverage against issues - Intelligent requirement extraction from issue descriptions using regex patterns - Automatic coverage gap detection with priority classification (critical/important/nice-to-have) - Smart keyword matching between requirements and existing tests - Comprehensive CLI interface with make test-coverage NUM=X command - Detailed recommendations with specific test suggestions and TDD workflow guidance Features: - Extracts requirements from issue text patterns (user can, must, should, examples, etc.) - Analyzes existing test files and methods for coverage keywords - Calculates coverage percentage based on requirement-to-test matching - Provides specific test name and file suggestions for gaps - Prioritizes recommendations by requirement criticality - Integrates with existing TDD workflow (tdd-start, tdd-add-test) Usage: make test-coverage NUM=5 Example output shows 28.6% coverage for Issue #5 with specific gap recommendations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
81
tddai_cli.py
81
tddai_cli.py
@@ -11,7 +11,7 @@ from pathlib import Path
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from tddai import (
|
||||
WorkspaceManager, IssueFetcher, TestGenerator,
|
||||
WorkspaceManager, IssueFetcher, TestGenerator, CoverageAnalyzer,
|
||||
WorkspaceStatus, TddaiError
|
||||
)
|
||||
|
||||
@@ -246,6 +246,80 @@ def list_open_issues():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def analyze_coverage(issue_number: int):
|
||||
"""Analyze test coverage for a specific issue."""
|
||||
try:
|
||||
analyzer = CoverageAnalyzer()
|
||||
print(f"🔍 Analyzing test coverage for Issue #{issue_number}")
|
||||
print("=" * 50)
|
||||
print()
|
||||
|
||||
assessment = analyzer.analyze_issue_coverage(issue_number)
|
||||
|
||||
print(f"📋 Issue: #{assessment.issue_number} - {assessment.issue_title}")
|
||||
print(f"📊 Coverage: {assessment.coverage_percentage:.1f}%")
|
||||
print()
|
||||
|
||||
# Show requirements analysis
|
||||
print("🎯 Identified Requirements:")
|
||||
if assessment.requirements:
|
||||
for req in assessment.requirements:
|
||||
priority_icon = {"critical": "🚨", "important": "⚠️", "nice-to-have": "💡"}
|
||||
icon = priority_icon.get(req.priority, "📝")
|
||||
print(f" {icon} [{req.priority.upper()}] {req.category}: {req.description}")
|
||||
else:
|
||||
print(" No specific requirements detected")
|
||||
print()
|
||||
|
||||
# Show existing tests
|
||||
print("🧪 Existing Test Coverage:")
|
||||
issue_related_tests = [t for t in assessment.existing_tests if t.related_issue == issue_number]
|
||||
if issue_related_tests:
|
||||
for test in issue_related_tests:
|
||||
test_count = len(test.test_methods)
|
||||
print(f" ✅ {test.file_path.name} ({test_count} test methods)")
|
||||
if test.test_methods:
|
||||
for method in test.test_methods[:3]: # Show first 3
|
||||
print(f" - {method}")
|
||||
if len(test.test_methods) > 3:
|
||||
print(f" - ... and {len(test.test_methods) - 3} more")
|
||||
else:
|
||||
print(" 📝 No tests specifically for this issue found")
|
||||
# Show general tests that might be relevant
|
||||
relevant_tests = [t for t in assessment.existing_tests
|
||||
if any(keyword in ' '.join(t.coverage_keywords)
|
||||
for req in assessment.requirements
|
||||
for keyword in req.keywords)]
|
||||
if relevant_tests:
|
||||
print(" 📋 Potentially relevant tests:")
|
||||
for test in relevant_tests[:3]:
|
||||
print(f" 📄 {test.file_path.name}")
|
||||
print()
|
||||
|
||||
# Show coverage gaps
|
||||
if assessment.coverage_gaps:
|
||||
print("❌ Coverage Gaps Found:")
|
||||
for gap in assessment.coverage_gaps:
|
||||
priority_icon = {"critical": "🚨", "important": "⚠️", "nice-to-have": "💡"}
|
||||
icon = priority_icon.get(gap.requirement.priority, "📝")
|
||||
print(f" {icon} Missing: {gap.requirement.description}")
|
||||
print(f" 💡 Suggested test: {gap.suggested_test_name}")
|
||||
print(f" 📄 Suggested file: {gap.suggested_test_file}")
|
||||
print()
|
||||
else:
|
||||
print("✅ No significant coverage gaps detected!")
|
||||
print()
|
||||
|
||||
# Show recommendations
|
||||
print("📝 Recommendations:")
|
||||
for recommendation in assessment.recommendations:
|
||||
print(f" {recommendation}")
|
||||
|
||||
except TddaiError as e:
|
||||
print(f"❌ Error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def show_issue(issue_number: int):
|
||||
"""Show detailed issue information."""
|
||||
try:
|
||||
@@ -301,6 +375,9 @@ def main():
|
||||
show_parser = subparsers.add_parser('show-issue', help='Show issue details')
|
||||
show_parser.add_argument('issue_number', type=int, help='Issue number')
|
||||
|
||||
coverage_parser = subparsers.add_parser('analyze-coverage', help='Analyze test coverage for issue')
|
||||
coverage_parser.add_argument('issue_number', type=int, help='Issue number')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.command:
|
||||
@@ -322,6 +399,8 @@ def main():
|
||||
list_open_issues()
|
||||
elif args.command == 'show-issue':
|
||||
show_issue(args.issue_number)
|
||||
elif args.command == 'analyze-coverage':
|
||||
analyze_coverage(args.issue_number)
|
||||
except KeyboardInterrupt:
|
||||
print("\n⚠️ Operation cancelled")
|
||||
sys.exit(1)
|
||||
|
||||
Reference in New Issue
Block a user