#!/usr/bin/env python3 """ Tests for NPC filtering functionality in Python clients """ import pytest from typing import Dict, Any import asyncio from gumyum_npc_client import GumYumClient as AsyncClient from gumyum_npc_sync import GumYumClient as SyncClient from gumyum_npc_client import NPC # Mark entire module as integration tests pytestmark = pytest.mark.integration class TestAsyncFiltering: """Test async client filtering functionality""" @pytest.mark.asyncio async def test_spawn_filtered_method_exists( self, authenticated_async_client: AsyncClient ): """Test that spawn_filtered method exists and is callable""" assert hasattr(authenticated_async_client.npcs, "spawn_filtered") assert callable(authenticated_async_client.npc.spawn_filtered) @pytest.mark.asyncio async def test_spawn_filtered_no_filters( self, authenticated_async_client: AsyncClient, test_universe_id: str ): """Test spawn_filtered with no filters""" result = await authenticated_async_client.npc.spawn_filtered( universe_id=test_universe_id, world_seed=12345, filters={} ) assert result is not None assert isinstance(result, NPC) assert result.npc_id > 0 assert result.name assert result.profession @pytest.mark.asyncio async def test_spawn_filtered_profession_filter( self, authenticated_async_client: AsyncClient, test_universe_id: str ): """Test spawn_filtered with profession filter""" filters = {"profession": ["warrior", "knight", "guard", "soldier"]} result = await authenticated_async_client.npc.spawn_filtered( universe_id=test_universe_id, world_seed=54321, filters=filters, max_attempts=500, ) if result: assert result.profession in filters["profession"] assert isinstance(result, NPC) @pytest.mark.asyncio async def test_spawn_filtered_personality_filter( self, authenticated_async_client: AsyncClient, test_universe_id: str ): """Test spawn_filtered with personality type filter""" filters = {"personality_type": [1, 8, 9]} # Specific personality types result = await authenticated_async_client.npc.spawn_filtered( universe_id=test_universe_id, world_seed=99999, filters=filters, max_attempts=300, ) if result: assert result.personality_type in filters["personality_type"] @pytest.mark.asyncio async def test_spawn_filtered_complex_filters( self, authenticated_async_client: AsyncClient, test_universe_id: str ): """Test spawn_filtered with multiple complex filters""" filters = { "profession": ["warrior", "knight", "guard"], "personality_type": [1, 8], "stress_level": {"min": 0, "max": 5}, } result = await authenticated_async_client.npc.spawn_filtered( universe_id=test_universe_id, world_seed=111111, filters=filters, start_npc_id=2000000000000000000, # Large 64-bit ID max_attempts=1000, ) if result: assert result.profession in filters["profession"] assert result.personality_type in filters["personality_type"] # Note: NPC doesn't include stress_level, # but the API filtering would have ensured it matches @pytest.mark.asyncio async def test_spawn_filtered_large_npc_ids( self, authenticated_async_client: AsyncClient, test_universe_id: str ): """Test spawn_filtered with large 64-bit NPC IDs""" large_start_id = 7000000000000000000 # 7 quintillion result = await authenticated_async_client.npc.spawn_filtered( universe_id=test_universe_id, world_seed=12345, filters={}, # No filters, just test large IDs start_npc_id=large_start_id, max_attempts=10, ) if result: assert result.npc_id >= large_start_id @pytest.mark.asyncio async def test_spawn_filtered_impossible_filter( self, authenticated_async_client: AsyncClient, test_universe_id: str ): """Test spawn_filtered with impossible filter returns None""" filters = { "profession": ["nonexistent_profession_12345"], "personality_type": [99], # Invalid type } result = await authenticated_async_client.npc.spawn_filtered( universe_id=test_universe_id, world_seed=12345, filters=filters, max_attempts=20, # Small number for quick test ) # Should return None when no match found assert result is None @pytest.mark.asyncio async def test_spawn_filtered_parameter_validation( self, authenticated_async_client: AsyncClient, test_universe_id: str ): """Test spawn_filtered parameter validation""" # Test with various parameter combinations result = await authenticated_async_client.npc.spawn_filtered( universe_id=test_universe_id, world_seed=12345, filters={"personality_type": [5]}, start_npc_id=1, max_attempts=100, ) # Should work with all parameters specified assert result is None or isinstance(result, NPC) class TestSyncFiltering: """Test sync client filtering functionality""" def test_spawn_filtered_method_exists(self, authenticated_sync_client: SyncClient): """Test that spawn_filtered method exists and is callable""" assert hasattr(authenticated_sync_client.npcs, "spawn_filtered") assert callable(authenticated_sync_client.npc.spawn_filtered) def test_spawn_filtered_no_filters( self, authenticated_sync_client: SyncClient, sync_test_universe_id: str ): """Test spawn_filtered with no filters""" result = authenticated_sync_client.npc.spawn_filtered( universe_id=sync_test_universe_id, world_seed=12345, filters={} ) assert result is not None assert isinstance(result, NPC) assert result.npc_id > 0 assert result.name assert result.profession def test_spawn_filtered_profession_filter( self, authenticated_sync_client: SyncClient, sync_test_universe_id: str ): """Test spawn_filtered with profession filter""" filters = {"profession": ["warrior", "knight", "guard", "soldier"]} result = authenticated_sync_client.npc.spawn_filtered( universe_id=sync_test_universe_id, world_seed=54321, filters=filters, max_attempts=500, ) if result: assert result.profession in filters["profession"] assert isinstance(result, NPC) def test_spawn_filtered_personality_filter( self, authenticated_sync_client: SyncClient, sync_test_universe_id: str ): """Test spawn_filtered with personality type filter""" filters = {"personality_type": [2, 7]} # Helper or Enthusiast result = authenticated_sync_client.npc.spawn_filtered( universe_id=sync_test_universe_id, world_seed=77777, filters=filters, max_attempts=300, ) if result: assert result.personality_type in filters["personality_type"] def test_spawn_filtered_stress_level_filter( self, authenticated_sync_client: SyncClient, sync_test_universe_id: str ): """Test spawn_filtered with stress level filter""" filters = {"stress_level": {"min": 7, "max": 10}} # High stress result = authenticated_sync_client.npc.spawn_filtered( universe_id=sync_test_universe_id, world_seed=88888, filters=filters, max_attempts=400, ) # Note: NPC doesn't include stress_level in return # but the server filtering ensures it matches if result: assert isinstance(result, NPC) def test_spawn_filtered_location_filter( self, authenticated_sync_client: SyncClient, sync_test_universe_id: str ): """Test spawn_filtered with location filter""" filters = {"location_filter": ["castle", "barracks", "training_ground"]} result = authenticated_sync_client.npc.spawn_filtered( universe_id=sync_test_universe_id, world_seed=66666, filters=filters, max_attempts=200, ) # Location filtering works during generation if result: assert isinstance(result, NPC) def test_spawn_filtered_gender_filter( self, authenticated_sync_client: SyncClient, sync_test_universe_id: str ): """Test spawn_filtered with gender filter""" filters = {"gender": ["female"]} result = authenticated_sync_client.npc.spawn_filtered( universe_id=sync_test_universe_id, world_seed=55555, filters=filters, max_attempts=300, ) # Note: NPC doesn't include gender in return # but the server filtering ensures it matches if result: assert isinstance(result, NPC) def test_spawn_filtered_mood_filter( self, authenticated_sync_client: SyncClient, sync_test_universe_id: str ): """Test spawn_filtered with mood filter""" filters = {"mood": ["happy", "excited", "cheerful"]} result = authenticated_sync_client.npc.spawn_filtered( universe_id=sync_test_universe_id, world_seed=44444, filters=filters, max_attempts=300, ) if result: assert result.mood in filters["mood"] def test_spawn_filtered_multiple_filters( self, authenticated_sync_client: SyncClient, sync_test_universe_id: str ): """Test spawn_filtered with multiple filter types""" filters = { "profession": ["merchant", "trader", "vendor"], "personality_type": [3, 7], # Achiever or Enthusiast "mood": ["happy", "excited", "confident"], } result = authenticated_sync_client.npc.spawn_filtered( universe_id=sync_test_universe_id, world_seed=33333, filters=filters, start_npc_id=3000000000000000000, # Large start ID max_attempts=800, ) if result: assert result.profession in filters["profession"] assert result.personality_type in filters["personality_type"] assert result.mood in filters["mood"] assert result.npc_id >= 3000000000000000000 def test_spawn_filtered_impossible_filter( self, authenticated_sync_client: SyncClient, sync_test_universe_id: str ): """Test spawn_filtered with impossible filter returns None""" filters = { "profession": ["impossible_profession_xyz"], "personality_type": [999], # Invalid type } result = authenticated_sync_client.npc.spawn_filtered( universe_id=sync_test_universe_id, world_seed=12345, filters=filters, max_attempts=30, # Small number for quick test ) # Should return None when no match found assert result is None def test_spawn_filtered_edge_cases( self, authenticated_sync_client: SyncClient, sync_test_universe_id: str ): """Test spawn_filtered edge cases""" # Test with minimal max_attempts result = authenticated_sync_client.npc.spawn_filtered( universe_id=sync_test_universe_id, world_seed=12345, filters={}, max_attempts=1, ) # Should still work with just 1 attempt (no filtering) assert result is not None # Test with very large start_npc_id large_id = 8000000000000000000 # 8 quintillion result = authenticated_sync_client.npc.spawn_filtered( universe_id=sync_test_universe_id, world_seed=12345, filters={}, start_npc_id=large_id, max_attempts=5, ) if result: assert result.npc_id >= large_id class TestFilteringPerformance: """Test performance characteristics of filtering""" def test_filtering_reasonable_performance( self, authenticated_sync_client: SyncClient, sync_test_universe_id: str ): """Test that filtering completes in reasonable time""" import time filters = { "personality_type": list(range(1, 10)) # All valid types (easy filter) } start_time = time.time() result = authenticated_sync_client.npc.spawn_filtered( universe_id=sync_test_universe_id, world_seed=12345, filters=filters, max_attempts=100, ) end_time = time.time() # Should complete quickly (within 10 seconds for integration test) assert end_time - start_time < 10.0 # Should find a match with such a broad filter assert result is not None @pytest.mark.asyncio async def test_async_filtering_performance( self, authenticated_async_client: AsyncClient, test_universe_id: str ): """Test async filtering performance""" import time filters = {"personality_type": [1, 2, 3, 4, 5]} # Half the personality types start_time = time.time() result = await authenticated_async_client.npc.spawn_filtered( universe_id=test_universe_id, world_seed=12345, filters=filters, max_attempts=200, ) end_time = time.time() # Should complete quickly assert end_time - start_time < 10.0 if result: assert result.personality_type in filters["personality_type"] if __name__ == "__main__": pytest.main([__file__])