"""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