major-domo-v2/tests/test_services_league_service.py
Cal Corum 8897b7fa5e CLAUDE: Add logged_command decorator and migrate Discord commands to reduce boilerplate
- Add @logged_command decorator in utils/decorators.py to eliminate try/catch/finally boilerplate
- Migrate all Discord commands to use new decorator pattern:
  * commands/league/info.py - /league command
  * commands/players/info.py - /player command
  * commands/teams/info.py - /team and /teams commands
  * commands/teams/roster.py - /roster command
- Fix PyLance type issues by making model IDs required for database entities
- Update Player and Team models to require id field since they come from database
- Fix test cases to provide required id values
- Add comprehensive test coverage for decorator functionality
- Add migration guide for applying decorator to additional commands
- Reduce codebase by ~100 lines of repetitive logging boilerplate

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-15 14:56:42 -05:00

356 lines
13 KiB
Python

"""
Tests for league service functionality
Comprehensive testing of league-related operations including current state,
standings, division standings, and league leaders.
"""
import pytest
from unittest.mock import AsyncMock, patch
from typing import Dict, Any, List
from services.league_service import LeagueService, league_service
from models.current import Current
from exceptions import APIException
class TestLeagueService:
"""Test league service functionality."""
@pytest.fixture
def mock_current_data(self) -> Dict[str, Any]:
"""Mock current league state data."""
return {
'week': 10,
'season': 12,
'freeze': False,
'bet_week': 'sheets',
'trade_deadline': 14,
'pick_trade_start': 15,
'pick_trade_end': 18,
'playoffs_begin': 19
}
@pytest.fixture
def mock_standings_data(self) -> List[Dict[str, Any]]:
"""Mock standings data."""
return [
{
'abbrev': 'NYY',
'wins': 85,
'losses': 45,
'pct': 0.654,
'gb': 0,
'division_id': 1
},
{
'abbrev': 'BOS',
'wins': 80,
'losses': 50,
'pct': 0.615,
'gb': 5,
'division_id': 1
},
{
'abbrev': 'LAD',
'wins': 88,
'losses': 42,
'pct': 0.677,
'gb': 0,
'division_id': 2
}
]
@pytest.fixture
def mock_leaders_data(self) -> List[Dict[str, Any]]:
"""Mock league leaders data."""
return [
{
'name': 'Mike Trout',
'avg': 0.325,
'war': 8.5,
'ops': 1.050
},
{
'name': 'Mookie Betts',
'avg': 0.318,
'war': 7.8,
'ops': 1.025
},
{
'name': 'Aaron Judge',
'avg': 0.305,
'war': 7.2,
'ops': 1.015
}
]
@pytest.mark.asyncio
async def test_get_current_state_success(self, mock_current_data):
"""Test successful retrieval of current league state."""
service = LeagueService()
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.return_value = mock_current_data
mock_client.return_value = mock_api
result = await service.get_current_state()
assert result is not None
assert isinstance(result, Current)
assert result.week == 10
assert result.season == 12
assert result.freeze is False
assert result.trade_deadline == 14
mock_api.get.assert_called_once_with('current')
@pytest.mark.asyncio
async def test_get_current_state_no_data(self):
"""Test get_current_state when no data is returned."""
service = LeagueService()
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.return_value = None
mock_client.return_value = mock_api
result = await service.get_current_state()
assert result is None
mock_api.get.assert_called_once_with('current')
@pytest.mark.asyncio
async def test_get_current_state_exception(self):
"""Test get_current_state exception handling."""
service = LeagueService()
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.side_effect = Exception("API Error")
mock_client.return_value = mock_api
result = await service.get_current_state()
assert result is None
@pytest.mark.asyncio
async def test_get_standings_success_list(self, mock_standings_data):
"""Test successful retrieval of standings as list."""
service = LeagueService()
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.return_value = mock_standings_data
mock_client.return_value = mock_api
result = await service.get_standings(12)
assert result is not None
assert len(result) == 3
assert result[0]['abbrev'] == 'NYY'
assert result[0]['wins'] == 85
mock_api.get.assert_called_once_with('standings', params=[('season', '12')])
@pytest.mark.asyncio
async def test_get_standings_success_dict(self, mock_standings_data):
"""Test successful retrieval of standings wrapped in dict."""
service = LeagueService()
wrapped_data = {'standings': mock_standings_data}
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.return_value = wrapped_data
mock_client.return_value = mock_api
result = await service.get_standings()
assert result is not None
assert len(result) == 3
assert result[0]['abbrev'] == 'NYY'
mock_api.get.assert_called_once_with('standings', params=[('season', '12')])
@pytest.mark.asyncio
async def test_get_standings_no_data(self):
"""Test get_standings when no data is returned."""
service = LeagueService()
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.return_value = None
mock_client.return_value = mock_api
result = await service.get_standings()
assert result is None
@pytest.mark.asyncio
async def test_get_standings_exception(self):
"""Test get_standings exception handling."""
service = LeagueService()
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.side_effect = Exception("API Error")
mock_client.return_value = mock_api
result = await service.get_standings()
assert result is None
@pytest.mark.asyncio
async def test_get_division_standings_success(self):
"""Test successful retrieval of division standings."""
service = LeagueService()
division_data = [
{'abbrev': 'NYY', 'wins': 85, 'losses': 45, 'division_id': 1},
{'abbrev': 'BOS', 'wins': 80, 'losses': 50, 'division_id': 1}
]
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.return_value = division_data
mock_client.return_value = mock_api
result = await service.get_division_standings(1, 12)
assert result is not None
assert len(result) == 2
assert all(team['division_id'] == 1 for team in result)
mock_api.get.assert_called_once_with('standings/division/1', params=[('season', '12')])
@pytest.mark.asyncio
async def test_get_division_standings_no_data(self):
"""Test get_division_standings when no data is returned."""
service = LeagueService()
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.return_value = None
mock_client.return_value = mock_api
result = await service.get_division_standings(1)
assert result is None
@pytest.mark.asyncio
async def test_get_division_standings_exception(self):
"""Test get_division_standings exception handling."""
service = LeagueService()
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.side_effect = Exception("API Error")
mock_client.return_value = mock_api
result = await service.get_division_standings(1, 12)
assert result is None
@pytest.mark.asyncio
async def test_get_league_leaders_success_list(self, mock_leaders_data):
"""Test successful retrieval of league leaders as list."""
service = LeagueService()
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.return_value = mock_leaders_data
mock_client.return_value = mock_api
result = await service.get_league_leaders('batting', 12, 10)
assert result is not None
assert len(result) == 3
assert result[0]['name'] == 'Mike Trout'
assert result[0]['avg'] == 0.325
expected_params = [('season', '12'), ('limit', '10')]
mock_api.get.assert_called_once_with('leaders/batting', params=expected_params)
@pytest.mark.asyncio
async def test_get_league_leaders_success_dict(self, mock_leaders_data):
"""Test successful retrieval of league leaders wrapped in dict."""
service = LeagueService()
wrapped_data = {'leaders': mock_leaders_data}
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.return_value = wrapped_data
mock_client.return_value = mock_api
result = await service.get_league_leaders('pitching', 12, 5)
assert result is not None
assert len(result) == 3
assert result[0]['name'] == 'Mike Trout'
expected_params = [('season', '12'), ('limit', '5')]
mock_api.get.assert_called_once_with('leaders/pitching', params=expected_params)
@pytest.mark.asyncio
async def test_get_league_leaders_limit_enforcement(self, mock_leaders_data):
"""Test that league leaders respects the limit parameter."""
service = LeagueService()
long_list = mock_leaders_data * 5 # 15 items
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.return_value = long_list
mock_client.return_value = mock_api
result = await service.get_league_leaders('batting', 12, 5)
assert result is not None
assert len(result) == 5 # Should be limited to 5
@pytest.mark.asyncio
async def test_get_league_leaders_default_params(self, mock_leaders_data):
"""Test league leaders with default parameters."""
service = LeagueService()
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.return_value = mock_leaders_data
mock_client.return_value = mock_api
result = await service.get_league_leaders()
assert result is not None
expected_params = [('season', '12'), ('limit', '10')]
mock_api.get.assert_called_once_with('leaders/batting', params=expected_params)
@pytest.mark.asyncio
async def test_get_league_leaders_no_data(self):
"""Test get_league_leaders when no data is returned."""
service = LeagueService()
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.return_value = None
mock_client.return_value = mock_api
result = await service.get_league_leaders()
assert result is None
@pytest.mark.asyncio
async def test_get_league_leaders_exception(self):
"""Test get_league_leaders exception handling."""
service = LeagueService()
with patch.object(service, 'get_client') as mock_client:
mock_api = AsyncMock()
mock_api.get.side_effect = Exception("API Error")
mock_client.return_value = mock_api
result = await service.get_league_leaders('batting', 12)
assert result is None
def test_league_service_global_instance(self):
"""Test that global league_service instance exists."""
assert league_service is not None
assert isinstance(league_service, LeagueService)