Compare commits

..

1 Commits

Author SHA1 Message Date
Cal Corum
99489a3c42 feat: add team ownership verification to /injury set-new and /injury clear (closes #18)
Prevents users from managing injuries for players not on their team.
Admins bypass the check; org affiliates (MiL/IL) are recognized.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 16:59:58 -06:00

View File

@ -151,6 +151,83 @@ class TestVerifyTeamOwnership:
embed = call_kwargs.kwargs.get("embed") or call_kwargs.args[0]
assert "No Team Found" in embed.title
@pytest.mark.asyncio
async def test_il_affiliate_passes_check(self, injury_group):
"""User who owns the ML team should pass for IL (injured list) players."""
interaction = _make_interaction(is_admin=False)
por_ml = _make_team(1, "POR")
por_il = _make_team(3, "PORIL", sname="POR IL")
player = _make_player(100, "IL Stash", por_il)
with patch("services.team_service.team_service") as mock_ts, patch(
"commands.injuries.management.get_config"
) as mock_config:
mock_config.return_value.sba_season = 13
mock_ts.get_team_by_owner = AsyncMock(return_value=por_ml)
result = await injury_group._verify_team_ownership(interaction, player)
assert result is True
@pytest.mark.asyncio
async def test_player_team_not_populated_fails(self, injury_group):
"""Player with team_id but unpopulated team object should be denied.
Callers are expected to populate player.team before calling
_verify_team_ownership. If they don't, the method treats the missing
team as a failed check rather than silently allowing access.
"""
interaction = _make_interaction(is_admin=False)
por_team = _make_team(1, "POR")
player = Player.model_construct(
id=100,
name="Orphan Player",
wara=2.0,
season=13,
team_id=99,
team=None,
)
with patch("services.team_service.team_service") as mock_ts, patch(
"commands.injuries.management.get_config"
) as mock_config:
mock_config.return_value.sba_season = 13
mock_ts.get_team_by_owner = AsyncMock(return_value=por_team)
result = await injury_group._verify_team_ownership(interaction, player)
assert result is False
@pytest.mark.asyncio
async def test_error_embeds_are_ephemeral(self, injury_group):
"""Error responses should be ephemeral so only the invoking user sees them."""
interaction = _make_interaction(is_admin=False)
nyy_team = _make_team(2, "NYY")
player = _make_player(100, "Mike Trout", nyy_team)
with patch("services.team_service.team_service") as mock_ts, patch(
"commands.injuries.management.get_config"
) as mock_config:
mock_config.return_value.sba_season = 13
# Test "No Team Found" path
mock_ts.get_team_by_owner = AsyncMock(return_value=None)
await injury_group._verify_team_ownership(interaction, player)
call_kwargs = interaction.followup.send.call_args
assert call_kwargs.kwargs.get("ephemeral") is True
# Reset and test "Not Your Player" path
interaction = _make_interaction(is_admin=False)
por_team = _make_team(1, "POR")
with patch("services.team_service.team_service") as mock_ts, patch(
"commands.injuries.management.get_config"
) as mock_config:
mock_config.return_value.sba_season = 13
mock_ts.get_team_by_owner = AsyncMock(return_value=por_team)
await injury_group._verify_team_ownership(interaction, player)
call_kwargs = interaction.followup.send.call_args
assert call_kwargs.kwargs.get("ephemeral") is True
@pytest.mark.asyncio
async def test_player_without_team_id_passes(self, injury_group):
"""Players with no team_id should pass (can't verify, allow through)."""