fix: invoke actual cog callback in test_error_handling_and_logging (#39)

The previous test patched api_calls.db_get and pygsheets.authorize then
called those mocks directly—never invoking any cog method. The test
passed even when all cog code was deleted.

Replace with a test that retrieves the real pull_roster_command.callback
from the cog instance, patches dependencies at the correct module-level
names, calls the callback, and asserts ctx.send was called with the
expected error message. If the cog cannot be imported, the test skips
gracefully via pytest.skip.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-03-05 02:34:54 -06:00 committed by cal
parent d116680800
commit 8b0c82f687

View File

@ -27,498 +27,596 @@ except ImportError:
@pytest.mark.asyncio @pytest.mark.asyncio
class TestTeamManagement: class TestTeamManagement:
"""Test suite for TeamManagement cog functionality.""" """Test suite for TeamManagement cog functionality."""
@pytest.fixture @pytest.fixture
def team_management_cog(self, mock_bot): def team_management_cog(self, mock_bot):
"""Create TeamManagement cog instance for testing.""" """Create TeamManagement cog instance for testing."""
return TeamManagement(mock_bot) return TeamManagement(mock_bot)
async def test_init(self, team_management_cog, mock_bot): async def test_init(self, team_management_cog, mock_bot):
"""Test cog initialization.""" """Test cog initialization."""
assert team_management_cog.bot == mock_bot assert team_management_cog.bot == mock_bot
@patch('api_calls.get_team_by_abbrev') @patch("api_calls.get_team_by_abbrev")
@patch('helpers.get_team_by_owner') @patch("helpers.get_team_by_owner")
@patch('helpers.team_summary_embed') @patch("helpers.team_summary_embed")
async def test_team_command_with_abbreviation_success(self, mock_team_summary, async def test_team_command_with_abbreviation_success(
mock_get_by_owner, mock_get_by_abbrev, self,
team_management_cog, mock_interaction, mock_team_summary,
sample_team_data, mock_embed): mock_get_by_owner,
mock_get_by_abbrev,
team_management_cog,
mock_interaction,
sample_team_data,
mock_embed,
):
"""Test team command with team abbreviation provided.""" """Test team command with team abbreviation provided."""
mock_get_by_abbrev.return_value = sample_team_data mock_get_by_abbrev.return_value = sample_team_data
mock_team_summary.return_value = mock_embed mock_team_summary.return_value = mock_embed
async def mock_team_command(interaction, team_abbrev=None): async def mock_team_command(interaction, team_abbrev=None):
await interaction.response.defer() await interaction.response.defer()
if team_abbrev: if team_abbrev:
team = await mock_get_by_abbrev(team_abbrev) team = await mock_get_by_abbrev(team_abbrev)
if not team: if not team:
await interaction.followup.send(f'Could not find team with abbreviation: {team_abbrev}') await interaction.followup.send(
f"Could not find team with abbreviation: {team_abbrev}"
)
return return
else: else:
team = await mock_get_by_owner(interaction.user.id) team = await mock_get_by_owner(interaction.user.id)
if not team: if not team:
await interaction.followup.send('You don\'t have a team yet! Use `/newteam` to create one.') await interaction.followup.send(
"You don't have a team yet! Use `/newteam` to create one."
)
return return
embed = await mock_team_summary(team, interaction, include_roster=True) embed = await mock_team_summary(team, interaction, include_roster=True)
await interaction.followup.send(embed=embed) await interaction.followup.send(embed=embed)
await mock_team_command(mock_interaction, 'TST') await mock_team_command(mock_interaction, "TST")
mock_interaction.response.defer.assert_called_once() mock_interaction.response.defer.assert_called_once()
mock_get_by_abbrev.assert_called_once_with('TST') mock_get_by_abbrev.assert_called_once_with("TST")
mock_team_summary.assert_called_once() mock_team_summary.assert_called_once()
mock_interaction.followup.send.assert_called_once() mock_interaction.followup.send.assert_called_once()
@patch('api_calls.get_team_by_abbrev') @patch("api_calls.get_team_by_abbrev")
async def test_team_command_abbreviation_not_found(self, mock_get_by_abbrev, async def test_team_command_abbreviation_not_found(
team_management_cog, mock_interaction): self, mock_get_by_abbrev, team_management_cog, mock_interaction
):
"""Test team command when abbreviation is not found.""" """Test team command when abbreviation is not found."""
mock_get_by_abbrev.return_value = None mock_get_by_abbrev.return_value = None
async def mock_team_command(interaction, team_abbrev): async def mock_team_command(interaction, team_abbrev):
await interaction.response.defer() await interaction.response.defer()
team = await mock_get_by_abbrev(team_abbrev) team = await mock_get_by_abbrev(team_abbrev)
if not team: if not team:
await interaction.followup.send(f'Could not find team with abbreviation: {team_abbrev}') await interaction.followup.send(
f"Could not find team with abbreviation: {team_abbrev}"
)
return return
await mock_team_command(mock_interaction, 'XYZ') await mock_team_command(mock_interaction, "XYZ")
mock_interaction.followup.send.assert_called_once_with('Could not find team with abbreviation: XYZ') mock_interaction.followup.send.assert_called_once_with(
"Could not find team with abbreviation: XYZ"
@patch('helpers.get_team_by_owner') )
@patch('helpers.team_summary_embed')
async def test_team_command_without_abbreviation_success(self, mock_team_summary, @patch("helpers.get_team_by_owner")
mock_get_by_owner, @patch("helpers.team_summary_embed")
team_management_cog, mock_interaction, async def test_team_command_without_abbreviation_success(
sample_team_data, mock_embed): self,
mock_team_summary,
mock_get_by_owner,
team_management_cog,
mock_interaction,
sample_team_data,
mock_embed,
):
"""Test team command without abbreviation (user's own team).""" """Test team command without abbreviation (user's own team)."""
mock_get_by_owner.return_value = sample_team_data mock_get_by_owner.return_value = sample_team_data
mock_team_summary.return_value = mock_embed mock_team_summary.return_value = mock_embed
async def mock_team_command(interaction, team_abbrev=None): async def mock_team_command(interaction, team_abbrev=None):
await interaction.response.defer() await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id) team = await mock_get_by_owner(interaction.user.id)
if not team: if not team:
await interaction.followup.send('You don\'t have a team yet! Use `/newteam` to create one.') await interaction.followup.send(
"You don't have a team yet! Use `/newteam` to create one."
)
return return
embed = await mock_team_summary(team, interaction, include_roster=True) embed = await mock_team_summary(team, interaction, include_roster=True)
await interaction.followup.send(embed=embed) await interaction.followup.send(embed=embed)
await mock_team_command(mock_interaction) await mock_team_command(mock_interaction)
mock_get_by_owner.assert_called_once_with(mock_interaction.user.id) mock_get_by_owner.assert_called_once_with(mock_interaction.user.id)
mock_team_summary.assert_called_once() mock_team_summary.assert_called_once()
mock_interaction.followup.send.assert_called_once() mock_interaction.followup.send.assert_called_once()
@patch('helpers.get_team_by_owner') @patch("helpers.get_team_by_owner")
async def test_team_command_user_no_team(self, mock_get_by_owner, async def test_team_command_user_no_team(
team_management_cog, mock_interaction): self, mock_get_by_owner, team_management_cog, mock_interaction
):
"""Test team command when user has no team.""" """Test team command when user has no team."""
mock_get_by_owner.return_value = None mock_get_by_owner.return_value = None
async def mock_team_command(interaction, team_abbrev=None): async def mock_team_command(interaction, team_abbrev=None):
await interaction.response.defer() await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id) team = await mock_get_by_owner(interaction.user.id)
if not team: if not team:
await interaction.followup.send('You don\'t have a team yet! Use `/newteam` to create one.') await interaction.followup.send(
"You don't have a team yet! Use `/newteam` to create one."
)
return return
await mock_team_command(mock_interaction) await mock_team_command(mock_interaction)
mock_interaction.followup.send.assert_called_once_with('You don\'t have a team yet! Use `/newteam` to create one.') mock_interaction.followup.send.assert_called_once_with(
"You don't have a team yet! Use `/newteam` to create one."
@patch('helpers.get_team_by_owner') )
@patch('api_calls.db_patch')
async def test_branding_command_success(self, mock_db_patch, mock_get_by_owner, @patch("helpers.get_team_by_owner")
team_management_cog, mock_interaction, @patch("api_calls.db_patch")
sample_team_data): async def test_branding_command_success(
self,
mock_db_patch,
mock_get_by_owner,
team_management_cog,
mock_interaction,
sample_team_data,
):
"""Test successful team branding update.""" """Test successful team branding update."""
mock_get_by_owner.return_value = sample_team_data mock_get_by_owner.return_value = sample_team_data
mock_db_patch.return_value = {'success': True} mock_db_patch.return_value = {"success": True}
async def mock_branding_command(interaction, new_color, new_logo_url=None): async def mock_branding_command(interaction, new_color, new_logo_url=None):
await interaction.response.defer() await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id) team = await mock_get_by_owner(interaction.user.id)
if not team: if not team:
await interaction.followup.send('You don\'t have a team yet!') await interaction.followup.send("You don't have a team yet!")
return return
update_data = {'color': new_color} update_data = {"color": new_color}
if new_logo_url: if new_logo_url:
update_data['logo'] = new_logo_url update_data["logo"] = new_logo_url
response = await mock_db_patch(f'teams/{team["id"]}', data=update_data) response = await mock_db_patch(f'teams/{team["id"]}', data=update_data)
if response.get('success'): if response.get("success"):
await interaction.followup.send(f'Successfully updated team branding!') await interaction.followup.send(f"Successfully updated team branding!")
else: else:
await interaction.followup.send('Failed to update team branding.') await interaction.followup.send("Failed to update team branding.")
await mock_branding_command(mock_interaction, '#FF0000', 'https://example.com/logo.png') await mock_branding_command(
mock_interaction, "#FF0000", "https://example.com/logo.png"
)
mock_get_by_owner.assert_called_once() mock_get_by_owner.assert_called_once()
mock_db_patch.assert_called_once() mock_db_patch.assert_called_once()
mock_interaction.followup.send.assert_called_once_with('Successfully updated team branding!') mock_interaction.followup.send.assert_called_once_with(
"Successfully updated team branding!"
@patch('helpers.get_team_by_owner') )
async def test_branding_command_no_team(self, mock_get_by_owner,
team_management_cog, mock_interaction): @patch("helpers.get_team_by_owner")
async def test_branding_command_no_team(
self, mock_get_by_owner, team_management_cog, mock_interaction
):
"""Test team branding command when user has no team.""" """Test team branding command when user has no team."""
mock_get_by_owner.return_value = None mock_get_by_owner.return_value = None
async def mock_branding_command(interaction, new_color): async def mock_branding_command(interaction, new_color):
await interaction.response.defer() await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id) team = await mock_get_by_owner(interaction.user.id)
if not team: if not team:
await interaction.followup.send('You don\'t have a team yet!') await interaction.followup.send("You don't have a team yet!")
return return
await mock_branding_command(mock_interaction, '#FF0000') await mock_branding_command(mock_interaction, "#FF0000")
mock_interaction.followup.send.assert_called_once_with('You don\'t have a team yet!') mock_interaction.followup.send.assert_called_once_with(
"You don't have a team yet!"
@patch('helpers.get_team_by_owner') )
@patch('api_calls.db_patch')
async def test_branding_command_failure(self, mock_db_patch, mock_get_by_owner, @patch("helpers.get_team_by_owner")
team_management_cog, mock_interaction, @patch("api_calls.db_patch")
sample_team_data): async def test_branding_command_failure(
self,
mock_db_patch,
mock_get_by_owner,
team_management_cog,
mock_interaction,
sample_team_data,
):
"""Test team branding update failure.""" """Test team branding update failure."""
mock_get_by_owner.return_value = sample_team_data mock_get_by_owner.return_value = sample_team_data
mock_db_patch.return_value = {'success': False} mock_db_patch.return_value = {"success": False}
async def mock_branding_command(interaction, new_color): async def mock_branding_command(interaction, new_color):
await interaction.response.defer() await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id) team = await mock_get_by_owner(interaction.user.id)
if not team: if not team:
await interaction.followup.send('You don\'t have a team yet!') await interaction.followup.send("You don't have a team yet!")
return return
update_data = {'color': new_color} update_data = {"color": new_color}
response = await mock_db_patch(f'teams/{team["id"]}', data=update_data) response = await mock_db_patch(f'teams/{team["id"]}', data=update_data)
if response.get('success'): if response.get("success"):
await interaction.followup.send('Successfully updated team branding!') await interaction.followup.send("Successfully updated team branding!")
else: else:
await interaction.followup.send('Failed to update team branding.') await interaction.followup.send("Failed to update team branding.")
await mock_branding_command(mock_interaction, '#FF0000') await mock_branding_command(mock_interaction, "#FF0000")
mock_interaction.followup.send.assert_called_once_with('Failed to update team branding.') mock_interaction.followup.send.assert_called_once_with(
"Failed to update team branding."
@patch('helpers.get_team_by_owner') )
@patch('pygsheets.authorize')
@patch('helpers.get_roster_sheet') @patch("helpers.get_team_by_owner")
async def test_pullroster_command_success(self, mock_get_roster_sheet, mock_authorize, @patch("pygsheets.authorize")
mock_get_by_owner, team_management_cog, @patch("helpers.get_roster_sheet")
mock_interaction, sample_team_data): async def test_pullroster_command_success(
self,
mock_get_roster_sheet,
mock_authorize,
mock_get_by_owner,
team_management_cog,
mock_interaction,
sample_team_data,
):
"""Test successful roster pull from Google Sheets.""" """Test successful roster pull from Google Sheets."""
mock_get_by_owner.return_value = sample_team_data mock_get_by_owner.return_value = sample_team_data
mock_gc = Mock() mock_gc = Mock()
mock_authorize.return_value = mock_gc mock_authorize.return_value = mock_gc
mock_get_roster_sheet.return_value = Mock() mock_get_roster_sheet.return_value = Mock()
async def mock_pullroster_command(interaction): async def mock_pullroster_command(interaction):
await interaction.response.defer() await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id) team = await mock_get_by_owner(interaction.user.id)
if not team: if not team:
await interaction.followup.send('You don\'t have a team yet!') await interaction.followup.send("You don't have a team yet!")
return return
if not team.get('gsheet'): if not team.get("gsheet"):
await interaction.followup.send('No Google Sheet configured for your team.') await interaction.followup.send(
"No Google Sheet configured for your team."
)
return return
try: try:
gc = mock_authorize() gc = mock_authorize()
roster_data = await mock_get_roster_sheet(gc, team['gsheet']) roster_data = await mock_get_roster_sheet(gc, team["gsheet"])
await interaction.followup.send('Successfully pulled roster from Google Sheets!') await interaction.followup.send(
"Successfully pulled roster from Google Sheets!"
)
except Exception as e: except Exception as e:
await interaction.followup.send(f'Error pulling roster: {str(e)}') await interaction.followup.send(f"Error pulling roster: {str(e)}")
await mock_pullroster_command(mock_interaction) await mock_pullroster_command(mock_interaction)
mock_get_by_owner.assert_called_once() mock_get_by_owner.assert_called_once()
mock_authorize.assert_called_once() mock_authorize.assert_called_once()
mock_get_roster_sheet.assert_called_once() mock_get_roster_sheet.assert_called_once()
mock_interaction.followup.send.assert_called_once_with('Successfully pulled roster from Google Sheets!') mock_interaction.followup.send.assert_called_once_with(
"Successfully pulled roster from Google Sheets!"
@patch('helpers.get_team_by_owner') )
async def test_pullroster_command_no_team(self, mock_get_by_owner,
team_management_cog, mock_interaction): @patch("helpers.get_team_by_owner")
async def test_pullroster_command_no_team(
self, mock_get_by_owner, team_management_cog, mock_interaction
):
"""Test roster pull when user has no team.""" """Test roster pull when user has no team."""
mock_get_by_owner.return_value = None mock_get_by_owner.return_value = None
async def mock_pullroster_command(interaction): async def mock_pullroster_command(interaction):
await interaction.response.defer() await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id) team = await mock_get_by_owner(interaction.user.id)
if not team: if not team:
await interaction.followup.send('You don\'t have a team yet!') await interaction.followup.send("You don't have a team yet!")
return return
await mock_pullroster_command(mock_interaction) await mock_pullroster_command(mock_interaction)
mock_interaction.followup.send.assert_called_once_with('You don\'t have a team yet!') mock_interaction.followup.send.assert_called_once_with(
"You don't have a team yet!"
@patch('helpers.get_team_by_owner') )
async def test_pullroster_command_no_sheet(self, mock_get_by_owner,
team_management_cog, mock_interaction): @patch("helpers.get_team_by_owner")
async def test_pullroster_command_no_sheet(
self, mock_get_by_owner, team_management_cog, mock_interaction
):
"""Test roster pull when team has no Google Sheet configured.""" """Test roster pull when team has no Google Sheet configured."""
team_data_no_sheet = {**sample_team_data, 'gsheet': None} team_data_no_sheet = {**sample_team_data, "gsheet": None}
mock_get_by_owner.return_value = team_data_no_sheet mock_get_by_owner.return_value = team_data_no_sheet
async def mock_pullroster_command(interaction): async def mock_pullroster_command(interaction):
await interaction.response.defer() await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id) team = await mock_get_by_owner(interaction.user.id)
if not team: if not team:
await interaction.followup.send('You don\'t have a team yet!') await interaction.followup.send("You don't have a team yet!")
return return
if not team.get('gsheet'): if not team.get("gsheet"):
await interaction.followup.send('No Google Sheet configured for your team.') await interaction.followup.send(
"No Google Sheet configured for your team."
)
return return
await mock_pullroster_command(mock_interaction) await mock_pullroster_command(mock_interaction)
mock_interaction.followup.send.assert_called_once_with('No Google Sheet configured for your team.') mock_interaction.followup.send.assert_called_once_with(
"No Google Sheet configured for your team."
@patch('helpers.get_team_by_owner') )
@patch('pygsheets.authorize')
async def test_pullroster_command_error(self, mock_authorize, mock_get_by_owner, @patch("helpers.get_team_by_owner")
team_management_cog, mock_interaction, @patch("pygsheets.authorize")
sample_team_data): async def test_pullroster_command_error(
self,
mock_authorize,
mock_get_by_owner,
team_management_cog,
mock_interaction,
sample_team_data,
):
"""Test roster pull error handling.""" """Test roster pull error handling."""
mock_get_by_owner.return_value = sample_team_data mock_get_by_owner.return_value = sample_team_data
mock_authorize.side_effect = Exception("Google Sheets API Error") mock_authorize.side_effect = Exception("Google Sheets API Error")
async def mock_pullroster_command(interaction): async def mock_pullroster_command(interaction):
await interaction.response.defer() await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id) team = await mock_get_by_owner(interaction.user.id)
if not team: if not team:
await interaction.followup.send('You don\'t have a team yet!') await interaction.followup.send("You don't have a team yet!")
return return
if not team.get('gsheet'): if not team.get("gsheet"):
await interaction.followup.send('No Google Sheet configured for your team.') await interaction.followup.send(
"No Google Sheet configured for your team."
)
return return
try: try:
gc = mock_authorize() gc = mock_authorize()
except Exception as e: except Exception as e:
await interaction.followup.send(f'Error pulling roster: {str(e)}') await interaction.followup.send(f"Error pulling roster: {str(e)}")
await mock_pullroster_command(mock_interaction) await mock_pullroster_command(mock_interaction)
mock_interaction.followup.send.assert_called_once_with('Error pulling roster: Google Sheets API Error') mock_interaction.followup.send.assert_called_once_with(
"Error pulling roster: Google Sheets API Error"
@patch('api_calls.db_get') )
async def test_ai_teams_command_success(self, mock_db_get, team_management_cog,
mock_interaction, mock_embed): @patch("api_calls.db_get")
async def test_ai_teams_command_success(
self, mock_db_get, team_management_cog, mock_interaction, mock_embed
):
"""Test successful AI teams listing.""" """Test successful AI teams listing."""
ai_teams_data = { ai_teams_data = {
'count': 2, "count": 2,
'teams': [ "teams": [
{'id': 1, 'abbrev': 'AI1', 'sname': 'AI Team 1', 'is_ai': True}, {"id": 1, "abbrev": "AI1", "sname": "AI Team 1", "is_ai": True},
{'id': 2, 'abbrev': 'AI2', 'sname': 'AI Team 2', 'is_ai': True} {"id": 2, "abbrev": "AI2", "sname": "AI Team 2", "is_ai": True},
] ],
} }
mock_db_get.return_value = ai_teams_data mock_db_get.return_value = ai_teams_data
async def mock_ai_teams_command(interaction): async def mock_ai_teams_command(interaction):
await interaction.response.defer() await interaction.response.defer()
teams_response = await mock_db_get('teams', params=[('is_ai', 'true')]) teams_response = await mock_db_get("teams", params=[("is_ai", "true")])
if not teams_response or teams_response['count'] == 0: if not teams_response or teams_response["count"] == 0:
await interaction.followup.send('No AI teams found.') await interaction.followup.send("No AI teams found.")
return return
ai_teams = teams_response['teams'] ai_teams = teams_response["teams"]
team_list = '\n'.join([f"{team['abbrev']} - {team['sname']}" for team in ai_teams]) team_list = "\n".join(
[f"{team['abbrev']} - {team['sname']}" for team in ai_teams]
)
embed = mock_embed embed = mock_embed
embed.title = f'AI Teams ({len(ai_teams)})' embed.title = f"AI Teams ({len(ai_teams)})"
embed.description = team_list embed.description = team_list
await interaction.followup.send(embed=embed) await interaction.followup.send(embed=embed)
await mock_ai_teams_command(mock_interaction) await mock_ai_teams_command(mock_interaction)
mock_db_get.assert_called_once_with('teams', params=[('is_ai', 'true')]) mock_db_get.assert_called_once_with("teams", params=[("is_ai", "true")])
mock_interaction.followup.send.assert_called_once() mock_interaction.followup.send.assert_called_once()
@patch('api_calls.db_get') @patch("api_calls.db_get")
async def test_ai_teams_command_no_teams(self, mock_db_get, team_management_cog, async def test_ai_teams_command_no_teams(
mock_interaction): self, mock_db_get, team_management_cog, mock_interaction
):
"""Test AI teams command when no AI teams exist.""" """Test AI teams command when no AI teams exist."""
mock_db_get.return_value = {'count': 0, 'teams': []} mock_db_get.return_value = {"count": 0, "teams": []}
async def mock_ai_teams_command(interaction): async def mock_ai_teams_command(interaction):
await interaction.response.defer() await interaction.response.defer()
teams_response = await mock_db_get('teams', params=[('is_ai', 'true')]) teams_response = await mock_db_get("teams", params=[("is_ai", "true")])
if not teams_response or teams_response['count'] == 0: if not teams_response or teams_response["count"] == 0:
await interaction.followup.send('No AI teams found.') await interaction.followup.send("No AI teams found.")
return return
await mock_ai_teams_command(mock_interaction) await mock_ai_teams_command(mock_interaction)
mock_interaction.followup.send.assert_called_once_with('No AI teams found.') mock_interaction.followup.send.assert_called_once_with("No AI teams found.")
@patch('api_calls.db_get') @patch("api_calls.db_get")
async def test_ai_teams_command_api_error(self, mock_db_get, team_management_cog, async def test_ai_teams_command_api_error(
mock_interaction): self, mock_db_get, team_management_cog, mock_interaction
):
"""Test AI teams command API error handling.""" """Test AI teams command API error handling."""
mock_db_get.return_value = None mock_db_get.return_value = None
async def mock_ai_teams_command(interaction): async def mock_ai_teams_command(interaction):
await interaction.response.defer() await interaction.response.defer()
teams_response = await mock_db_get('teams', params=[('is_ai', 'true')]) teams_response = await mock_db_get("teams", params=[("is_ai", "true")])
if not teams_response: if not teams_response:
await interaction.followup.send('Error retrieving AI teams.') await interaction.followup.send("Error retrieving AI teams.")
return return
await mock_ai_teams_command(mock_interaction) await mock_ai_teams_command(mock_interaction)
mock_interaction.followup.send.assert_called_once_with('Error retrieving AI teams.') mock_interaction.followup.send.assert_called_once_with(
"Error retrieving AI teams."
)
def test_color_validation(self, team_management_cog): def test_color_validation(self, team_management_cog):
"""Test color format validation for branding command.""" """Test color format validation for branding command."""
valid_colors = ['#FF0000', '#00FF00', '#0000FF', 'FF0000', '123ABC'] valid_colors = ["#FF0000", "#00FF00", "#0000FF", "FF0000", "123ABC"]
invalid_colors = ['invalid', '#GGGGGG', '12345', '#1234567'] invalid_colors = ["invalid", "#GGGGGG", "12345", "#1234567"]
def is_valid_color(color): def is_valid_color(color):
# Basic hex color validation # Basic hex color validation
if color.startswith('#'): if color.startswith("#"):
color = color[1:] color = color[1:]
return len(color) == 6 and all(c in '0123456789ABCDEFabcdef' for c in color) return len(color) == 6 and all(c in "0123456789ABCDEFabcdef" for c in color)
for color in valid_colors: for color in valid_colors:
assert is_valid_color(color), f"Color {color} should be valid" assert is_valid_color(color), f"Color {color} should be valid"
for color in invalid_colors: for color in invalid_colors:
assert not is_valid_color(color), f"Color {color} should be invalid" assert not is_valid_color(color), f"Color {color} should be invalid"
def test_url_validation(self, team_management_cog): def test_url_validation(self, team_management_cog):
"""Test URL validation for logo updates.""" """Test URL validation for logo updates."""
valid_urls = [ valid_urls = [
'https://example.com/image.png', "https://example.com/image.png",
'https://cdn.example.com/logo.jpg', "https://cdn.example.com/logo.jpg",
'http://test.com/image.gif' "http://test.com/image.gif",
] ]
invalid_urls = [ invalid_urls = [
'not_a_url', "not_a_url",
'ftp://example.com/file.txt', "ftp://example.com/file.txt",
'javascript:alert(1)' "javascript:alert(1)",
] ]
def is_valid_url(url): def is_valid_url(url):
return url.startswith(('http://', 'https://')) return url.startswith(("http://", "https://"))
for url in valid_urls: for url in valid_urls:
assert is_valid_url(url), f"URL {url} should be valid" assert is_valid_url(url), f"URL {url} should be valid"
for url in invalid_urls: for url in invalid_urls:
assert not is_valid_url(url), f"URL {url} should be invalid" assert not is_valid_url(url), f"URL {url} should be invalid"
@patch('helpers.get_rosters') @patch("helpers.get_rosters")
async def test_roster_integration(self, mock_get_rosters, team_management_cog, async def test_roster_integration(
sample_team_data): self, mock_get_rosters, team_management_cog, sample_team_data
):
"""Test roster data integration with team display.""" """Test roster data integration with team display."""
roster_data = { roster_data = {
'active_roster': [ "active_roster": [
{'card_id': 1, 'player_name': 'Player 1', 'position': 'C'}, {"card_id": 1, "player_name": "Player 1", "position": "C"},
{'card_id': 2, 'player_name': 'Player 2', 'position': '1B'} {"card_id": 2, "player_name": "Player 2", "position": "1B"},
], ],
'bench': [ "bench": [{"card_id": 3, "player_name": "Player 3", "position": "OF"}],
{'card_id': 3, 'player_name': 'Player 3', 'position': 'OF'}
]
} }
mock_get_rosters.return_value = roster_data mock_get_rosters.return_value = roster_data
rosters = await mock_get_rosters(sample_team_data['id']) rosters = await mock_get_rosters(sample_team_data["id"])
assert rosters is not None assert rosters is not None
assert 'active_roster' in rosters assert "active_roster" in rosters
assert 'bench' in rosters assert "bench" in rosters
assert len(rosters['active_roster']) == 2 assert len(rosters["active_roster"]) == 2
assert len(rosters['bench']) == 1 assert len(rosters["bench"]) == 1
def test_team_embed_formatting(self, team_management_cog, sample_team_data, mock_embed): def test_team_embed_formatting(
self, team_management_cog, sample_team_data, mock_embed
):
"""Test proper formatting of team summary embeds.""" """Test proper formatting of team summary embeds."""
# Mock the team summary embed creation # Mock the team summary embed creation
def create_team_summary_embed(team, include_roster=False): def create_team_summary_embed(team, include_roster=False):
embed = mock_embed embed = mock_embed
embed.title = f"{team['abbrev']} - {team['sname']}" embed.title = f"{team['abbrev']} - {team['sname']}"
embed.add_field(name="GM", value=team['gmname'], inline=True) embed.add_field(name="GM", value=team["gmname"], inline=True)
embed.add_field(name="Wallet", value=f"${team['wallet']}", inline=True) embed.add_field(name="Wallet", value=f"${team['wallet']}", inline=True)
embed.add_field(name="Team Value", value=f"${team['team_value']}", inline=True) embed.add_field(
name="Team Value", value=f"${team['team_value']}", inline=True
if team['color']: )
embed.color = int(team['color'], 16)
if team["color"]:
embed.color = int(team["color"], 16)
if include_roster: if include_roster:
embed.add_field(name="Roster", value="Active roster info...", inline=False) embed.add_field(
name="Roster", value="Active roster info...", inline=False
)
return embed return embed
embed = create_team_summary_embed(sample_team_data, include_roster=True) embed = create_team_summary_embed(sample_team_data, include_roster=True)
assert embed.title == f"{sample_team_data['abbrev']} - {sample_team_data['sname']}" assert (
embed.title == f"{sample_team_data['abbrev']} - {sample_team_data['sname']}"
)
embed.add_field.assert_called() embed.add_field.assert_called()
def test_permission_checks(self, team_management_cog, mock_interaction): def test_permission_checks(self, team_management_cog, mock_interaction):
"""Test role and channel permission checking.""" """Test role and channel permission checking."""
# Test role check # Test role check
mock_member_with_role = Mock() mock_member_with_role = Mock()
mock_member_with_role.roles = [Mock(name='Paper Dynasty')] mock_member_with_role.roles = [Mock(name="Paper Dynasty")]
mock_interaction.user = mock_member_with_role mock_interaction.user = mock_member_with_role
# Test channel check # Test channel check
with patch('helpers.legal_channel') as mock_legal_check: with patch("helpers.legal_channel") as mock_legal_check:
mock_legal_check.return_value = True mock_legal_check.return_value = True
result = mock_legal_check(mock_interaction.channel) result = mock_legal_check(mock_interaction.channel)
assert result is True assert result is True
@patch('logging.getLogger') async def test_error_handling_and_logging(self, team_management_cog, mock_context):
async def test_error_handling_and_logging(self, mock_logger, team_management_cog): """Test that pull_roster_command sends an error message when get_rosters raises.
"""Test error handling and logging across team management operations."""
mock_logger_instance = Mock() Invokes the actual cog method callback so the test fails if the method body is
mock_logger.return_value = mock_logger_instance removed or the exception-handling branch is broken.
"""
# Test API timeout error cmd = getattr(team_management_cog, "pull_roster_command", None)
with patch('api_calls.db_get') as mock_db_get: if cmd is None or not hasattr(cmd, "callback"):
mock_db_get.side_effect = asyncio.TimeoutError("Request timeout") pytest.skip(
"TeamManagement cog not importable; cannot test callback directly"
try: )
await mock_db_get('teams')
except asyncio.TimeoutError: team_with_sheet = {
# In actual implementation, this would be caught and logged "id": 1,
pass "abbrev": "TST",
"sname": "Test",
# Test Google Sheets authentication error "gsheet": "valid-sheet-id",
with patch('pygsheets.authorize') as mock_authorize: }
mock_authorize.side_effect = Exception("Auth failed") with patch(
"cogs.players_new.team_management.get_context_user"
try: ) as mock_get_ctx_user, patch(
mock_authorize() "cogs.players_new.team_management.get_team_by_owner",
except Exception: new=AsyncMock(return_value=team_with_sheet),
# In actual implementation, this would be caught and logged ), patch(
pass "cogs.players_new.team_management.get_rosters",
side_effect=Exception("Connection error"),
):
mock_get_ctx_user.return_value = mock_context.author
await cmd.callback(team_management_cog, mock_context)
mock_context.send.assert_called_once_with(
"Could not retrieve rosters from your sheet."
)