#!/usr/bin/env python3 """ Backend System Test Script Tests all backend components: - Database connections (MTG Online + MTG) - Redis connection and caching - Card database queries - API endpoints - Health checks """ import asyncio import sys from datetime import datetime # Add backend to path sys.path.insert(0, '/home/wall-o/projects/mtgonline/backend') from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy import text # Database URLs COCKATRICE_DB = "postgresql+asyncpg://mtgonline:mtgonline_pass@localhost:5432/mtgonline" MTG_DB = "postgresql+asyncpg://mtgonline:mtgonline_pass@localhost:5432/mtgdata" REDIS_URL = "redis://localhost:6379/0" # Test URLs - detect environment import socket try: # Try Docker network first socket.getaddrinfo('mtg_backend', 8000) API_BASE = 'http://mtg_backend:8000' except: # Fall back to host port API_BASE = 'http://localhost:8001' print(f"Using API base: {API_BASE}") results = { "tests_run": 0, "tests_passed": 0, "tests_failed": 0, "failures": [], "timestamp": datetime.now().isoformat() } async def test_connection(db_url: str, name: str) -> bool: """Test database connection.""" results["tests_run"] += 1 try: engine = create_async_engine(db_url, echo=False) async with engine.connect() as conn: result = await conn.execute(text("SELECT 1")) await engine.dispose() results["tests_passed"] += 1 print(f"✓ {name} connection: OK") return True except Exception as e: results["tests_failed"] += 1 results["failures"].append(f"{name} connection: {str(e)}") print(f"✗ {name} connection: FAILED - {str(e)}") return False async def test_redis() -> bool: """Test Redis connection.""" results["tests_run"] += 1 try: import redis.asyncio as aioredis client = aioredis.from_url(REDIS_URL, decode_responses=True) await client.ping() await client.close() results["tests_passed"] += 1 print("✓ Redis connection: OK") return True except Exception as e: results["tests_failed"] += 1 results["failures"].append(f"Redis connection: {str(e)}") print(f"✗ Redis connection: FAILED - {str(e)}") return False async def test_cache_operations() -> bool: """Test Redis cache operations.""" results["tests_run"] += 1 try: import redis.asyncio as aioredis client = aioredis.from_url(REDIS_URL, decode_responses=True) # Test set/get await client.set("test_key", "test_value", ex=60) value = await client.get("test_key") assert value == "test_value", f"Expected 'test_value', got '{value}'" # Test delete await client.delete("test_key") value = await client.get("test_key") assert value is None, f"Expected None after delete, got '{value}'" await client.close() results["tests_passed"] += 1 print("✓ Redis cache operations: OK") return True except Exception as e: results["tests_failed"] += 1 results["failures"].append(f"Redis cache operations: {str(e)}") print(f"✗ Redis cache operations: FAILED - {str(e)}") return False async def test_mtgonline_tables() -> bool: """Test MTG Online database tables.""" results["tests_run"] += 1 try: engine = create_async_engine(COCKATRICE_DB, echo=False) async with engine.connect() as conn: # Test users table result = await conn.execute(text("SELECT COUNT(*) FROM mtgonline_users")) user_count = result.scalar() # Test decks table result = await conn.execute(text("SELECT COUNT(*) FROM mtgonline_decklist_files")) deck_count = result.scalar() await engine.dispose() results["tests_passed"] += 1 print(f"✓ MTG Online tables: OK (users: {user_count}, decks: {deck_count})") return True except Exception as e: results["tests_failed"] += 1 results["failures"].append(f"MTG Online tables: {str(e)}") print(f"✗ MTG Online tables: FAILED - {str(e)}") return False async def test_mtg_tables() -> bool: """Test MTG database tables.""" results["tests_run"] += 1 try: engine = create_async_engine(MTG_DB, echo=False) async with engine.connect() as conn: # Test mtg_sets table result = await conn.execute(text("SELECT COUNT(*) FROM mtg_sets")) set_count = result.scalar() # Test mtg_cards table result = await conn.execute(text("SELECT COUNT(*) FROM mtg_cards")) card_count = result.scalar() await engine.dispose() results["tests_passed"] += 1 print(f"✓ MTG tables: OK (sets: {set_count}, cards: {card_count})") return True except Exception as e: results["tests_failed"] += 1 results["failures"].append(f"MTG tables: {str(e)}") print(f"✗ MTG tables: FAILED - {str(e)}") return False async def test_mtg_card_query() -> bool: """Test MTG card query functionality.""" results["tests_run"] += 1 try: engine = create_async_engine(MTG_DB, echo=False) async with engine.connect() as conn: # Search for a common card result = await conn.execute( text("SELECT name, type_line, rarity FROM mtg_cards WHERE name ILIKE '%Black Lotus%' LIMIT 1") ) row = result.fetchone() await engine.dispose() if row: results["tests_passed"] += 1 print(f"✓ MTG card query: OK - Found '{row[0]}' ({row[1]}, {row[2]})") return True else: results["tests_failed"] += 1 results["failures"].append("MTG card query: No results found") print("✗ MTG card query: FAILED - No results found") return False except Exception as e: results["tests_failed"] += 1 results["failures"].append(f"MTG card query: {str(e)}") print(f"✗ MTG card query: FAILED - {str(e)}") return False async def test_health_endpoint() -> bool: """Test backend health endpoint.""" results["tests_run"] += 1 try: import requests response = requests.get("http://localhost:8001/health", timeout=5) if response.status_code == 200: results["tests_passed"] += 1 print(f"✓ Health endpoint: OK ({response.json()})") return True else: results["tests_failed"] += 1 results["failures"].append(f"Health endpoint: Status {response.status_code}") print(f"✗ Health endpoint: FAILED - Status {response.status_code}") return False except Exception as e: results["tests_failed"] += 1 results["failures"].append(f"Health endpoint: {str(e)}") print(f"✗ Health endpoint: FAILED - {str(e)}") return False async def test_card_search_endpoint() -> bool: """Test card search API endpoint.""" results["tests_run"] += 1 try: import requests response = requests.get( "http://localhost:8001/api/v1/mtg/cards/search", params={"q": "Lightning Bolt", "limit": 5}, timeout=10 ) if response.status_code == 200: data = response.json() results["tests_passed"] += 1 print(f"✓ Card search endpoint: OK - Found {len(data.get('results', {}).get('results', []))} cards") return True else: results["tests_failed"] += 1 results["failures"].append(f"Card search endpoint: Status {response.status_code}") print(f"✗ Card search endpoint: FAILED - Status {response.status_code}") return False except Exception as e: results["tests_failed"] += 1 results["failures"].append(f"Card search endpoint: {str(e)}") print(f"✗ Card search endpoint: FAILED - {str(e)}") return False async def test_statistics_endpoint() -> bool: """Test card statistics API endpoint.""" results["tests_run"] += 1 try: import requests response = requests.get( "http://localhost:8001/api/v1/mtg/cards/statistics", timeout=10 ) if response.status_code == 200: data = response.json() results["tests_passed"] += 1 stats = data.get("results", {}).get("results", {}) print(f"✓ Statistics endpoint: OK - {stats.get('total_cards', 0)} cards, {stats.get('total_sets', 0)} sets") return True else: results["tests_failed"] += 1 results["failures"].append(f"Statistics endpoint: Status {response.status_code}") print(f"✗ Statistics endpoint: FAILED - Status {response.status_code}") return False except Exception as e: results["tests_failed"] += 1 results["failures"].append(f"Statistics endpoint: {str(e)}") print(f"✗ Statistics endpoint: FAILED - {str(e)}") return False async def test_auth_endpoint() -> bool: """Test authentication endpoint.""" results["tests_run"] += 1 try: import requests response = requests.post( "http://localhost:8001/api/v1/auth/register", json={ "username": "test_user", "email": "test@example.com", "password": "TestPass123!", "confirm_password": "TestPass123!" }, timeout=10 ) if response.status_code in [200, 409]: # 409 means user already exists results["tests_passed"] += 1 print(f"✓ Auth endpoint: OK - Status {response.status_code}") return True else: results["tests_failed"] += 1 results["failures"].append(f"Auth endpoint: Status {response.status_code}") print(f"✗ Auth endpoint: FAILED - Status {response.status_code}") return False except Exception as e: results["tests_failed"] += 1 results["failures"].append(f"Auth endpoint: {str(e)}") print(f"✗ Auth endpoint: FAILED - {str(e)}") return False async def run_all_tests(): """Run all system tests.""" print("=" * 60) print("BACKEND SYSTEM TEST") print("=" * 60) print(f"Started at: {datetime.now().isoformat()}") print("=" * 60) print() # Database connections print("Testing Database Connections:") print("-" * 60) await test_connection(COCKATRICE_DB, "MTG Online PostgreSQL") await test_connection(MTG_DB, "MTG PostgreSQL") await test_redis() print() # Cache operations print("Testing Cache Operations:") print("-" * 60) await test_cache_operations() print() # Database tables print("Testing Database Tables:") print("-" * 60) await test_mtgonline_tables() await test_mtg_tables() await test_mtg_card_query() print() # API endpoints print("Testing API Endpoints:") print("-" * 60) await test_health_endpoint() await test_card_search_endpoint() await test_statistics_endpoint() await test_auth_endpoint() print() # Summary print("=" * 60) print("TEST SUMMARY") print("=" * 60) print(f"Tests Run: {results['tests_run']}") print(f"Tests Passed: {results['tests_passed']}") print(f"Tests Failed: {results['tests_failed']}") print(f"Success Rate: {(results['tests_passed'] / results['tests_run'] * 100):.1f}%") print("=" * 60) if results["failures"]: print("\nFailures:") for failure in results["failures"]: print(f" • {failure}") print(f"\nCompleted at: {datetime.now().isoformat()}") print("=" * 60) return results["tests_failed"] == 0 if __name__ == "__main__": success = asyncio.run(run_all_tests()) sys.exit(0 if success else 1)