""" Test index generator - creates browsable view of available tests. """ from pathlib import Path from typing import Dict, List import ast def parse_test_file(file_path: Path) -> Dict: """Parse a test file and extract test methods with docstrings.""" try: with open(file_path, 'r') as f: tree = ast.parse(f.read()) module_doc = ast.get_docstring(tree) classes = [] for node in ast.walk(tree): if isinstance(node, ast.ClassDef): class_doc = ast.get_docstring(node) methods = [] for item in node.body: if isinstance(item, ast.FunctionDef) and item.name.startswith('test_'): method_doc = ast.get_docstring(item) methods.append({ 'name': item.name, 'doc': method_doc or "No description" }) if methods: # Only include classes with test methods classes.append({ 'name': node.name, 'doc': class_doc or "No description", 'methods': methods }) return { 'file': file_path.name, 'module_doc': module_doc or "No module description", 'classes': classes } except Exception as e: return { 'file': file_path.name, 'error': str(e) } def build_test_index(tests_dir: Path) -> Dict: """ Build a hierarchical index of all tests. Returns structure: { 'mascotas': { 'test_pet_owners.py': {...}, 'test_pets.py': {...} }, 'productos': {...}, ... } """ index = {} # Find all domain directories (mascotas, productos, etc.) for domain_dir in tests_dir.iterdir(): if not domain_dir.is_dir(): continue if domain_dir.name.startswith('_'): continue domain_tests = {} # Find all test_*.py files in domain for test_file in domain_dir.glob('test_*.py'): test_info = parse_test_file(test_file) domain_tests[test_file.name] = test_info if domain_tests: # Only include domains with tests index[domain_dir.name] = domain_tests return index def generate_markdown_index(index: Dict) -> str: """Generate markdown representation of test index.""" lines = ["# Contract Tests Index\n"] for domain, files in sorted(index.items()): lines.append(f"## {domain.capitalize()}\n") for filename, file_info in sorted(files.items()): if 'error' in file_info: lines.append(f"### {filename} ⚠️ Parse Error") lines.append(f"```\n{file_info['error']}\n```\n") continue lines.append(f"### {filename}") lines.append(f"{file_info['module_doc']}\n") for cls in file_info['classes']: lines.append(f"#### {cls['name']}") lines.append(f"*{cls['doc']}*\n") for method in cls['methods']: # Extract first line of docstring first_line = method['doc'].split('\n')[0].strip() lines.append(f"- `{method['name']}` - {first_line}") lines.append("") lines.append("") return "\n".join(lines) def generate_html_index(index: Dict) -> str: """Generate HTML representation of test index.""" html = [''] html.append('') html.append('Contract Tests Index') html.append('') html.append('

Contract Tests Index

') html.append(f'

Total domains: {len(index)}

') for domain, files in sorted(index.items()): test_count = sum(len(f.get('classes', [])) for f in files.values()) html.append(f'

{domain.capitalize()} {test_count} test classes

') for filename, file_info in sorted(files.items()): if 'error' in file_info: html.append(f'

{filename} ⚠️

') html.append(f'
Parse Error: {file_info["error"]}
') continue html.append(f'

{filename}

') html.append(f'
{file_info["module_doc"]}
') for cls in file_info['classes']: html.append(f'

{cls["name"]}

') html.append(f'
{cls["doc"]}
') for method in cls['methods']: first_line = method['doc'].split('\n')[0].strip() html.append(f'
') html.append(f'{method["name"]}') html.append(f'{first_line}') html.append('
') html.append('') return '\n'.join(html) if __name__ == '__main__': # CLI usage import sys tests_dir = Path(__file__).parent / 'tests' index = build_test_index(tests_dir) if '--html' in sys.argv: print(generate_html_index(index)) else: print(generate_markdown_index(index))