Problem
All 30 tests in tests/ui/test_websocket_integration.py hang indefinitely when trying to test WebSocket broadcasts. The tests fail to complete even after several minutes.
Root Cause
FastAPI's TestClient creates synchronous WebSocket sessions that are incompatible with the async manager.broadcast() method used in production code.
Technical Details
-
TestClient WebSocket behavior:
websocket.send_json() - client sends to server (synchronous)
websocket.receive_json() - client receives from server (synchronous, blocking)
- Runs in a background thread with its own event loop
-
Production broadcast behavior:
await manager.broadcast(message, project_id) - async method
- Calls
await connection.send_json(message) on server-side WebSocket objects
- Requires async event loop to execute
-
The conflict:
- When tests call
manager.broadcast(), it tries to send to WebSocket objects managed by TestClient
- TestClient's synchronous WebSocket sessions don't properly handle async sends from the manager
- Tests hang waiting for messages that never arrive
Affected Tests
All 30 tests in tests/ui/test_websocket_integration.py:
TestFullSubscriptionWorkflow (3 tests)
TestMultiClientScenario (2 tests)
TestSubscribeUnsubscribeFlow (3 tests)
TestDisconnectCleanup (2 tests)
TestBackwardCompatibility (2 tests)
TestInvalidMessages (9 tests)
TestEdgeCases (9 tests)
Attempted Solutions
❌ Threading with new event loop
def broadcast_sync(manager, message, project_id=None):
def run_broadcast():
loop = asyncio.new_event_loop()
loop.run_until_complete(manager.broadcast(message, project_id))
thread = threading.Thread(target=run_broadcast)
thread.start()
thread.join(timeout=2.0)
Result: Still hangs. TestClient's WebSocket objects don't work across threads.
❌ Direct asyncio.run()
asyncio.run(manager.broadcast(message, project_id=1))
Result: Hangs. Creates event loop conflict with TestClient's background thread.
Recommended Solutions
Option 1: Mock the broadcast method (Fastest)
@pytest.fixture
def mock_broadcast(monkeypatch):
async def fake_broadcast(message, project_id=None):
# Don't actually broadcast in tests
pass
monkeypatch.setattr('codeframe.ui.shared.manager.broadcast', fake_broadcast)
Pros: Quick fix, tests can verify subscription logic
Cons: Doesn't test actual message delivery
Option 2: Use real WebSocket client library (Most thorough)
Replace TestClient with websockets library:
import websockets
async def test_broadcast():
async with websockets.connect('ws://localhost:8000/ws') as ws:
await ws.send(json.dumps({"type": "subscribe", "project_id": 1}))
# Server can now broadcast properly
response = await ws.recv()
Pros: Tests real WebSocket behavior
Cons: Requires running server, more complex setup
Option 3: Refactor to test broadcast logic separately
- Test subscription management (which websockets are subscribed)
- Test message filtering logic (which subscribers get which messages)
- Don't test actual WebSocket send/receive in unit tests
Pros: Clean separation of concerns
Cons: Doesn't test end-to-end WebSocket flow
Recommendation
Use Option 2 (real WebSocket client) for comprehensive testing, with Option 1 (mocking) as a short-term workaround to unblock CI/CD.
Related Issues
Files
tests/ui/test_websocket_integration.py - All 30 tests affected
codeframe/ui/shared.py - ConnectionManager.broadcast() method
codeframe/ui/routers/websocket.py - WebSocket endpoint
Environment
- Python 3.13.3
- FastAPI + Starlette TestClient
- pytest 8.4.2
Acceptance Criteria
Problem
All 30 tests in
tests/ui/test_websocket_integration.pyhang indefinitely when trying to test WebSocket broadcasts. The tests fail to complete even after several minutes.Root Cause
FastAPI's
TestClientcreates synchronous WebSocket sessions that are incompatible with the asyncmanager.broadcast()method used in production code.Technical Details
TestClient WebSocket behavior:
websocket.send_json()- client sends to server (synchronous)websocket.receive_json()- client receives from server (synchronous, blocking)Production broadcast behavior:
await manager.broadcast(message, project_id)- async methodawait connection.send_json(message)on server-side WebSocket objectsThe conflict:
manager.broadcast(), it tries to send to WebSocket objects managed by TestClientAffected Tests
All 30 tests in
tests/ui/test_websocket_integration.py:TestFullSubscriptionWorkflow(3 tests)TestMultiClientScenario(2 tests)TestSubscribeUnsubscribeFlow(3 tests)TestDisconnectCleanup(2 tests)TestBackwardCompatibility(2 tests)TestInvalidMessages(9 tests)TestEdgeCases(9 tests)Attempted Solutions
❌ Threading with new event loop
Result: Still hangs. TestClient's WebSocket objects don't work across threads.
❌ Direct asyncio.run()
Result: Hangs. Creates event loop conflict with TestClient's background thread.
Recommended Solutions
Option 1: Mock the broadcast method (Fastest)
Pros: Quick fix, tests can verify subscription logic
Cons: Doesn't test actual message delivery
Option 2: Use real WebSocket client library (Most thorough)
Replace TestClient with
websocketslibrary:Pros: Tests real WebSocket behavior
Cons: Requires running server, more complex setup
Option 3: Refactor to test broadcast logic separately
Pros: Clean separation of concerns
Cons: Doesn't test end-to-end WebSocket flow
Recommendation
Use Option 2 (real WebSocket client) for comprehensive testing, with Option 1 (mocking) as a short-term workaround to unblock CI/CD.
Related Issues
Files
tests/ui/test_websocket_integration.py- All 30 tests affectedcodeframe/ui/shared.py- ConnectionManager.broadcast() methodcodeframe/ui/routers/websocket.py- WebSocket endpointEnvironment
Acceptance Criteria