From 6fbb79e66c79a0201c33c260fa7e4f0c6ed9104c Mon Sep 17 00:00:00 2001 From: 4gray Date: Tue, 29 Sep 2026 17:42:26 +0200 Subject: [PATCH] test(perf): fail the J2 quiet wait when a sample stalls past its deadline waitForJourneyQuiet accepted a sample that returned unchanged after a stall longer than the timeout as the end of a quiet period, before the deadline check ran. The deadline is now checked first, so a stalled sample fails the wait instead of letting the click go ahead unobserved. Co-Authored-By: Claude Opus 5.5 --- .../performance/journey-quiet-wait.spec.ts | 27 +++++++++++++++++++ .../src/performance/journey-quiet-wait.ts | 9 ++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/apps/electron-backend-e2e/src/performance/journey-quiet-wait.spec.ts b/apps/electron-backend-e2e/src/performance/journey-quiet-wait.spec.ts index e2030374f..808f9ce84 100644 --- a/apps/electron-backend-e2e/src/performance/journey-quiet-wait.spec.ts +++ b/apps/electron-backend-e2e/src/performance/journey-quiet-wait.spec.ts @@ -70,3 +70,30 @@ test('never returns while work stays in flight and fails at the timeout', async /not-quiet \{"inFlight":1,"requests":1\}/ ); }); + +test('fails when a sample stalls past the deadline instead of accepting it as quiet', async () => { + let clock = 0; + let samples = 0; + await assert.rejects( + waitForJourneyQuiet({ + inFlight: (activity) => activity.inFlight, + now: () => clock, + pollMs: 100, + quietMs: 1_000, + sample: async () => { + samples += 1; + // The second sample hangs for 5 s (e.g. behind a busy main + // process) and then reports nothing changed. + if (samples === 2) clock += 5_000; + return { inFlight: 0, requests: 1 }; + }, + sleep: async (ms) => { + clock += ms; + }, + timeoutError: () => new Error('not-quiet-stalled'), + timeoutMs: 3_000, + }), + /not-quiet-stalled/ + ); + assert.equal(samples, 2); +}); diff --git a/apps/electron-backend-e2e/src/performance/journey-quiet-wait.ts b/apps/electron-backend-e2e/src/performance/journey-quiet-wait.ts index 493cafb98..aa4df186c 100644 --- a/apps/electron-backend-e2e/src/performance/journey-quiet-wait.ts +++ b/apps/electron-backend-e2e/src/performance/journey-quiet-wait.ts @@ -41,14 +41,17 @@ export async function waitForJourneyQuiet( const next = await options.sample(); const nextKey = JSON.stringify(next); const sampledMs = now(); + // The deadline is checked first: a sample that stalled past it (for + // example behind a busy main process) must fail the wait, not be + // accepted as the end of a quiet period nobody observed. + if (sampledMs - startedMs > options.timeoutMs) { + throw options.timeoutError(next); + } if (nextKey !== lastKey || options.inFlight(next) > 0) { lastKey = nextKey; quietSinceMs = sampledMs; } else if (sampledMs - quietSinceMs >= options.quietMs) { return { sample: next, waitedMs: sampledMs - startedMs }; } - if (sampledMs - startedMs > options.timeoutMs) { - throw options.timeoutError(next); - } } }