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:
Cal Corum 2025-10-29 01:00:57 -05:00
parent 2bfc87ac1b
commit 15d0513740

View 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
)