#!/usr/bin/env python3 """ Comprehensive API Testing Suite Tests all API endpoints with realistic test data """ import asyncio import httpx import json from typing import Dict, Any # Configuration API_URL = "http://localhost:8000" API_INTERNAL_KEY = "bot-internal-key-9f8e7d6c5b4a3210fedcba9876543210" class Colors: GREEN = '\033[92m' RED = '\033[91m' YELLOW = '\033[93m' BLUE = '\033[94m' RESET = '\033[0m' class APITester: def __init__(self): self.client = httpx.AsyncClient(timeout=30.0) self.test_results = [] self.auth_token = None self.test_user_id = None self.test_telegram_id = 999999999 self.test_username = "test_user" self.test_password = "Test123!@#" async def log(self, message: str, color: str = Colors.RESET): print(f"{color}{message}{Colors.RESET}") async def test_endpoint(self, name: str, method: str, url: str, expected_status: int = 200, **kwargs) -> Dict[str, Any]: """Test a single endpoint""" try: if method == "GET": response = await self.client.get(url, **kwargs) elif method == "POST": response = await self.client.post(url, **kwargs) elif method == "PATCH": response = await self.client.patch(url, **kwargs) elif method == "DELETE": response = await self.client.delete(url, **kwargs) else: raise ValueError(f"Unsupported method: {method}") success = response.status_code == expected_status result = { "name": name, "success": success, "status": response.status_code, "expected": expected_status } if success: await self.log(f"āœ… {name} - Status: {response.status_code}", Colors.GREEN) try: data = response.json() if data and not isinstance(data, dict): await self.log(f" Response: {str(data)[:100]}", Colors.BLUE) elif data: await self.log(f" Response: {json.dumps(data, indent=2)[:200]}...", Colors.BLUE) except: pass else: await self.log(f"āŒ {name} - Expected: {expected_status}, Got: {response.status_code}", Colors.RED) await self.log(f" Response: {response.text[:200]}", Colors.RED) self.test_results.append(result) return response except Exception as e: await self.log(f"āŒ {name} - Exception: {str(e)}", Colors.RED) self.test_results.append({ "name": name, "success": False, "error": str(e) }) return None async def setup_test_data(self): """Create test data in the database""" await self.log("\nšŸ”§ Setting up test data...", Colors.YELLOW) # Create internal API headers internal_headers = {"X-Internal-Key": API_INTERNAL_KEY} # Create a Telegram user await self.test_endpoint( "Create Telegram Player", "POST", f"{API_URL}/api/internal/player", params={"telegram_id": self.test_telegram_id, "name": "Test Telegram User"}, headers=internal_headers, expected_status=200 ) # Get the player to get their ID response = await self.test_endpoint( "Get Telegram Player by telegram_id", "GET", f"{API_URL}/api/internal/player/{self.test_telegram_id}", headers=internal_headers, expected_status=200 ) if response and response.status_code == 200: player_data = response.json() self.test_user_id = player_data.get('id') await self.log(f" Test user ID: {self.test_user_id}", Colors.BLUE) # Add some items to inventory await self.test_endpoint( "Add item to inventory (knife)", "POST", f"{API_URL}/api/internal/player/{self.test_user_id}/inventory", headers=internal_headers, json={"item_id": "knife", "quantity": 1}, expected_status=200 ) await self.test_endpoint( "Add item to inventory (water)", "POST", f"{API_URL}/api/internal/player/{self.test_user_id}/inventory", headers=internal_headers, json={"item_id": "water", "quantity": 3}, expected_status=200 ) # Create a dropped item await self.test_endpoint( "Create dropped item", "POST", f"{API_URL}/api/internal/dropped-items", headers=internal_headers, params={"item_id": "bandage", "quantity": 2, "location_id": "start_point"}, expected_status=200 ) # Create a wandering enemy await self.test_endpoint( "Spawn wandering enemy", "POST", f"{API_URL}/api/internal/wandering-enemies", headers=internal_headers, params={"npc_id": "mutant_rat", "location_id": "start_point", "current_hp": 30, "max_hp": 30}, expected_status=200 ) async def test_health_check(self): await self.log("\nšŸ“‹ Testing Health Check", Colors.YELLOW) await self.test_endpoint( "Health Check", "GET", f"{API_URL}/health" ) async def test_auth_endpoints(self): await self.log("\nšŸ” Testing Authentication Endpoints", Colors.YELLOW) # Register response = await self.test_endpoint( "Register Web User", "POST", f"{API_URL}/api/auth/register", json={ "username": self.test_username, "password": self.test_password, "name": "Test User" }, expected_status=200 ) # Login response = await self.test_endpoint( "Login Web User", "POST", f"{API_URL}/api/auth/login", json={ "username": self.test_username, "password": self.test_password }, expected_status=200 ) if response and response.status_code == 200: data = response.json() self.auth_token = data.get('access_token') await self.log(f" Auth token obtained", Colors.BLUE) # Get current user if self.auth_token: await self.test_endpoint( "Get Current User (Me)", "GET", f"{API_URL}/api/auth/me", headers={"Authorization": f"Bearer {self.auth_token}"} ) async def test_game_endpoints(self): if not self.auth_token: await self.log("\nāš ļø Skipping game endpoints (no auth token)", Colors.YELLOW) return await self.log("\nšŸŽ® Testing Game Endpoints", Colors.YELLOW) headers = {"Authorization": f"Bearer {self.auth_token}"} # Game state await self.test_endpoint( "Get Game State", "GET", f"{API_URL}/api/game/state", headers=headers ) # Profile await self.test_endpoint( "Get Player Profile", "GET", f"{API_URL}/api/game/profile", headers=headers ) # Location await self.test_endpoint( "Get Current Location", "GET", f"{API_URL}/api/game/location", headers=headers ) # Inventory await self.test_endpoint( "Get Inventory", "GET", f"{API_URL}/api/game/inventory", headers=headers ) # Move (should fail - need stamina/valid direction) await self.test_endpoint( "Move (expect failure)", "POST", f"{API_URL}/api/game/move", headers=headers, json={"direction": "north"}, expected_status=400 # Expect failure ) # Inspect await self.test_endpoint( "Inspect Area", "POST", f"{API_URL}/api/game/inspect", headers=headers ) async def test_internal_endpoints(self): await self.log("\nšŸ”§ Testing Internal Bot API Endpoints", Colors.YELLOW) internal_headers = {"X-Internal-Key": API_INTERNAL_KEY} if not self.test_user_id: await self.log(" No test user ID available", Colors.RED) return # Player operations await self.test_endpoint( "Get Player by ID", "GET", f"{API_URL}/api/internal/player/by_id/{self.test_user_id}", headers=internal_headers ) await self.test_endpoint( "Update Player", "PATCH", f"{API_URL}/api/internal/player/{self.test_user_id}", headers=internal_headers, json={"hp": 95} ) # Inventory operations await self.test_endpoint( "Get Player Inventory", "GET", f"{API_URL}/api/internal/player/{self.test_user_id}/inventory", headers=internal_headers ) # Movement await self.test_endpoint( "Move Player", "POST", f"{API_URL}/api/internal/player/{self.test_user_id}/move", headers=internal_headers, json={"location_id": "abandoned_house"} ) # Location queries await self.test_endpoint( "Get Dropped Items in Location", "GET", f"{API_URL}/api/internal/location/start_point/dropped-items", headers=internal_headers ) await self.test_endpoint( "Get Wandering Enemies in Location", "GET", f"{API_URL}/api/internal/location/start_point/wandering-enemies", headers=internal_headers ) # Combat operations await self.test_endpoint( "Get Combat State", "GET", f"{API_URL}/api/internal/player/{self.test_user_id}/combat", headers=internal_headers ) # Create combat combat_response = await self.test_endpoint( "Create Combat", "POST", f"{API_URL}/api/internal/combat/create", headers=internal_headers, json={ "player_id": self.test_user_id, "npc_id": "zombie", "npc_hp": 50, "npc_max_hp": 50, "location_id": "abandoned_house", "from_wandering": False } ) if combat_response and combat_response.status_code == 200: # Update combat await self.test_endpoint( "Update Combat", "PATCH", f"{API_URL}/api/internal/combat/{self.test_user_id}", headers=internal_headers, json={"npc_hp": 40, "turn": "npc"} ) # End combat await self.test_endpoint( "End Combat", "DELETE", f"{API_URL}/api/internal/combat/{self.test_user_id}", headers=internal_headers ) # Cooldown operations await self.test_endpoint( "Set Cooldown", "POST", f"{API_URL}/api/internal/cooldown/test_cooldown_key", headers=internal_headers, params={"duration_seconds": 300} ) await self.test_endpoint( "Get Cooldown", "GET", f"{API_URL}/api/internal/cooldown/test_cooldown_key", headers=internal_headers ) # Corpse operations await self.test_endpoint( "Create NPC Corpse", "POST", f"{API_URL}/api/internal/corpses/npc", headers=internal_headers, params={ "npc_id": "zombie", "location_id": "abandoned_house", "loot_remaining": json.dumps([{"item_id": "cloth", "quantity": 2}]) } ) await self.test_endpoint( "Get NPC Corpses in Location", "GET", f"{API_URL}/api/internal/location/abandoned_house/corpses/npc", headers=internal_headers ) # Status effects await self.test_endpoint( "Get Player Status Effects", "GET", f"{API_URL}/api/internal/player/{self.test_user_id}/status-effects", headers=internal_headers ) async def print_summary(self): await self.log("\n" + "="*60, Colors.BLUE) await self.log("šŸ“Š TEST SUMMARY", Colors.BLUE) await self.log("="*60, Colors.BLUE) total = len(self.test_results) passed = sum(1 for r in self.test_results if r.get('success', False)) failed = total - passed await self.log(f"\nTotal Tests: {total}", Colors.BLUE) await self.log(f"Passed: {passed}", Colors.GREEN) await self.log(f"Failed: {failed}", Colors.RED if failed > 0 else Colors.GREEN) await self.log(f"Success Rate: {(passed/total*100):.1f}%", Colors.GREEN if failed == 0 else Colors.YELLOW) if failed > 0: await self.log("\nāŒ Failed Tests:", Colors.RED) for result in self.test_results: if not result.get('success', False): await self.log(f" - {result['name']}", Colors.RED) if 'error' in result: await self.log(f" Error: {result['error']}", Colors.RED) elif 'status' in result: await self.log(f" Expected: {result['expected']}, Got: {result['status']}", Colors.RED) async def run_all_tests(self): await self.log("šŸš€ Starting API Test Suite", Colors.BLUE) await self.log("="*60, Colors.BLUE) try: await self.test_health_check() await self.setup_test_data() await self.test_auth_endpoints() await self.test_game_endpoints() await self.test_internal_endpoints() await self.print_summary() finally: await self.client.aclose() async def main(): tester = APITester() await tester.run_all_tests() if __name__ == "__main__": asyncio.run(main())