285 lines
7.9 KiB
Python
285 lines
7.9 KiB
Python
"""Shared test fixtures."""
|
|
|
|
import os
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from geoguessr_mcp.api import GeoGuessrClient
|
|
from geoguessr_mcp.api.dynamic_response import DynamicResponse
|
|
from geoguessr_mcp.auth import SessionManager, UserSession
|
|
from geoguessr_mcp.config import settings
|
|
from geoguessr_mcp.models import RoundGuess, Game
|
|
from geoguessr_mcp.services import AnalysisService, GameService, ProfileService
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def mock_env(request, monkeypatch):
|
|
"""Set up environment variables for testing."""
|
|
# Skip this fixture if the test has the 'real_env' marker
|
|
if "real_env" in request.keywords:
|
|
yield
|
|
return
|
|
|
|
# Clear the default cookie in settings to avoid interference
|
|
monkeypatch.setattr(settings, "DEFAULT_NCFA_COOKIE", None)
|
|
|
|
# Clear schema registry to avoid interference from registered schemas
|
|
from geoguessr_mcp.monitoring.schema.schema_registry import schema_registry
|
|
|
|
# Store original schemas
|
|
original_schemas = schema_registry.schemas.copy()
|
|
# Clear all schemas for testing
|
|
schema_registry.schemas.clear()
|
|
|
|
# Ensure required settings are set
|
|
if not hasattr(settings, "GEOGUESSR_API_URL") or not settings.GEOGUESSR_API_URL:
|
|
monkeypatch.setattr(settings, "GEOGUESSR_API_URL", "https://api.geoguessr.com")
|
|
if not hasattr(settings, "GEOGUESSR_DOMAIN_NAME") or not settings.GEOGUESSR_DOMAIN_NAME:
|
|
monkeypatch.setattr(settings, "GEOGUESSR_DOMAIN_NAME", ".geoguessr.com")
|
|
|
|
# Restore schemas after test
|
|
yield
|
|
schema_registry._schemas = original_schemas
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_client():
|
|
"""Create a mock GeoGuessrClient."""
|
|
client = MagicMock()
|
|
client.get = AsyncMock()
|
|
return client
|
|
|
|
|
|
@pytest.fixture
|
|
def real_client():
|
|
"""Create a real client with environment authentication."""
|
|
real_cookie = os.getenv("GEOGUESSR_NCFA_COOKIE")
|
|
session_manager = SessionManager(default_cookie=real_cookie)
|
|
return GeoGuessrClient(session_manager)
|
|
|
|
|
|
@pytest.fixture
|
|
def client(mock_session_manager):
|
|
"""Create a GeoGuessrClient with mocked session manager."""
|
|
return GeoGuessrClient(mock_session_manager)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_session_manager():
|
|
"""Create a mock session manager."""
|
|
manager = MagicMock(spec=SessionManager)
|
|
manager.get_session = AsyncMock(
|
|
return_value=UserSession(
|
|
ncfa_cookie="test_cookie",
|
|
user_id="test-user",
|
|
username="TestUser",
|
|
email="test@example.com",
|
|
)
|
|
)
|
|
return manager
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_session():
|
|
"""Create a mock async HTTP session."""
|
|
mock_client = AsyncMock()
|
|
mock_client.__aenter__.return_value = mock_client
|
|
mock_client.__aexit__.return_value = None
|
|
return mock_client
|
|
|
|
|
|
@pytest.fixture
|
|
def session_manager():
|
|
"""Create a SessionManager without default cookie."""
|
|
return SessionManager(default_cookie=None)
|
|
|
|
|
|
@pytest.fixture
|
|
def session_manager_with_default():
|
|
"""Create a SessionManager with a default cookie."""
|
|
return SessionManager(default_cookie="test_cookie_value")
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_httpx_client():
|
|
"""Create a mock httpx client for testing."""
|
|
with patch("httpx.AsyncClient") as mock_client_class:
|
|
mock_client = AsyncMock()
|
|
mock_client.__aenter__.return_value = mock_client
|
|
mock_client.__aexit__.return_value = None
|
|
mock_client_class.return_value = mock_client
|
|
yield mock_client
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_game_service():
|
|
"""Create a mock GameService."""
|
|
service = MagicMock()
|
|
service.get_recent_games = AsyncMock()
|
|
service.get_season_stats = AsyncMock()
|
|
return service
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_profile_service():
|
|
"""Create a mock ProfileService."""
|
|
service = MagicMock()
|
|
service.get_comprehensive_profile = AsyncMock()
|
|
return service
|
|
|
|
|
|
@pytest.fixture
|
|
def analysis_service(mock_client, mock_game_service, mock_profile_service):
|
|
"""Create AnalysisService with mocked dependencies."""
|
|
return AnalysisService(
|
|
mock_client,
|
|
game_service=mock_game_service,
|
|
profile_service=mock_profile_service,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def game_service(mock_client):
|
|
"""Create GameService with mocked client."""
|
|
return GameService(mock_client)
|
|
|
|
|
|
@pytest.fixture
|
|
def profile_service(mock_client):
|
|
"""Create ProfileService with mocked client."""
|
|
return ProfileService(mock_client)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_dynamic_response():
|
|
"""Create a DynamicResponse factory for testing."""
|
|
|
|
def create_response(data, success=True, status_code=200, endpoint="/mock/endpoint"):
|
|
"""Create a real DynamicResponse instance for testing.
|
|
:param data:
|
|
:param status_code:
|
|
:param endpoint:
|
|
:type success: object
|
|
"""
|
|
return DynamicResponse(
|
|
data=data,
|
|
endpoint=endpoint,
|
|
status_code=status_code,
|
|
response_time_ms=100.0,
|
|
)
|
|
|
|
return create_response
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_profile_data():
|
|
"""Standard profile response data."""
|
|
return {
|
|
"id": "test-user-id",
|
|
"nick": "TestPlayer",
|
|
"email": "test@example.com",
|
|
"country": "FR",
|
|
"created": "2025-01-01T00:00:00.000Z",
|
|
"isVerified": True,
|
|
"level": 50,
|
|
"rating": {"rating": 1500, "deviation": 100},
|
|
"isProUser": True,
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_game_data():
|
|
"""Standard game response data."""
|
|
return {
|
|
"token": "ABC123",
|
|
"type": "standard",
|
|
"map": {"name": "World"},
|
|
"player": {
|
|
"guesses": [
|
|
{"roundScoreInPoints": 5000, "distanceInMeters": 0, "time": 10},
|
|
{"roundScoreInPoints": 4500, "distanceInMeters": 120, "time": 15},
|
|
{"roundScoreInPoints": 3800, "distanceInMeters": 100, "time": 20},
|
|
{"roundScoreInPoints": 4900, "distanceInMeters": 100, "time": 25},
|
|
{"roundScoreInPoints": 5000, "distanceInMeters": 100, "time": 35},
|
|
]
|
|
},
|
|
"state": "finished",
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_stats_data():
|
|
"""Standard user stats response data."""
|
|
return {
|
|
"games": 100,
|
|
"totalRounds": 500,
|
|
"score": 2250000,
|
|
"perfectGames": 10,
|
|
"winRate": 0.65,
|
|
"bestStreak": 25,
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_season_stats_data():
|
|
"""Standard season stats response data."""
|
|
return {
|
|
"id": "season-2024-1",
|
|
"name": "Season 1 2024",
|
|
"position": 150,
|
|
"elo": 1850,
|
|
"games": 45,
|
|
"wins": 30,
|
|
"tier": "Gold",
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_activity_feed_data():
|
|
"""Activity feed response data."""
|
|
return {
|
|
"entries": [
|
|
{
|
|
"type": "PlayedGame",
|
|
"payload": {"gameToken": "game-token-1"},
|
|
"timestamp": "2024-01-15T10:00:00Z",
|
|
},
|
|
{
|
|
"type": "PlayedGame",
|
|
"payload": {"gameToken": "game-token-2"},
|
|
"timestamp": "2024-01-14T10:00:00Z",
|
|
},
|
|
{
|
|
"type": "Achievement",
|
|
"payload": {"achievementId": "ach-1"},
|
|
"timestamp": "2024-01-13T10:00:00Z",
|
|
},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_games():
|
|
"""Create sample Game objects for testing."""
|
|
games = []
|
|
for i in range(5):
|
|
rounds = [
|
|
RoundGuess(
|
|
round_number=j + 1,
|
|
score=5000 - (i * 200) - (j * 100), # Varying scores
|
|
distance_meters=100.0 * (i + 1),
|
|
time_seconds=30 + i * 5,
|
|
)
|
|
for j in range(5)
|
|
]
|
|
game = Game(
|
|
token=f"game-{i}",
|
|
map_name="World",
|
|
mode="standard",
|
|
total_score=sum(r.score for r in rounds),
|
|
rounds=rounds,
|
|
finished=True,
|
|
)
|
|
games.append(game)
|
|
return games
|