| |
| """ |
| Run All Tests β Comprehensive Test Suite |
| |
| Exécute tous les tests (représentatif + chatbot + E2E) et génère un rapport final. |
| """ |
|
|
| import subprocess |
| import sys |
| import json |
| import os |
| from pathlib import Path |
| from datetime import datetime |
|
|
|
|
| class TestRunner: |
| """Coordonne l'exΓ©cution de tous les tests.""" |
|
|
| def __init__(self): |
| self.results = [] |
| self.backend_dir = Path(__file__).parent |
| self.reports_dir = self.backend_dir / "reports" |
| self.reports_dir.mkdir(exist_ok=True) |
|
|
| def run_command(self, cmd: list, description: str) -> bool: |
| """Exécute une commande et retourne succès/échec.""" |
| print(f"\n{'='*70}") |
| print(f"π {description}") |
| print(f"{'='*70}") |
| print(f"Command: {' '.join(cmd)}") |
| print() |
| |
| try: |
| result = subprocess.run(cmd, cwd=self.backend_dir, timeout=300) |
| success = result.returncode == 0 |
| |
| if success: |
| print(f"\nβ
{description} β PASSED") |
| else: |
| print(f"\nβ {description} β FAILED (exit code: {result.returncode})") |
| |
| self.results.append({ |
| "test": description, |
| "passed": success, |
| "timestamp": datetime.now().isoformat() |
| }) |
| |
| return success |
| |
| except subprocess.TimeoutExpired: |
| print(f"\nβ {description} β TIMEOUT (>5min)") |
| self.results.append({ |
| "test": description, |
| "passed": False, |
| "error": "Timeout", |
| "timestamp": datetime.now().isoformat() |
| }) |
| return False |
| |
| except Exception as e: |
| print(f"\nβ {description} β ERROR: {e}") |
| self.results.append({ |
| "test": description, |
| "passed": False, |
| "error": str(e), |
| "timestamp": datetime.now().isoformat() |
| }) |
| return False |
|
|
| def run_all_tests(self): |
| """ExΓ©cute tous les tests.""" |
| print("\n" + "#"*70) |
| print("# AI TALENT FINDER β COMPREHENSIVE TEST SUITE") |
| print("#"*70) |
| print(f"Backend directory: {self.backend_dir}") |
| print(f"Reports directory: {self.reports_dir}") |
| print(f"Start time: {datetime.now().isoformat()}") |
| |
| |
| test1_passed = self.run_command( |
| [sys.executable, "run_representative_tests.py"], |
| "Test 1: Representative Tests (CV, Skills, Matching, Edge Cases)" |
| ) |
| |
| |
| if os.getenv("ANTHROPIC_API_KEY"): |
| test2_passed = self.run_command( |
| [sys.executable, "test_chatbot_recruiter_scenarios.py"], |
| "Test 2: Chatbot Quality Tests" |
| ) |
| else: |
| print("\nβ οΈ Test 2: Chatbot Quality Tests β SKIPPED (ANTHROPIC_API_KEY not set)") |
| self.results.append({ |
| "test": "Test 2: Chatbot Quality Tests", |
| "passed": None, |
| "skipped": True, |
| "reason": "ANTHROPIC_API_KEY not set", |
| "timestamp": datetime.now().isoformat() |
| }) |
| test2_passed = True |
| |
| |
| frontend_url = os.getenv("FRONTEND_URL") |
| if frontend_url: |
| test3_passed = self.run_command( |
| [sys.executable, "test_e2e_recruiter_flow.py"], |
| "Test 3: E2E Recruiter Flow Tests" |
| ) |
| else: |
| print("\nβ οΈ Test 3: E2E Recruiter Flow Tests β SKIPPED (FRONTEND_URL not set)") |
| self.results.append({ |
| "test": "Test 3: E2E Recruiter Flow Tests", |
| "passed": None, |
| "skipped": True, |
| "reason": "FRONTEND_URL not set", |
| "timestamp": datetime.now().isoformat() |
| }) |
| test3_passed = True |
| |
| |
| self.print_summary() |
| |
| |
| self.save_report() |
| |
| |
| all_passed = all(r.get("passed", True) for r in self.results if not r.get("skipped")) |
| return all_passed |
|
|
| def print_summary(self): |
| """Affiche un rΓ©sumΓ© des rΓ©sultats.""" |
| print("\n" + "="*70) |
| print("TEST EXECUTION SUMMARY") |
| print("="*70) |
| |
| total = len(self.results) |
| passed = sum(1 for r in self.results if r.get("passed", False)) |
| failed = sum(1 for r in self.results if r.get("passed", False) == False) |
| skipped = sum(1 for r in self.results if r.get("skipped", False)) |
| |
| print(f"\nπ Results:") |
| print(f" β
Passed: {passed}/{total}") |
| print(f" β Failed: {failed}/{total}") |
| print(f" βοΈ Skipped: {skipped}/{total}") |
| |
| print(f"\nπ Details:") |
| for result in self.results: |
| test_name = result.get("test", "unknown") |
| if result.get("skipped"): |
| reason = result.get("reason", "unknown") |
| print(f" βοΈ {test_name}") |
| print(f" Reason: {reason}") |
| elif result.get("passed"): |
| print(f" β
{test_name}") |
| else: |
| error = result.get("error", "unknown") |
| print(f" β {test_name}") |
| if error: |
| print(f" Error: {error}") |
| |
| print() |
|
|
| def save_report(self): |
| """Sauvegarde un rapport JSON complet.""" |
| report_path = self.reports_dir / "test_suite_final_report.json" |
| |
| final_report = { |
| "title": "AI Talent Finder β Comprehensive Test Suite", |
| "execution_date": datetime.now().isoformat(), |
| "backend_path": str(self.backend_dir), |
| "backend_env": { |
| "PYTHONPATH": os.getenv("PYTHONPATH", ""), |
| "VIRTUAL_ENV": os.getenv("VIRTUAL_ENV", ""), |
| "CONDA_DEFAULT_ENV": os.getenv("CONDA_DEFAULT_ENV", ""), |
| }, |
| "test_results": self.results, |
| "summary": { |
| "total_tests": len(self.results), |
| "passed": sum(1 for r in self.results if r.get("passed", False)), |
| "failed": sum(1 for r in self.results if r.get("passed", False) == False), |
| "skipped": sum(1 for r in self.results if r.get("skipped", False)), |
| } |
| } |
| |
| with open(report_path, "w") as f: |
| json.dump(final_report, f, indent=2) |
| |
| print(f"\nπ Full report saved to: {report_path}") |
| |
| |
| txt_path = self.reports_dir / "test_suite_final_report.txt" |
| with open(txt_path, "w") as f: |
| f.write("="*70 + "\n") |
| f.write("AI TALENT FINDER β COMPREHENSIVE TEST SUITE\n") |
| f.write("="*70 + "\n\n") |
| |
| f.write(f"Execution Date: {datetime.now().isoformat()}\n") |
| f.write(f"Backend Path: {self.backend_dir}\n\n") |
| |
| f.write("RESULTS\n") |
| f.write("-"*70 + "\n") |
| |
| for result in self.results: |
| test_name = result.get("test", "unknown") |
| timestamp = result.get("timestamp", "") |
| |
| if result.get("skipped"): |
| reason = result.get("reason", "unknown") |
| f.write(f"βοΈ {test_name}\n") |
| f.write(f" Status: SKIPPED\n") |
| f.write(f" Reason: {reason}\n") |
| elif result.get("passed"): |
| f.write(f"β
{test_name}\n") |
| f.write(f" Status: PASSED\n") |
| else: |
| error = result.get("error", "unknown") |
| f.write(f"β {test_name}\n") |
| f.write(f" Status: FAILED\n") |
| if error: |
| f.write(f" Error: {error}\n") |
| |
| if timestamp: |
| f.write(f" Timestamp: {timestamp}\n") |
| f.write("\n") |
| |
| f.write("="*70 + "\n") |
| f.write("SUMMARY\n") |
| f.write("-"*70 + "\n") |
| f.write(f"Total Tests: {len(self.results)}\n") |
| f.write(f"Passed: {sum(1 for r in self.results if r.get('passed', False))}\n") |
| f.write(f"Failed: {sum(1 for r in self.results if r.get('passed', False) == False)}\n") |
| f.write(f"Skipped: {sum(1 for r in self.results if r.get('skipped', False))}\n") |
| |
| print(f"π Text report saved to: {txt_path}") |
|
|
|
|
| def main(): |
| """Main entry point.""" |
| print(""" |
| |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| β β |
| β AI TALENT FINDER β COMPREHENSIVE TEST SUITE β |
| β β |
| β Tests: Representative | Chatbot | E2E β |
| β Reports: JSON + Text + Per-test artifacts β |
| β β |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| """) |
| |
| print("CONFIGURATION") |
| print("-"*70) |
| print(f"Python Version: {sys.version.split()[0]}") |
| print(f"Executable: {sys.executable}") |
| |
| api_key_set = bool(os.getenv("ANTHROPIC_API_KEY")) |
| frontend_url = os.getenv("FRONTEND_URL") |
| |
| print(f"ANTHROPIC_API_KEY: {'β
SET' if api_key_set else 'β NOT SET'} (chatbot tests: {'enabled' if api_key_set else 'skipped'})") |
| print(f"FRONTEND_URL: {'β
SET' if frontend_url else 'β NOT SET'} (E2E tests: {'enabled' if frontend_url else 'skipped'})") |
| print() |
| |
| runner = TestRunner() |
| success = runner.run_all_tests() |
| |
| print("\n" + "#"*70) |
| if success: |
| print("# β
ALL TESTS PASSED β SYSTEM READY FOR PRODUCTION") |
| else: |
| print("# β SOME TESTS FAILED β SEE REPORTS FOR DETAILS") |
| print("#"*70) |
| |
| return 0 if success else 1 |
|
|
|
|
| if __name__ == "__main__": |
| sys.exit(main()) |
|
|