@@ -22,6 +22,19 @@ def jpeg_bytes():
2222 return _make_jpeg ()
2323
2424
25+ def _await_clients (host , expected : int = 1 , timeout : float = 5.0 ) -> None :
26+ """Wait until the host has authenticated ``expected`` viewers.
27+
28+ ``viewer.connect()`` returns once the *client* side is up; the host's accept
29+ thread may not have authenticated the viewer yet, so a broadcast issued
30+ immediately can reach zero clients (reliably so on a slow CI container). This
31+ closes that race using the host's own authenticated-client count.
32+ """
33+ deadline = time .monotonic () + timeout
34+ while host .connected_clients < expected and time .monotonic () < deadline :
35+ time .sleep (0.02 )
36+
37+
2538# --- Phase 5.2: chat ----------------------------------------------------
2639
2740def test_host_broadcasts_chat_to_viewer (jpeg_bytes ):
@@ -44,6 +57,7 @@ def on_chat(sender: str, text: str) -> None:
4457 )
4558 viewer .connect (timeout = 5.0 )
4659 try :
60+ _await_clients (host )
4761 sent = host .broadcast_chat ("hello viewer" )
4862 assert sent == 1
4963 deadline = time .monotonic () + 2.0
@@ -75,6 +89,7 @@ def host_on_chat(sender: str, text: str) -> None:
7589 viewer = RemoteDesktopViewer (host = "127.0.0.1" , port = host .port , token = "t" )
7690 viewer .connect (timeout = 5.0 )
7791 try :
92+ _await_clients (host )
7893 viewer .send_chat ("ping from viewer" , sender = "alice" )
7994 deadline = time .monotonic () + 2.0
8095 while not received and time .monotonic () < deadline :
@@ -124,6 +139,7 @@ def test_viewer_cursor_payload_routes_to_separate_callback(jpeg_bytes):
124139 )
125140 viewer .connect (timeout = 5.0 )
126141 try :
142+ _await_clients (host )
127143 # Simulate MultiViewerHost relaying another operator's cursor.
128144 host .broadcast_viewer_cursor ("alice" , 200 , 300 )
129145 deadline = time .monotonic () + 2.0
0 commit comments