""" Tests for the ProfileService behaviors and functionality. This module includes tests for various aspects of ProfileService such as profile management, statistics retrieval, achievement retrieval, and handling edge cases or failures during service operations. """ import pytest from geoguessr_mcp.models import UserProfile, UserStats, Achievement class TestProfileService: """Tests for ProfileService.""" @pytest.mark.asyncio async def test_get_profile_success( self, profile_service, mock_client, mock_profile_data, mock_dynamic_response ): """Test successful profile retrieval.""" mock_client.get.return_value = mock_dynamic_response(mock_profile_data) profile, response = await profile_service.get_profile() assert isinstance(profile, UserProfile) assert profile.id == "test-user-id" assert profile.nick == "TestPlayer" assert profile.email == "test@example.com" assert profile.country == "FR" assert profile.level == 50 mock_client.get.assert_called_once() @pytest.mark.asyncio async def test_get_profile_with_session_token( self, profile_service, mock_client, mock_profile_data, mock_dynamic_response ): """Test profile retrieval with explicit session token.""" mock_client.get.return_value = mock_dynamic_response(mock_profile_data) await profile_service.get_profile(session_token="test_token") mock_client.get.assert_called_once() call_args = mock_client.get.call_args assert call_args[0][1] == "test_token" @pytest.mark.asyncio 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 ) with pytest.raises(ValueError, match="Failed to get profile"): await profile_service.get_profile() @pytest.mark.asyncio async def test_get_stats_success( self, profile_service, mock_client, mock_stats_data, mock_dynamic_response ): """Test successful stats retrieval.""" mock_client.get.return_value = mock_dynamic_response(mock_stats_data) stats, response = await profile_service.get_stats() assert isinstance(stats, UserStats) assert stats.games_played == 100 assert stats.rounds_played == 500 assert stats.total_score == 2250000 assert stats.win_rate == 0.65 @pytest.mark.asyncio 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 ) 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): """Test extended stats retrieval.""" extended_data = { "totalDistance": 1500000, "averageTime": 45.5, "favoriteMap": "World", } mock_client.get.return_value = mock_dynamic_response(extended_data) response = await profile_service.get_extended_stats() assert response.is_success assert response.data["totalDistance"] == 1500000 @pytest.mark.asyncio async def test_get_achievements_list_format( self, profile_service, mock_client, mock_dynamic_response ): """Test achievements retrieval with list format response.""" achievements_data = [ { "id": "ach-1", "name": "First Win", "description": "Win your first game", "unlocked": True, "unlockedAt": "2024-01-15T00:00:00Z", }, { "id": "ach-2", "name": "Explorer", "description": "Play 100 games", "unlocked": False, "progress": 0.45, }, ] mock_client.get.return_value = mock_dynamic_response(achievements_data) achievements, response = await profile_service.get_achievements() assert len(achievements) == 2 assert all(isinstance(a, Achievement) for a in achievements) assert achievements[0].name == "First Win" assert achievements[0].unlocked is True assert achievements[1].unlocked is False @pytest.mark.asyncio async def test_get_achievements_dict_format( self, profile_service, mock_client, mock_dynamic_response ): """Test achievements retrieval with dict format response.""" achievements_data = { "achievements": [ {"id": "ach-1", "name": "Winner", "unlocked": True}, ] } mock_client.get.return_value = mock_dynamic_response(achievements_data) achievements, response = await profile_service.get_achievements() assert len(achievements) == 1 assert achievements[0].name == "Winner" @pytest.mark.asyncio 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", "nick": "OtherPlayer", "country": "US", "level": 75, } mock_client.get.return_value = mock_dynamic_response(public_profile_data) profile, response = await profile_service.get_public_profile("other-user-123") assert profile.id == "other-user-123" assert profile.nick == "OtherPlayer" @pytest.mark.asyncio 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"}, {"id": "map-2", "name": "Another Map"}, ] mock_client.get.return_value = mock_dynamic_response(maps_data) response = await profile_service.get_user_maps() assert response.is_success assert len(response.data) == 2 @pytest.mark.asyncio async def test_get_comprehensive_profile_success( self, profile_service, mock_client, mock_profile_data, mock_stats_data, mock_dynamic_response, ): """Test comprehensive profile aggregation.""" # Setup mock responses for each call mock_client.get.side_effect = [ 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 {"id": "ach-1", "name": "Test", "unlocked": True, "unlockedAt": "2024-01-01"}, ] ), ] result = await profile_service.get_comprehensive_profile() assert result["profile"] is not None assert result["profile"]["nick"] == "TestPlayer" assert result["stats"] is not None assert result["stats"]["games_played"] == 100 assert result["extended_stats"] is not None assert result["achievements"]["total"] == 1 assert result["achievements"]["unlocked"] == 1 assert len(result["errors"]) == 0 @pytest.mark.asyncio async def test_get_comprehensive_profile_partial_failure( self, profile_service, mock_client, mock_profile_data, mock_dynamic_response ): """Test comprehensive profile with some endpoints failing.""" mock_client.get.side_effect = [ mock_dynamic_response(mock_profile_data), # profile succeeds Exception("Stats endpoint down"), # stats fails mock_dynamic_response({"data": "test"}), # extended stats succeed Exception("Achievements unavailable"), # achievements fails ] result = await profile_service.get_comprehensive_profile() assert result["profile"] is not None assert result["stats"] is None assert result["extended_stats"] is not None assert result["achievements"] is None assert len(result["errors"]) == 2 assert any("Stats" in e for e in result["errors"]) assert any("Achievements" in e for e in result["errors"])