mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-24 00:52:24 +00:00
fix(team): 404 a role update whose target left the roster before the locked read
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
431ddbdd22
commit
b7db48c7c1
2 changed files with 42 additions and 0 deletions
|
|
@ -3069,6 +3069,9 @@ async def _update_team_member_role(
|
|||
raise HTTPException(status_code=404, detail={"error": f"Team id={team_id} does not exist in db"})
|
||||
|
||||
before: Final = tuple(locked_members)
|
||||
if all(member.user_id != user_id for member in before):
|
||||
raise HTTPException(status_code=404, detail={"error": f"User {user_id} is not a member of team {team_id}"})
|
||||
|
||||
after: Final = tuple(
|
||||
Member(user_id=member.user_id, role=role, user_email=user_email or member.user_email)
|
||||
if member.user_id == user_id
|
||||
|
|
|
|||
|
|
@ -13675,6 +13675,45 @@ async def test_team_member_update_role_change_404s_when_the_team_is_gone_under_t
|
|||
mock_prisma_client.db.litellm_teamtable.update.assert_not_awaited()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_team_member_update_role_change_404s_when_the_member_left_before_the_locked_read(monkeypatch):
|
||||
"""Regression: a member removed between the pre-lock read and the locked read was reported as updated."""
|
||||
audit_logger = _wire_audit_log_callback(monkeypatch)
|
||||
snapshot = LiteLLM_TeamTable(
|
||||
team_id="team-member-gone-race",
|
||||
team_alias="member-gone-race",
|
||||
metadata={},
|
||||
members_with_roles=[Member(user_id="alice", role="admin"), Member(user_id="bob", role="user")],
|
||||
)
|
||||
locked_row = LiteLLM_TeamTable(
|
||||
team_id="team-member-gone-race",
|
||||
team_alias="member-gone-race",
|
||||
metadata={},
|
||||
members_with_roles=[Member(user_id="alice", role="admin")],
|
||||
)
|
||||
mock_prisma_client = MagicMock()
|
||||
mock_prisma_client.db.litellm_teamtable.find_unique = AsyncMock(side_effect=[snapshot, locked_row])
|
||||
mock_prisma_client.db.litellm_teamtable.update = AsyncMock()
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client)
|
||||
_wire_member_delete_tx(mock_prisma_client)
|
||||
|
||||
team_info_patch, upsert_patch = _member_update_patches(snapshot)
|
||||
with team_info_patch, upsert_patch, pytest.raises(HTTPException) as exc_info:
|
||||
await team_member_update(
|
||||
data=TeamMemberUpdateRequest(team_id="team-member-gone-race", user_id="bob", role="admin"),
|
||||
http_request=MagicMock(),
|
||||
user_api_key_dict=UserAPIKeyAuth(
|
||||
user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-admin", user_id="admin-user"
|
||||
),
|
||||
)
|
||||
|
||||
assert exc_info.value.status_code == 404
|
||||
assert "bob" in str(exc_info.value.detail)
|
||||
mock_prisma_client.db.litellm_teamtable.update.assert_not_awaited()
|
||||
await _settle_audit_log_tasks()
|
||||
assert audit_logger.payloads == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_team_member_delete_response_does_not_wait_for_the_audit_insert(
|
||||
monkeypatch, mock_db_client, mock_admin_auth
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue