test(canvas): e2e for desktop take-control reconnect + lease renewal (core#2332) #2335
Reference in New Issue
Block a user
Delete Branch "feat/core-2332-display-reconnect-renewal-e2e"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Adds the e2e coverage that core#2216 (desktop take-control reconnect + 300s lease-renewal) shipped without. core#2332 P0.7.
The happy path is already covered by
canvas/e2e/staging-display.spec.ts(acquire -> noVNC WS upgrade -> first framebuffer frame). core#2216 layered two behaviours on top that had no e2e:DisplayTabrunsconnect(reacquire=true)->reacquireSession()->POST /display/control/acquireto mint a non-stale lease+token before reopening the socket -- otherwise the cached ~300s token can be past its expiry and the reconnect 401s (a dead session that looks like a reconnect).DisplayTabruns a 120ssetIntervalthat re-acquires as the same holder. The server'sON CONFLICT ... controlled_by = EXCLUDED.controlled_byupsert treats that as a lease extension, soexpires_atmoves forward and the user isn't kicked every ~5 min.New spec:
canvas/e2e/staging-display-reconnect.spec.tsSibling to
staging-display.spec.ts-- identical gating (STAGING_DISPLAY_WORKSPACE_ID, skips loud, never fail-open), same same-origin-canvas WS auth model, same fail-closed "no flaky disposition" philosophy. Reuses the exactdisplayWebSocketConnectionwire mechanics (subprotocols["binary", "molecule-display-token.<token>"],#token=fragment parse, http->ws).Test 1 -- reconnect re-acquires a FRESH token + framebuffer resumes
acquire -> open real noVNC WS (assert frame) -> drop -> re-acquire (same holder) and assert the new
session_urlcarries a different signed token bound to a renewedexpires_at(a reused token = the core#2216 401 bug) -> reopen WS on the fresh token and assert the framebuffer resumes (real frame, not a 1006/403 dead session).Test 2 -- renewal pushes the lease past the original 300s window
Drives the renewal call the 120s timer fires (the same re-acquire POST) rather than sleeping 300s of wall-clock, then asserts:
expires_atstrictly past the original 300s deadline;GET /display/controlstill reports a live holder on the renewed lease;RENEWAL_INTERVAL_MS (120s) < TTL (300s)(so a future change widening the interval past the TTL fails here).What couldn't be exercised without a live desktop tenant
The literal "hold one WS open >300s of real wall-clock while the 120s timer renews underneath and assert the same socket never 1006s" variant needs >5 min of standing-desktop wall-clock per run and a funded standing desktop EC2. It's left as a precise
TODO(core#2332)in the spec. The observable renewal cadence/effect (token freshness on reconnect + lease-extension past the original window) IS asserted deterministically.Validation
npx playwright test --config=playwright.staging.config.ts --listfinds both new tests (Total: 4 tests in 3 files).tsc --noEmiton the spec (playwright+dom libs) -> exit 0;eslint-> exit 0. Parity-checked against the sibling spec under the same flags. (e2e/is excluded from the maintsconfig; Playwright type-checks specs via its own transform.)Gate / promote
Skips unless
STAGING_DISPLAY_WORKSPACE_IDpoints at a standing desktop-capable staging workspace. Promote-to-required is a CTO call (standing desktop EC2 cost + >5 min cadence).🤖 Generated with Claude Code
core#2216 added two behaviours on top of the happy-path take-control flow that staging-display.spec.ts already covers (acquire -> noVNC WS upgrade -> first framebuffer frame), but neither had e2e coverage: (A) On an unclean WS drop the canvas re-acquires a FRESH control token before reconnecting (DisplayTab connect(reacquire=true) -> reacquireSession), so the ~300s cached token can't 401 the reconnect. (B) A 120s renewal timer re-acquires as the same holder, which the server's ON-CONFLICT upsert treats as a lease extension, keeping the 300s lease alive past its original window so the user isn't kicked every ~5 min. New staging-display-reconnect.spec.ts (sibling to staging-display.spec.ts, same gating/auth/fail-closed model): - reconnect test: acquire -> open real noVNC WS (frame) -> drop -> re-acquire and assert the new session_url carries a DIFFERENT signed token bound to a renewed expires_at -> reopen WS on the fresh token and assert the framebuffer RESUMES (real frame, not a 1006/403 dead session). - renewal test: drive the renewal CALL the 120s timer fires (the same re-acquire POST) and assert it pushes expires_at strictly past the original 300s deadline, and that GET /display/control still reports a live holder on the renewed lease. We assert the observable renewal cadence/effect rather than sleeping 300s of wall-clock; a precise TODO notes the full real-time >300s-idle-WS variant is gated on a funded standing desktop EC2. Gated on STAGING_DISPLAY_WORKSPACE_ID (skips loud otherwise, never fail-open), identical to its sibling. Promote-to-required is a CTO call (standing desktop EC2 cost + >5min cadence). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>