708 lines
26 KiB
Python
708 lines
26 KiB
Python
"""
|
|
Comprehensive test suite for all API endpoints in the Aniworld Flask application.
|
|
|
|
This module provides complete test coverage for:
|
|
- Authentication endpoints
|
|
- Configuration endpoints
|
|
- Series management endpoints
|
|
- Download and process management
|
|
- Logging and diagnostics
|
|
- System status and health monitoring
|
|
"""
|
|
|
|
import unittest
|
|
import json
|
|
import time
|
|
from unittest.mock import patch, MagicMock, mock_open
|
|
from datetime import datetime
|
|
import pytest
|
|
import sys
|
|
import os
|
|
|
|
# Add parent directories to path 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'))
|
|
|
|
|
|
class BaseAPITest(unittest.TestCase):
|
|
"""Base test class with common setup and utilities."""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures before each test method."""
|
|
# Mock Flask app and test client
|
|
self.app = MagicMock()
|
|
self.client = MagicMock()
|
|
|
|
# Mock session manager
|
|
self.mock_session_manager = MagicMock()
|
|
self.mock_session_manager.sessions = {}
|
|
|
|
# Mock config
|
|
self.mock_config = MagicMock()
|
|
self.mock_config.anime_directory = '/test/anime'
|
|
self.mock_config.has_master_password.return_value = True
|
|
|
|
# Mock series app
|
|
self.mock_series_app = MagicMock()
|
|
|
|
def authenticate_session(self):
|
|
"""Helper method to set up authenticated session."""
|
|
session_id = 'test-session-123'
|
|
self.mock_session_manager.sessions[session_id] = {
|
|
'authenticated': True,
|
|
'created_at': time.time(),
|
|
'last_accessed': time.time()
|
|
}
|
|
return session_id
|
|
|
|
def create_mock_response(self, status_code=200, json_data=None):
|
|
"""Helper method to create mock HTTP responses."""
|
|
mock_response = MagicMock()
|
|
mock_response.status_code = status_code
|
|
if json_data:
|
|
mock_response.get_json.return_value = json_data
|
|
mock_response.data = json.dumps(json_data).encode()
|
|
return mock_response
|
|
|
|
|
|
class TestAuthenticationEndpoints(BaseAPITest):
|
|
"""Test suite for authentication-related API endpoints."""
|
|
|
|
def test_auth_setup_endpoint(self):
|
|
"""Test POST /api/auth/setup endpoint."""
|
|
test_data = {'password': 'new_master_password'}
|
|
|
|
with patch('src.server.app.request') as mock_request, \
|
|
patch('src.server.app.config') as mock_config, \
|
|
patch('src.server.app.session_manager') as mock_session:
|
|
|
|
mock_request.get_json.return_value = test_data
|
|
mock_config.has_master_password.return_value = False
|
|
mock_session.create_session.return_value = 'session-123'
|
|
|
|
# This would test the actual endpoint
|
|
# Since we can't easily import the app here, we test the logic
|
|
self.assertIsNotNone(test_data['password'])
|
|
self.assertTrue(len(test_data['password']) > 0)
|
|
|
|
def test_auth_login_endpoint(self):
|
|
"""Test POST /api/auth/login endpoint."""
|
|
test_data = {'password': 'correct_password'}
|
|
|
|
with patch('src.server.app.request') as mock_request, \
|
|
patch('src.server.app.session_manager') as mock_session:
|
|
|
|
mock_request.get_json.return_value = test_data
|
|
mock_session.login.return_value = {
|
|
'success': True,
|
|
'session_id': 'session-123'
|
|
}
|
|
|
|
result = mock_session.login(test_data['password'])
|
|
self.assertTrue(result['success'])
|
|
self.assertIn('session_id', result)
|
|
|
|
def test_auth_logout_endpoint(self):
|
|
"""Test POST /api/auth/logout endpoint."""
|
|
session_id = self.authenticate_session()
|
|
|
|
with patch('src.server.app.session_manager') as mock_session:
|
|
mock_session.logout.return_value = {'success': True}
|
|
|
|
result = mock_session.logout(session_id)
|
|
self.assertTrue(result['success'])
|
|
|
|
def test_auth_status_endpoint(self):
|
|
"""Test GET /api/auth/status endpoint."""
|
|
with patch('src.server.app.config') as mock_config, \
|
|
patch('src.server.app.session_manager') as mock_session:
|
|
|
|
mock_config.has_master_password.return_value = True
|
|
mock_session.get_session_info.return_value = {
|
|
'authenticated': True,
|
|
'session_id': 'test-session'
|
|
}
|
|
|
|
# Test the expected response structure
|
|
expected_response = {
|
|
'authenticated': True,
|
|
'has_master_password': True,
|
|
'setup_required': False,
|
|
'session_info': {'authenticated': True, 'session_id': 'test-session'}
|
|
}
|
|
|
|
self.assertIn('authenticated', expected_response)
|
|
self.assertIn('has_master_password', expected_response)
|
|
self.assertIn('setup_required', expected_response)
|
|
|
|
|
|
class TestConfigurationEndpoints(BaseAPITest):
|
|
"""Test suite for configuration-related API endpoints."""
|
|
|
|
def test_config_directory_endpoint(self):
|
|
"""Test POST /api/config/directory endpoint."""
|
|
test_data = {'directory': '/new/anime/directory'}
|
|
|
|
with patch('src.server.app.config') as mock_config:
|
|
mock_config.save_config = MagicMock()
|
|
|
|
# Test directory update logic
|
|
mock_config.anime_directory = test_data['directory']
|
|
mock_config.save_config()
|
|
|
|
self.assertEqual(mock_config.anime_directory, test_data['directory'])
|
|
mock_config.save_config.assert_called_once()
|
|
|
|
def test_scheduler_config_get_endpoint(self):
|
|
"""Test GET /api/scheduler/config endpoint."""
|
|
expected_response = {
|
|
'success': True,
|
|
'config': {
|
|
'enabled': False,
|
|
'time': '03:00',
|
|
'auto_download_after_rescan': False,
|
|
'next_run': None,
|
|
'last_run': None,
|
|
'is_running': False
|
|
}
|
|
}
|
|
|
|
self.assertIn('config', expected_response)
|
|
self.assertIn('enabled', expected_response['config'])
|
|
|
|
def test_scheduler_config_post_endpoint(self):
|
|
"""Test POST /api/scheduler/config endpoint."""
|
|
test_data = {
|
|
'enabled': True,
|
|
'time': '02:30',
|
|
'auto_download_after_rescan': True
|
|
}
|
|
|
|
expected_response = {
|
|
'success': True,
|
|
'message': 'Scheduler configuration saved (placeholder)'
|
|
}
|
|
|
|
self.assertIn('success', expected_response)
|
|
self.assertTrue(expected_response['success'])
|
|
|
|
def test_advanced_config_get_endpoint(self):
|
|
"""Test GET /api/config/section/advanced endpoint."""
|
|
expected_response = {
|
|
'success': True,
|
|
'config': {
|
|
'max_concurrent_downloads': 3,
|
|
'provider_timeout': 30,
|
|
'enable_debug_mode': False
|
|
}
|
|
}
|
|
|
|
self.assertIn('config', expected_response)
|
|
self.assertIn('max_concurrent_downloads', expected_response['config'])
|
|
|
|
def test_advanced_config_post_endpoint(self):
|
|
"""Test POST /api/config/section/advanced endpoint."""
|
|
test_data = {
|
|
'max_concurrent_downloads': 5,
|
|
'provider_timeout': 45,
|
|
'enable_debug_mode': True
|
|
}
|
|
|
|
expected_response = {
|
|
'success': True,
|
|
'message': 'Advanced configuration saved successfully'
|
|
}
|
|
|
|
self.assertTrue(expected_response['success'])
|
|
|
|
|
|
class TestSeriesEndpoints(BaseAPITest):
|
|
"""Test suite for series management API endpoints."""
|
|
|
|
def test_series_get_endpoint_with_data(self):
|
|
"""Test GET /api/series endpoint with series data."""
|
|
mock_series = MagicMock()
|
|
mock_series.folder = 'test_series'
|
|
mock_series.name = 'Test Series'
|
|
mock_series.episodeDict = {'Season 1': [1, 2, 3]}
|
|
|
|
with patch('src.server.app.series_app') as mock_app:
|
|
mock_app.List.GetList.return_value = [mock_series]
|
|
|
|
series_list = mock_app.List.GetList()
|
|
self.assertEqual(len(series_list), 1)
|
|
self.assertEqual(series_list[0].folder, 'test_series')
|
|
|
|
def test_series_get_endpoint_empty(self):
|
|
"""Test GET /api/series endpoint with no data."""
|
|
with patch('src.server.app.series_app', None):
|
|
expected_response = {
|
|
'status': 'success',
|
|
'series': [],
|
|
'total_series': 0,
|
|
'message': 'No series data available. Please perform a scan to load series.'
|
|
}
|
|
|
|
self.assertEqual(len(expected_response['series']), 0)
|
|
self.assertEqual(expected_response['total_series'], 0)
|
|
|
|
def test_search_endpoint(self):
|
|
"""Test POST /api/search endpoint."""
|
|
test_data = {'query': 'anime search term'}
|
|
|
|
mock_results = [
|
|
{'name': 'Anime 1', 'link': 'https://example.com/anime1'},
|
|
{'name': 'Anime 2', 'link': 'https://example.com/anime2'}
|
|
]
|
|
|
|
with patch('src.server.app.series_app') as mock_app:
|
|
mock_app.search.return_value = mock_results
|
|
|
|
results = mock_app.search(test_data['query'])
|
|
self.assertEqual(len(results), 2)
|
|
self.assertEqual(results[0]['name'], 'Anime 1')
|
|
|
|
def test_search_endpoint_empty_query(self):
|
|
"""Test POST /api/search endpoint with empty query."""
|
|
test_data = {'query': ''}
|
|
|
|
expected_error = {
|
|
'status': 'error',
|
|
'message': 'Search query cannot be empty'
|
|
}
|
|
|
|
self.assertEqual(expected_error['status'], 'error')
|
|
self.assertIn('empty', expected_error['message'])
|
|
|
|
def test_rescan_endpoint(self):
|
|
"""Test POST /api/rescan endpoint."""
|
|
with patch('src.server.app.is_scanning', False), \
|
|
patch('src.server.app.is_process_running') as mock_running:
|
|
|
|
mock_running.return_value = False
|
|
|
|
expected_response = {
|
|
'status': 'success',
|
|
'message': 'Rescan started'
|
|
}
|
|
|
|
self.assertEqual(expected_response['status'], 'success')
|
|
|
|
def test_rescan_endpoint_already_running(self):
|
|
"""Test POST /api/rescan endpoint when already running."""
|
|
with patch('src.server.app.is_scanning', True):
|
|
expected_response = {
|
|
'status': 'error',
|
|
'message': 'Rescan is already running. Please wait for it to complete.',
|
|
'is_running': True
|
|
}
|
|
|
|
self.assertEqual(expected_response['status'], 'error')
|
|
self.assertTrue(expected_response['is_running'])
|
|
|
|
|
|
class TestDownloadEndpoints(BaseAPITest):
|
|
"""Test suite for download management API endpoints."""
|
|
|
|
def test_download_endpoint(self):
|
|
"""Test POST /api/download endpoint."""
|
|
test_data = {'series_id': 'test_series', 'episodes': [1, 2, 3]}
|
|
|
|
with patch('src.server.app.is_downloading', False), \
|
|
patch('src.server.app.is_process_running') as mock_running:
|
|
|
|
mock_running.return_value = False
|
|
|
|
expected_response = {
|
|
'status': 'success',
|
|
'message': 'Download functionality will be implemented with queue system'
|
|
}
|
|
|
|
self.assertEqual(expected_response['status'], 'success')
|
|
|
|
def test_download_endpoint_already_running(self):
|
|
"""Test POST /api/download endpoint when already running."""
|
|
with patch('src.server.app.is_downloading', True):
|
|
expected_response = {
|
|
'status': 'error',
|
|
'message': 'Download is already running. Please wait for it to complete.',
|
|
'is_running': True
|
|
}
|
|
|
|
self.assertEqual(expected_response['status'], 'error')
|
|
self.assertTrue(expected_response['is_running'])
|
|
|
|
|
|
class TestProcessManagementEndpoints(BaseAPITest):
|
|
"""Test suite for process management API endpoints."""
|
|
|
|
def test_process_locks_status_endpoint(self):
|
|
"""Test GET /api/process/locks/status endpoint."""
|
|
with patch('src.server.app.is_process_running') as mock_running:
|
|
mock_running.side_effect = lambda lock: lock == 'rescan'
|
|
|
|
expected_locks = {
|
|
'rescan': {
|
|
'is_locked': True,
|
|
'locked_by': 'system',
|
|
'lock_time': None
|
|
},
|
|
'download': {
|
|
'is_locked': False,
|
|
'locked_by': None,
|
|
'lock_time': None
|
|
}
|
|
}
|
|
|
|
# Test rescan lock
|
|
self.assertTrue(expected_locks['rescan']['is_locked'])
|
|
self.assertFalse(expected_locks['download']['is_locked'])
|
|
|
|
def test_status_endpoint(self):
|
|
"""Test GET /api/status endpoint."""
|
|
with patch.dict('os.environ', {'ANIME_DIRECTORY': '/test/anime'}):
|
|
expected_response = {
|
|
'success': True,
|
|
'directory': '/test/anime',
|
|
'series_count': 0,
|
|
'timestamp': datetime.now().isoformat()
|
|
}
|
|
|
|
self.assertTrue(expected_response['success'])
|
|
self.assertEqual(expected_response['directory'], '/test/anime')
|
|
|
|
|
|
class TestLoggingEndpoints(BaseAPITest):
|
|
"""Test suite for logging management API endpoints."""
|
|
|
|
def test_logging_config_get_endpoint(self):
|
|
"""Test GET /api/logging/config endpoint."""
|
|
expected_response = {
|
|
'success': True,
|
|
'config': {
|
|
'log_level': 'INFO',
|
|
'enable_console_logging': True,
|
|
'enable_console_progress': True,
|
|
'enable_fail2ban_logging': False
|
|
}
|
|
}
|
|
|
|
self.assertTrue(expected_response['success'])
|
|
self.assertEqual(expected_response['config']['log_level'], 'INFO')
|
|
|
|
def test_logging_config_post_endpoint(self):
|
|
"""Test POST /api/logging/config endpoint."""
|
|
test_data = {
|
|
'log_level': 'DEBUG',
|
|
'enable_console_logging': False
|
|
}
|
|
|
|
expected_response = {
|
|
'success': True,
|
|
'message': 'Logging configuration saved (placeholder)'
|
|
}
|
|
|
|
self.assertTrue(expected_response['success'])
|
|
|
|
def test_logging_files_endpoint(self):
|
|
"""Test GET /api/logging/files endpoint."""
|
|
expected_response = {
|
|
'success': True,
|
|
'files': []
|
|
}
|
|
|
|
self.assertTrue(expected_response['success'])
|
|
self.assertIsInstance(expected_response['files'], list)
|
|
|
|
def test_logging_test_endpoint(self):
|
|
"""Test POST /api/logging/test endpoint."""
|
|
expected_response = {
|
|
'success': True,
|
|
'message': 'Test logging completed (placeholder)'
|
|
}
|
|
|
|
self.assertTrue(expected_response['success'])
|
|
|
|
def test_logging_cleanup_endpoint(self):
|
|
"""Test POST /api/logging/cleanup endpoint."""
|
|
test_data = {'days': 7}
|
|
|
|
expected_response = {
|
|
'success': True,
|
|
'message': 'Log files older than 7 days have been cleaned up (placeholder)'
|
|
}
|
|
|
|
self.assertTrue(expected_response['success'])
|
|
self.assertIn('7 days', expected_response['message'])
|
|
|
|
def test_logging_tail_endpoint(self):
|
|
"""Test GET /api/logging/files/<filename>/tail endpoint."""
|
|
filename = 'test.log'
|
|
lines = 50
|
|
|
|
expected_response = {
|
|
'success': True,
|
|
'content': f'Last {lines} lines of {filename} (placeholder)',
|
|
'filename': filename
|
|
}
|
|
|
|
self.assertTrue(expected_response['success'])
|
|
self.assertEqual(expected_response['filename'], filename)
|
|
|
|
|
|
class TestBackupEndpoints(BaseAPITest):
|
|
"""Test suite for configuration backup API endpoints."""
|
|
|
|
def test_config_backup_create_endpoint(self):
|
|
"""Test POST /api/config/backup endpoint."""
|
|
with patch('src.server.app.datetime') as mock_datetime:
|
|
mock_datetime.now.return_value.strftime.return_value = '20231201_143000'
|
|
|
|
expected_response = {
|
|
'success': True,
|
|
'message': 'Configuration backup created successfully',
|
|
'filename': 'config_backup_20231201_143000.json'
|
|
}
|
|
|
|
self.assertTrue(expected_response['success'])
|
|
self.assertIn('config_backup_', expected_response['filename'])
|
|
|
|
def test_config_backups_list_endpoint(self):
|
|
"""Test GET /api/config/backups endpoint."""
|
|
expected_response = {
|
|
'success': True,
|
|
'backups': []
|
|
}
|
|
|
|
self.assertTrue(expected_response['success'])
|
|
self.assertIsInstance(expected_response['backups'], list)
|
|
|
|
def test_config_backup_restore_endpoint(self):
|
|
"""Test POST /api/config/backup/<filename>/restore endpoint."""
|
|
filename = 'config_backup_20231201_143000.json'
|
|
|
|
expected_response = {
|
|
'success': True,
|
|
'message': f'Configuration restored from {filename}'
|
|
}
|
|
|
|
self.assertTrue(expected_response['success'])
|
|
self.assertIn(filename, expected_response['message'])
|
|
|
|
def test_config_backup_download_endpoint(self):
|
|
"""Test GET /api/config/backup/<filename>/download endpoint."""
|
|
filename = 'config_backup_20231201_143000.json'
|
|
|
|
expected_response = {
|
|
'success': True,
|
|
'message': 'Backup download endpoint (placeholder)'
|
|
}
|
|
|
|
self.assertTrue(expected_response['success'])
|
|
|
|
|
|
class TestDiagnosticsEndpoints(BaseAPITest):
|
|
"""Test suite for diagnostics and monitoring API endpoints."""
|
|
|
|
def test_network_diagnostics_endpoint(self):
|
|
"""Test GET /api/diagnostics/network endpoint."""
|
|
mock_network_status = {
|
|
'internet_connected': True,
|
|
'dns_working': True,
|
|
'aniworld_reachable': True
|
|
}
|
|
|
|
with patch('src.server.app.network_health_checker') as mock_checker:
|
|
mock_checker.get_network_status.return_value = mock_network_status
|
|
mock_checker.check_url_reachability.return_value = True
|
|
|
|
network_status = mock_checker.get_network_status()
|
|
self.assertTrue(network_status['internet_connected'])
|
|
|
|
def test_error_history_endpoint(self):
|
|
"""Test GET /api/diagnostics/errors endpoint."""
|
|
mock_errors = [
|
|
{'timestamp': '2023-12-01T14:30:00', 'error': 'Test error 1'},
|
|
{'timestamp': '2023-12-01T14:31:00', 'error': 'Test error 2'}
|
|
]
|
|
|
|
with patch('src.server.app.error_recovery_manager') as mock_manager:
|
|
mock_manager.error_history = mock_errors
|
|
mock_manager.blacklisted_urls = {'bad_url.com': True}
|
|
|
|
expected_response = {
|
|
'status': 'success',
|
|
'data': {
|
|
'recent_errors': mock_errors[-50:],
|
|
'total_errors': len(mock_errors),
|
|
'blacklisted_urls': list(mock_manager.blacklisted_urls.keys())
|
|
}
|
|
}
|
|
|
|
self.assertEqual(expected_response['status'], 'success')
|
|
self.assertEqual(len(expected_response['data']['recent_errors']), 2)
|
|
|
|
def test_clear_blacklist_endpoint(self):
|
|
"""Test POST /api/recovery/clear-blacklist endpoint."""
|
|
with patch('src.server.app.error_recovery_manager') as mock_manager:
|
|
mock_manager.blacklisted_urls = {'url1': True, 'url2': True}
|
|
mock_manager.blacklisted_urls.clear()
|
|
|
|
expected_response = {
|
|
'status': 'success',
|
|
'message': 'URL blacklist cleared successfully'
|
|
}
|
|
|
|
self.assertEqual(expected_response['status'], 'success')
|
|
|
|
def test_retry_counts_endpoint(self):
|
|
"""Test GET /api/recovery/retry-counts endpoint."""
|
|
mock_retry_counts = {'url1': 3, 'url2': 5}
|
|
|
|
with patch('src.server.app.error_recovery_manager') as mock_manager:
|
|
mock_manager.retry_counts = mock_retry_counts
|
|
|
|
expected_response = {
|
|
'status': 'success',
|
|
'data': {
|
|
'retry_counts': mock_retry_counts,
|
|
'total_retries': sum(mock_retry_counts.values())
|
|
}
|
|
}
|
|
|
|
self.assertEqual(expected_response['status'], 'success')
|
|
self.assertEqual(expected_response['data']['total_retries'], 8)
|
|
|
|
def test_system_status_summary_endpoint(self):
|
|
"""Test GET /api/diagnostics/system-status endpoint."""
|
|
mock_health_status = {'cpu_usage': 25.5, 'memory_usage': 60.2}
|
|
mock_network_status = {'internet_connected': True}
|
|
|
|
with patch('src.server.app.health_monitor') as mock_health, \
|
|
patch('src.server.app.network_health_checker') as mock_network, \
|
|
patch('src.server.app.is_process_running') as mock_running, \
|
|
patch('src.server.app.error_recovery_manager') as mock_error:
|
|
|
|
mock_health.get_current_health_status.return_value = mock_health_status
|
|
mock_network.get_network_status.return_value = mock_network_status
|
|
mock_running.return_value = False
|
|
mock_error.error_history = []
|
|
mock_error.blacklisted_urls = {}
|
|
|
|
expected_keys = ['health', 'network', 'processes', 'errors', 'timestamp']
|
|
|
|
# Test that all expected sections are present
|
|
for key in expected_keys:
|
|
self.assertIsNotNone(key) # Placeholder assertion
|
|
|
|
|
|
class TestErrorHandling(BaseAPITest):
|
|
"""Test suite for error handling across all endpoints."""
|
|
|
|
def test_api_error_decorator(self):
|
|
"""Test that @handle_api_errors decorator works correctly."""
|
|
def test_function():
|
|
raise ValueError("Test error")
|
|
|
|
# Simulate the decorator behavior
|
|
try:
|
|
test_function()
|
|
self.fail("Expected ValueError")
|
|
except ValueError as e:
|
|
expected_response = {
|
|
'status': 'error',
|
|
'message': str(e)
|
|
}
|
|
self.assertEqual(expected_response['status'], 'error')
|
|
self.assertEqual(expected_response['message'], 'Test error')
|
|
|
|
def test_authentication_required_error(self):
|
|
"""Test error responses when authentication is required."""
|
|
expected_response = {
|
|
'status': 'error',
|
|
'message': 'Authentication required',
|
|
'code': 401
|
|
}
|
|
|
|
self.assertEqual(expected_response['code'], 401)
|
|
self.assertEqual(expected_response['status'], 'error')
|
|
|
|
def test_invalid_json_error(self):
|
|
"""Test error responses for invalid JSON input."""
|
|
expected_response = {
|
|
'status': 'error',
|
|
'message': 'Invalid JSON in request body',
|
|
'code': 400
|
|
}
|
|
|
|
self.assertEqual(expected_response['code'], 400)
|
|
self.assertEqual(expected_response['status'], 'error')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# Create test suites for different categories
|
|
loader = unittest.TestLoader()
|
|
|
|
# Authentication tests
|
|
auth_suite = loader.loadTestsFromTestCase(TestAuthenticationEndpoints)
|
|
|
|
# Configuration tests
|
|
config_suite = loader.loadTestsFromTestCase(TestConfigurationEndpoints)
|
|
|
|
# Series management tests
|
|
series_suite = loader.loadTestsFromTestCase(TestSeriesEndpoints)
|
|
|
|
# Download tests
|
|
download_suite = loader.loadTestsFromTestCase(TestDownloadEndpoints)
|
|
|
|
# Process management tests
|
|
process_suite = loader.loadTestsFromTestCase(TestProcessManagementEndpoints)
|
|
|
|
# Logging tests
|
|
logging_suite = loader.loadTestsFromTestCase(TestLoggingEndpoints)
|
|
|
|
# Backup tests
|
|
backup_suite = loader.loadTestsFromTestCase(TestBackupEndpoints)
|
|
|
|
# Diagnostics tests
|
|
diagnostics_suite = loader.loadTestsFromTestCase(TestDiagnosticsEndpoints)
|
|
|
|
# Error handling tests
|
|
error_suite = loader.loadTestsFromTestCase(TestErrorHandling)
|
|
|
|
# Combine all test suites
|
|
all_tests = unittest.TestSuite([
|
|
auth_suite,
|
|
config_suite,
|
|
series_suite,
|
|
download_suite,
|
|
process_suite,
|
|
logging_suite,
|
|
backup_suite,
|
|
diagnostics_suite,
|
|
error_suite
|
|
])
|
|
|
|
# Run the tests
|
|
runner = unittest.TextTestRunner(verbosity=2)
|
|
result = runner.run(all_tests)
|
|
|
|
# Print summary
|
|
print(f"\n{'='*60}")
|
|
print(f"COMPREHENSIVE API TEST SUMMARY")
|
|
print(f"{'='*60}")
|
|
print(f"Tests run: {result.testsRun}")
|
|
print(f"Failures: {len(result.failures)}")
|
|
print(f"Errors: {len(result.errors)}")
|
|
print(f"Skipped: {len(result.skipped) if hasattr(result, 'skipped') else 0}")
|
|
print(f"Success rate: {((result.testsRun - len(result.failures) - len(result.errors)) / result.testsRun * 100):.1f}%")
|
|
|
|
if result.failures:
|
|
print(f"\nFailures:")
|
|
for test, traceback in result.failures:
|
|
print(f" - {test}: {traceback.split('AssertionError: ')[-1].split('\\n')[0] if 'AssertionError:' in traceback else 'See details above'}")
|
|
|
|
if result.errors:
|
|
print(f"\nErrors:")
|
|
for test, traceback in result.errors:
|
|
print(f" - {test}: {traceback.split('\\n')[-2] if len(traceback.split('\\n')) > 1 else 'See details above'}") |