Skip to content

Commit 7f60f35

Browse files
cloudwebrtcclaude
andauthored
fix(tests): fix mypy errors in test_basic.py and reuse utils helpers (#913)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 91bc88a commit 7f60f35

1 file changed

Lines changed: 29 additions & 126 deletions

File tree

‎livekit-rtc/tests/test_basic.py‎

Lines changed: 29 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -11,118 +11,23 @@
1111

1212
from __future__ import annotations
1313

14-
import asyncio
1514
import os
16-
import uuid
17-
from typing import Callable, Optional
1815

1916
import pytest
2017

21-
from livekit import api, rtc
22-
from livekit.rtc.room import EventTypes
18+
from livekit import rtc
2319

24-
25-
WAIT_TIMEOUT = 20.0
26-
WAIT_INTERVAL = 0.1
27-
28-
29-
def skip_if_no_credentials():
30-
required_vars = ["LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET"]
31-
missing = [var for var in required_vars if not os.getenv(var)]
32-
return pytest.mark.skipif(
33-
bool(missing), reason=f"Missing environment variables: {', '.join(missing)}"
34-
)
35-
36-
37-
def create_token(identity: str, room_name: str) -> str:
38-
return (
39-
api.AccessToken()
40-
.with_identity(identity)
41-
.with_name(identity)
42-
.with_grants(
43-
api.VideoGrants(
44-
room_join=True,
45-
room=room_name,
46-
)
47-
)
48-
.to_jwt()
49-
)
50-
51-
52-
def unique_room_name(base: str) -> str:
53-
return f"{base}-{uuid.uuid4().hex[:8]}"
54-
55-
56-
async def _wait_until(
57-
predicate: Callable[[], bool],
58-
*,
59-
timeout: float = WAIT_TIMEOUT,
60-
interval: float = WAIT_INTERVAL,
61-
message: str = "condition not met",
62-
) -> None:
63-
loop = asyncio.get_event_loop()
64-
deadline = loop.time() + timeout
65-
while loop.time() < deadline:
66-
if predicate():
67-
return
68-
await asyncio.sleep(interval)
69-
raise AssertionError(f"timeout waiting: {message}")
70-
71-
72-
async def _connect(room: rtc.Room, identity: str, room_name: str) -> str:
73-
"""Mints a token, connects `room`, and returns the token (for reconnect)."""
74-
token = create_token(identity, room_name)
75-
url = os.environ["LIVEKIT_URL"]
76-
await room.connect(url, token)
77-
return token
78-
79-
80-
async def _ensure_all_connected(rooms: list[rtc.Room]) -> None:
81-
await _wait_until(
82-
lambda: all(r.connection_state == rtc.ConnectionState.CONN_CONNECTED for r in rooms),
83-
message="not all rooms reached CONN_CONNECTED",
84-
)
85-
86-
87-
async def _ensure_track_subscribed(room: rtc.Room, track_sid: str) -> None:
88-
def _has_subscribed() -> bool:
89-
for participant in room.remote_participants.values():
90-
pub = participant.track_publications.get(track_sid)
91-
if pub is not None and pub.subscribed:
92-
return True
93-
return False
94-
95-
await _wait_until(
96-
_has_subscribed,
97-
message=f"room did not subscribe to track {track_sid}",
98-
)
99-
100-
101-
def _expect_event(
102-
room: rtc.Room,
103-
event: EventTypes,
104-
predicate: Optional[Callable[..., bool]] = None,
105-
) -> asyncio.Future:
106-
"""Returns a future that resolves when `event` (optionally matching
107-
`predicate`) is fired on `room`."""
108-
loop = asyncio.get_event_loop()
109-
fut: asyncio.Future = loop.create_future()
110-
111-
def _on_event(*args, **kwargs) -> None:
112-
if fut.done():
113-
return
114-
if predicate is None or predicate(*args, **kwargs):
115-
fut.set_result(args)
116-
117-
room.on(event, _on_event)
118-
return fut
119-
120-
121-
async def _await_event(fut: asyncio.Future, timeout: float = WAIT_TIMEOUT) -> None:
122-
try:
123-
await asyncio.wait_for(fut, timeout=timeout)
124-
except asyncio.TimeoutError as e:
125-
raise AssertionError("timed out waiting for event") from e
20+
from utils import ( # type: ignore[import-not-found]
21+
await_event,
22+
connect_room,
23+
create_token,
24+
ensure_rooms_all_connected,
25+
ensure_track_subscribed,
26+
expect_room_event,
27+
skip_if_no_credentials,
28+
unique_room_name,
29+
wait_until,
30+
)
12631

12732

12833
async def _publish_video(room: rtc.Room, track_name: str) -> rtc.LocalTrackPublication:
@@ -139,40 +44,39 @@ async def _publish_audio(room: rtc.Room, track_name: str) -> rtc.LocalTrackPubli
13944
return await room.local_participant.publish_track(track, options)
14045

14146

142-
@skip_if_no_credentials()
47+
@skip_if_no_credentials() # type: ignore[untyped-decorator]
14348
@pytest.mark.asyncio
14449
async def test_connection_basics() -> None:
14550
room_name = unique_room_name("py-basics")
14651

147-
p1, p2 = rtc.Room(), rtc.Room()
148-
await _connect(p1, "p1", room_name)
149-
await _connect(p2, "p2", room_name)
150-
await _ensure_all_connected([p1, p2])
52+
p1 = await connect_room("p1", room_name)
53+
p2 = await connect_room("p2", room_name)
54+
await ensure_rooms_all_connected([p1, p2])
15155

15256
# p2 should observe p1 leaving
153-
p2_saw_p1_left = _expect_event(
57+
p2_saw_p1_left = expect_room_event(
15458
p2,
15559
"participant_disconnected",
15660
predicate=lambda p: p.identity == "p1",
15761
)
15862
await p1.disconnect()
159-
await _await_event(p2_saw_p1_left)
63+
await await_event(p2_saw_p1_left)
16064

161-
await _wait_until(
65+
await wait_until(
16266
lambda: p1.connection_state == rtc.ConnectionState.CONN_DISCONNECTED,
16367
message="p1 did not reach CONN_DISCONNECTED",
16468
)
16569

16670
await p2.disconnect()
167-
await _wait_until(
71+
await wait_until(
16872
lambda: p2.connection_state == rtc.ConnectionState.CONN_DISCONNECTED,
16973
message="p2 did not reach CONN_DISCONNECTED",
17074
)
17175

17276
# p3: connect, disconnect, reconnect, disconnect cycle
173-
p3 = rtc.Room()
174-
p3_token = await _connect(p3, "p3", room_name)
77+
p3 = await connect_room("p3", room_name)
17578
p3_url = os.environ["LIVEKIT_URL"]
79+
p3_token = create_token("p3", room_name)
17680

17781
await p3.disconnect()
17882
assert p3.connection_state == rtc.ConnectionState.CONN_DISCONNECTED, (
@@ -190,29 +94,28 @@ async def test_connection_basics() -> None:
19094
)
19195

19296
# p4 joins, then p3 reconnects to publish to p4
193-
p4 = rtc.Room()
194-
await _connect(p4, "p4", room_name)
97+
p4 = await connect_room("p4", room_name)
19598

19699
await p3.connect(p3_url, p3_token)
197100
assert p3.connection_state == rtc.ConnectionState.CONN_CONNECTED, (
198101
f"expected p3 reconnected, got {p3.connection_state}"
199102
)
200103

201104
# publish camera from p3, expect p4 to see track_published
202-
video_published = _expect_event(p4, "track_published")
105+
video_published = expect_room_event(p4, "track_published")
203106
video_pub = await _publish_video(p3, "p3-camera")
204-
await _await_event(video_published)
205-
await _ensure_track_subscribed(p4, video_pub.sid)
107+
await await_event(video_published)
108+
await ensure_track_subscribed(p4, video_pub.sid)
206109

207110
# publish microphone from p3, expect p4 to see another track_published
208-
audio_published = _expect_event(
111+
audio_published = expect_room_event(
209112
p4,
210113
"track_published",
211114
predicate=lambda pub, _p: pub.sid != video_pub.sid,
212115
)
213116
audio_pub = await _publish_audio(p3, "p3-mic")
214-
await _await_event(audio_published)
215-
await _ensure_track_subscribed(p4, audio_pub.sid)
117+
await await_event(audio_published)
118+
await ensure_track_subscribed(p4, audio_pub.sid)
216119

217120
await p3.disconnect()
218121
await p4.disconnect()

0 commit comments

Comments
 (0)