From 2f6dad5c1698a98a64c4556d04612f5e6b0b94eb Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Thu, 22 Oct 2026 18:08:24 -0700 Subject: [PATCH] test(activity): judge the lost-server backoff by its gaps, not a timed window "lost server clears the pill and marks jobs ended" counted failed job-list re-reads in a 3.5 s waitForTimeout window that started once the test saw the ended job. Re-reads back off 1 s, 2 s, then 4 s, so the third lands 4 s after the second. A runner that noticed the ended job more than 0.5 s late moved the window over the third read (firefox shard 1/2 on #287: 3 reads, expected at most 2). The test now waits for the third failed read and asserts the gaps between reads: at least 2 s, then at least 4 s, with 100 ms allowed for the request being reported late. --- tests/browser/workspace-activity.spec.mjs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/browser/workspace-activity.spec.mjs b/tests/browser/workspace-activity.spec.mjs index 05cb2d75..8badc8c7 100644 --- a/tests/browser/workspace-activity.spec.mjs +++ b/tests/browser/workspace-activity.spec.mjs @@ -278,6 +278,8 @@ test('reload during a scan reattaches to the running job', async ({ page, reques }); test('lost server clears the pill and marks jobs ended', async ({ page, request }) => { + // The failed re-reads back off for 1 + 2 + 4 s before the third is seen. + test.setTimeout(60_000); await page.setViewportSize({ width: 1600, height: 1100 }); const job = await startJob(request); await step(request, job.id, { processed: 4000 }); @@ -308,9 +310,14 @@ test('lost server clears the pill and marks jobs ended', async ({ page, request await expect(await request.post('/__test__/jobs/restart')).toBeOK(); await expect(pill(page)).toBeHidden(); await expect(items(page).filter({ hasText: LOST })).toHaveCount(1); - await page.waitForTimeout(3500); - // 1 s, then 2 s: at most two re-reads in 3.5 s. - expect(failed.length).toBeLessThanOrEqual(2); + // 1 s, then 2 s, then 4 s: each failed re-read waits twice as long as the one before. The gaps between the + // reads themselves are the evidence, so a slow runner noticing the ended job late cannot add a read to a window. + await expect.poll(() => failed.length, { timeout: 20_000 }).toBeGreaterThanOrEqual(3); + const gaps = failed.slice(1, 3).map((at, index) => at - failed[index]); + // A request is reported to the test a little after the browser sends it; that delay is not a shorter backoff. + const REPORT_SLACK_MS = 100; + expect(gaps[0]).toBeGreaterThanOrEqual(2000 - REPORT_SLACK_MS); + expect(gaps[1]).toBeGreaterThanOrEqual(4000 - REPORT_SLACK_MS); await page.unroute('**/prompt_manager/jobs**'); expect(second.id).not.toBe(job.id); });