""" Tests for StatsService Validates stats service functionality including concurrent stat retrieval and error handling in get_player_stats(). """ import pytest from unittest.mock import AsyncMock, MagicMock from services.stats_service import StatsService class TestStatsServiceGetPlayerStats: """Test StatsService.get_player_stats() concurrent retrieval.""" @pytest.fixture def service(self): """Create a fresh StatsService instance for testing.""" return StatsService() @pytest.fixture def mock_batting_stats(self): """Create a mock BattingStats object.""" stats = MagicMock() stats.avg = 0.300 return stats @pytest.fixture def mock_pitching_stats(self): """Create a mock PitchingStats object.""" stats = MagicMock() stats.era = 3.50 return stats @pytest.mark.asyncio async def test_both_stats_returned( self, service, mock_batting_stats, mock_pitching_stats ): """When both batting and pitching stats exist, both are returned. Verifies that get_player_stats returns a tuple of (batting, pitching) when both stat types are available for the player. """ service.get_batting_stats = AsyncMock(return_value=mock_batting_stats) service.get_pitching_stats = AsyncMock(return_value=mock_pitching_stats) batting, pitching = await service.get_player_stats(player_id=100, season=12) assert batting is mock_batting_stats assert pitching is mock_pitching_stats service.get_batting_stats.assert_called_once_with(100, 12) service.get_pitching_stats.assert_called_once_with(100, 12) @pytest.mark.asyncio async def test_batting_only(self, service, mock_batting_stats): """When only batting stats exist, pitching is None. Covers the case of a position player with no pitching record. """ service.get_batting_stats = AsyncMock(return_value=mock_batting_stats) service.get_pitching_stats = AsyncMock(return_value=None) batting, pitching = await service.get_player_stats(player_id=200, season=12) assert batting is mock_batting_stats assert pitching is None @pytest.mark.asyncio async def test_pitching_only(self, service, mock_pitching_stats): """When only pitching stats exist, batting is None. Covers the case of a pitcher with no batting record. """ service.get_batting_stats = AsyncMock(return_value=None) service.get_pitching_stats = AsyncMock(return_value=mock_pitching_stats) batting, pitching = await service.get_player_stats(player_id=300, season=12) assert batting is None assert pitching is mock_pitching_stats @pytest.mark.asyncio async def test_no_stats_found(self, service): """When no stats exist for the player, both are None. Covers the case where a player has no stats for the given season (e.g., didn't play). """ service.get_batting_stats = AsyncMock(return_value=None) service.get_pitching_stats = AsyncMock(return_value=None) batting, pitching = await service.get_player_stats(player_id=400, season=12) assert batting is None assert pitching is None @pytest.mark.asyncio async def test_exception_returns_none_tuple(self, service): """When an exception occurs, (None, None) is returned. The get_player_stats method wraps both calls in a try/except and returns (None, None) on any error, ensuring callers always get a tuple. """ service.get_batting_stats = AsyncMock(side_effect=RuntimeError("API down")) service.get_pitching_stats = AsyncMock(return_value=None) batting, pitching = await service.get_player_stats(player_id=500, season=12) assert batting is None assert pitching is None