CLAUDE: Add comprehensive tests for injury modal playoff week validation
Added 16 tests covering all aspects of injury modal validation including regular season and playoff-specific game limits. Test Coverage: - BatterInjuryModal week validation (5 tests) * Regular season weeks (1-18) acceptance * Playoff weeks (19-21) acceptance * Invalid weeks rejection (0, 22+) - BatterInjuryModal game validation (6 tests) * Regular season: games 1-4 valid, game 5+ rejected * Playoff round 1 (week 19): games 1-5 valid, game 6+ rejected * Playoff round 2 (week 20): games 1-7 valid * Playoff round 3 (week 21): games 1-7 valid - PitcherRestModal validation (4 tests) * Same week validation as BatterInjuryModal * Same game validation as BatterInjuryModal - Config-driven validation (1 test) * Verifies custom config values are respected All tests use proper mocking patterns: - PropertyMock for TextInput.value (read-only property) - Correct patch paths for config and services - Complete model data for Pydantic validation Test Results: 16/16 passing ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
2bfc87ac1b
commit
15d0513740
501
tests/test_views_injury_modals.py
Normal file
501
tests/test_views_injury_modals.py
Normal file
@ -0,0 +1,501 @@
|
||||
"""
|
||||
Tests for Injury Modal Validation in Discord Bot v2.0
|
||||
|
||||
Tests week and game validation for BatterInjuryModal and PitcherRestModal,
|
||||
including regular season and playoff round validation.
|
||||
"""
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock, Mock, patch, PropertyMock
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import discord
|
||||
|
||||
from views.modals import BatterInjuryModal, PitcherRestModal
|
||||
from views.embeds import EmbedTemplate
|
||||
from models.player import Player
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config():
|
||||
"""Mock configuration with standard season structure."""
|
||||
config = MagicMock()
|
||||
config.weeks_per_season = 18
|
||||
config.playoff_weeks_per_season = 3
|
||||
config.games_per_week = 4
|
||||
config.playoff_round_one_games = 5
|
||||
config.playoff_round_two_games = 7
|
||||
config.playoff_round_three_games = 7
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_player():
|
||||
"""Create a sample player for testing."""
|
||||
return Player(
|
||||
id=1,
|
||||
name="Test Player",
|
||||
wara=2.5,
|
||||
season=12,
|
||||
team_id=1,
|
||||
image="https://example.com/player.jpg",
|
||||
pos_1="1B"
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_interaction():
|
||||
"""Create a mock Discord interaction."""
|
||||
interaction = MagicMock(spec=discord.Interaction)
|
||||
interaction.response = MagicMock()
|
||||
interaction.response.send_message = AsyncMock()
|
||||
return interaction
|
||||
|
||||
|
||||
def create_mock_text_input(value: str):
|
||||
"""Create a mock TextInput with a specific value."""
|
||||
mock_input = MagicMock()
|
||||
type(mock_input).value = PropertyMock(return_value=value)
|
||||
return mock_input
|
||||
|
||||
|
||||
class TestBatterInjuryModalWeekValidation:
|
||||
"""Test week validation in BatterInjuryModal."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_regular_season_week_valid(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that regular season weeks (1-18) are accepted."""
|
||||
modal = BatterInjuryModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
# Mock the TextInput values
|
||||
modal.current_week = create_mock_text_input("10")
|
||||
modal.current_game = create_mock_text_input("2")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config), \
|
||||
patch('services.player_service.player_service') as mock_player_service, \
|
||||
patch('services.injury_service.injury_service') as mock_injury_service:
|
||||
|
||||
# Mock successful injury creation
|
||||
mock_injury_service.create_injury = AsyncMock(return_value=MagicMock(id=1))
|
||||
mock_player_service.update_player = AsyncMock()
|
||||
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should not send error message
|
||||
assert not any(
|
||||
call[1].get('embed') and
|
||||
'Invalid Week' in str(call[1]['embed'].title)
|
||||
for call in mock_interaction.response.send_message.call_args_list
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_playoff_week_19_valid(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that playoff week 19 (round 1) is accepted."""
|
||||
modal = BatterInjuryModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("19")
|
||||
modal.current_game = create_mock_text_input("3")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config), \
|
||||
patch('services.player_service.player_service') as mock_player_service, \
|
||||
patch('services.injury_service.injury_service') as mock_injury_service:
|
||||
|
||||
mock_injury_service.create_injury = AsyncMock(return_value=MagicMock(id=1))
|
||||
mock_player_service.update_player = AsyncMock()
|
||||
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should not send error message
|
||||
assert not any(
|
||||
call[1].get('embed') and
|
||||
'Invalid Week' in str(call[1]['embed'].title)
|
||||
for call in mock_interaction.response.send_message.call_args_list
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_playoff_week_21_valid(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that playoff week 21 (round 3) is accepted."""
|
||||
modal = BatterInjuryModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("21")
|
||||
modal.current_game = create_mock_text_input("5")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config), \
|
||||
patch('services.player_service.player_service') as mock_player_service, \
|
||||
patch('services.injury_service.injury_service') as mock_injury_service:
|
||||
|
||||
mock_injury_service.create_injury = AsyncMock(return_value=MagicMock(id=1))
|
||||
mock_player_service.update_player = AsyncMock()
|
||||
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should not send error message
|
||||
assert not any(
|
||||
call[1].get('embed') and
|
||||
'Invalid Week' in str(call[1]['embed'].title)
|
||||
for call in mock_interaction.response.send_message.call_args_list
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_week_too_high_rejected(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that week > 21 is rejected."""
|
||||
modal = BatterInjuryModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("22")
|
||||
modal.current_game = create_mock_text_input("2")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config):
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should send error message
|
||||
mock_interaction.response.send_message.assert_called_once()
|
||||
call_kwargs = mock_interaction.response.send_message.call_args[1]
|
||||
assert 'embed' in call_kwargs
|
||||
assert 'Invalid Week' in call_kwargs['embed'].title
|
||||
assert '21 (including playoffs)' in call_kwargs['embed'].description
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_week_zero_rejected(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that week 0 is rejected."""
|
||||
modal = BatterInjuryModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("0")
|
||||
modal.current_game = create_mock_text_input("2")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config):
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should send error message
|
||||
mock_interaction.response.send_message.assert_called_once()
|
||||
call_kwargs = mock_interaction.response.send_message.call_args[1]
|
||||
assert 'embed' in call_kwargs
|
||||
assert 'Invalid Week' in call_kwargs['embed'].title
|
||||
|
||||
|
||||
class TestBatterInjuryModalGameValidation:
|
||||
"""Test game validation in BatterInjuryModal."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_regular_season_game_4_valid(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that game 4 is accepted in regular season."""
|
||||
modal = BatterInjuryModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("10")
|
||||
modal.current_game = create_mock_text_input("4")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config), \
|
||||
patch('services.player_service.player_service') as mock_player_service, \
|
||||
patch('services.injury_service.injury_service') as mock_injury_service:
|
||||
|
||||
mock_injury_service.create_injury = AsyncMock(return_value=MagicMock(id=1))
|
||||
mock_player_service.update_player = AsyncMock()
|
||||
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should not send error about invalid game
|
||||
assert not any(
|
||||
call[1].get('embed') and
|
||||
'Invalid Game' in str(call[1]['embed'].title)
|
||||
for call in mock_interaction.response.send_message.call_args_list
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_regular_season_game_5_rejected(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that game 5 is rejected in regular season (only 4 games)."""
|
||||
modal = BatterInjuryModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("10")
|
||||
modal.current_game = create_mock_text_input("5")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config):
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should send error message
|
||||
mock_interaction.response.send_message.assert_called_once()
|
||||
call_kwargs = mock_interaction.response.send_message.call_args[1]
|
||||
assert 'embed' in call_kwargs
|
||||
assert 'Invalid Game' in call_kwargs['embed'].title
|
||||
assert 'between 1 and 4' in call_kwargs['embed'].description
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_playoff_round_1_game_5_valid(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that game 5 is accepted in playoff round 1 (week 19)."""
|
||||
modal = BatterInjuryModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("19")
|
||||
modal.current_game = create_mock_text_input("5")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config), \
|
||||
patch('services.player_service.player_service') as mock_player_service, \
|
||||
patch('services.injury_service.injury_service') as mock_injury_service:
|
||||
|
||||
mock_injury_service.create_injury = AsyncMock(return_value=MagicMock(id=1))
|
||||
mock_player_service.update_player = AsyncMock()
|
||||
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should not send error about invalid game
|
||||
assert not any(
|
||||
call[1].get('embed') and
|
||||
'Invalid Game' in str(call[1]['embed'].title)
|
||||
for call in mock_interaction.response.send_message.call_args_list
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_playoff_round_1_game_6_rejected(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that game 6 is rejected in playoff round 1 (only 5 games)."""
|
||||
modal = BatterInjuryModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("19")
|
||||
modal.current_game = create_mock_text_input("6")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config):
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should send error message
|
||||
mock_interaction.response.send_message.assert_called_once()
|
||||
call_kwargs = mock_interaction.response.send_message.call_args[1]
|
||||
assert 'embed' in call_kwargs
|
||||
assert 'Invalid Game' in call_kwargs['embed'].title
|
||||
assert 'between 1 and 5' in call_kwargs['embed'].description
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_playoff_round_2_game_7_valid(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that game 7 is accepted in playoff round 2 (week 20)."""
|
||||
modal = BatterInjuryModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("20")
|
||||
modal.current_game = create_mock_text_input("7")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config), \
|
||||
patch('services.player_service.player_service') as mock_player_service, \
|
||||
patch('services.injury_service.injury_service') as mock_injury_service:
|
||||
|
||||
mock_injury_service.create_injury = AsyncMock(return_value=MagicMock(id=1))
|
||||
mock_player_service.update_player = AsyncMock()
|
||||
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should not send error about invalid game
|
||||
assert not any(
|
||||
call[1].get('embed') and
|
||||
'Invalid Game' in str(call[1]['embed'].title)
|
||||
for call in mock_interaction.response.send_message.call_args_list
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_playoff_round_3_game_7_valid(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that game 7 is accepted in playoff round 3 (week 21)."""
|
||||
modal = BatterInjuryModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("21")
|
||||
modal.current_game = create_mock_text_input("7")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config), \
|
||||
patch('services.player_service.player_service') as mock_player_service, \
|
||||
patch('services.injury_service.injury_service') as mock_injury_service:
|
||||
|
||||
mock_injury_service.create_injury = AsyncMock(return_value=MagicMock(id=1))
|
||||
mock_player_service.update_player = AsyncMock()
|
||||
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should not send error about invalid game
|
||||
assert not any(
|
||||
call[1].get('embed') and
|
||||
'Invalid Game' in str(call[1]['embed'].title)
|
||||
for call in mock_interaction.response.send_message.call_args_list
|
||||
)
|
||||
|
||||
|
||||
class TestPitcherRestModalValidation:
|
||||
"""Test week and game validation in PitcherRestModal (should match BatterInjuryModal)."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_playoff_week_19_valid(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that playoff week 19 is accepted for pitchers."""
|
||||
modal = PitcherRestModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("19")
|
||||
modal.current_game = create_mock_text_input("3")
|
||||
modal.rest_games = create_mock_text_input("2")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config), \
|
||||
patch('services.player_service.player_service') as mock_player_service, \
|
||||
patch('services.injury_service.injury_service') as mock_injury_service:
|
||||
|
||||
mock_injury_service.create_injury = AsyncMock(return_value=MagicMock(id=1))
|
||||
mock_player_service.update_player = AsyncMock()
|
||||
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should not send error about invalid week
|
||||
assert not any(
|
||||
call[1].get('embed') and
|
||||
'Invalid Week' in str(call[1]['embed'].title)
|
||||
for call in mock_interaction.response.send_message.call_args_list
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_week_22_rejected(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that week 22 is rejected for pitchers."""
|
||||
modal = PitcherRestModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("22")
|
||||
modal.current_game = create_mock_text_input("2")
|
||||
modal.rest_games = create_mock_text_input("2")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config):
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should send error message
|
||||
mock_interaction.response.send_message.assert_called_once()
|
||||
call_kwargs = mock_interaction.response.send_message.call_args[1]
|
||||
assert 'embed' in call_kwargs
|
||||
assert 'Invalid Week' in call_kwargs['embed'].title
|
||||
assert '21 (including playoffs)' in call_kwargs['embed'].description
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_playoff_round_2_game_7_valid(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that game 7 is accepted in playoff round 2 for pitchers."""
|
||||
modal = PitcherRestModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("20")
|
||||
modal.current_game = create_mock_text_input("7")
|
||||
modal.rest_games = create_mock_text_input("3")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config), \
|
||||
patch('services.player_service.player_service') as mock_player_service, \
|
||||
patch('services.injury_service.injury_service') as mock_injury_service:
|
||||
|
||||
mock_injury_service.create_injury = AsyncMock(return_value=MagicMock(id=1))
|
||||
mock_player_service.update_player = AsyncMock()
|
||||
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should not send error about invalid game
|
||||
assert not any(
|
||||
call[1].get('embed') and
|
||||
'Invalid Game' in str(call[1]['embed'].title)
|
||||
for call in mock_interaction.response.send_message.call_args_list
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_playoff_round_1_game_6_rejected(self, sample_player, mock_interaction, mock_config):
|
||||
"""Test that game 6 is rejected in playoff round 1 for pitchers (only 5 games)."""
|
||||
modal = PitcherRestModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
modal.current_week = create_mock_text_input("19")
|
||||
modal.current_game = create_mock_text_input("6")
|
||||
modal.rest_games = create_mock_text_input("2")
|
||||
|
||||
with patch('config.get_config', return_value=mock_config):
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should send error message
|
||||
mock_interaction.response.send_message.assert_called_once()
|
||||
call_kwargs = mock_interaction.response.send_message.call_args[1]
|
||||
assert 'embed' in call_kwargs
|
||||
assert 'Invalid Game' in call_kwargs['embed'].title
|
||||
assert 'between 1 and 5' in call_kwargs['embed'].description
|
||||
|
||||
|
||||
class TestConfigDrivenValidation:
|
||||
"""Test that validation correctly uses config values."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_custom_config_values_respected(self, sample_player, mock_interaction):
|
||||
"""Test that custom config values change validation behavior."""
|
||||
# Create config with different values
|
||||
custom_config = MagicMock()
|
||||
custom_config.weeks_per_season = 20 # Different from default
|
||||
custom_config.playoff_weeks_per_season = 2 # Different from default
|
||||
custom_config.games_per_week = 4
|
||||
custom_config.playoff_round_one_games = 5
|
||||
custom_config.playoff_round_two_games = 7
|
||||
custom_config.playoff_round_three_games = 7
|
||||
|
||||
modal = BatterInjuryModal(
|
||||
player=sample_player,
|
||||
injury_games=4,
|
||||
season=12
|
||||
)
|
||||
|
||||
# Week 22 should be valid with this config (20 + 2 = 22)
|
||||
modal.current_week = create_mock_text_input("22")
|
||||
modal.current_game = create_mock_text_input("3")
|
||||
|
||||
with patch('config.get_config', return_value=custom_config), \
|
||||
patch('services.player_service.player_service') as mock_player_service, \
|
||||
patch('services.injury_service.injury_service') as mock_injury_service:
|
||||
|
||||
mock_injury_service.create_injury = AsyncMock(return_value=MagicMock(id=1))
|
||||
mock_player_service.update_player = AsyncMock()
|
||||
|
||||
await modal.on_submit(mock_interaction)
|
||||
|
||||
# Should not send error about invalid week
|
||||
assert not any(
|
||||
call[1].get('embed') and
|
||||
'Invalid Week' in str(call[1]['embed'].title)
|
||||
for call in mock_interaction.response.send_message.call_args_list
|
||||
)
|
||||
Loading…
Reference in New Issue
Block a user