diff --git a/docs/_newsfragments/2585.misc.rst b/docs/_newsfragments/2585.misc.rst new file mode 100644 index 000000000..4d7e7950c --- /dev/null +++ b/docs/_newsfragments/2585.misc.rst @@ -0,0 +1,7 @@ +Documented that exiting the context manager returned by +:meth:`~falcon.testing.ASGIConductor.simulate_ws` awaits completion of the +background task running the simulated ASGI request. If the +``on_websocket`` responder never returns (for example, an infinite loop +that does not break out when the connection is closed), this await will +hang indefinitely, regardless of any ``timeout`` passed to +:meth:`~falcon.testing.ASGIWebSocketSimulator.wait_ready`. \ No newline at end of file diff --git a/falcon/testing/client.py b/falcon/testing/client.py index 3259be5f8..53f0a6bc9 100644 --- a/falcon/testing/client.py +++ b/falcon/testing/client.py @@ -1219,10 +1219,23 @@ def simulate_ws(self, path: str = '/', **kwargs: Any) -> _WSContextManager: This method returns an async context manager that can be used to obtain a managed :class:`falcon.testing.ASGIWebSocketSimulator` instance. + Exiting the context will simulate a close on the WebSocket (if not already closed) and await the completion of the task that is running the simulated ASGI request. + .. warning:: + If your ``on_websocket`` responder does not return (for example, + it runs an infinite loop without ever breaking out on + disconnect), exiting this context manager will hang + indefinitely while awaiting that task, regardless of any + ``timeout`` passed to + :meth:`~falcon.testing.ASGIWebSocketSimulator.wait_ready`. + Ensure your responder returns promptly after the client + disconnects, or explicitly break out of any long-running + loops when :attr:`~falcon.testing.ASGIWebSocketSimulator.closed` + becomes ``True``. + In the following example, a series of WebSocket TEXT events are received from the ASGI app::