diff --git a/app/modules/meeting/ws_router.py b/app/modules/meeting/ws_router.py index ff4730e..39f95b5 100644 --- a/app/modules/meeting/ws_router.py +++ b/app/modules/meeting/ws_router.py @@ -74,6 +74,19 @@ async def signaling_websocket( sender_id=user_id, # Don't echo back to the joiner themselves ) + # Tell the new user about all existing users so they can update their UI immediately + participants = await MeetingStateService().get_participants(room_code) + existing_users = [ + { + "user_id": pid, + "display_name": pstate.get("display_name", ""), + "role": pstate.get("role", "guest"), + } + for pid, pstate in participants.items() + if pid != user_id + ] + await websocket.send_json({"type": "existing_users", "users": existing_users}) + try: while True: data = await websocket.receive_text() diff --git a/tests/meeting/test_ws_router.py b/tests/meeting/test_ws_router.py index 91b0254..1ff8bdc 100644 --- a/tests/meeting/test_ws_router.py +++ b/tests/meeting/test_ws_router.py @@ -60,12 +60,27 @@ def mock_kafka_consumer(): yield consumer -@pytest.mark.usefixtures("mock_room_participant") +@pytest.fixture +def mock_meeting_state(): + with patch( + "app.modules.meeting.ws_router.MeetingStateService" + ) as mock_service_class: + service = MagicMock() + service.get_participants = AsyncMock(return_value={}) + mock_service_class.return_value = service + yield service + + +@pytest.mark.usefixtures("mock_room_participant", "mock_meeting_state") def test_signaling_websocket(mock_connection_manager): # This will connect, send a text message, and then close with client.websocket_connect( "/api/v1/ws/signaling/room1?token=mock_token" ) as websocket: + # Consume the initial existing_users message + data = websocket.receive_json() + assert data["type"] == "existing_users" + websocket.send_text(json.dumps({"type": "offer", "target_user_id": "user2"})) # The connection manager's send_to_user should be called