From 8d8d0826a750caf2a29a69955cec7d5c289fd2ea Mon Sep 17 00:00:00 2001 From: Brian Krabach Date: Wed, 1 Apr 2026 10:52:22 -0700 Subject: [PATCH] refactor: harden lifespan shutdown with try/finally and assert client closed in test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main.py: Wrapped `app.state.federation_client.aclose()` in a `try/finally` block so the poll task cleanup always runs even if `aclose()` raises unexpectedly. test_api.py: Extended `test_federation_client_exists_on_app_state` to capture the client reference and assert `client_ref.is_closed` after the `TestClient` context exits, verifying the lifespan shutdown closes the client. 🤖 Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- muxplex/main.py | 19 ++++++++++--------- muxplex/tests/test_api.py | 7 +++++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/muxplex/main.py b/muxplex/main.py index 434eab9..cb5417f 100644 --- a/muxplex/main.py +++ b/muxplex/main.py @@ -180,15 +180,16 @@ async def lifespan(app: FastAPI): yield - await app.state.federation_client.aclose() - - # Shutdown: cancel the poll loop task and wait for it to finish. - if _poll_task is not None: - _poll_task.cancel() - try: - await _poll_task - except (asyncio.CancelledError, Exception): - pass + try: + await app.state.federation_client.aclose() + finally: + # Shutdown: cancel the poll loop task and wait for it to finish. + if _poll_task is not None: + _poll_task.cancel() + try: + await _poll_task + except (asyncio.CancelledError, Exception): + pass # --------------------------------------------------------------------------- diff --git a/muxplex/tests/test_api.py b/muxplex/tests/test_api.py index 339142e..4234bbc 100644 --- a/muxplex/tests/test_api.py +++ b/muxplex/tests/test_api.py @@ -1464,3 +1464,10 @@ def test_federation_client_exists_on_app_state(monkeypatch): assert isinstance(app.state.federation_client, httpx.AsyncClient), ( "app.state.federation_client must be an httpx.AsyncClient instance" ) + # Capture reference before lifespan shuts down + client_ref = app.state.federation_client + + # After lifespan shutdown completes, the client must be closed + assert client_ref.is_closed, ( + "app.state.federation_client must be closed after lifespan shutdown" + )