npc-clients-python/tests/test_filtering.py

410 lines
14 KiB
Python

#!/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__])