410 lines
14 KiB
Python
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__])
|