#!/usr/bin/env python3 """ Comprehensive test script for SamCloud Music download functionality. This script tests the download flow end-to-end with detailed logging. """ import os import sys import time import json import logging import requests import socketio import subprocess from urllib.parse import urljoin from typing import Dict, Any, Optional, List # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(sys.stdout), logging.FileHandler('test_download.log') ] ) logger = logging.getLogger('test_download') class DownloadTester: def __init__(self, base_url: str = 'http://localhost:6544'): self.base_url = base_url.rstrip('/') self.session = requests.Session() self.sio = socketio.Client() self.downloads = {} self._setup_socketio_handlers() def _make_request(self, method: str, endpoint: str, **kwargs) -> Optional[Dict]: """Make an HTTP request and return the JSON response.""" url = urljoin(f"{self.base_url}/", endpoint.lstrip('/')) try: response = self.session.request(method, url, **kwargs) response.raise_for_status() return response.json() except requests.RequestException as e: logger.error(f"Request failed: {e}") return None def _setup_socketio_handlers(self): """Define Socket.IO event handlers.""" @self.sio.event def connect(): logger.info("✅ Connected to SamCloud backend") @self.sio.event def disconnect(): logger.warning("❌ Disconnected from backend") @self.sio.on('download_progress') def download_progress(data): self.handle_download_update(data) @self.sio.on('update_status') def update_status(data): history = data.get('history') or data.get('downloads') or [] if isinstance(history, list): for item in history: self.handle_download_update(item) def connect_socketio(self): """Set up WebSocket connection for real-time updates.""" logger.info("Connecting to Socket.IO backend ...") try: self.sio.connect(self.base_url, transports=['websocket']) except Exception as e: logger.error(f"Failed to connect: {e}") return False return True def handle_download_update(self, data: Dict): """Handle download progress updates.""" logger.info(f"Download update: {json.dumps(data, indent=2)}") download_id = data.get('id') if download_id: if download_id not in self.downloads: self.downloads[download_id] = {} self.downloads[download_id].update(data) # Log important state changes status = data.get('status') if status in ['completed', 'failed']: logger.info(f"Download {status.upper()}: {download_id}") def test_download(self, url: str, download_type: str = 'track') -> bool: """Test downloading a track/album/playlist.""" logger.info(f"Starting download test for {url} (type: {download_type})") # Start the download payload = { 'url': url, 'type': download_type, 'name': 'Test ' + download_type.capitalize(), 'artist': 'Test Artist' } # Emit Socket.IO event to trigger download if not self.sio.connected: if not self.connect_socketio(): logger.error("Unable to establish Socket.IO connection") return False self.sio.emit('download_item', payload) download_id = url # use URL as identifier for tracking logger.info(f"Download event emitted for URL: {url}") # Wait for download to complete or timeout timeout = 300 # 5 minutes start_time = time.time() while time.time() - start_time < timeout: if download_id in self.downloads: status = self.downloads[download_id].get('status') progress = self.downloads[download_id].get('progress', 0) logger.info(f"Progress: {progress}% - Status: {status}") if status in ['completed', 'failed']: logger.info(f"Download {status} in {time.time() - start_time:.1f} seconds") return status == 'completed' time.sleep(1) logger.error("Download timed out") return False def cleanup(self): """Clean up resources.""" # SocketIO client disconnect handled try: self.sio.disconnect() except Exception: pass self.session.close() def main(): # Test configuration test_cases = [ {'url': 'https://open.spotify.com/track/11dFghVXANMlKm9XsVMjMG', 'type': 'track'}, # Test track # Add more test cases as needed ] # Initialize tester tester = DownloadTester() try: # Set up WebSocket for real-time updates tester.connect_socketio() time.sleep(2) # Give WebSocket time to connect # Run test cases results = [] for test in test_cases: logger.info(f"\n{'='*80}") logger.info(f"TESTING: {test['url']} ({test['type']})") logger.info(f"{'='*80}") success = tester.test_download(test['url'], test['type']) results.append({ 'url': test['url'], 'type': test['type'], 'success': success }) # Brief pause between tests time.sleep(2) # Print summary logger.info("\n" + "="*80) logger.info("TEST SUMMARY") logger.info("="*80) for i, result in enumerate(results, 1): status = "PASSED" if result['success'] else "FAILED" logger.info(f"{i}. {result['type'].upper()}: {result['url']} - {status}") # Check if all tests passed all_passed = all(r['success'] for r in results) logger.info("\nOVERALL RESULT: " + ("ALL TESTS PASSED" if all_passed else "SOME TESTS FAILED")) return 0 if all_passed else 1 except KeyboardInterrupt: logger.info("\nTest interrupted by user") return 1 except Exception as e: logger.exception("An error occurred during testing") return 1 finally: tester.cleanup() if __name__ == "__main__": sys.exit(main())