#!/usr/bin/env python3 """ Test runner for comprehensive API testing. This script runs all API-related tests and provides detailed reporting on test coverage and results. """ import unittest import sys import os from io import StringIO import json from datetime import datetime # Add paths for imports sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', 'src')) sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', 'src', 'server')) def run_api_tests(): """Run all API tests and generate comprehensive report.""" print("๐Ÿš€ Starting Aniworld API Test Suite") print("=" * 60) # Test discovery loader = unittest.TestLoader() start_dir = os.path.dirname(__file__) # Discover tests from different modules test_suites = [] # Unit tests try: from test_api_endpoints import ( TestAuthenticationEndpoints, TestConfigurationEndpoints, TestSeriesEndpoints, TestDownloadEndpoints, TestProcessManagementEndpoints, TestLoggingEndpoints, TestBackupEndpoints, TestDiagnosticsEndpoints, TestErrorHandling ) unit_test_classes = [ TestAuthenticationEndpoints, TestConfigurationEndpoints, TestSeriesEndpoints, TestDownloadEndpoints, TestProcessManagementEndpoints, TestLoggingEndpoints, TestBackupEndpoints, TestDiagnosticsEndpoints, TestErrorHandling ] print("โœ… Loaded unit test classes") for test_class in unit_test_classes: suite = loader.loadTestsFromTestCase(test_class) test_suites.append(('Unit Tests', test_class.__name__, suite)) except ImportError as e: print(f"โš ๏ธ Could not load unit test classes: {e}") # Integration tests try: integration_path = os.path.join(os.path.dirname(__file__), '..', '..', 'integration') integration_file = os.path.join(integration_path, 'test_api_integration.py') if os.path.exists(integration_file): sys.path.insert(0, integration_path) # Import dynamically to handle potential import errors gracefully import importlib.util spec = importlib.util.spec_from_file_location("test_api_integration", integration_file) if spec and spec.loader: test_api_integration = importlib.util.module_from_spec(spec) spec.loader.exec_module(test_api_integration) # Get test classes dynamically integration_test_classes = [] for name in dir(test_api_integration): obj = getattr(test_api_integration, name) if (isinstance(obj, type) and issubclass(obj, unittest.TestCase) and name.startswith('Test') and name != 'APIIntegrationTestBase'): integration_test_classes.append(obj) print(f"โœ… Loaded {len(integration_test_classes)} integration test classes") for test_class in integration_test_classes: suite = loader.loadTestsFromTestCase(test_class) test_suites.append(('Integration Tests', test_class.__name__, suite)) else: print("โš ๏ธ Could not create module spec for integration tests") else: print(f"โš ๏ธ Integration test file not found: {integration_file}") except ImportError as e: print(f"โš ๏ธ Could not load integration test classes: {e}") # Run tests and collect results total_results = { 'total_tests': 0, 'total_failures': 0, 'total_errors': 0, 'total_skipped': 0, 'suite_results': [] } print(f"\n๐Ÿงช Running {len(test_suites)} test suites...") print("-" * 60) for suite_type, suite_name, suite in test_suites: print(f"\n๐Ÿ“‹ {suite_type}: {suite_name}") # Capture output test_output = StringIO() runner = unittest.TextTestRunner( stream=test_output, verbosity=1, buffer=True ) # Run the test suite result = runner.run(suite) # Update totals total_results['total_tests'] += result.testsRun total_results['total_failures'] += len(result.failures) total_results['total_errors'] += len(result.errors) total_results['total_skipped'] += len(result.skipped) if hasattr(result, 'skipped') else 0 # Store suite result suite_result = { 'suite_type': suite_type, 'suite_name': suite_name, 'tests_run': result.testsRun, 'failures': len(result.failures), 'errors': len(result.errors), 'skipped': len(result.skipped) if hasattr(result, 'skipped') else 0, 'success_rate': ((result.testsRun - len(result.failures) - len(result.errors)) / result.testsRun * 100) if result.testsRun > 0 else 0, 'failure_details': [f"{test}: {traceback.split('AssertionError: ')[-1].split(chr(10))[0] if 'AssertionError:' in traceback else 'See details'}" for test, traceback in result.failures], 'error_details': [f"{test}: {traceback.split(chr(10))[-2] if len(traceback.split(chr(10))) > 1 else 'Unknown error'}" for test, traceback in result.errors] } total_results['suite_results'].append(suite_result) # Print immediate results status = "โœ…" if result.wasSuccessful() else "โŒ" print(f" {status} Tests: {result.testsRun}, Failures: {len(result.failures)}, Errors: {len(result.errors)}") if result.failures: print(" ๐Ÿ”ฅ Failures:") for test, _ in result.failures[:3]: # Show first 3 failures print(f" - {test}") if result.errors: print(" ๐Ÿ’ฅ Errors:") for test, _ in result.errors[:3]: # Show first 3 errors print(f" - {test}") # Generate comprehensive report print("\n" + "=" * 60) print("๐Ÿ“Š COMPREHENSIVE TEST REPORT") print("=" * 60) # Overall statistics print(f"๐Ÿ“ˆ OVERALL STATISTICS:") print(f" Total Tests Run: {total_results['total_tests']}") print(f" Total Failures: {total_results['total_failures']}") print(f" Total Errors: {total_results['total_errors']}") print(f" Total Skipped: {total_results['total_skipped']}") if total_results['total_tests'] > 0: overall_success_rate = ((total_results['total_tests'] - total_results['total_failures'] - total_results['total_errors']) / total_results['total_tests'] * 100) print(f" Overall Success Rate: {overall_success_rate:.1f}%") # Per-suite breakdown print(f"\n๐Ÿ“Š PER-SUITE BREAKDOWN:") for suite_result in total_results['suite_results']: status_icon = "โœ…" if suite_result['failures'] == 0 and suite_result['errors'] == 0 else "โŒ" print(f" {status_icon} {suite_result['suite_name']}") print(f" Tests: {suite_result['tests_run']}, Success Rate: {suite_result['success_rate']:.1f}%") if suite_result['failures'] > 0: print(f" Failures ({suite_result['failures']}):") for failure in suite_result['failure_details'][:2]: print(f" - {failure}") if suite_result['errors'] > 0: print(f" Errors ({suite_result['errors']}):") for error in suite_result['error_details'][:2]: print(f" - {error}") # API Coverage Report print(f"\n๐ŸŽฏ API ENDPOINT COVERAGE:") tested_endpoints = { 'Authentication': [ 'POST /api/auth/setup', 'POST /api/auth/login', 'POST /api/auth/logout', 'GET /api/auth/status' ], 'Configuration': [ 'POST /api/config/directory', 'GET /api/scheduler/config', 'POST /api/scheduler/config', 'GET /api/config/section/advanced', 'POST /api/config/section/advanced' ], 'Series Management': [ 'GET /api/series', 'POST /api/search', 'POST /api/rescan' ], 'Download Management': [ 'POST /api/download' ], 'System Status': [ 'GET /api/process/locks/status', 'GET /api/status' ], 'Logging': [ 'GET /api/logging/config', 'POST /api/logging/config', 'GET /api/logging/files', 'POST /api/logging/test', 'POST /api/logging/cleanup', 'GET /api/logging/files//tail' ], 'Backup Management': [ 'POST /api/config/backup', 'GET /api/config/backups', 'POST /api/config/backup//restore', 'GET /api/config/backup//download' ], 'Diagnostics': [ 'GET /api/diagnostics/network', 'GET /api/diagnostics/errors', 'POST /api/recovery/clear-blacklist', 'GET /api/recovery/retry-counts', 'GET /api/diagnostics/system-status' ] } total_endpoints = sum(len(endpoints) for endpoints in tested_endpoints.values()) for category, endpoints in tested_endpoints.items(): print(f" ๐Ÿ“‚ {category}: {len(endpoints)} endpoints") for endpoint in endpoints: print(f" โœ“ {endpoint}") print(f"\n ๐ŸŽฏ Total API Endpoints Covered: {total_endpoints}") # Recommendations print(f"\n๐Ÿ’ก RECOMMENDATIONS:") if total_results['total_failures'] > 0: print(" ๐Ÿ”ง Address test failures to improve code reliability") if total_results['total_errors'] > 0: print(" ๐Ÿ› ๏ธ Fix test errors - these often indicate setup/import issues") if overall_success_rate < 80: print(" โš ๏ธ Success rate below 80% - consider improving test coverage") elif overall_success_rate >= 95: print(" ๐ŸŽ‰ Excellent test success rate! Consider adding more edge cases") print(" ๐Ÿ“‹ Consider adding performance tests for API endpoints") print(" ๐Ÿ”’ Add security testing for authentication endpoints") print(" ๐Ÿ“ Add API documentation tests (OpenAPI/Swagger validation)") # Save detailed report to file timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") report_file = f"api_test_report_{timestamp}.json" try: report_data = { 'timestamp': datetime.now().isoformat(), 'summary': { 'total_tests': total_results['total_tests'], 'total_failures': total_results['total_failures'], 'total_errors': total_results['total_errors'], 'total_skipped': total_results['total_skipped'], 'overall_success_rate': overall_success_rate if total_results['total_tests'] > 0 else 0 }, 'suite_results': total_results['suite_results'], 'endpoint_coverage': tested_endpoints } with open(report_file, 'w', encoding='utf-8') as f: json.dump(report_data, f, indent=2, ensure_ascii=False) print(f"\n๐Ÿ’พ Detailed report saved to: {report_file}") except Exception as e: print(f"\nโš ๏ธ Could not save detailed report: {e}") # Final summary print("\n" + "=" * 60) if total_results['total_failures'] == 0 and total_results['total_errors'] == 0: print("๐ŸŽ‰ ALL TESTS PASSED! API is working correctly.") exit_code = 0 else: print("โŒ Some tests failed. Please review the issues above.") exit_code = 1 print(f"๐Ÿ Test run completed at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print("=" * 60) return exit_code if __name__ == '__main__': exit_code = run_api_tests() sys.exit(exit_code)