🥅(backend) refine Twirp error handling for participant operations

Avoid mapping all Twirp errors to generic 500 responses.

Explicitly handle the case where a participant is no longer in
the room, as this may indicate suspicious behavior or a client
state issue.

Improve error discrimination to provide more accurate responses.
This commit is contained in:
lebaudantoine
2026-04-08 18:57:32 +02:00
parent 6180ac4e4f
commit 07af7a85ff
4 changed files with 138 additions and 0 deletions
@@ -545,3 +545,55 @@ def test_remove_participant_unexpected_twirp_error(mock_livekit_client):
assert response.data == {"error": "Failed to remove participant"}
mock_livekit_client.aclose.assert_called_once()
def test_update_participant_not_found(mock_livekit_client):
"""Test update participant returns 404 when the participant no longer exists in the room."""
client = APIClient()
mock_livekit_client.room.update_participant.side_effect = TwirpError(
msg="participant does not exist", code="not_found", status=404
)
room = RoomFactory()
user = UserFactory()
UserResourceAccessFactory(
resource=room, user=user, role=random.choice(["administrator", "owner"])
)
client.force_authenticate(user=user)
payload = {"participant_identity": str(uuid4()), "name": "Test User"}
url = reverse("rooms-update-participant", kwargs={"pk": room.id})
response = client.post(url, payload, format="json")
assert response.status_code == status.HTTP_404_NOT_FOUND
assert response.data == {"error": "Participant not found"}
mock_livekit_client.aclose.assert_called_once()
def test_remove_participant_not_found(mock_livekit_client):
"""Test remove participant returns 404 when the participant no longer exists in the room."""
client = APIClient()
mock_livekit_client.room.remove_participant.side_effect = TwirpError(
msg="participant does not exist", code="not_found", status=404
)
room = RoomFactory()
user = UserFactory()
UserResourceAccessFactory(
resource=room, user=user, role=random.choice(["administrator", "owner"])
)
client.force_authenticate(user=user)
payload = {"participant_identity": str(uuid4())}
url = reverse("rooms-remove-participant", kwargs={"pk": room.id})
response = client.post(url, payload, format="json")
assert response.status_code == status.HTTP_404_NOT_FOUND
assert response.data == {"error": "Participant not found"}
mock_livekit_client.aclose.assert_called_once()
@@ -440,6 +440,22 @@ def test_toggle_hand_room_not_found(user):
assert response.status_code == status.HTTP_404_NOT_FOUND
def test_toggle_hand_participant_not_found(mock_livekit_client, room, token):
"""Test toggle hand returns 404 when the participant no longer exists in the room."""
mock_livekit_client.room.update_participant.side_effect = TwirpError(
msg="participant does not exist", code="not_found", status=404
)
client = APIClient()
url = reverse("rooms-toggle-hand", kwargs={"pk": room.id})
response = client.post(url, {"raised": True, "token": token}, format="json")
assert response.status_code == status.HTTP_404_NOT_FOUND
assert response.data == {"error": "Participant not found"}
mock_livekit_client.aclose.assert_called_once()
def test_rename_participant_malformed_token(room):
"""Test rename returns 403 when the LiveKit token is malformed."""
client = APIClient()
@@ -461,3 +477,19 @@ def test_rename_participant_room_not_found(user):
response = client.post(url, {"name": "John Doe", "token": token}, format="json")
assert response.status_code == status.HTTP_404_NOT_FOUND
def test_rename_participant_not_found(mock_livekit_client, room, token):
"""Test rename returns 404 when the participant no longer exists in the room."""
mock_livekit_client.room.update_participant.side_effect = TwirpError(
msg="participant does not exist", code="not_found", status=404
)
client = APIClient()
url = reverse("rooms-rename", kwargs={"pk": room.id})
response = client.post(url, {"name": "John Doe", "token": token}, format="json")
assert response.status_code == status.HTTP_404_NOT_FOUND
assert response.data == {"error": "Participant not found"}
mock_livekit_client.aclose.assert_called_once()