chart/tests/e2e
Chris Amow a395818581 Post cross-thread events through the loop, and measure how late it runs
P0 from docs/async_refactor.md. The mutating routes are sync `def`, so FastAPI
runs them in a threadpool, and they reach Runtime.broadcast through
rebuild_levels — writing asyncio.Queue directly from there. That queue is not
thread-safe: it wakes a consumer by resolving a Future, which only the loop
thread may do. A dropped wakeup means a drawing made in one browser does not
reach another until the next market tick.

broadcast now posts through call_soon_threadsafe when it is off the loop, and
publishes directly when it is on it, so the stream's own path pays nothing.

Worth being straight about the tests: the race is timing-dependent and did not
reproduce in twenty attempts — a foreign-thread put_nowait usually lands in the
ready queue before the loop sleeps, and a tick every second covers the rest.
Even asyncio's debug thread-affinity check stays quiet unless a consumer is
parked on the Future at that instant. So the tests assert the contract rather
than provoke the failure: a broadcast from a worker thread must go through
call_soon_threadsafe, one from the loop must deliver synchronously, and both
must arrive.

Also adds the loop-lag probe, which reports scheduling drift as loop_lag_ms on
/api/status. It found P1 on its first run: 19,441ms worst against 1.5ms in
steady state, which is seeding blocking the loop. "The chart feels laggy" is now
a number.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 15:30:45 -05:00
..
chart.test.mjs - Drag a selected trendline body to reposition the entire line. 2026-08-11 05:46:09 -05:00
comments.test.mjs Snap to the nearest extreme on screen, and add an e2e suite 2026-08-10 20:16:09 -05:00
drawings.test.mjs Post cross-thread events through the loop, and measure how late it runs 2026-08-11 15:30:45 -05:00
harness.test.mjs - Drag a selected trendline body to reposition the entire line. 2026-08-11 05:46:09 -05:00
helpers.mjs - Drag a selected trendline body to reposition the entire line. 2026-08-11 05:46:09 -05:00
preferences.test.mjs Expand regression coverage and document next steps 2026-08-11 03:22:00 -05:00
price-scale.test.mjs better default names 2026-08-11 14:36:58 -05:00
trendline.test.mjs Post cross-thread events through the loop, and measure how late it runs 2026-08-11 15:30:45 -05:00
volume.test.mjs Expand regression coverage and document next steps 2026-08-11 03:22:00 -05:00