-
Notifications
You must be signed in to change notification settings - Fork 5
Description
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
- When tests call
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
- Miscellaneous backend test failures #145 - Test suite failures (parent issue, mostly resolved)
Files
tests/ui/test_websocket_integration.py- All 30 tests affectedcodeframe/ui/shared.py- ConnectionManager.broadcast() methodcodeframe/ui/routers/websocket.py- WebSocket endpoint
Environment
- Python 3.13.3
- FastAPI + Starlette TestClient
- pytest 8.4.2
Acceptance Criteria
- All 30 WebSocket integration tests pass
- Tests complete in <30 seconds total
- Tests properly validate WebSocket subscription and broadcast behavior
- CI/CD pipeline runs successfully