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:
parent
d116680800
commit
8b0c82f687
@ -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."
|
||||||
|
)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user