289 lines
10 KiB
Python
289 lines
10 KiB
Python
"""
|
|
A module for testing the functionalities of `GameService` in the GeoGuessr
|
|
ecosystem.
|
|
|
|
This module contains a collection of test cases designed to validate
|
|
the correctness, edge cases, and error handling for various
|
|
asynchronous methods in the `GameService` class. These include
|
|
retrieving game details, unfinished games, streak games, activity
|
|
feeds, recent games, season statistics, and daily challenges.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from geoguessr_mcp.models import DailyChallenge, Game, SeasonStats
|
|
|
|
|
|
class TestGameService:
|
|
"""Tests for GameService."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_game_details_success(
|
|
self, game_service, mock_client, mock_game_data, mock_dynamic_response
|
|
):
|
|
"""Test successful game details retrieval."""
|
|
mock_client.get.return_value = mock_dynamic_response(mock_game_data)
|
|
|
|
game, response = await game_service.get_game_details("ABC123")
|
|
|
|
assert isinstance(game, Game)
|
|
assert game.token == "ABC123"
|
|
assert game.map_name == "World"
|
|
assert game.mode == "standard"
|
|
assert len(game.rounds) == 5
|
|
assert game.total_score == 23200 # Sum of all round scores
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_game_details_with_session_token(
|
|
self, game_service, mock_client, mock_game_data, mock_dynamic_response
|
|
):
|
|
"""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")
|
|
|
|
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):
|
|
"""Test game details retrieval failure."""
|
|
mock_client.get.return_value = mock_dynamic_response(
|
|
{"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):
|
|
"""Test unfinished games retrieval."""
|
|
unfinished_data = [
|
|
{"token": "game-1", "map": {"name": "World"}},
|
|
{"token": "game-2", "map": {"name": "Europe"}},
|
|
]
|
|
mock_client.get.return_value = mock_dynamic_response(unfinished_data)
|
|
|
|
response = await game_service.get_unfinished_games()
|
|
|
|
assert response.is_success
|
|
assert len(response.data) == 2
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_streak_game(self, game_service, mock_client, mock_dynamic_response):
|
|
"""Test streak game retrieval."""
|
|
streak_data = {
|
|
"token": "streak-123",
|
|
"currentStreak": 15,
|
|
"bestStreak": 25,
|
|
}
|
|
mock_client.get.return_value = mock_dynamic_response(streak_data)
|
|
|
|
response = await game_service.get_streak_game("streak-123")
|
|
|
|
assert response.is_success
|
|
assert response.data["currentStreak"] == 15
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_activity_feed(
|
|
self, game_service, mock_client, mock_activity_feed_data, mock_dynamic_response
|
|
):
|
|
"""Test activity feed retrieval."""
|
|
mock_client.get.return_value = mock_dynamic_response(mock_activity_feed_data)
|
|
|
|
response = await game_service.get_activity_feed(count=10, page=0)
|
|
|
|
assert response.is_success
|
|
assert len(response.data["entries"]) == 3
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_activity_feed_pagination(
|
|
self, game_service, mock_client, mock_dynamic_response
|
|
):
|
|
"""Test activity feed with pagination."""
|
|
page_2_data = {"entries": [{"type": "PlayedGame", "payload": {"gameToken": "old-game"}}]}
|
|
mock_client.get.return_value = mock_dynamic_response(page_2_data)
|
|
|
|
response = await game_service.get_activity_feed(count=10, page=1)
|
|
|
|
assert response.is_success
|
|
mock_client.get.assert_called_once()
|
|
|
|
@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,
|
|
):
|
|
"""Test recent games retrieval."""
|
|
# First call returns activity feed, subsequent calls return game details
|
|
mock_client.get.side_effect = [
|
|
mock_dynamic_response(mock_activity_feed_data),
|
|
mock_dynamic_response(mock_game_data),
|
|
mock_dynamic_response({**mock_game_data, "token": "game-token-2"}),
|
|
]
|
|
|
|
games = await game_service.get_recent_games(count=2)
|
|
|
|
assert len(games) == 2
|
|
assert all(isinstance(g, Game) for g in games)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_recent_games_empty_feed(
|
|
self, game_service, mock_client, mock_dynamic_response
|
|
):
|
|
"""Test recent games with empty activity feed."""
|
|
mock_client.get.return_value = mock_dynamic_response({"entries": []})
|
|
|
|
games = await game_service.get_recent_games(count=5)
|
|
|
|
assert len(games) == 0
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_recent_games_feed_failure(
|
|
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)
|
|
|
|
games = await game_service.get_recent_games(count=5)
|
|
|
|
assert len(games) == 0
|
|
|
|
@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,
|
|
):
|
|
"""Test that failed individual game fetches are skipped."""
|
|
mock_client.get.side_effect = [
|
|
mock_dynamic_response(mock_activity_feed_data),
|
|
Exception("Game fetch failed"), # First game fails
|
|
mock_dynamic_response(mock_game_data), # Second game succeeds
|
|
]
|
|
|
|
games = await game_service.get_recent_games(count=2)
|
|
|
|
assert len(games) == 1
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_season_stats_success(
|
|
self, game_service, mock_client, mock_season_stats_data, mock_dynamic_response
|
|
):
|
|
"""Test season stats retrieval."""
|
|
mock_client.get.return_value = mock_dynamic_response(mock_season_stats_data)
|
|
|
|
stats, response = await game_service.get_season_stats()
|
|
|
|
assert isinstance(stats, SeasonStats)
|
|
assert stats.season_id == "season-2024-1"
|
|
assert stats.rank == 150
|
|
assert stats.rating == 1850
|
|
assert stats.division == "Gold"
|
|
|
|
@pytest.mark.asyncio
|
|
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
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="Failed to get season stats"):
|
|
await game_service.get_season_stats()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_daily_challenge_today(
|
|
self, game_service, mock_client, mock_dynamic_response
|
|
):
|
|
"""Test daily challenge retrieval for today."""
|
|
challenge_data = {
|
|
"token": "daily-2024-01-15",
|
|
"map": {"name": "World"},
|
|
"date": "2024-01-15",
|
|
"timeLimit": 180,
|
|
}
|
|
mock_client.get.return_value = mock_dynamic_response(challenge_data)
|
|
|
|
challenge, response = await game_service.get_daily_challenge()
|
|
|
|
assert isinstance(challenge, DailyChallenge)
|
|
assert challenge.token == "daily-2024-01-15"
|
|
assert challenge.time_limit == 180
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_daily_challenge_specific_day(
|
|
self, game_service, mock_client, mock_dynamic_response
|
|
):
|
|
"""Test daily challenge for specific day."""
|
|
challenge_data = {
|
|
"token": "daily-2024-01-10",
|
|
"date": "2024-01-10",
|
|
}
|
|
mock_client.get.return_value = mock_dynamic_response(challenge_data)
|
|
|
|
challenge, response = await game_service.get_daily_challenge(day="2024-01-10")
|
|
|
|
assert challenge.date == "2024-01-10"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_daily_challenge_failure(
|
|
self, game_service, mock_client, mock_dynamic_response
|
|
):
|
|
"""Test daily challenge failure."""
|
|
mock_client.get.return_value = mock_dynamic_response(
|
|
{"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):
|
|
"""Test battle royale game retrieval."""
|
|
br_data = {
|
|
"gameId": "br-123",
|
|
"players": 10,
|
|
"status": "in_progress",
|
|
}
|
|
mock_client.get.return_value = mock_dynamic_response(br_data)
|
|
|
|
response = await game_service.get_battle_royale("br-123")
|
|
|
|
assert response.is_success
|
|
assert response.data["players"] == 10
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_duel(self, game_service, mock_client, mock_dynamic_response):
|
|
"""Test duel game retrieval."""
|
|
duel_data = {
|
|
"duelId": "duel-456",
|
|
"player1": {"id": "p1", "score": 5000},
|
|
"player2": {"id": "p2", "score": 4500},
|
|
}
|
|
mock_client.get.return_value = mock_dynamic_response(duel_data)
|
|
|
|
response = await game_service.get_duel("duel-456")
|
|
|
|
assert response.is_success
|
|
assert response.data["player1"]["score"] == 5000
|
|
|
|
@pytest.mark.asyncio
|
|
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"},
|
|
{"id": "t2", "name": "Monthly Cup", "status": "upcoming"},
|
|
]
|
|
mock_client.get.return_value = mock_dynamic_response(tournaments_data)
|
|
|
|
response = await game_service.get_tournaments()
|
|
|
|
assert response.is_success
|
|
assert len(response.data) == 2
|