Add license and protect repository #2
9 changed files with 100 additions and 99 deletions
|
|
@ -1,7 +1,6 @@
|
||||||
"""Monitoring module for API endpoint tracking and schema detection."""
|
"""Monitoring module for API endpoint tracking and schema detection."""
|
||||||
|
|
||||||
from .endpoint.endpoint_monitor import (MONITORED_ENDPOINTS, EndpointMonitor,
|
from .endpoint.endpoint_monitor import MONITORED_ENDPOINTS, EndpointMonitor, endpoint_monitor
|
||||||
endpoint_monitor)
|
|
||||||
from .schema.endpoint_schema import EndpointSchema
|
from .schema.endpoint_schema import EndpointSchema
|
||||||
from .schema.schema_detector import SchemaDetector, SchemaField
|
from .schema.schema_detector import SchemaDetector, SchemaField
|
||||||
from .schema.schema_registry import SchemaRegistry, schema_registry
|
from .schema.schema_registry import SchemaRegistry, schema_registry
|
||||||
|
|
|
||||||
|
|
@ -74,9 +74,7 @@ def register_game_tools(mcp: FastMCP, game_service: GameService):
|
||||||
"success": True,
|
"success": True,
|
||||||
"total_entries": len(entries),
|
"total_entries": len(entries),
|
||||||
"entry_types": list(categorized.keys()),
|
"entry_types": list(categorized.keys()),
|
||||||
"entries_by_type": {
|
"entries_by_type": {t: len(e) for t, e in categorized.items()},
|
||||||
t: len(e) for t, e in categorized.items()
|
|
||||||
},
|
|
||||||
"recent_entries": entries[:5], # First 5 for context
|
"recent_entries": entries[:5], # First 5 for context
|
||||||
"available_fields": response.available_fields,
|
"available_fields": response.available_fields,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ def register_monitoring_tools(mcp: FastMCP):
|
||||||
session_token = get_current_session_token()
|
session_token = get_current_session_token()
|
||||||
if session_token:
|
if session_token:
|
||||||
from ..auth.session import SessionManager
|
from ..auth.session import SessionManager
|
||||||
|
|
||||||
session_manager = SessionManager()
|
session_manager = SessionManager()
|
||||||
session = await session_manager.get_session(session_token)
|
session = await session_manager.get_session(session_token)
|
||||||
if session:
|
if session:
|
||||||
|
|
@ -136,14 +137,16 @@ def register_monitoring_tools(mcp: FastMCP):
|
||||||
previous = history[-1] if history else None
|
previous = history[-1] if history else None
|
||||||
|
|
||||||
if current and previous:
|
if current and previous:
|
||||||
changes.append({
|
changes.append(
|
||||||
|
{
|
||||||
"endpoint": endpoint,
|
"endpoint": endpoint,
|
||||||
"current_hash": current.schema_hash,
|
"current_hash": current.schema_hash,
|
||||||
"previous_hash": previous.schema_hash,
|
"previous_hash": previous.schema_hash,
|
||||||
"current_fields": len(current.fields),
|
"current_fields": len(current.fields),
|
||||||
"previous_fields": len(previous.fields),
|
"previous_fields": len(previous.fields),
|
||||||
"changed_at": current.last_updated.isoformat(),
|
"changed_at": current.last_updated.isoformat(),
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"total_changes_tracked": len(changes),
|
"total_changes_tracked": len(changes),
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,9 @@ def register_profile_tools(mcp: FastMCP, profile_service: ProfileService):
|
||||||
"total": len(achievements),
|
"total": len(achievements),
|
||||||
"unlocked": len(unlocked),
|
"unlocked": len(unlocked),
|
||||||
"locked": len(locked),
|
"locked": len(locked),
|
||||||
"completion_rate": f"{len(unlocked) / len(achievements) * 100:.1f}%" if achievements else "0%",
|
"completion_rate": (
|
||||||
|
f"{len(unlocked) / len(achievements) * 100:.1f}%" if achievements else "0%"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
"unlocked_achievements": [
|
"unlocked_achievements": [
|
||||||
{"name": a.name, "description": a.description, "unlocked_at": a.unlocked_at}
|
{"name": a.name, "description": a.description, "unlocked_at": a.unlocked_at}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
"""Shared test fixtures."""
|
"""Shared test fixtures."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
|
|
@ -16,7 +17,7 @@ from geoguessr_mcp.services import AnalysisService, GameService, ProfileService
|
||||||
def mock_env(request, monkeypatch):
|
def mock_env(request, monkeypatch):
|
||||||
"""Set up environment variables for testing."""
|
"""Set up environment variables for testing."""
|
||||||
# Skip this fixture if the test has the 'real_env' marker
|
# Skip this fixture if the test has the 'real_env' marker
|
||||||
if 'real_env' in request.keywords:
|
if "real_env" in request.keywords:
|
||||||
yield
|
yield
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -25,6 +26,7 @@ def mock_env(request, monkeypatch):
|
||||||
|
|
||||||
# Clear schema registry to avoid interference from registered schemas
|
# Clear schema registry to avoid interference from registered schemas
|
||||||
from geoguessr_mcp.monitoring.schema.schema_registry import schema_registry
|
from geoguessr_mcp.monitoring.schema.schema_registry import schema_registry
|
||||||
|
|
||||||
# Store original schemas
|
# Store original schemas
|
||||||
original_schemas = schema_registry.schemas.copy()
|
original_schemas = schema_registry.schemas.copy()
|
||||||
# Clear all schemas for testing
|
# Clear all schemas for testing
|
||||||
|
|
|
||||||
|
|
@ -304,6 +304,7 @@ class TestGeoGuessrClientIntegration:
|
||||||
"""Test real API call to profile endpoint."""
|
"""Test real API call to profile endpoint."""
|
||||||
# This test requires GEOGUESSR_NCFA_COOKIE to be set
|
# This test requires GEOGUESSR_NCFA_COOKIE to be set
|
||||||
import os
|
import os
|
||||||
|
|
||||||
if not os.environ.get("GEOGUESSR_NCFA_COOKIE"):
|
if not os.environ.get("GEOGUESSR_NCFA_COOKIE"):
|
||||||
pytest.skip("GEOGUESSR_NCFA_COOKIE not set")
|
pytest.skip("GEOGUESSR_NCFA_COOKIE not set")
|
||||||
|
|
||||||
|
|
@ -317,6 +318,7 @@ class TestGeoGuessrClientIntegration:
|
||||||
"""Test real API call to stats' endpoint."""
|
"""Test real API call to stats' endpoint."""
|
||||||
# This test requires GEOGUESSR_NCFA_COOKIE to be set
|
# This test requires GEOGUESSR_NCFA_COOKIE to be set
|
||||||
import os
|
import os
|
||||||
|
|
||||||
if not os.environ.get("GEOGUESSR_NCFA_COOKIE"):
|
if not os.environ.get("GEOGUESSR_NCFA_COOKIE"):
|
||||||
pytest.skip("GEOGUESSR_NCFA_COOKIE not set")
|
pytest.skip("GEOGUESSR_NCFA_COOKIE not set")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,9 @@ class TestAnalysisService:
|
||||||
games = []
|
games = []
|
||||||
for i in range(6):
|
for i in range(6):
|
||||||
base_score = 15000 + (i * 2000) # Increasing scores
|
base_score = 15000 + (i * 2000) # Increasing scores
|
||||||
rounds = [RoundGuess(round_number=1, score=base_score, distance_meters=100, time_seconds=30)]
|
rounds = [
|
||||||
|
RoundGuess(round_number=1, score=base_score, distance_meters=100, time_seconds=30)
|
||||||
|
]
|
||||||
game = Game(
|
game = Game(
|
||||||
token=f"game-{i}",
|
token=f"game-{i}",
|
||||||
map_name="World",
|
map_name="World",
|
||||||
|
|
@ -124,7 +126,9 @@ class TestAnalysisService:
|
||||||
games = []
|
games = []
|
||||||
for i in range(6):
|
for i in range(6):
|
||||||
base_score = 25000 - (i * 2000) # Decreasing scores
|
base_score = 25000 - (i * 2000) # Decreasing scores
|
||||||
rounds = [RoundGuess(round_number=1, score=base_score, distance_meters=100, time_seconds=30)]
|
rounds = [
|
||||||
|
RoundGuess(round_number=1, score=base_score, distance_meters=100, time_seconds=30)
|
||||||
|
]
|
||||||
game = Game(
|
game = Game(
|
||||||
token=f"game-{i}",
|
token=f"game-{i}",
|
||||||
map_name="World",
|
map_name="World",
|
||||||
|
|
@ -182,17 +186,11 @@ class TestAnalysisService:
|
||||||
assert all(area["score"] >= 4500 for area in result.strong_areas)
|
assert all(area["score"] >= 4500 for area in result.strong_areas)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_analyze_recent_games(
|
async def test_analyze_recent_games(self, analysis_service, mock_game_service, sample_games):
|
||||||
self, analysis_service, mock_game_service, sample_games
|
|
||||||
):
|
|
||||||
"""Test analyze_recent_games method."""
|
"""Test analyze_recent_games method."""
|
||||||
mock_game_service.get_recent_games.return_value = sample_games
|
mock_game_service.get_recent_games.return_value = sample_games
|
||||||
|
|
||||||
with patch.object(
|
with patch.object(analysis_service, "analyze_games", wraps=AnalysisService.analyze_games):
|
||||||
analysis_service,
|
|
||||||
'analyze_games',
|
|
||||||
wraps=AnalysisService.analyze_games
|
|
||||||
):
|
|
||||||
result = await analysis_service.analyze_recent_games(count=5)
|
result = await analysis_service.analyze_recent_games(count=5)
|
||||||
|
|
||||||
assert "analysis" in result
|
assert "analysis" in result
|
||||||
|
|
@ -207,17 +205,20 @@ class TestAnalysisService:
|
||||||
"""Test analyze_recent_games with session token."""
|
"""Test analyze_recent_games with session token."""
|
||||||
mock_game_service.get_recent_games.return_value = sample_games
|
mock_game_service.get_recent_games.return_value = sample_games
|
||||||
|
|
||||||
await analysis_service.analyze_recent_games(
|
await analysis_service.analyze_recent_games(count=10, session_token="test_token")
|
||||||
count=10,
|
|
||||||
session_token="test_token"
|
|
||||||
)
|
|
||||||
|
|
||||||
mock_game_service.get_recent_games.assert_called_once_with(10, "test_token")
|
mock_game_service.get_recent_games.assert_called_once_with(10, "test_token")
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_performance_summary(
|
async def test_get_performance_summary(
|
||||||
self, analysis_service, mock_game_service, mock_profile_service,
|
self,
|
||||||
mock_client, sample_games, mock_season_stats_data, mock_dynamic_response
|
analysis_service,
|
||||||
|
mock_game_service,
|
||||||
|
mock_profile_service,
|
||||||
|
mock_client,
|
||||||
|
sample_games,
|
||||||
|
mock_season_stats_data,
|
||||||
|
mock_dynamic_response,
|
||||||
):
|
):
|
||||||
"""Test comprehensive performance summary."""
|
"""Test comprehensive performance summary."""
|
||||||
mock_profile_service.get_comprehensive_profile.return_value = {
|
mock_profile_service.get_comprehensive_profile.return_value = {
|
||||||
|
|
@ -227,6 +228,7 @@ class TestAnalysisService:
|
||||||
|
|
||||||
mock_season_response = mock_dynamic_response(mock_season_stats_data)
|
mock_season_response = mock_dynamic_response(mock_season_stats_data)
|
||||||
from geoguessr_mcp.models import SeasonStats
|
from geoguessr_mcp.models import SeasonStats
|
||||||
|
|
||||||
mock_season_stats = SeasonStats.from_api_response(mock_season_stats_data)
|
mock_season_stats = SeasonStats.from_api_response(mock_season_stats_data)
|
||||||
mock_game_service.get_season_stats.return_value = (mock_season_stats, mock_season_response)
|
mock_game_service.get_season_stats.return_value = (mock_season_stats, mock_season_response)
|
||||||
mock_game_service.get_recent_games.return_value = sample_games[:3]
|
mock_game_service.get_recent_games.return_value = sample_games[:3]
|
||||||
|
|
@ -267,7 +269,14 @@ class TestAnalysisService:
|
||||||
for i in range(5)
|
for i in range(5)
|
||||||
]
|
]
|
||||||
games = [
|
games = [
|
||||||
Game(token="g1", map_name="World", mode="standard", total_score=15000, rounds=rounds, finished=True)
|
Game(
|
||||||
|
token="g1",
|
||||||
|
map_name="World",
|
||||||
|
mode="standard",
|
||||||
|
total_score=15000,
|
||||||
|
rounds=rounds,
|
||||||
|
finished=True,
|
||||||
|
)
|
||||||
for _ in range(5)
|
for _ in range(5)
|
||||||
]
|
]
|
||||||
mock_game_service.get_recent_games.return_value = games
|
mock_game_service.get_recent_games.return_value = games
|
||||||
|
|
@ -289,7 +298,14 @@ class TestAnalysisService:
|
||||||
for i in range(5)
|
for i in range(5)
|
||||||
]
|
]
|
||||||
games = [
|
games = [
|
||||||
Game(token="g1", map_name="World", mode="standard", total_score=17500, rounds=rounds, finished=True)
|
Game(
|
||||||
|
token="g1",
|
||||||
|
map_name="World",
|
||||||
|
mode="standard",
|
||||||
|
total_score=17500,
|
||||||
|
rounds=rounds,
|
||||||
|
finished=True,
|
||||||
|
)
|
||||||
for _ in range(5)
|
for _ in range(5)
|
||||||
]
|
]
|
||||||
mock_game_service.get_recent_games.return_value = games
|
mock_game_service.get_recent_games.return_value = games
|
||||||
|
|
@ -308,7 +324,9 @@ class TestAnalysisService:
|
||||||
games = []
|
games = []
|
||||||
for i in range(6):
|
for i in range(6):
|
||||||
base_score = 25000 - (i * 3000)
|
base_score = 25000 - (i * 3000)
|
||||||
rounds = [RoundGuess(round_number=1, score=base_score, distance_meters=100, time_seconds=45)]
|
rounds = [
|
||||||
|
RoundGuess(round_number=1, score=base_score, distance_meters=100, time_seconds=45)
|
||||||
|
]
|
||||||
game = Game(
|
game = Game(
|
||||||
token=f"game-{i}",
|
token=f"game-{i}",
|
||||||
map_name="World",
|
map_name="World",
|
||||||
|
|
|
||||||
|
|
@ -40,32 +40,23 @@ class TestGameService:
|
||||||
"""Test game details with explicit session token."""
|
"""Test game details with explicit session token."""
|
||||||
mock_client.get.return_value = mock_dynamic_response(mock_game_data)
|
mock_client.get.return_value = mock_dynamic_response(mock_game_data)
|
||||||
|
|
||||||
game, response = await game_service.get_game_details(
|
game, response = await game_service.get_game_details("ABC123", session_token="test_token")
|
||||||
"ABC123",
|
|
||||||
session_token="test_token"
|
|
||||||
)
|
|
||||||
|
|
||||||
call_args = mock_client.get.call_args
|
call_args = mock_client.get.call_args
|
||||||
assert call_args[0][1] == "test_token"
|
assert call_args[0][1] == "test_token"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_game_details_failure(
|
async def test_get_game_details_failure(self, game_service, mock_client, mock_dynamic_response):
|
||||||
self, game_service, mock_client, mock_dynamic_response
|
|
||||||
):
|
|
||||||
"""Test game details retrieval failure."""
|
"""Test game details retrieval failure."""
|
||||||
mock_client.get.return_value = mock_dynamic_response(
|
mock_client.get.return_value = mock_dynamic_response(
|
||||||
{"error": "Game not found"},
|
{"error": "Game not found"}, success=False, status_code=404
|
||||||
success=False,
|
|
||||||
status_code=404
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="Failed to get game details"):
|
with pytest.raises(ValueError, match="Failed to get game details"):
|
||||||
await game_service.get_game_details("INVALID")
|
await game_service.get_game_details("INVALID")
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_unfinished_games(
|
async def test_get_unfinished_games(self, game_service, mock_client, mock_dynamic_response):
|
||||||
self, game_service, mock_client, mock_dynamic_response
|
|
||||||
):
|
|
||||||
"""Test unfinished games retrieval."""
|
"""Test unfinished games retrieval."""
|
||||||
unfinished_data = [
|
unfinished_data = [
|
||||||
{"token": "game-1", "map": {"name": "World"}},
|
{"token": "game-1", "map": {"name": "World"}},
|
||||||
|
|
@ -79,9 +70,7 @@ class TestGameService:
|
||||||
assert len(response.data) == 2
|
assert len(response.data) == 2
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_streak_game(
|
async def test_get_streak_game(self, game_service, mock_client, mock_dynamic_response):
|
||||||
self, game_service, mock_client, mock_dynamic_response
|
|
||||||
):
|
|
||||||
"""Test streak game retrieval."""
|
"""Test streak game retrieval."""
|
||||||
streak_data = {
|
streak_data = {
|
||||||
"token": "streak-123",
|
"token": "streak-123",
|
||||||
|
|
@ -122,7 +111,12 @@ class TestGameService:
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_recent_games_success(
|
async def test_get_recent_games_success(
|
||||||
self, game_service, mock_client, mock_activity_feed_data, mock_game_data, mock_dynamic_response
|
self,
|
||||||
|
game_service,
|
||||||
|
mock_client,
|
||||||
|
mock_activity_feed_data,
|
||||||
|
mock_game_data,
|
||||||
|
mock_dynamic_response,
|
||||||
):
|
):
|
||||||
"""Test recent games retrieval."""
|
"""Test recent games retrieval."""
|
||||||
# First call returns activity feed, subsequent calls return game details
|
# First call returns activity feed, subsequent calls return game details
|
||||||
|
|
@ -153,10 +147,7 @@ class TestGameService:
|
||||||
self, game_service, mock_client, mock_dynamic_response
|
self, game_service, mock_client, mock_dynamic_response
|
||||||
):
|
):
|
||||||
"""Test recent games when feed fails."""
|
"""Test recent games when feed fails."""
|
||||||
mock_client.get.return_value = mock_dynamic_response(
|
mock_client.get.return_value = mock_dynamic_response({"error": "Failed"}, success=False)
|
||||||
{"error": "Failed"},
|
|
||||||
success=False
|
|
||||||
)
|
|
||||||
|
|
||||||
games = await game_service.get_recent_games(count=5)
|
games = await game_service.get_recent_games(count=5)
|
||||||
|
|
||||||
|
|
@ -164,7 +155,12 @@ class TestGameService:
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_recent_games_skips_failed_game_fetch(
|
async def test_get_recent_games_skips_failed_game_fetch(
|
||||||
self, game_service, mock_client, mock_activity_feed_data, mock_game_data, mock_dynamic_response
|
self,
|
||||||
|
game_service,
|
||||||
|
mock_client,
|
||||||
|
mock_activity_feed_data,
|
||||||
|
mock_game_data,
|
||||||
|
mock_dynamic_response,
|
||||||
):
|
):
|
||||||
"""Test that failed individual game fetches are skipped."""
|
"""Test that failed individual game fetches are skipped."""
|
||||||
mock_client.get.side_effect = [
|
mock_client.get.side_effect = [
|
||||||
|
|
@ -193,14 +189,10 @@ class TestGameService:
|
||||||
assert stats.division == "Gold"
|
assert stats.division == "Gold"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_season_stats_failure(
|
async def test_get_season_stats_failure(self, game_service, mock_client, mock_dynamic_response):
|
||||||
self, game_service, mock_client, mock_dynamic_response
|
|
||||||
):
|
|
||||||
"""Test season stats failure."""
|
"""Test season stats failure."""
|
||||||
mock_client.get.return_value = mock_dynamic_response(
|
mock_client.get.return_value = mock_dynamic_response(
|
||||||
{"error": "No active season"},
|
{"error": "No active season"}, success=False, status_code=404
|
||||||
success=False,
|
|
||||||
status_code=404
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="Failed to get season stats"):
|
with pytest.raises(ValueError, match="Failed to get season stats"):
|
||||||
|
|
@ -246,18 +238,14 @@ class TestGameService:
|
||||||
):
|
):
|
||||||
"""Test daily challenge failure."""
|
"""Test daily challenge failure."""
|
||||||
mock_client.get.return_value = mock_dynamic_response(
|
mock_client.get.return_value = mock_dynamic_response(
|
||||||
{"error": "Challenge not found"},
|
{"error": "Challenge not found"}, success=False, status_code=404
|
||||||
success=False,
|
|
||||||
status_code=404
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="Failed to get daily challenge"):
|
with pytest.raises(ValueError, match="Failed to get daily challenge"):
|
||||||
await game_service.get_daily_challenge()
|
await game_service.get_daily_challenge()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_battle_royale(
|
async def test_get_battle_royale(self, game_service, mock_client, mock_dynamic_response):
|
||||||
self, game_service, mock_client, mock_dynamic_response
|
|
||||||
):
|
|
||||||
"""Test battle royale game retrieval."""
|
"""Test battle royale game retrieval."""
|
||||||
br_data = {
|
br_data = {
|
||||||
"gameId": "br-123",
|
"gameId": "br-123",
|
||||||
|
|
@ -272,9 +260,7 @@ class TestGameService:
|
||||||
assert response.data["players"] == 10
|
assert response.data["players"] == 10
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_duel(
|
async def test_get_duel(self, game_service, mock_client, mock_dynamic_response):
|
||||||
self, game_service, mock_client, mock_dynamic_response
|
|
||||||
):
|
|
||||||
"""Test duel game retrieval."""
|
"""Test duel game retrieval."""
|
||||||
duel_data = {
|
duel_data = {
|
||||||
"duelId": "duel-456",
|
"duelId": "duel-456",
|
||||||
|
|
@ -289,9 +275,7 @@ class TestGameService:
|
||||||
assert response.data["player1"]["score"] == 5000
|
assert response.data["player1"]["score"] == 5000
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_tournaments(
|
async def test_get_tournaments(self, game_service, mock_client, mock_dynamic_response):
|
||||||
self, game_service, mock_client, mock_dynamic_response
|
|
||||||
):
|
|
||||||
"""Test tournaments retrieval."""
|
"""Test tournaments retrieval."""
|
||||||
tournaments_data = [
|
tournaments_data = [
|
||||||
{"id": "t1", "name": "Weekly Tournament", "status": "active"},
|
{"id": "t1", "name": "Weekly Tournament", "status": "active"},
|
||||||
|
|
|
||||||
|
|
@ -46,14 +46,10 @@ class TestProfileService:
|
||||||
assert call_args[0][1] == "test_token"
|
assert call_args[0][1] == "test_token"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_profile_failure(
|
async def test_get_profile_failure(self, profile_service, mock_client, mock_dynamic_response):
|
||||||
self, profile_service, mock_client, mock_dynamic_response
|
|
||||||
):
|
|
||||||
"""Test profile retrieval failure."""
|
"""Test profile retrieval failure."""
|
||||||
mock_client.get.return_value = mock_dynamic_response(
|
mock_client.get.return_value = mock_dynamic_response(
|
||||||
{"error": "Unauthorized"},
|
{"error": "Unauthorized"}, success=False, status_code=401
|
||||||
success=False,
|
|
||||||
status_code=401
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="Failed to get profile"):
|
with pytest.raises(ValueError, match="Failed to get profile"):
|
||||||
|
|
@ -75,23 +71,17 @@ class TestProfileService:
|
||||||
assert stats.win_rate == 0.65
|
assert stats.win_rate == 0.65
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_stats_failure(
|
async def test_get_stats_failure(self, profile_service, mock_client, mock_dynamic_response):
|
||||||
self, profile_service, mock_client, mock_dynamic_response
|
|
||||||
):
|
|
||||||
"""Test stats retrieval failure."""
|
"""Test stats retrieval failure."""
|
||||||
mock_client.get.return_value = mock_dynamic_response(
|
mock_client.get.return_value = mock_dynamic_response(
|
||||||
{"error": "Server error"},
|
{"error": "Server error"}, success=False, status_code=500
|
||||||
success=False,
|
|
||||||
status_code=500
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="Failed to get stats"):
|
with pytest.raises(ValueError, match="Failed to get stats"):
|
||||||
await profile_service.get_stats()
|
await profile_service.get_stats()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_extended_stats(
|
async def test_get_extended_stats(self, profile_service, mock_client, mock_dynamic_response):
|
||||||
self, profile_service, mock_client, mock_dynamic_response
|
|
||||||
):
|
|
||||||
"""Test extended stats retrieval."""
|
"""Test extended stats retrieval."""
|
||||||
extended_data = {
|
extended_data = {
|
||||||
"totalDistance": 1500000,
|
"totalDistance": 1500000,
|
||||||
|
|
@ -154,9 +144,7 @@ class TestProfileService:
|
||||||
assert achievements[0].name == "Winner"
|
assert achievements[0].name == "Winner"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_public_profile(
|
async def test_get_public_profile(self, profile_service, mock_client, mock_dynamic_response):
|
||||||
self, profile_service, mock_client, mock_dynamic_response
|
|
||||||
):
|
|
||||||
"""Test public profile retrieval."""
|
"""Test public profile retrieval."""
|
||||||
public_profile_data = {
|
public_profile_data = {
|
||||||
"id": "other-user-123",
|
"id": "other-user-123",
|
||||||
|
|
@ -172,9 +160,7 @@ class TestProfileService:
|
||||||
assert profile.nick == "OtherPlayer"
|
assert profile.nick == "OtherPlayer"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_user_maps(
|
async def test_get_user_maps(self, profile_service, mock_client, mock_dynamic_response):
|
||||||
self, profile_service, mock_client, mock_dynamic_response
|
|
||||||
):
|
|
||||||
"""Test user maps retrieval."""
|
"""Test user maps retrieval."""
|
||||||
maps_data = [
|
maps_data = [
|
||||||
{"id": "map-1", "name": "My Custom Map"},
|
{"id": "map-1", "name": "My Custom Map"},
|
||||||
|
|
@ -189,7 +175,12 @@ class TestProfileService:
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_comprehensive_profile_success(
|
async def test_get_comprehensive_profile_success(
|
||||||
self, profile_service, mock_client, mock_profile_data, mock_stats_data, mock_dynamic_response
|
self,
|
||||||
|
profile_service,
|
||||||
|
mock_client,
|
||||||
|
mock_profile_data,
|
||||||
|
mock_stats_data,
|
||||||
|
mock_dynamic_response,
|
||||||
):
|
):
|
||||||
"""Test comprehensive profile aggregation."""
|
"""Test comprehensive profile aggregation."""
|
||||||
# Setup mock responses for each call
|
# Setup mock responses for each call
|
||||||
|
|
@ -197,9 +188,11 @@ class TestProfileService:
|
||||||
mock_dynamic_response(mock_profile_data), # profile
|
mock_dynamic_response(mock_profile_data), # profile
|
||||||
mock_dynamic_response(mock_stats_data), # stats
|
mock_dynamic_response(mock_stats_data), # stats
|
||||||
mock_dynamic_response({"totalDistance": 1000}), # extended stats
|
mock_dynamic_response({"totalDistance": 1000}), # extended stats
|
||||||
mock_dynamic_response([ # achievements
|
mock_dynamic_response(
|
||||||
|
[ # achievements
|
||||||
{"id": "ach-1", "name": "Test", "unlocked": True, "unlockedAt": "2024-01-01"},
|
{"id": "ach-1", "name": "Test", "unlocked": True, "unlockedAt": "2024-01-01"},
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
result = await profile_service.get_comprehensive_profile()
|
result = await profile_service.get_comprehensive_profile()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue