@@ -288,3 +288,75 @@ async def mock_receive() -> HTTPDisconnectEvent:
288288 body = b"" .join (received ).decode ()
289289 assert "hello" in body
290290 assert "world" in body
291+
292+
293+ async def test_sse_send_raises_without_ping_interval () -> None :
294+ """_send requires a lock (and therefore ping_interval); RuntimeError if missing."""
295+
296+ async def empty () -> AsyncIterator [str ]:
297+ return
298+ yield
299+
300+ response = ASGIStreamingSSEResponse (
301+ iterator = empty (),
302+ media_type = "text/event-stream" ,
303+ status_code = 200 ,
304+ )
305+
306+ async def mock_send (message : Message ) -> None :
307+ pass
308+
309+ with pytest .raises (RuntimeError , match = "_send called without a send lock" ):
310+ await response ._send (mock_send , b"data" )
311+
312+
313+ async def test_sse_ping_raises_without_ping_interval () -> None :
314+ """_ping requires ping_interval; RuntimeError if called without one."""
315+
316+ async def empty () -> AsyncIterator [str ]:
317+ return
318+ yield
319+
320+ response = ASGIStreamingSSEResponse (
321+ iterator = empty (),
322+ media_type = "text/event-stream" ,
323+ status_code = 200 ,
324+ )
325+
326+ async def mock_send (message : Message ) -> None :
327+ pass
328+
329+ with pytest .raises (RuntimeError , match = "_ping called without a ping interval" ):
330+ await response ._ping (mock_send , anyio .Event ())
331+
332+
333+ async def test_sse_send_body_delegates_to_parent_without_ping () -> None :
334+ """Without ping_interval, send_body falls through to the parent (no lock, no task group)."""
335+
336+ async def gen () -> AsyncIterator [str ]:
337+ yield "chunk1"
338+ yield "chunk2"
339+
340+ response = ASGIStreamingSSEResponse (
341+ iterator = gen (),
342+ media_type = "text/event-stream" ,
343+ status_code = 200 ,
344+ )
345+
346+ received : list [bytes ] = []
347+
348+ async def mock_send (message : Message ) -> None :
349+ if message .get ("type" ) == "http.response.body" :
350+ body = message .get ("body" , b"" )
351+ received .append (body if isinstance (body , bytes ) else b"" )
352+
353+ async def mock_receive () -> HTTPDisconnectEvent :
354+ await anyio .sleep (10 )
355+ return {"type" : "http.disconnect" }
356+
357+ await response .send_body (mock_send , mock_receive )
358+
359+ body = b"" .join (received ).decode ()
360+ assert "chunk1" in body
361+ assert "chunk2" in body
362+ assert ": ping" not in body
0 commit comments