Conversation
…te so lifespan runs
The `test_client` fixture in `test_websocket_state.py` was constructing
`TestClient(app)` and returning it directly, never entering it as a
context manager. FastAPI's lifespan only fires on `__enter__`, so the
module-level `backend` global in `main.py` was never initialized for
these tests. Every test in the file then crashed at the WebSocket
handler's `backend.get_status()` call with:
AttributeError: 'NoneType' object has no attribute 'get_status'
Fix: wrap the TestClient in `with ... as client: yield client` so
lifespan startup runs (initializing `backend = create_backend(...)`) and
shutdown runs at fixture teardown. This is the same pattern used by the
project-wide `client` fixture in `tests/conftest.py:520`.
Verified: all 7 tests in `test_websocket_state.py` now pass. The other
two test files that use the same anti-pattern (`test_phase_b_bridge.py`
and `test_phase_b_pre_b_csrf.py`) don't hit lifespan-initialized
state and currently pass — left untouched to keep this change scoped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the 7 failing tests in
src/tests/integration/test_websocket_state.py. Thetest_clientfixture was constructingTestClient(app)andreturning it directly, never entering the TestClient as a context manager. FastAPI's lifespan only fires on__enter__, so the module-levelbackendglobal inmain.pystayedNoneand every test crashed atmain.py:502withAttributeError: 'NoneType' object has no attribute 'get_status'.The change is one fixture: wrap in
with ... as client: yield client, matching the pattern already used by the project-wideclientfixture insrc/tests/conftest.py:520.Investigation context (worth reading — saved 30+ false-positive failures from being chased)
The original bug report listed ~770 errors plus 117 failures on canopy. Triage split them into three categories:
~/.bashrcexportsLIBTORCH=/.../rust_mudgeon/.../libtorchand prepends${LIBTORCH}/libtoLD_LIBRARY_PATHfor tch-rs work; the 2023-vintagelibtorch_python.soshadows the env's torch and fails withundefined symbol: _PyObject_NextNotImplemented/opt/miniforge3/envs/JuniperCanopy1/etc/conda/{activate,deactivate}.d/strip rust_mudgeon paths on activate, restore on deactivate. No code change needed.import demo_modefailed (Cat 1), conftest's_reset_all_singletons(wrapped incontextlib.suppress(ImportError)) silently no-op'd, soget_settings.cache_clear()never ran → the Settingslru_cacheaccumulateddemo_mode=Truefrom earlier tests → Redis/Cassandra/MetricsPanel kept short-circuiting through the demo path returningUP+DEMOinstead of the configuredDISABLED/UNAVAILABLE/DOWN/LIVEconftest.pyworks as designed when the imports succeed.test_websocket_state.pywithon TestClient → lifespan never runs →backendisNoneTest plan
test_websocket_state.pypass after the fix.origin/mainand unrelated to this PR:test_main_endpoints_coverage.py::TestSnapshotDetailRealMode::test_real_mode_snapshot_dir_missing— test/code drift on a 404 detail message ('directory' in 'snapshot not found')test_status_bar_updates.py::TestStatusEndpointFSMIntegration::test_api_status_reflects_training_start— passes in isolation on both branches; flaky in full suite due to test pollution unrelated to this fixreturn TestClient(app)anti-pattern (test_phase_b_bridge.py,test_phase_b_pre_b_csrf.py) currently pass because they don't hit lifespan-initialized state — left untouched to keep this change scoped.Out of scope (follow-up candidates)
_reset_all_singletonsswallowingImportErrorsilently. Removing the suppress would have caught Cat 1 → Cat 3 cascade much earlier, but changing it might destabilize tests that rely on it. Documented in the memory note as future hardening.return TestClient(app)callers — currently green, but they're a latent bug if those tests ever start hitting lifespan-initialized globals.🤖 Generated with Claude Code