diff --git a/src/tests/e2e/test_full_workflow.py b/src/tests/e2e/test_full_workflow.py deleted file mode 100644 index 5a5ca13..0000000 --- a/src/tests/e2e/test_full_workflow.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Integration tests for authentication.""" - -import pytest - -from geoguessr_mcp.auth.session import SessionManager - - -@pytest.mark.integration -class TestAuthFlow: - """Integration tests for authentication flow.""" - - @pytest.mark.asyncio - async def test_full_login_logout_cycle(self): - """Test complete login and logout cycle.""" - # This would use real API calls in a test environment - pass diff --git a/src/tests/integration/__init__.py b/src/tests/integration/__init__.py index e69de29..cf8880c 100644 --- a/src/tests/integration/__init__.py +++ b/src/tests/integration/__init__.py @@ -0,0 +1 @@ +"""Integration tests for GeoGuessr MCP Server.""" \ No newline at end of file diff --git a/src/tests/integration/test_api_client.py b/src/tests/integration/test_api_client.py index e69de29..f87f5c1 100644 --- a/src/tests/integration/test_api_client.py +++ b/src/tests/integration/test_api_client.py @@ -0,0 +1 @@ +# TODO \ No newline at end of file diff --git a/src/tests/integration/test_auth_flow.py b/src/tests/integration/test_auth_flow.py index e69de29..f87f5c1 100644 --- a/src/tests/integration/test_auth_flow.py +++ b/src/tests/integration/test_auth_flow.py @@ -0,0 +1 @@ +# TODO \ No newline at end of file diff --git a/src/tests/unit/__init__.py b/src/tests/unit/__init__.py index e69de29..ea702e5 100644 --- a/src/tests/unit/__init__.py +++ b/src/tests/unit/__init__.py @@ -0,0 +1 @@ +"""Unit tests for GeoGuessr MCP Server.""" \ No newline at end of file diff --git a/src/geoguessr_mcp/auth/middleware.py b/src/tests/unit/auth/__init__.py similarity index 100% rename from src/geoguessr_mcp/auth/middleware.py rename to src/tests/unit/auth/__init__.py diff --git a/src/tests/unit/test_session.py b/src/tests/unit/auth/test_session.py similarity index 62% rename from src/tests/unit/test_session.py rename to src/tests/unit/auth/test_session.py index a34a1da..bf4ae09 100644 --- a/src/tests/unit/test_session.py +++ b/src/tests/unit/auth/test_session.py @@ -1,16 +1,30 @@ -"""Unit tests for session management.""" +""" +This module contains test cases for the UserSession dataclass and the +SessionManager class, which handle session authentication and management +functionality. -from datetime import UTC, datetime, timedelta -from unittest.mock import AsyncMock, MagicMock, patch +The test cases validate the implementation of session validity, login, +logout, and retrieval features for these classes under various scenarios. +These tests ensure that the session and authentication-related operations +perform correctly and as expected under different conditions. + +Classes +------- +TestUserSession + Provides unit tests for the UserSession dataclass, which represents + user session details. + +TestSessionManager + Provides unit tests for the SessionManager class, which facilitates + login, logout, and session management operations in an async context. +""" import pytest +from datetime import datetime, timedelta, UTC +from unittest.mock import AsyncMock, MagicMock, patch from geoguessr_mcp.auth.session import SessionManager, UserSession -# ============================================================================ -# USER SESSION TESTS -# ============================================================================ - class TestUserSession: """Tests for UserSession dataclass.""" @@ -40,14 +54,24 @@ class TestUserSession: def test_session_without_cookie(self): """Test that a session without cookie is invalid.""" session = UserSession( - ncfa_cookie="", user_id="user123", username="TestUser", email="test@example.com" + ncfa_cookie="", + user_id="user123", + username="TestUser", + email="test@example.com", ) assert not session.is_valid() + def test_session_no_expiry(self): + """Test session without expiration date.""" + session = UserSession( + ncfa_cookie="test_cookie", + user_id="user123", + username="TestUser", + email="test@example.com", + expires_at=None, + ) + assert session.is_valid() -# ============================================================================ -# SESSION MANAGER TESTS -# ============================================================================ class TestSessionManager: """Tests for SessionManager.""" @@ -55,11 +79,9 @@ class TestSessionManager: @pytest.mark.asyncio async def test_login_success(self, mock_profile_response): """Test successful login flow.""" - manager = SessionManager() with patch("httpx.AsyncClient") as mock_client_class: - # Create mock client mock_client = AsyncMock() mock_client.__aenter__.return_value = mock_client mock_client.__aexit__.return_value = None @@ -70,7 +92,6 @@ class TestSessionManager: login_response.status_code = 200 login_response.cookies.jar = [] - # Create mock cookie mock_cookie = MagicMock() mock_cookie.name = "_ncfa" mock_cookie.value = "test_ncfa_cookie_value" @@ -81,7 +102,6 @@ class TestSessionManager: profile_response.status_code = 200 profile_response.json.return_value = mock_profile_response - # Set up mock client responses mock_client.post = AsyncMock(return_value=login_response) mock_client.get = AsyncMock(return_value=profile_response) mock_client.cookies.set = MagicMock() @@ -89,13 +109,11 @@ class TestSessionManager: # Perform login session_token, session = await manager.login("test@example.com", "password123") - # Assertions assert session_token is not None assert len(session_token) > 0 assert session.ncfa_cookie == "test_ncfa_cookie_value" - assert session.user_id == "test-user-id" + assert session.user_id == "test-user-id-123" assert session.username == "TestPlayer" - assert session.email == "test@example.com" assert session.is_valid() @pytest.mark.asyncio @@ -109,23 +127,37 @@ class TestSessionManager: mock_client.__aexit__.return_value = None mock_client_class.return_value = mock_client - # Mock 401 response login_response = MagicMock() login_response.status_code = 401 mock_client.post = AsyncMock(return_value=login_response) - # Attempt login and expect error with pytest.raises(ValueError, match="Invalid email or password"): await manager.login("wrong@example.com", "wrong_pass") @pytest.mark.asyncio - async def test_logout(self, mock_profile_response): - """Test logout functionality.""" - + async def test_login_rate_limited(self): + """Test login when rate limited.""" + manager = SessionManager() + + 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 + + login_response = MagicMock() + login_response.status_code = 429 + mock_client.post = AsyncMock(return_value=login_response) + + with pytest.raises(ValueError, match="Too many login attempts"): + await manager.login("test@example.com", "password") + + @pytest.mark.asyncio + async def test_logout(self, mock_profile_response): + """Test logout functionality.""" manager = SessionManager() with patch("httpx.AsyncClient") as mock_client_class: - # Set up successful login first mock_client = AsyncMock() mock_client.__aenter__.return_value = mock_client mock_client.__aexit__.return_value = None @@ -149,7 +181,7 @@ class TestSessionManager: session_token, _ = await manager.login("test@example.com", "password") - # Now logout + # Logout result = await manager.logout(session_token) assert result is True @@ -158,12 +190,38 @@ class TestSessionManager: assert session is None @pytest.mark.asyncio - async def test_get_session_with_default_cookie(self): - """Test getting session with default cookie from environment.""" + async def test_logout_invalid_token(self): + """Test logout with invalid token.""" + manager = SessionManager() + result = await manager.logout("invalid_token") + assert result is False + @pytest.mark.asyncio + async def test_get_session_with_default_cookie(self): + """Test getting session with default cookie.""" + manager = SessionManager(default_cookie="default_test_cookie") + + session = await manager.get_session() + + assert session is not None + assert session.ncfa_cookie == "default_test_cookie" + assert session.user_id == "default" + + @pytest.mark.asyncio + async def test_get_session_no_auth(self): + """Test getting session with no authentication.""" + manager = SessionManager(default_cookie=None) + + session = await manager.get_session() + assert session is None + + @pytest.mark.asyncio + async def test_set_default_cookie(self): + """Test setting default cookie.""" manager = SessionManager() - # Should use default cookie from environment + await manager.set_default_cookie("new_cookie") + session = await manager.get_session() assert session is not None - assert session.ncfa_cookie == "test_cookie_value" + assert session.ncfa_cookie == "new_cookie" diff --git a/src/tests/unit/models/__init__.py b/src/tests/unit/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/unit/models/test_achievement.py b/src/tests/unit/models/test_achievement.py new file mode 100644 index 0000000..a6c2f8c --- /dev/null +++ b/src/tests/unit/models/test_achievement.py @@ -0,0 +1,45 @@ +""" +Tests for the Achievement model. + +This module provides integration tests to verify that the Achievement +model behaves correctly when constructed using API response data. It +includes scenarios for both locked and unlocked achievements with relevant +data transformations. +""" + +from geoguessr_mcp.models import Achievement + + +class TestAchievement: + """Tests for Achievement model.""" + + def test_from_api_response_unlocked(self): + """Test creating unlocked achievement.""" + data = { + "id": "ach-1", + "name": "First Steps", + "description": "Complete your first game", + "unlocked": True, + "unlockedAt": "2024-01-15T00:00:00.000Z", + } + achievement = Achievement.from_api_response(data) + + assert achievement.id == "ach-1" + assert achievement.name == "First Steps" + assert achievement.unlocked is True + assert achievement.unlocked_at == "2024-01-15T00:00:00.000Z" + + def test_from_api_response_locked(self): + """Test creating locked achievement with progress.""" + data = { + "id": "ach-2", + "name": "Explorer", + "description": "Play 100 games", + "unlocked": False, + "progress": 0.45, + } + achievement = Achievement.from_api_response(data) + + assert achievement.id == "ach-2" + assert achievement.unlocked is False + assert achievement.progress == 0.45 diff --git a/src/tests/unit/models/test_game.py b/src/tests/unit/models/test_game.py new file mode 100644 index 0000000..66a7529 --- /dev/null +++ b/src/tests/unit/models/test_game.py @@ -0,0 +1,62 @@ +""" +Unit tests for validating the functionality of the Game model and related components. + +These tests ensure the proper creation and behavior of Game and RoundGuess +instances when interacting with API responses or performing operations like +serialization. The tests cover both standard and edge cases. +""" + +from geoguessr_mcp.models.Game import Game +from geoguessr_mcp.models.RoundGuess import RoundGuess + + +class TestGame: + """Tests for Game model.""" + + def test_from_api_response(self, mock_game_response): + """Test creating game from API response.""" + game = Game.from_api_response(mock_game_response) + + assert game.token == "ABC123XYZ" + assert game.map_name == "World" + assert game.mode == "standard" + assert game.finished is True + assert len(game.rounds) == 5 + assert game.total_score == 5000 + 4500 + 3800 + 4900 + 5000 + + def test_from_api_response_minimal(self): + """Test creating game from minimal response.""" + data = { + "token": "TEST", + "type": "challenge", + "player": {"guesses": []}, + } + game = Game.from_api_response(data) + + assert game.token == "TEST" + assert game.mode == "challenge" + assert game.total_score == 0 + assert len(game.rounds) == 0 + + def test_round_guess(self): + """Test creating round guess.""" + data = { + "roundScoreInPoints": 4500, + "distanceInMeters": 150.5, + "time": 25, + } + guess = RoundGuess.from_api_response(data, round_num=1) + + assert guess.round_number == 1 + assert guess.score == 4500 + assert guess.distance_meters == 150.5 + assert guess.time_seconds == 25 + + def test_to_dict(self, mock_game_response): + """Test serializing game to dict.""" + game = Game.from_api_response(mock_game_response) + result = game.to_dict() + + assert result["token"] == "ABC123XYZ" + assert len(result["rounds"]) == 5 + assert result["total_score"] > 0 diff --git a/src/tests/unit/models/test_season_stats.py b/src/tests/unit/models/test_season_stats.py new file mode 100644 index 0000000..7c0209d --- /dev/null +++ b/src/tests/unit/models/test_season_stats.py @@ -0,0 +1,46 @@ +""" +Tests for the SeasonStats model. + +This module includes test cases to verify the functionality of the SeasonStats +model, especially its method for creating an instance from an API response. +The tests validate proper handling of fields and alternative field names in +the response. + +Classes: + TestSeasonStats: Contains test cases for the SeasonStats model. +""" + +from geoguessr_mcp.models import SeasonStats + + +class TestSeasonStats: + """Tests for SeasonStats model.""" + + def test_from_api_response(self, mock_season_stats_response): + """Test creating season stats from API response.""" + stats = SeasonStats.from_api_response(mock_season_stats_response) + + assert stats.season_id == "season-2024-1" + assert stats.season_name == "Season 1 2024" + assert stats.rank == 150 + assert stats.rating == 1850 + assert stats.games_played == 45 + assert stats.wins == 30 + assert stats.division == "Gold" + + def test_from_api_response_alternative_fields(self): + """Test handling alternative field names.""" + data = { + "id": "s1", + "name": "Season One", + "position": 100, + "elo": 1500, + "games": 20, + "tier": "Silver", + } + stats = SeasonStats.from_api_response(data) + + assert stats.season_id == "s1" + assert stats.rank == 100 + assert stats.rating == 1500 + assert stats.division == "Silver" diff --git a/src/tests/unit/models/test_user_profile.py b/src/tests/unit/models/test_user_profile.py new file mode 100644 index 0000000..e8999ad --- /dev/null +++ b/src/tests/unit/models/test_user_profile.py @@ -0,0 +1,43 @@ +"""Test suite for the UserProfile model. + +This module contains a collection of test cases designed to validate +the behavior of the UserProfile model, including creating instances +from API responses and serializing them back into dictionaries. +""" + +from geoguessr_mcp.models import UserProfile + + +class TestUserProfile: + """Tests for UserProfile model.""" + + def test_from_api_response(self, mock_profile_response): + """Test creating profile from API response.""" + profile = UserProfile.from_api_response(mock_profile_response) + + assert profile.id == "test-user-id-123" + assert profile.nick == "TestPlayer" + assert profile.email == "test@example.com" + assert profile.country == "US" + assert profile.level == 50 + assert profile.is_verified is True + assert profile.is_pro is True + + def test_from_api_response_minimal(self): + """Test creating profile from minimal response.""" + data = {"id": "123", "nick": "Player"} + profile = UserProfile.from_api_response(data) + + assert profile.id == "123" + assert profile.nick == "Player" + assert profile.email == "" + assert profile.level == 0 + + def test_to_dict(self, mock_profile_response): + """Test serializing profile to dict.""" + profile = UserProfile.from_api_response(mock_profile_response) + result = profile.to_dict() + + assert result["id"] == "test-user-id-123" + assert result["nick"] == "TestPlayer" + assert "raw_data" not in result diff --git a/src/tests/unit/models/test_user_stats.py b/src/tests/unit/models/test_user_stats.py new file mode 100644 index 0000000..c7ea4d6 --- /dev/null +++ b/src/tests/unit/models/test_user_stats.py @@ -0,0 +1,51 @@ +""" +A set of tests for the UserStats model. + +This module is responsible for testing functionalities of the UserStats +model, including testing the creation of model instances from API +responses, handling alternative field names in the API data, and +serialization of model instances into dictionaries. +""" + +from geoguessr_mcp.models import UserStats + + +class TestUserStats: + """Tests for UserStats model.""" + + def test_from_api_response(self, mock_stats_response): + """Test creating stats from API response.""" + stats = UserStats.from_api_response(mock_stats_response) + + assert stats.games_played == 100 + assert stats.rounds_played == 500 + assert stats.total_score == 2250000 + assert stats.average_score == 22500 + assert stats.perfect_games == 10 + assert stats.win_rate == 0.65 + assert stats.streak_best == 25 + + def test_from_api_response_alternative_fields(self): + """Test handling alternative field names.""" + data = { + "totalGames": 50, + "totalRounds": 250, + "score": 1000000, + "fiveKs": 5, + "countryStreakBest": 15, + } + stats = UserStats.from_api_response(data) + + assert stats.games_played == 50 + assert stats.rounds_played == 250 + assert stats.total_score == 1000000 + assert stats.perfect_games == 5 + assert stats.streak_best == 15 + + def test_to_dict(self, mock_stats_response): + """Test serializing stats to dict.""" + stats = UserStats.from_api_response(mock_stats_response) + result = stats.to_dict() + + assert result["games_played"] == 100 + assert "raw_data" not in result diff --git a/src/tests/unit/monitoring/__init__.py b/src/tests/unit/monitoring/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/unit/monitoring/endpoint/__init__.py b/src/tests/unit/monitoring/endpoint/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/unit/monitoring/schema/__init__.py b/src/tests/unit/monitoring/schema/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/unit/monitoring/schema/test_endpoint_schema.py b/src/tests/unit/monitoring/schema/test_endpoint_schema.py new file mode 100644 index 0000000..70dc841 --- /dev/null +++ b/src/tests/unit/monitoring/schema/test_endpoint_schema.py @@ -0,0 +1,58 @@ +""" +Module for testing the EndpointSchema class and its functionality. + +This module contains unit tests for the EndpointSchema class, focusing on +the serialization and deserialization functionality. The tests validate +the `to_dict` and `from_dict` methods, ensuring correct conversion between +dictionary representation and the EndpointSchema object. +""" + +from geoguessr_mcp.monitoring import EndpointSchema, SchemaField + + +class TestEndpointSchema: + """Tests for EndpointSchema class.""" + + def test_to_dict(self): + """Test serialization to dictionary.""" + schema = EndpointSchema( + endpoint="/v3/profiles", + method="GET", + fields={ + "id": SchemaField(name="id", field_type="string"), + }, + response_code=200, + is_available=True, + ) + + result = schema.to_dict() + + assert result["endpoint"] == "/v3/profiles" + assert result["method"] == "GET" + assert result["is_available"] is True + assert "id" in result["fields"] + + def test_from_dict(self): + """Test deserialization from dictionary.""" + data = { + "endpoint": "/v3/profiles", + "method": "GET", + "fields": { + "id": { + "name": "id", + "field_type": "string", + "nullable": False, + } + }, + "last_updated": "2024-01-15T12:00:00+00:00", + "schema_hash": "abc123", + "response_code": 200, + "is_available": True, + } + + schema = EndpointSchema.from_dict(data) + + assert schema.endpoint == "/v3/profiles" + assert schema.method == "GET" + assert "id" in schema.fields + assert schema.fields["id"].field_type == "string" diff --git a/src/tests/unit/monitoring/schema/test_schema_detector.py b/src/tests/unit/monitoring/schema/test_schema_detector.py new file mode 100644 index 0000000..59ec440 --- /dev/null +++ b/src/tests/unit/monitoring/schema/test_schema_detector.py @@ -0,0 +1,131 @@ +""" +Unit tests for the SchemaDetector class. + +This module provides a suite of unit tests to validate the functionality of +the SchemaDetector class from the monitoring.schema package. The tests ensure +the correct detection and classification of data types, handling of nested +objects, computation of schema hashes, and parsing of specific data formats +such as datetime strings, URLs, and UUIDs. +""" + +from geoguessr_mcp.monitoring.schema.SchemaDetector import SchemaDetector +from geoguessr_mcp.monitoring.schema.EndpointSchema import SchemaField + + +class TestSchemaDetector: + """Tests for SchemaDetector class.""" + + def test_detect_type_string(self): + """Test string type detection.""" + detector = SchemaDetector() + assert detector.detect_type("hello") == "string" + + def test_detect_type_integer(self): + """Test integer type detection.""" + detector = SchemaDetector() + assert detector.detect_type(42) == "integer" + + def test_detect_type_float(self): + """Test float type detection.""" + detector = SchemaDetector() + assert detector.detect_type(3.14) == "number" + + def test_detect_type_boolean(self): + """Test boolean type detection.""" + detector = SchemaDetector() + assert detector.detect_type(True) == "boolean" + assert detector.detect_type(False) == "boolean" + + def test_detect_type_null(self): + """Test null type detection.""" + detector = SchemaDetector() + assert detector.detect_type(None) == "null" + + def test_detect_type_array(self): + """Test array type detection.""" + detector = SchemaDetector() + assert detector.detect_type([1, 2, 3]) == "array" + + def test_detect_type_object(self): + """Test object type detection.""" + detector = SchemaDetector() + assert detector.detect_type({"key": "value"}) == "object" + + def test_detect_type_datetime(self): + """Test datetime string detection.""" + detector = SchemaDetector() + assert detector.detect_type("2024-01-15T12:00:00Z") == "datetime" + assert detector.detect_type("2024-01-15T12:00:00+00:00") == "datetime" + + def test_detect_type_uuid(self): + """Test UUID string detection.""" + detector = SchemaDetector() + assert detector.detect_type("550e8400-e29b-41d4-a716-446655440000") == "uuid" + + def test_detect_type_url(self): + """Test URL string detection.""" + detector = SchemaDetector() + assert detector.detect_type("https://example.com/path") == "url" + assert detector.detect_type("http://test.com") == "url" + + def test_analyze_response_simple(self): + """Test analyzing a simple response.""" + detector = SchemaDetector() + data = { + "id": "123", + "name": "Test", + "count": 42, + "active": True, + } + + fields = detector.analyze_response(data) + + assert len(fields) == 4 + assert fields["id"].field_type == "string" + assert fields["name"].field_type == "string" + assert fields["count"].field_type == "integer" + assert fields["active"].field_type == "boolean" + + def test_analyze_response_nested(self): + """Test analyzing a nested response.""" + detector = SchemaDetector() + data = { + "user": { + "id": "123", + "profile": { + "name": "Test", + } + } + } + + fields = detector.analyze_response(data) + + assert "user" in fields + assert fields["user"].field_type == "object" + assert fields["user"].nested_schema is not None + + def test_compute_schema_hash(self): + """Test schema hash computation.""" + detector = SchemaDetector() + + fields1 = { + "id": SchemaField(name="id", field_type="string"), + "name": SchemaField(name="name", field_type="string"), + } + + fields2 = { + "id": SchemaField(name="id", field_type="string"), + "name": SchemaField(name="name", field_type="string"), + } + + fields3 = { + "id": SchemaField(name="id", field_type="integer"), # Different type + "name": SchemaField(name="name", field_type="string"), + } + + hash1 = detector.compute_schema_hash(fields1) + hash2 = detector.compute_schema_hash(fields2) + hash3 = detector.compute_schema_hash(fields3) + + assert hash1 == hash2 # Same schema + assert hash1 != hash3 # Different schema diff --git a/src/tests/unit/monitoring/schema/test_schema_registry.py b/src/tests/unit/monitoring/schema/test_schema_registry.py new file mode 100644 index 0000000..c3cb86d --- /dev/null +++ b/src/tests/unit/monitoring/schema/test_schema_registry.py @@ -0,0 +1,93 @@ +""" +Unit tests for the SchemaRegistry class. + +This module contains test cases to verify the behavior of the +SchemaRegistry class, including the ability to update schemas, +store schema metadata, track schema availability, and retrieve +dynamic descriptions for endpoints. + +Classes: + TestSchemaRegistry: Contains test methods for testing schema + registry functionality. +""" + +from geoguessr_mcp.monitoring import SchemaRegistry + + +class TestSchemaRegistry: + """Tests for SchemaRegistry class.""" + + def test_update_schema_new(self, tmp_path): + """Test adding a new schema.""" + registry = SchemaRegistry(cache_dir=str(tmp_path)) + + data = {"id": "123", "name": "Test"} + schema, changed = registry.update_schema("/v3/test", data) + + assert changed is True + assert schema.endpoint == "/v3/test" + assert schema.is_available is True + + def test_update_schema_unchanged(self, tmp_path): + """Test updating with same schema.""" + registry = SchemaRegistry(cache_dir=str(tmp_path)) + + data = {"id": "123", "name": "Test"} + + # First update + schema1, changed1 = registry.update_schema("/v3/test", data) + assert changed1 is True + + # Second update with same data + schema2, changed2 = registry.update_schema("/v3/test", data) + assert changed2 is False + + def test_update_schema_changed(self, tmp_path): + """Test detecting schema changes.""" + registry = SchemaRegistry(cache_dir=str(tmp_path)) + + data1 = {"id": "123", "name": "Test"} + data2 = {"id": "123", "name": "Test", "new_field": 42} # Added field + + schema1, changed1 = registry.update_schema("/v3/test", data1) + schema2, changed2 = registry.update_schema("/v3/test", data2) + + assert changed1 is True + assert changed2 is True + assert len(schema2.fields) > len(schema1.fields) + + def test_mark_unavailable(self, tmp_path): + """Test marking endpoint as unavailable.""" + registry = SchemaRegistry(cache_dir=str(tmp_path)) + + registry.mark_unavailable("/v3/test", "Server error", 500) + + schema = registry.get_schema("/v3/test") + assert schema is not None + assert schema.is_available is False + assert schema.error_message == "Server error" + assert schema.response_code == 500 + + def test_get_available_endpoints(self, tmp_path): + """Test getting list of available endpoints.""" + registry = SchemaRegistry(cache_dir=str(tmp_path)) + + registry.update_schema("/v3/available", {"id": "1"}) + registry.mark_unavailable("/v3/unavailable", "Error") + + available = registry.get_available_endpoints() + + assert "/v3/available" in available + assert "/v3/unavailable" not in available + + def test_generate_dynamic_description(self, tmp_path): + """Test generating endpoint description.""" + registry = SchemaRegistry(cache_dir=str(tmp_path)) + + registry.update_schema("/v3/test", {"id": "123", "name": "Test"}) + + description = registry.generate_dynamic_description("/v3/test") + + assert "/v3/test" in description + assert "id" in description + assert "name" in description diff --git a/src/tests/unit/services/__init__.py b/src/tests/unit/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/unit/services/test_analysis_service.py b/src/tests/unit/services/test_analysis_service.py new file mode 100644 index 0000000..f87f5c1 --- /dev/null +++ b/src/tests/unit/services/test_analysis_service.py @@ -0,0 +1 @@ +# TODO \ No newline at end of file diff --git a/src/tests/unit/services/test_game_service.py b/src/tests/unit/services/test_game_service.py new file mode 100644 index 0000000..f87f5c1 --- /dev/null +++ b/src/tests/unit/services/test_game_service.py @@ -0,0 +1 @@ +# TODO \ No newline at end of file diff --git a/src/tests/unit/services/test_profile_service.py b/src/tests/unit/services/test_profile_service.py new file mode 100644 index 0000000..f87f5c1 --- /dev/null +++ b/src/tests/unit/services/test_profile_service.py @@ -0,0 +1 @@ +# TODO \ No newline at end of file diff --git a/src/tests/unit/test_profile_service.py b/src/tests/unit/test_profile_service.py deleted file mode 100644 index ef2a313..0000000 --- a/src/tests/unit/test_profile_service.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Unit tests for ProfileService.""" - -from unittest.mock import AsyncMock, MagicMock - -import pytest - -from geoguessr_mcp.models.profile import UserProfile -from geoguessr_mcp.services.profile_service import ProfileService -from geoguessr_mcp.config import settings - - -class TestProfileService: - """Tests for ProfileService.""" - - @pytest.mark.asyncio - async def test_get_profile_success(self, mock_session, mock_profile_data): - """Test successful profile retrieval.""" - # Create mock client - mock_client = MagicMock() - mock_client.base_url = settings.GEOGUESSR_BASE_URL - mock_client.get_async_session = AsyncMock(return_value=mock_session) - - # Mock HTTP response - mock_response = MagicMock() - mock_response.json.return_value = mock_profile_data - mock_response.raise_for_status = MagicMock() - mock_session.get = AsyncMock(return_value=mock_response) - - # Test - service = ProfileService(mock_client) - profile = await service.get_profile() - - assert isinstance(profile, UserProfile) - assert profile.nick == "TestPlayer" - assert profile.id == "test-user-id" - - @pytest.mark.asyncio - async def test_get_my_stats_success(self, mock_session, mock_profile_data): - """Test successful stats retrieval.""" - # Create mock client - mock_client = MagicMock() - mock_client.base_url = settings.GEOGUESSR_BASE_URL - mock_client.get_async_session = AsyncMock(return_value=mock_session) - - # Mock HTTP response - mock_response = MagicMock() - mock_response.json.return_value = mock_profile_data - mock_response.raise_for_status = MagicMock() - - mock_session.get = AsyncMock(return_value=mock_response) - - service = ProfileService(mock_client) - profile = await service.get_stats() - - assert isinstance(profile, UserProfile) - assert profile. == 100 - assert result["averageScore"] == 4500 - - @pytest.mark.asyncio - async def test_get_extended_stats(self, mock_session): - """Test extended stats retrieval.""" - from server import get_extended_stats - - extended_stats = { - "totalGames": 150, - "winRate": 0.65, - "averageTime": 180 - } - - with patch("server.get_async_session") as mock_get_session: - mock_http_response = MagicMock() - mock_http_response.json.return_value = extended_stats - mock_http_response.raise_for_status = MagicMock() - - mock_session.get = AsyncMock(return_value=mock_http_response) - mock_get_session.return_value = mock_session - - result = await get_extended_stats() - - assert result["totalGames"] == 150 - assert result["winRate"] == 0.65