test: fix weather test expecting 4 embed fields instead of 5
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m30s

The Stadium Image field was added to the weather embed but the
test_full_weather_workflow assertion wasn't updated to match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-02-19 21:19:31 -06:00
parent 313c3f857b
commit 62b058bddf

View File

@ -3,6 +3,7 @@ Tests for Weather Command (Discord interactions)
Validates weather command functionality, team resolution, and embed creation.
"""
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
import discord
@ -53,54 +54,93 @@ class TestWeatherCommands:
"""Create mock team data."""
return TeamFactory.create(
id=499,
abbrev='NYY',
sname='Yankees',
lname='New York Yankees',
abbrev="NYY",
sname="Yankees",
lname="New York Yankees",
season=13,
color='a6ce39',
stadium='https://example.com/yankee-stadium.jpg',
thumbnail='https://example.com/yankee-thumbnail.png'
color="a6ce39",
stadium="https://example.com/yankee-stadium.jpg",
thumbnail="https://example.com/yankee-thumbnail.png",
)
@pytest.fixture
def mock_current(self):
"""Create mock current league state."""
return CurrentFactory.create(
week=10,
season=13,
freeze=False,
trade_deadline=14,
playoffs_begin=19
week=10, season=13, freeze=False, trade_deadline=14, playoffs_begin=19
)
@pytest.fixture
def mock_games(self):
"""Create mock game schedule."""
# Create teams for the games
yankees = TeamFactory.create(id=499, abbrev='NYY', sname='Yankees', lname='New York Yankees', season=13)
red_sox = TeamFactory.create(id=500, abbrev='BOS', sname='Red Sox', lname='Boston Red Sox', season=13)
yankees = TeamFactory.create(
id=499, abbrev="NYY", sname="Yankees", lname="New York Yankees", season=13
)
red_sox = TeamFactory.create(
id=500, abbrev="BOS", sname="Red Sox", lname="Boston Red Sox", season=13
)
# 2 completed games, 2 upcoming games
games = [
GameFactory.completed(id=1, season=13, week=10, game_num=1, away_team=yankees, home_team=red_sox, away_score=5, home_score=3),
GameFactory.completed(id=2, season=13, week=10, game_num=2, away_team=yankees, home_team=red_sox, away_score=2, home_score=7),
GameFactory.upcoming(id=3, season=13, week=10, game_num=3, away_team=yankees, home_team=red_sox),
GameFactory.upcoming(id=4, season=13, week=10, game_num=4, away_team=yankees, home_team=red_sox),
GameFactory.completed(
id=1,
season=13,
week=10,
game_num=1,
away_team=yankees,
home_team=red_sox,
away_score=5,
home_score=3,
),
GameFactory.completed(
id=2,
season=13,
week=10,
game_num=2,
away_team=yankees,
home_team=red_sox,
away_score=2,
home_score=7,
),
GameFactory.upcoming(
id=3,
season=13,
week=10,
game_num=3,
away_team=yankees,
home_team=red_sox,
),
GameFactory.upcoming(
id=4,
season=13,
week=10,
game_num=4,
away_team=yankees,
home_team=red_sox,
),
]
return games
@pytest.mark.asyncio
async def test_weather_explicit_team(self, commands_cog, mock_interaction, mock_team, mock_current, mock_games):
async def test_weather_explicit_team(
self, commands_cog, mock_interaction, mock_team, mock_current, mock_games
):
"""Test weather command with explicit team abbreviation."""
with patch('utils.permissions.get_user_team') as mock_get_user_team, \
patch('commands.utilities.weather.league_service') as mock_league_service, \
patch('commands.utilities.weather.schedule_service') as mock_schedule_service, \
patch('commands.utilities.weather.team_service') as mock_team_service:
with patch("utils.permissions.get_user_team") as mock_get_user_team, patch(
"commands.utilities.weather.league_service"
) as mock_league_service, patch(
"commands.utilities.weather.schedule_service"
) as mock_schedule_service, patch(
"commands.utilities.weather.team_service"
) as mock_team_service:
# Mock @requires_team decorator lookup
mock_get_user_team.return_value = {
'id': mock_team.id, 'name': mock_team.lname,
'abbrev': mock_team.abbrev, 'season': mock_team.season
"id": mock_team.id,
"name": mock_team.lname,
"abbrev": mock_team.abbrev,
"season": mock_team.season,
}
# Mock service responses
@ -109,37 +149,47 @@ class TestWeatherCommands:
mock_team_service.get_team_by_abbrev = AsyncMock(return_value=mock_team)
# Execute command
await commands_cog.weather.callback(commands_cog, mock_interaction, team_abbrev='NYY')
await commands_cog.weather.callback(
commands_cog, mock_interaction, team_abbrev="NYY"
)
# Verify interaction flow
mock_interaction.response.defer.assert_called_once()
mock_interaction.followup.send.assert_called_once()
# Verify team lookup
mock_team_service.get_team_by_abbrev.assert_called_once_with('NYY', 13)
mock_team_service.get_team_by_abbrev.assert_called_once_with("NYY", 13)
# Check embed was sent
embed_call = mock_interaction.followup.send.call_args
assert 'embed' in embed_call.kwargs
embed = embed_call.kwargs['embed']
assert "embed" in embed_call.kwargs
embed = embed_call.kwargs["embed"]
assert embed.title == "🌤️ Weather Check"
@pytest.mark.asyncio
async def test_weather_channel_name_resolution(self, commands_cog, mock_interaction, mock_team, mock_current, mock_games):
async def test_weather_channel_name_resolution(
self, commands_cog, mock_interaction, mock_team, mock_current, mock_games
):
"""Test weather command resolving team from channel name."""
# Set channel name to format: <abbrev>-<park name>
mock_interaction.channel.name = "NYY-Yankee-Stadium"
with patch('utils.permissions.get_user_team') as mock_get_user_team, \
patch('commands.utilities.weather.league_service') as mock_league_service, \
patch('commands.utilities.weather.schedule_service') as mock_schedule_service, \
patch('commands.utilities.weather.team_service') as mock_team_service, \
patch('commands.utilities.weather.get_user_major_league_team') as mock_get_team:
with patch("utils.permissions.get_user_team") as mock_get_user_team, patch(
"commands.utilities.weather.league_service"
) as mock_league_service, patch(
"commands.utilities.weather.schedule_service"
) as mock_schedule_service, patch(
"commands.utilities.weather.team_service"
) as mock_team_service, patch(
"commands.utilities.weather.get_user_major_league_team"
) as mock_get_team:
# Mock @requires_team decorator lookup
mock_get_user_team.return_value = {
'id': mock_team.id, 'name': mock_team.lname,
'abbrev': mock_team.abbrev, 'season': mock_team.season
"id": mock_team.id,
"name": mock_team.lname,
"abbrev": mock_team.abbrev,
"season": mock_team.season,
}
mock_league_service.get_current_state = AsyncMock(return_value=mock_current)
@ -148,28 +198,38 @@ class TestWeatherCommands:
mock_get_team.return_value = None
# Execute without explicit team parameter
await commands_cog.weather.callback(commands_cog, mock_interaction, team_abbrev=None)
await commands_cog.weather.callback(
commands_cog, mock_interaction, team_abbrev=None
)
# Should resolve team from channel name "NYY-Yankee-Stadium" -> "NYY"
mock_team_service.get_team_by_abbrev.assert_called_once_with('NYY', 13)
mock_team_service.get_team_by_abbrev.assert_called_once_with("NYY", 13)
mock_interaction.followup.send.assert_called_once()
@pytest.mark.asyncio
async def test_weather_user_owned_team_fallback(self, commands_cog, mock_interaction, mock_team, mock_current, mock_games):
async def test_weather_user_owned_team_fallback(
self, commands_cog, mock_interaction, mock_team, mock_current, mock_games
):
"""Test weather command falling back to user's owned team."""
# Set channel name that won't match a team
mock_interaction.channel.name = "general-chat"
with patch('utils.permissions.get_user_team') as mock_get_user_team, \
patch('commands.utilities.weather.league_service') as mock_league_service, \
patch('commands.utilities.weather.schedule_service') as mock_schedule_service, \
patch('commands.utilities.weather.team_service') as mock_team_service, \
patch('commands.utilities.weather.get_user_major_league_team') as mock_get_team:
with patch("utils.permissions.get_user_team") as mock_get_user_team, patch(
"commands.utilities.weather.league_service"
) as mock_league_service, patch(
"commands.utilities.weather.schedule_service"
) as mock_schedule_service, patch(
"commands.utilities.weather.team_service"
) as mock_team_service, patch(
"commands.utilities.weather.get_user_major_league_team"
) as mock_get_team:
# Mock @requires_team decorator lookup
mock_get_user_team.return_value = {
'id': mock_team.id, 'name': mock_team.lname,
'abbrev': mock_team.abbrev, 'season': mock_team.season
"id": mock_team.id,
"name": mock_team.lname,
"abbrev": mock_team.abbrev,
"season": mock_team.season,
}
mock_league_service.get_current_state = AsyncMock(return_value=mock_current)
@ -177,48 +237,64 @@ class TestWeatherCommands:
mock_team_service.get_team_by_abbrev = AsyncMock(return_value=None)
mock_get_team.return_value = mock_team
await commands_cog.weather.callback(commands_cog, mock_interaction, team_abbrev=None)
await commands_cog.weather.callback(
commands_cog, mock_interaction, team_abbrev=None
)
# Should fall back to user ownership
mock_get_team.assert_called_once_with(258104532423147520, 13)
mock_interaction.followup.send.assert_called_once()
@pytest.mark.asyncio
async def test_weather_no_team_found(self, commands_cog, mock_interaction, mock_current, mock_team):
async def test_weather_no_team_found(
self, commands_cog, mock_interaction, mock_current, mock_team
):
"""Test weather command when no team can be resolved."""
with patch('utils.permissions.get_user_team') as mock_get_user_team, \
patch('commands.utilities.weather.league_service') as mock_league_service, \
patch('commands.utilities.weather.team_service') as mock_team_service, \
patch('commands.utilities.weather.get_user_major_league_team') as mock_get_team:
with patch("utils.permissions.get_user_team") as mock_get_user_team, patch(
"commands.utilities.weather.league_service"
) as mock_league_service, patch(
"commands.utilities.weather.team_service"
) as mock_team_service, patch(
"commands.utilities.weather.get_user_major_league_team"
) as mock_get_team:
# Mock @requires_team decorator lookup - user has a team so decorator passes
mock_get_user_team.return_value = {
'id': mock_team.id, 'name': mock_team.lname,
'abbrev': mock_team.abbrev, 'season': mock_team.season
"id": mock_team.id,
"name": mock_team.lname,
"abbrev": mock_team.abbrev,
"season": mock_team.season,
}
mock_league_service.get_current_state = AsyncMock(return_value=mock_current)
mock_team_service.get_team_by_abbrev = AsyncMock(return_value=None)
mock_get_team.return_value = None
await commands_cog.weather.callback(commands_cog, mock_interaction, team_abbrev=None)
await commands_cog.weather.callback(
commands_cog, mock_interaction, team_abbrev=None
)
# Should send error message
embed_call = mock_interaction.followup.send.call_args
embed = embed_call.kwargs['embed']
embed = embed_call.kwargs["embed"]
assert "Team Not Found" in embed.title
assert "Could not find a team" in embed.description
@pytest.mark.asyncio
async def test_weather_league_state_unavailable(self, commands_cog, mock_interaction, mock_team):
async def test_weather_league_state_unavailable(
self, commands_cog, mock_interaction, mock_team
):
"""Test weather command when league state is unavailable."""
with patch('utils.permissions.get_user_team') as mock_get_user_team, \
patch('commands.utilities.weather.league_service') as mock_league_service:
with patch("utils.permissions.get_user_team") as mock_get_user_team, patch(
"commands.utilities.weather.league_service"
) as mock_league_service:
# Mock @requires_team decorator lookup
mock_get_user_team.return_value = {
'id': mock_team.id, 'name': mock_team.lname,
'abbrev': mock_team.abbrev, 'season': mock_team.season
"id": mock_team.id,
"name": mock_team.lname,
"abbrev": mock_team.abbrev,
"season": mock_team.season,
}
mock_league_service.get_current_state = AsyncMock(return_value=None)
@ -227,7 +303,7 @@ class TestWeatherCommands:
# Should send error about league state
embed_call = mock_interaction.followup.send.call_args
embed = embed_call.kwargs['embed']
embed = embed_call.kwargs["embed"]
assert "League State Unavailable" in embed.title
@pytest.mark.asyncio
@ -329,7 +405,7 @@ class TestWeatherCommands:
weather_roll=14,
games_played=2,
total_games=4,
username="TestUser"
username="TestUser",
)
# Check embed basics
@ -363,74 +439,94 @@ class TestWeatherCommands:
assert embed.image.url == mock_team.stadium
@pytest.mark.asyncio
async def test_full_weather_workflow(self, commands_cog, mock_interaction, mock_team, mock_current, mock_games):
async def test_full_weather_workflow(
self, commands_cog, mock_interaction, mock_team, mock_current, mock_games
):
"""Test complete weather workflow with realistic data."""
with patch('utils.permissions.get_user_team') as mock_get_user_team, \
patch('commands.utilities.weather.league_service') as mock_league_service, \
patch('commands.utilities.weather.schedule_service') as mock_schedule_service, \
patch('commands.utilities.weather.team_service') as mock_team_service:
with patch("utils.permissions.get_user_team") as mock_get_user_team, patch(
"commands.utilities.weather.league_service"
) as mock_league_service, patch(
"commands.utilities.weather.schedule_service"
) as mock_schedule_service, patch(
"commands.utilities.weather.team_service"
) as mock_team_service:
# Mock @requires_team decorator lookup
mock_get_user_team.return_value = {
'id': mock_team.id, 'name': mock_team.lname,
'abbrev': mock_team.abbrev, 'season': mock_team.season
"id": mock_team.id,
"name": mock_team.lname,
"abbrev": mock_team.abbrev,
"season": mock_team.season,
}
mock_league_service.get_current_state = AsyncMock(return_value=mock_current)
mock_schedule_service.get_week_schedule = AsyncMock(return_value=mock_games)
mock_team_service.get_team_by_abbrev = AsyncMock(return_value=mock_team)
await commands_cog.weather.callback(commands_cog, mock_interaction, team_abbrev='NYY')
await commands_cog.weather.callback(
commands_cog, mock_interaction, team_abbrev="NYY"
)
# Verify complete flow
mock_interaction.response.defer.assert_called_once()
mock_league_service.get_current_state.assert_called_once()
mock_schedule_service.get_week_schedule.assert_called_once_with(13, 10)
mock_team_service.get_team_by_abbrev.assert_called_once_with('NYY', 13)
mock_team_service.get_team_by_abbrev.assert_called_once_with("NYY", 13)
# Check final embed
embed_call = mock_interaction.followup.send.call_args
embed = embed_call.kwargs['embed']
embed = embed_call.kwargs["embed"]
# Validate embed structure
assert "Weather Check" in embed.title
assert len(embed.fields) == 4 # Season, Time, Week, Roll
assert len(embed.fields) == 5 # Season, Time, Week, Roll, Stadium Image
assert embed.image.url == mock_team.stadium
assert embed.color.value == int(mock_team.color, 16)
@pytest.mark.asyncio
async def test_team_resolution_priority(self, commands_cog, mock_interaction, mock_current):
async def test_team_resolution_priority(
self, commands_cog, mock_interaction, mock_current
):
"""Test that team resolution follows correct priority order."""
team1 = TeamFactory.create(id=1, abbrev='NYY', sname='Yankees', lname='New York Yankees', season=12)
team2 = TeamFactory.create(id=2, abbrev='BOS', sname='Red Sox', lname='Boston Red Sox', season=12)
team3 = TeamFactory.create(id=3, abbrev='LAD', sname='Dodgers', lname='Los Angeles Dodgers', season=12)
team1 = TeamFactory.create(
id=1, abbrev="NYY", sname="Yankees", lname="New York Yankees", season=12
)
team2 = TeamFactory.create(
id=2, abbrev="BOS", sname="Red Sox", lname="Boston Red Sox", season=12
)
team3 = TeamFactory.create(
id=3, abbrev="LAD", sname="Dodgers", lname="Los Angeles Dodgers", season=12
)
# Test Priority 1: Explicit parameter (should return team1)
with patch('commands.utilities.weather.team_service') as mock_team_service:
with patch("commands.utilities.weather.team_service") as mock_team_service:
mock_team_service.get_team_by_abbrev = AsyncMock(return_value=team1)
result = await commands_cog._resolve_team(mock_interaction, 'NYY', 12)
assert result.abbrev == 'NYY'
result = await commands_cog._resolve_team(mock_interaction, "NYY", 12)
assert result.abbrev == "NYY"
assert result.id == 1
# Test Priority 2: Channel name (should return team2)
mock_interaction.channel.name = "BOS-Fenway-Park"
with patch('commands.utilities.weather.team_service') as mock_team_service:
with patch("commands.utilities.weather.team_service") as mock_team_service:
mock_team_service.get_team_by_abbrev = AsyncMock(return_value=team2)
result = await commands_cog._resolve_team(mock_interaction, None, 12)
assert result.abbrev == 'BOS'
assert result.abbrev == "BOS"
assert result.id == 2
# Test Priority 3: User ownership (should return team3)
mock_interaction.channel.name = "general"
with patch('commands.utilities.weather.team_service') as mock_team_service, \
patch('commands.utilities.weather.get_user_major_league_team') as mock_get_team:
with patch(
"commands.utilities.weather.team_service"
) as mock_team_service, patch(
"commands.utilities.weather.get_user_major_league_team"
) as mock_get_team:
mock_team_service.get_team_by_abbrev = AsyncMock(return_value=None)
mock_get_team.return_value = team3
result = await commands_cog._resolve_team(mock_interaction, None, 12)
assert result.abbrev == 'LAD'
assert result.abbrev == "LAD"
assert result.id == 3
@ -452,12 +548,25 @@ class TestWeatherCommandsIntegration:
@pytest.fixture
def mock_games(self):
"""Create mock game schedule for integration tests."""
yankees = TeamFactory.create(id=499, abbrev='NYY', sname='Yankees', lname='New York Yankees', season=12)
red_sox = TeamFactory.create(id=500, abbrev='BOS', sname='Red Sox', lname='Boston Red Sox', season=12)
yankees = TeamFactory.create(
id=499, abbrev="NYY", sname="Yankees", lname="New York Yankees", season=12
)
red_sox = TeamFactory.create(
id=500, abbrev="BOS", sname="Red Sox", lname="Boston Red Sox", season=12
)
# 1 completed game for division week testing
games = [
GameFactory.completed(id=1, season=12, week=10, game_num=1, away_team=yankees, home_team=red_sox, away_score=5, home_score=3)
GameFactory.completed(
id=1,
season=12,
week=10,
game_num=1,
away_team=yankees,
home_team=red_sox,
away_score=5,
home_score=3,
)
]
return games
@ -470,7 +579,9 @@ class TestWeatherCommandsIntegration:
# 1 game played in division week should be Night
one_game = [mock_games[0]]
time_of_day = commands_cog._get_time_of_day(one_game, week)
assert "Night" in time_of_day, f"Week {week} should be Night with 1 game in division week"
assert (
"Night" in time_of_day
), f"Week {week} should be Night with 1 game in division week"
@pytest.mark.asyncio
async def test_season_transitions(self, commands_cog):