diff --git a/app/modules/meeting/router.py b/app/modules/meeting/router.py index dee1ecd..bee1927 100644 --- a/app/modules/meeting/router.py +++ b/app/modules/meeting/router.py @@ -130,14 +130,14 @@ async def get_room( @router.get( "/{room_code}/participants", status_code=status.HTTP_200_OK, - summary="Get live active participants and lobby waiting list (Host only)", + summary="Get live active participants and lobby waiting list", ) async def get_live_state( room_code: str, current_user: User = Depends(get_current_user), service: MeetingService = Depends(get_meeting_service), ) -> JSONResponse: - state = await service.get_live_state(host=current_user, room_code=room_code) + state = await service.get_live_state(user=current_user, room_code=room_code) return JSONResponse( content={ "status": "success", diff --git a/app/modules/meeting/service.py b/app/modules/meeting/service.py index c0f39ff..75d1ecb 100644 --- a/app/modules/meeting/service.py +++ b/app/modules/meeting/service.py @@ -179,19 +179,16 @@ async def get_room_details(self, room_code: str) -> Room: return room - async def get_live_state(self, host: User, room_code: str) -> dict: - """Fetch active participant and waiting lobby details. Host only.""" + async def get_live_state(self, user: User, room_code: str) -> dict: + """Fetch active participant and waiting lobby details.""" room = self.repo.get_room_by_code(room_code) if not room: raise NotFoundException(message="Room not found.") - if room.host_id != host.id: - raise ForbiddenException( - message="Only the host can view live room state payload." - ) + is_host = room.host_id == user.id active = await self.state.get_participants(room_code) - lobby = await self.state.get_lobby(room_code) + lobby = await self.state.get_lobby(room_code) if is_host else {} return {"active": active, "lobby": lobby} @@ -507,6 +504,11 @@ async def admit_user(self, host: User, room_code: str, target_user_id: str) -> N if not was_in_lobby: raise BadRequestException(message="User is not in the lobby.") + cm = get_connection_manager() + await cm.send_to_user( + room_code, target_user_id, {"type": "admitted", "room_code": room_code} + ) + async def end_room(self, host: User, room_code: str) -> Room: """Host forcibly ends the meeting for everyone.""" room = self.repo.get_room_by_code(room_code) diff --git a/tests/meeting/test_meeting_router.py b/tests/meeting/test_meeting_router.py index 4d060f8..899ad43 100644 --- a/tests/meeting/test_meeting_router.py +++ b/tests/meeting/test_meeting_router.py @@ -713,7 +713,7 @@ async def test_host_gets_live_state( assert "lobby" in body["data"] @pytest.mark.asyncio - async def test_non_host_cannot_get_live_state( + async def test_non_host_gets_live_state_without_lobby( self, client: httpx.AsyncClient, db_session: Session ) -> None: _seed_user(db_session, email="host@example.com") @@ -729,7 +729,10 @@ async def test_non_host_cannot_get_live_state( f"/api/v1/meetings/{room_code}/participants", headers=_auth_headers(other_token), ) - assert resp.status_code == 403 + assert resp.status_code == 200 + body = resp.json() + assert "active" in body["data"] + assert body["data"]["lobby"] == {} # --------------------------------------------------------------------------- diff --git a/tests/meeting/test_meeting_service.py b/tests/meeting/test_meeting_service.py index 9e19cd9..e6dc927 100644 --- a/tests/meeting/test_meeting_service.py +++ b/tests/meeting/test_meeting_service.py @@ -238,24 +238,28 @@ async def test_returns_active_and_lobby(self) -> None: state.get_participants.return_value = {"u1": {"status": "connected"}} state.get_lobby.return_value = {"u2": {"display_name": "Guest"}} - result = await svc.get_live_state(host=host, room_code="ABCDEF123456") + result = await svc.get_live_state(user=host, room_code="ABCDEF123456") assert "active" in result assert "lobby" in result assert len(result["active"]) == 1 assert len(result["lobby"]) == 1 - @pytest.mark.asyncio - async def test_raises_forbidden_for_non_host(self) -> None: - svc, repo, _state = _build_service() + async def test_non_host_receives_empty_lobby(self) -> None: + svc, repo, state = _build_service() host = _make_user() other_user = _make_user(email="other@example.com") room = _make_room(host_id=host.id) repo.get_room_by_code.return_value = room + state.get_participants.return_value = {"u1": {"status": "connected"}} + state.get_lobby.return_value = {"u2": {"display_name": "Guest"}} - with pytest.raises(ForbiddenException, match="Only the host"): - await svc.get_live_state(host=other_user, room_code="ABCDEF123456") + result = await svc.get_live_state(user=other_user, room_code="ABCDEF123456") + + assert "active" in result + assert result["lobby"] == {} + assert len(result["active"]) == 1 @pytest.mark.asyncio async def test_raises_not_found_for_missing_room(self) -> None: @@ -264,7 +268,7 @@ async def test_raises_not_found_for_missing_room(self) -> None: repo.get_room_by_code.return_value = None with pytest.raises(NotFoundException): - await svc.get_live_state(host=host, room_code="INVALID") + await svc.get_live_state(user=host, room_code="INVALID") # ---------------------------------------------------------------------------