fixup! fixup! wip adapt lobby to be functional in an iframe

This commit is contained in:
lebaudantoine
2026-08-17 12:30:27 +02:00
parent 087b0cb06e
commit aefaa0ce98
3 changed files with 86 additions and 67 deletions
+6 -9
View File
@@ -145,12 +145,7 @@ class LobbyService:
user_role = room.get_role(request.user) user_role = room.get_role(request.user)
if self.can_bypass_lobby(room=room, user=request.user, role=user_role): if self.can_bypass_lobby(room=room, user=request.user, role=user_role):
self._update_participant_status( participant = self.handle_participant_entry(room_id, participant.id, True)
room_id,
participant_id,
LobbyParticipantStatus.ACCEPTED,
settings.LOBBY_ACCEPTED_TIMEOUT
)
livekit_config = utils.generate_livekit_config( livekit_config = utils.generate_livekit_config(
room_id=room_id, room_id=room_id,
@@ -280,7 +275,7 @@ class LobbyService:
room_id: UUID, room_id: UUID,
participant_id: str, participant_id: str,
allow_entry: bool, allow_entry: bool,
) -> None: ) -> LobbyParticipant:
"""Handle decision on participant entry. """Handle decision on participant entry.
Updates participant status based on allow_entry: Updates participant status based on allow_entry:
@@ -298,7 +293,7 @@ class LobbyService:
"timeout": settings.LOBBY_DENIED_TIMEOUT, "timeout": settings.LOBBY_DENIED_TIMEOUT,
} }
self._update_participant_status(room_id, participant_id, **decision) return self._update_participant_status(room_id, participant_id, **decision)
def _update_participant_status( def _update_participant_status(
self, self,
@@ -306,7 +301,7 @@ class LobbyService:
participant_id: str, participant_id: str,
status: LobbyParticipantStatus, status: LobbyParticipantStatus,
timeout: int, timeout: int,
) -> None: ) -> LobbyParticipant:
"""Update participant status with appropriate timeout.""" """Update participant status with appropriate timeout."""
cache_key = self._get_cache_key(room_id, participant_id) cache_key = self._get_cache_key(room_id, participant_id)
@@ -328,6 +323,8 @@ class LobbyService:
participant.status = status participant.status = status
cache.set(cache_key, participant.to_dict(), timeout=timeout) cache.set(cache_key, participant.to_dict(), timeout=timeout)
return participant
def clear_room_cache(self, room_id: UUID) -> None: def clear_room_cache(self, room_id: UUID) -> None:
"""Clear all participant entries from the cache for a specific room.""" """Clear all participant entries from the cache for a specific room."""
@@ -221,6 +221,10 @@ def test_request_entry_public_room(settings):
# The accepted participant is persisted, out of the waiting list # The accepted participant is persisted, out of the waiting list
lobby_keys = cache.keys(f"mocked-cache-prefix_{room.id}_*") lobby_keys = cache.keys(f"mocked-cache-prefix_{room.id}_*")
assert len(lobby_keys) == 1 assert len(lobby_keys) == 1
ttl = cache.ttl(lobby_keys[0])
assert ttl is not None
assert ttl == pytest.approx(settings.LOBBY_ACCEPTED_TIMEOUT, abs=2000)
assert cache.get(lobby_keys[0])["status"] == "accepted" assert cache.get(lobby_keys[0])["status"] == "accepted"
@@ -268,6 +272,9 @@ def test_request_entry_authenticated_user_public_room(settings):
lobby_keys = cache.keys(f"mocked-cache-prefix_{room.id}_*") lobby_keys = cache.keys(f"mocked-cache-prefix_{room.id}_*")
assert len(lobby_keys) == 1 assert len(lobby_keys) == 1
assert cache.get(lobby_keys[0])["status"] == "accepted" assert cache.get(lobby_keys[0])["status"] == "accepted"
ttl = cache.ttl(lobby_keys[0])
assert ttl is not None
assert ttl == pytest.approx(settings.LOBBY_ACCEPTED_TIMEOUT, abs=2000)
def test_request_entry_waiting_participant_public_room(settings): def test_request_entry_waiting_participant_public_room(settings):
@@ -318,6 +325,11 @@ def test_request_entry_waiting_participant_public_room(settings):
lobby_keys = cache.keys(f"mocked-cache-prefix_{room.id}_*") lobby_keys = cache.keys(f"mocked-cache-prefix_{room.id}_*")
assert len(lobby_keys) == 1 assert len(lobby_keys) == 1
ttl = cache.ttl(lobby_keys[0])
assert ttl is not None
assert ttl == pytest.approx(settings.LOBBY_ACCEPTED_TIMEOUT, abs=2000)
assert cache.get(lobby_keys[0])["status"] == "accepted"
def test_request_entry_invalid_data(): def test_request_entry_invalid_data():
"""Should return 400 for invalid request data.""" """Should return 400 for invalid request data."""
+68 -58
View File
@@ -7,7 +7,7 @@ Test lobby service.
import uuid import uuid
from unittest import mock from unittest import mock
from django.conf import settings from django.conf import settings as django_settings
from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import AnonymousUser
from django.core.cache import cache from django.core.cache import cache
@@ -129,7 +129,7 @@ def test_get_cache_key(lobby_service, participant_id):
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED) room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
cache_key = lobby_service._get_cache_key(room.id, participant_id) cache_key = lobby_service._get_cache_key(room.id, participant_id)
expected_key = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_{participant_id}" expected_key = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_{participant_id}"
assert cache_key == expected_key assert cache_key == expected_key
@@ -196,22 +196,26 @@ def test_can_bypass_lobby_private_room_with_any_role(role, lobby_service):
@mock.patch("core.utils.generate_livekit_config") @mock.patch("core.utils.generate_livekit_config")
def test_request_entry_public_room( def test_request_entry_public_room(
mock_generate_config, lobby_service, participant_id, username mock_generate_config, lobby_service, participant_id, username, settings
): ):
"""Test requesting entry to a public room.""" """Test requesting entry to a public room."""
settings.LOBBY_KEY_PREFIX = "mocked-cache-prefix"
request = mock.Mock() request = mock.Mock()
request.user = AnonymousUser() request.user = AnonymousUser()
room = RoomFactory(access_level=RoomAccessLevel.PUBLIC) room = RoomFactory(access_level=RoomAccessLevel.PUBLIC)
mocked_participant = LobbyParticipant( cache.set(
status=LobbyParticipantStatus.UNKNOWN, f"mocked-cache-prefix_{room.id}_{participant_id}",
username=username, {
id=participant_id, "id": participant_id,
color="#123456", "username": username,
"status": "waiting",
"color": "#123456",
},
) )
lobby_service._get_participant = mock.Mock(return_value=mocked_participant)
mock_generate_config.return_value = {"token": "test-token"} mock_generate_config.return_value = {"token": "test-token"}
participant, livekit_config = lobby_service.request_entry( participant, livekit_config = lobby_service.request_entry(
@@ -226,31 +230,33 @@ def test_request_entry_public_room(
username=username, username=username,
color=participant.color, color=participant.color,
configuration=room.configuration, configuration=room.configuration,
participant_id="test-participant-id", participant_id=participant_id,
role=None, role=None,
) )
lobby_service._get_participant.assert_called_once_with(room.id, participant_id)
@mock.patch("core.utils.generate_livekit_config") @mock.patch("core.utils.generate_livekit_config")
def test_request_entry_trusted_room( def test_request_entry_trusted_room(
mock_generate_config, lobby_service, participant_id, username mock_generate_config, lobby_service, participant_id, username, settings
): ):
"""Test requesting entry to a trusted room when the user is authenticated.""" """Test requesting entry to a trusted room when the user is authenticated."""
settings.LOBBY_KEY_PREFIX = "mocked-cache-prefix"
request = mock.Mock() request = mock.Mock()
request.user = UserFactory() request.user = UserFactory()
room = RoomFactory(access_level=RoomAccessLevel.TRUSTED) room = RoomFactory(access_level=RoomAccessLevel.TRUSTED)
mocked_participant = LobbyParticipant( cache.set(
status=LobbyParticipantStatus.UNKNOWN, f"mocked-cache-prefix_{room.id}_{participant_id}",
username=username, {
id=participant_id, "id": participant_id,
color="#123456", "username": username,
"status": "waiting",
"color": "#123456",
},
) )
lobby_service._get_participant = mock.Mock(return_value=mocked_participant)
mock_generate_config.return_value = {"token": "test-token"} mock_generate_config.return_value = {"token": "test-token"}
participant, livekit_config = lobby_service.request_entry( participant, livekit_config = lobby_service.request_entry(
@@ -265,12 +271,10 @@ def test_request_entry_trusted_room(
username=username, username=username,
color=participant.color, color=participant.color,
configuration=room.configuration, configuration=room.configuration,
participant_id="test-participant-id", participant_id=participant_id,
role=None, role=None,
) )
lobby_service._get_participant.assert_called_once_with(room.id, participant_id)
@mock.patch("core.services.lobby.LobbyService._notify_entry_request") @mock.patch("core.services.lobby.LobbyService._notify_entry_request")
@mock.patch("core.services.lobby.LobbyService._create_participant") @mock.patch("core.services.lobby.LobbyService._create_participant")
@@ -339,21 +343,24 @@ def test_request_entry_waiting_participant(
@mock.patch("core.utils.generate_livekit_config") @mock.patch("core.utils.generate_livekit_config")
def test_request_entry_accepted_participant( def test_request_entry_accepted_participant(
mock_generate_config, lobby_service, participant_id, username mock_generate_config, lobby_service, participant_id, username, settings
): ):
"""Test requesting entry for an accepted participant.""" """Test requesting entry for an accepted participant."""
settings.LOBBY_KEY_PREFIX = "mocked-cache-prefix"
request = mock.Mock() request = mock.Mock()
request.user = AnonymousUser() request.user = AnonymousUser()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED) room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
mocked_participant = LobbyParticipant( cache.set(
status=LobbyParticipantStatus.ACCEPTED, f"mocked-cache-prefix_{room.id}_{participant_id}",
username=username, {
id=participant_id, "id": participant_id,
color="#123456", "username": username,
"status": "accepted",
"color": "#123456",
},
) )
lobby_service._get_participant = mock.Mock(return_value=mocked_participant)
mock_generate_config.return_value = {"token": "test-token"} mock_generate_config.return_value = {"token": "test-token"}
@@ -372,14 +379,15 @@ def test_request_entry_accepted_participant(
participant_id="test-participant-id", participant_id="test-participant-id",
role=None, role=None,
) )
lobby_service._get_participant.assert_called_once_with(room.id, participant_id)
@mock.patch("core.utils.generate_livekit_config") @mock.patch("core.utils.generate_livekit_config")
def test_request_entry_participant_with_role( def test_request_entry_participant_with_role(
mock_generate_config, lobby_service, participant_id, username mock_generate_config, lobby_service, participant_id, username, settings
): ):
"""Test requesting entry for a participant with a role on the room.""" """Test requesting entry for a participant with a role on the room."""
settings.LOBBY_KEY_PREFIX = "mocked-cache-prefix"
request = mock.Mock() request = mock.Mock()
request.user = UserFactory() request.user = UserFactory()
@@ -387,13 +395,15 @@ def test_request_entry_participant_with_role(
UserResourceAccessFactory(resource=room, user=request.user, role="administrator") UserResourceAccessFactory(resource=room, user=request.user, role="administrator")
mocked_participant = LobbyParticipant( cache.set(
status=LobbyParticipantStatus.ACCEPTED, f"mocked-cache-prefix_{room.id}_{participant_id}",
username=username, {
id=participant_id, "id": participant_id,
color="#123456", "username": username,
"status": "accepted",
"color": "#123456",
},
) )
lobby_service._get_participant = mock.Mock(return_value=mocked_participant)
mock_generate_config.return_value = {"token": "test-token"} mock_generate_config.return_value = {"token": "test-token"}
@@ -412,7 +422,6 @@ def test_request_entry_participant_with_role(
participant_id="test-participant-id", participant_id="test-participant-id",
role="administrator", role="administrator",
) )
lobby_service._get_participant.assert_called_once_with(room.id, participant_id)
@mock.patch("core.services.lobby.cache") @mock.patch("core.services.lobby.cache")
@@ -422,7 +431,7 @@ def test_refresh_waiting_status(mock_cache, lobby_service, participant_id):
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED) room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
lobby_service.refresh_waiting_status(room.id, participant_id) lobby_service.refresh_waiting_status(room.id, participant_id)
mock_cache.touch.assert_called_once_with( mock_cache.touch.assert_called_once_with(
"mocked_cache_key", settings.LOBBY_WAITING_TIMEOUT "mocked_cache_key", django_settings.LOBBY_WAITING_TIMEOUT
) )
@@ -433,7 +442,6 @@ def test_create_participant(
mock_cache, mock_cache,
lobby_service, lobby_service,
username, username,
settings,
): ):
"""A created participant is waiting, colored, and persisted.""" """A created participant is waiting, colored, and persisted."""
mock_generate_color.return_value = "#123456" mock_generate_color.return_value = "#123456"
@@ -454,7 +462,7 @@ def test_create_participant(
mock_cache.set.assert_called_once_with( mock_cache.set.assert_called_once_with(
"mocked_cache_key", "mocked_cache_key",
participant.to_dict(), participant.to_dict(),
timeout=settings.LOBBY_WAITING_TIMEOUT, timeout=django_settings.LOBBY_WAITING_TIMEOUT,
) )
@@ -512,7 +520,7 @@ def test_list_waiting_participants_empty(mock_cache, lobby_service):
result = lobby_service.list_waiting_participants(room.id) result = lobby_service.list_waiting_participants(room.id)
assert result == [] assert result == []
pattern = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_*" pattern = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_*"
mock_cache.keys.assert_called_once_with(pattern) mock_cache.keys.assert_called_once_with(pattern)
mock_cache.get_many.assert_not_called() mock_cache.get_many.assert_not_called()
@@ -521,7 +529,7 @@ def test_list_waiting_participants_empty(mock_cache, lobby_service):
def test_list_waiting_participants(mock_cache, lobby_service, participant_dict): def test_list_waiting_participants(mock_cache, lobby_service, participant_dict):
"""Test listing waiting participants with valid data.""" """Test listing waiting participants with valid data."""
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED) room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
cache_key = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant1" cache_key = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant1"
mock_cache.keys.return_value = [cache_key] mock_cache.keys.return_value = [cache_key]
mock_cache.get_many.return_value = {cache_key: participant_dict} mock_cache.get_many.return_value = {cache_key: participant_dict}
@@ -530,7 +538,7 @@ def test_list_waiting_participants(mock_cache, lobby_service, participant_dict):
assert len(result) == 1 assert len(result) == 1
assert result[0]["status"] == "waiting" assert result[0]["status"] == "waiting"
assert result[0]["username"] == "test-username" assert result[0]["username"] == "test-username"
pattern = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_*" pattern = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_*"
mock_cache.keys.assert_called_once_with(pattern) mock_cache.keys.assert_called_once_with(pattern)
mock_cache.get_many.assert_called_once_with([cache_key]) mock_cache.get_many.assert_called_once_with([cache_key])
@@ -539,8 +547,8 @@ def test_list_waiting_participants(mock_cache, lobby_service, participant_dict):
def test_list_waiting_participants_multiple(mock_cache, lobby_service): def test_list_waiting_participants_multiple(mock_cache, lobby_service):
"""Test listing multiple waiting participants with valid data.""" """Test listing multiple waiting participants with valid data."""
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED) room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
cache_key1 = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant1" cache_key1 = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant1"
cache_key2 = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant2" cache_key2 = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant2"
participant1 = { participant1 = {
"status": "waiting", "status": "waiting",
@@ -573,7 +581,7 @@ def test_list_waiting_participants_multiple(mock_cache, lobby_service):
# Verify all participants have waiting status # Verify all participants have waiting status
assert all(p["status"] == "waiting" for p in result) assert all(p["status"] == "waiting" for p in result)
pattern = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_*" pattern = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_*"
mock_cache.keys.assert_called_once_with(pattern) mock_cache.keys.assert_called_once_with(pattern)
mock_cache.get_many.assert_called_once_with([cache_key1, cache_key2]) mock_cache.get_many.assert_called_once_with([cache_key1, cache_key2])
@@ -582,7 +590,7 @@ def test_list_waiting_participants_multiple(mock_cache, lobby_service):
def test_list_waiting_participants_corrupted_data(mock_cache, lobby_service): def test_list_waiting_participants_corrupted_data(mock_cache, lobby_service):
"""Test listing waiting participants with corrupted data.""" """Test listing waiting participants with corrupted data."""
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED) room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
cache_key = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant1" cache_key = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant1"
mock_cache.keys.return_value = [cache_key] mock_cache.keys.return_value = [cache_key]
mock_cache.get_many.return_value = {cache_key: {"invalid": "data"}} mock_cache.get_many.return_value = {cache_key: {"invalid": "data"}}
@@ -596,8 +604,8 @@ def test_list_waiting_participants_corrupted_data(mock_cache, lobby_service):
def test_list_waiting_participants_partially_corrupted(mock_cache, lobby_service): def test_list_waiting_participants_partially_corrupted(mock_cache, lobby_service):
"""Test listing waiting participants with one valid and one corrupted entry.""" """Test listing waiting participants with one valid and one corrupted entry."""
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED) room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
cache_key1 = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant1" cache_key1 = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant1"
cache_key2 = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant2" cache_key2 = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant2"
valid_participant = { valid_participant = {
"status": "waiting", "status": "waiting",
@@ -626,7 +634,7 @@ def test_list_waiting_participants_partially_corrupted(mock_cache, lobby_service
mock_cache.delete.assert_called_once_with(cache_key1) mock_cache.delete.assert_called_once_with(cache_key1)
# Verify both cache keys were queried # Verify both cache keys were queried
pattern = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_*" pattern = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_*"
mock_cache.keys.assert_called_once_with(pattern) mock_cache.keys.assert_called_once_with(pattern)
mock_cache.get_many.assert_called_once_with([cache_key1, cache_key2]) mock_cache.get_many.assert_called_once_with([cache_key1, cache_key2])
@@ -635,8 +643,8 @@ def test_list_waiting_participants_partially_corrupted(mock_cache, lobby_service
def test_list_waiting_participants_non_waiting(mock_cache, lobby_service): def test_list_waiting_participants_non_waiting(mock_cache, lobby_service):
"""Test listing only waiting participants (not accepted/denied).""" """Test listing only waiting participants (not accepted/denied)."""
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED) room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
cache_key1 = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant1" cache_key1 = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant1"
cache_key2 = f"{settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant2" cache_key2 = f"{django_settings.LOBBY_KEY_PREFIX}_{room.id!s}_participant2"
participant1 = { participant1 = {
"status": "waiting", "status": "waiting",
@@ -674,7 +682,7 @@ def test_handle_participant_entry_allow(mock_update, lobby_service, participant_
room.id, room.id,
participant_id, participant_id,
status=LobbyParticipantStatus.ACCEPTED, status=LobbyParticipantStatus.ACCEPTED,
timeout=settings.LOBBY_ACCEPTED_TIMEOUT, timeout=django_settings.LOBBY_ACCEPTED_TIMEOUT,
) )
@@ -688,7 +696,7 @@ def test_handle_participant_entry_deny(mock_update, lobby_service, participant_i
room.id, room.id,
participant_id, participant_id,
status=LobbyParticipantStatus.DENIED, status=LobbyParticipantStatus.DENIED,
timeout=settings.LOBBY_DENIED_TIMEOUT, timeout=django_settings.LOBBY_DENIED_TIMEOUT,
) )
@@ -829,14 +837,16 @@ def test_clear_participant_cache(lobby_service):
room_id = uuid.uuid4() room_id = uuid.uuid4()
participant_id = "test-participant-id" participant_id = "test-participant-id"
cache_key = f"{settings.LOBBY_KEY_PREFIX}_{room_id!s}_{participant_id}" cache_key = f"{django_settings.LOBBY_KEY_PREFIX}_{room_id!s}_{participant_id}"
participant_data = { participant_data = {
"status": "waiting", "status": "waiting",
"username": "test-username", "username": "test-username",
"id": participant_id, "id": participant_id,
"color": "#123456", "color": "#123456",
} }
cache.set(cache_key, participant_data, timeout=settings.LOBBY_WAITING_TIMEOUT) cache.set(
cache_key, participant_data, timeout=django_settings.LOBBY_WAITING_TIMEOUT
)
assert cache.get(cache_key) is not None assert cache.get(cache_key) is not None
lobby_service.clear_participant_cache(room_id, participant_id) lobby_service.clear_participant_cache(room_id, participant_id)
@@ -848,7 +858,7 @@ def test_clear_participant_cache_nonexistent(lobby_service):
room_id = uuid.uuid4() room_id = uuid.uuid4()
participant_id = "nonexistent-participant" participant_id = "nonexistent-participant"
cache_key = f"{settings.LOBBY_KEY_PREFIX}_{room_id!s}_{participant_id}" cache_key = f"{django_settings.LOBBY_KEY_PREFIX}_{room_id!s}_{participant_id}"
assert cache.get(cache_key) is None assert cache.get(cache_key) is None
lobby_service.clear_participant_cache(room_id, participant_id) lobby_service.clear_participant_cache(room_id, participant_id)