112 lines
3.8 KiB
Python
112 lines
3.8 KiB
Python
"""
|
|
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
|