Fix format with black

This commit is contained in:
Yûki VACHOT 2025-11-29 06:29:35 +01:00
parent 265e9d25d3
commit 0482fff8c5
9 changed files with 100 additions and 99 deletions

View file

@ -1,7 +1,6 @@
"""Monitoring module for API endpoint tracking and schema detection."""
from .endpoint.endpoint_monitor import (MONITORED_ENDPOINTS, EndpointMonitor,
endpoint_monitor)
from .endpoint.endpoint_monitor import MONITORED_ENDPOINTS, EndpointMonitor, endpoint_monitor
from .schema.endpoint_schema import EndpointSchema
from .schema.schema_detector import SchemaDetector, SchemaField
from .schema.schema_registry import SchemaRegistry, schema_registry

View file

@ -74,9 +74,7 @@ def register_game_tools(mcp: FastMCP, game_service: GameService):
"success": True,
"total_entries": len(entries),
"entry_types": list(categorized.keys()),
"entries_by_type": {
t: len(e) for t, e in categorized.items()
},
"entries_by_type": {t: len(e) for t, e in categorized.items()},
"recent_entries": entries[:5], # First 5 for context
"available_fields": response.available_fields,
}

View file

@ -36,6 +36,7 @@ def register_monitoring_tools(mcp: FastMCP):
session_token = get_current_session_token()
if session_token:
from ..auth.session import SessionManager
session_manager = SessionManager()
session = await session_manager.get_session(session_token)
if session:
@ -136,14 +137,16 @@ def register_monitoring_tools(mcp: FastMCP):
previous = history[-1] if history else None
if current and previous:
changes.append({
changes.append(
{
"endpoint": endpoint,
"current_hash": current.schema_hash,
"previous_hash": previous.schema_hash,
"current_fields": len(current.fields),
"previous_fields": len(previous.fields),
"changed_at": current.last_updated.isoformat(),
})
}
)
return {
"total_changes_tracked": len(changes),

View file

@ -87,7 +87,9 @@ def register_profile_tools(mcp: FastMCP, profile_service: ProfileService):
"total": len(achievements),
"unlocked": len(unlocked),
"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": [
{"name": a.name, "description": a.description, "unlocked_at": a.unlocked_at}

View file

@ -1,4 +1,5 @@
"""Shared test fixtures."""
import os
from unittest.mock import AsyncMock, MagicMock, patch
@ -16,7 +17,7 @@ from geoguessr_mcp.services import AnalysisService, GameService, ProfileService
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:
if "real_env" in request.keywords:
yield
return
@ -25,6 +26,7 @@ def mock_env(request, monkeypatch):
# 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

View file

@ -304,6 +304,7 @@ class TestGeoGuessrClientIntegration:
"""Test real API call to profile endpoint."""
# This test requires GEOGUESSR_NCFA_COOKIE to be set
import os
if not os.environ.get("GEOGUESSR_NCFA_COOKIE"):
pytest.skip("GEOGUESSR_NCFA_COOKIE not set")
@ -317,6 +318,7 @@ class TestGeoGuessrClientIntegration:
"""Test real API call to stats' endpoint."""
# This test requires GEOGUESSR_NCFA_COOKIE to be set
import os
if not os.environ.get("GEOGUESSR_NCFA_COOKIE"):
pytest.skip("GEOGUESSR_NCFA_COOKIE not set")

View file

@ -103,7 +103,9 @@ class TestAnalysisService:
games = []
for i in range(6):
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(
token=f"game-{i}",
map_name="World",
@ -124,7 +126,9 @@ class TestAnalysisService:
games = []
for i in range(6):
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(
token=f"game-{i}",
map_name="World",
@ -182,17 +186,11 @@ class TestAnalysisService:
assert all(area["score"] >= 4500 for area in result.strong_areas)
@pytest.mark.asyncio
async def test_analyze_recent_games(
self, analysis_service, mock_game_service, sample_games
):
async def test_analyze_recent_games(self, analysis_service, mock_game_service, sample_games):
"""Test analyze_recent_games method."""
mock_game_service.get_recent_games.return_value = sample_games
with patch.object(
analysis_service,
'analyze_games',
wraps=AnalysisService.analyze_games
):
with patch.object(analysis_service, "analyze_games", wraps=AnalysisService.analyze_games):
result = await analysis_service.analyze_recent_games(count=5)
assert "analysis" in result
@ -207,17 +205,20 @@ class TestAnalysisService:
"""Test analyze_recent_games with session token."""
mock_game_service.get_recent_games.return_value = sample_games
await analysis_service.analyze_recent_games(
count=10,
session_token="test_token"
)
await analysis_service.analyze_recent_games(count=10, session_token="test_token")
mock_game_service.get_recent_games.assert_called_once_with(10, "test_token")
@pytest.mark.asyncio
async def test_get_performance_summary(
self, analysis_service, mock_game_service, mock_profile_service,
mock_client, sample_games, mock_season_stats_data, mock_dynamic_response
self,
analysis_service,
mock_game_service,
mock_profile_service,
mock_client,
sample_games,
mock_season_stats_data,
mock_dynamic_response,
):
"""Test comprehensive performance summary."""
mock_profile_service.get_comprehensive_profile.return_value = {
@ -227,6 +228,7 @@ class TestAnalysisService:
mock_season_response = mock_dynamic_response(mock_season_stats_data)
from geoguessr_mcp.models import SeasonStats
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_recent_games.return_value = sample_games[:3]
@ -267,7 +269,14 @@ class TestAnalysisService:
for i in range(5)
]
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)
]
mock_game_service.get_recent_games.return_value = games
@ -289,7 +298,14 @@ class TestAnalysisService:
for i in range(5)
]
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)
]
mock_game_service.get_recent_games.return_value = games
@ -308,7 +324,9 @@ class TestAnalysisService:
games = []
for i in range(6):
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(
token=f"game-{i}",
map_name="World",

View file

@ -40,32 +40,23 @@ class TestGameService:
"""Test game details with explicit session token."""
mock_client.get.return_value = mock_dynamic_response(mock_game_data)
game, response = await game_service.get_game_details(
"ABC123",
session_token="test_token"
)
game, response = await game_service.get_game_details("ABC123", session_token="test_token")
call_args = mock_client.get.call_args
assert call_args[0][1] == "test_token"
@pytest.mark.asyncio
async def test_get_game_details_failure(
self, game_service, mock_client, mock_dynamic_response
):
async def test_get_game_details_failure(self, game_service, mock_client, mock_dynamic_response):
"""Test game details retrieval failure."""
mock_client.get.return_value = mock_dynamic_response(
{"error": "Game not found"},
success=False,
status_code=404
{"error": "Game not found"}, success=False, status_code=404
)
with pytest.raises(ValueError, match="Failed to get game details"):
await game_service.get_game_details("INVALID")
@pytest.mark.asyncio
async def test_get_unfinished_games(
self, game_service, mock_client, mock_dynamic_response
):
async def test_get_unfinished_games(self, game_service, mock_client, mock_dynamic_response):
"""Test unfinished games retrieval."""
unfinished_data = [
{"token": "game-1", "map": {"name": "World"}},
@ -79,9 +70,7 @@ class TestGameService:
assert len(response.data) == 2
@pytest.mark.asyncio
async def test_get_streak_game(
self, game_service, mock_client, mock_dynamic_response
):
async def test_get_streak_game(self, game_service, mock_client, mock_dynamic_response):
"""Test streak game retrieval."""
streak_data = {
"token": "streak-123",
@ -122,7 +111,12 @@ class TestGameService:
@pytest.mark.asyncio
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."""
# First call returns activity feed, subsequent calls return game details
@ -153,10 +147,7 @@ class TestGameService:
self, game_service, mock_client, mock_dynamic_response
):
"""Test recent games when feed fails."""
mock_client.get.return_value = mock_dynamic_response(
{"error": "Failed"},
success=False
)
mock_client.get.return_value = mock_dynamic_response({"error": "Failed"}, success=False)
games = await game_service.get_recent_games(count=5)
@ -164,7 +155,12 @@ class TestGameService:
@pytest.mark.asyncio
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."""
mock_client.get.side_effect = [
@ -193,14 +189,10 @@ class TestGameService:
assert stats.division == "Gold"
@pytest.mark.asyncio
async def test_get_season_stats_failure(
self, game_service, mock_client, mock_dynamic_response
):
async def test_get_season_stats_failure(self, game_service, mock_client, mock_dynamic_response):
"""Test season stats failure."""
mock_client.get.return_value = mock_dynamic_response(
{"error": "No active season"},
success=False,
status_code=404
{"error": "No active season"}, success=False, status_code=404
)
with pytest.raises(ValueError, match="Failed to get season stats"):
@ -246,18 +238,14 @@ class TestGameService:
):
"""Test daily challenge failure."""
mock_client.get.return_value = mock_dynamic_response(
{"error": "Challenge not found"},
success=False,
status_code=404
{"error": "Challenge not found"}, success=False, status_code=404
)
with pytest.raises(ValueError, match="Failed to get daily challenge"):
await game_service.get_daily_challenge()
@pytest.mark.asyncio
async def test_get_battle_royale(
self, game_service, mock_client, mock_dynamic_response
):
async def test_get_battle_royale(self, game_service, mock_client, mock_dynamic_response):
"""Test battle royale game retrieval."""
br_data = {
"gameId": "br-123",
@ -272,9 +260,7 @@ class TestGameService:
assert response.data["players"] == 10
@pytest.mark.asyncio
async def test_get_duel(
self, game_service, mock_client, mock_dynamic_response
):
async def test_get_duel(self, game_service, mock_client, mock_dynamic_response):
"""Test duel game retrieval."""
duel_data = {
"duelId": "duel-456",
@ -289,9 +275,7 @@ class TestGameService:
assert response.data["player1"]["score"] == 5000
@pytest.mark.asyncio
async def test_get_tournaments(
self, game_service, mock_client, mock_dynamic_response
):
async def test_get_tournaments(self, game_service, mock_client, mock_dynamic_response):
"""Test tournaments retrieval."""
tournaments_data = [
{"id": "t1", "name": "Weekly Tournament", "status": "active"},

View file

@ -46,14 +46,10 @@ class TestProfileService:
assert call_args[0][1] == "test_token"
@pytest.mark.asyncio
async def test_get_profile_failure(
self, profile_service, mock_client, mock_dynamic_response
):
async def test_get_profile_failure(self, profile_service, mock_client, mock_dynamic_response):
"""Test profile retrieval failure."""
mock_client.get.return_value = mock_dynamic_response(
{"error": "Unauthorized"},
success=False,
status_code=401
{"error": "Unauthorized"}, success=False, status_code=401
)
with pytest.raises(ValueError, match="Failed to get profile"):
@ -75,23 +71,17 @@ class TestProfileService:
assert stats.win_rate == 0.65
@pytest.mark.asyncio
async def test_get_stats_failure(
self, profile_service, mock_client, mock_dynamic_response
):
async def test_get_stats_failure(self, profile_service, mock_client, mock_dynamic_response):
"""Test stats retrieval failure."""
mock_client.get.return_value = mock_dynamic_response(
{"error": "Server error"},
success=False,
status_code=500
{"error": "Server error"}, success=False, status_code=500
)
with pytest.raises(ValueError, match="Failed to get stats"):
await profile_service.get_stats()
@pytest.mark.asyncio
async def test_get_extended_stats(
self, profile_service, mock_client, mock_dynamic_response
):
async def test_get_extended_stats(self, profile_service, mock_client, mock_dynamic_response):
"""Test extended stats retrieval."""
extended_data = {
"totalDistance": 1500000,
@ -154,9 +144,7 @@ class TestProfileService:
assert achievements[0].name == "Winner"
@pytest.mark.asyncio
async def test_get_public_profile(
self, profile_service, mock_client, mock_dynamic_response
):
async def test_get_public_profile(self, profile_service, mock_client, mock_dynamic_response):
"""Test public profile retrieval."""
public_profile_data = {
"id": "other-user-123",
@ -172,9 +160,7 @@ class TestProfileService:
assert profile.nick == "OtherPlayer"
@pytest.mark.asyncio
async def test_get_user_maps(
self, profile_service, mock_client, mock_dynamic_response
):
async def test_get_user_maps(self, profile_service, mock_client, mock_dynamic_response):
"""Test user maps retrieval."""
maps_data = [
{"id": "map-1", "name": "My Custom Map"},
@ -189,7 +175,12 @@ class TestProfileService:
@pytest.mark.asyncio
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."""
# Setup mock responses for each call
@ -197,9 +188,11 @@ class TestProfileService:
mock_dynamic_response(mock_profile_data), # profile
mock_dynamic_response(mock_stats_data), # 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"},
]),
]
),
]
result = await profile_service.get_comprehensive_profile()