From 1a625b1318e3e4c27e8868f75d2ecb83b0ef115d Mon Sep 17 00:00:00 2001 From: Karn Date: Wed, 19 Aug 2026 15:40:29 +0530 Subject: [PATCH] test(relay): stop betting the pairing deadline against CI runners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four pair tests 504ed in one CI run when a stalled runner stretched a daemon answer past the 250 ms PAIR_TIMEOUT_MS test binding. The handshake and idle deadlines already learned this lesson — bind long ambient so no test can outlive the deadline, and let the tests that are about the deadline bind their own short one. The pairing deadline now follows the same rule via a pairTimeout() harness helper, used by the one test that asserts the 504. Co-Authored-By: Claude Fable 5 --- relay/test/harness.ts | 17 +++++++++++++++++ relay/test/pair.test.ts | 4 +++- relay/vitest.config.ts | 12 ++++++++---- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/relay/test/harness.ts b/relay/test/harness.ts index 97c7258..1ea76da 100644 --- a/relay/test/harness.ts +++ b/relay/test/harness.ts @@ -107,6 +107,23 @@ export async function idleTimeout(hub: DurableObjectStub, ms: number): Promise { + await runInDurableObject(hub, (instance) => { + const withEnv = instance as unknown as { env: Record } + withEnv.env = { ...withEnv.env, PAIR_TIMEOUT_MS: ms } + }) +} + export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)) } diff --git a/relay/test/pair.test.ts b/relay/test/pair.test.ts index 2abfe6c..6b64a52 100644 --- a/relay/test/pair.test.ts +++ b/relay/test/pair.test.ts @@ -10,10 +10,11 @@ import { freshHub, hubPath, MACHINE, + pairTimeout, type Leg, } from './harness' -/** Mirrors the PAIR_TIMEOUT_MS binding in vitest.config.ts. */ +/** The deadline the one timeout test binds for itself via pairTimeout(). */ const PAIR_TIMEOUT_MS = 250 /** The body cap, matching the daemon's own maxPairBytes (internal/daemon/pairing.go). */ @@ -292,6 +293,7 @@ describe('the cap on concurrent pairing attempts', () => { describe('a daemon that does not answer', () => { it('times out with 504 daemon did not answer', async () => { const hub = freshHub() + await pairTimeout(hub, PAIR_TIMEOUT_MS) const daemon = await dial(hub, `/daemon/${MACHINE}`) const started = Date.now() const res = await post(hub, '{"token":"t"}') diff --git a/relay/vitest.config.ts b/relay/vitest.config.ts index 946d8af..0fa5f68 100644 --- a/relay/vitest.config.ts +++ b/relay/vitest.config.ts @@ -23,9 +23,13 @@ import { defineConfig } from 'vitest/config' * before they dial. A test that says nothing about the deadline is no longer * making a silent bet on how fast the runner is. * - * The pairing deadline stays short and stays the looser of the two, because the - * tests that must *not* hit it run a whole HTTP request through a WebSocket - * round trip first. + * The pairing deadline is bound long by the same rule, and it earned its + * place the same way the handshake deadline did. It used to stay short — + * 250 ms, on the reasoning that the tests that must not hit it run a whole + * HTTP request through a WebSocket round trip first — and a loaded CI + * runner eventually stalled past that: four pair tests 504ed in one run, + * none reproducible locally. The one test about the timeout binds its own + * short deadline with `pairTimeout()` before it dials. * * CLIENT_IDLE_TIMEOUT_MS is bound the same way and for the same reason: a test * client sends when its test needs it to and is otherwise silent — it sends no @@ -41,7 +45,7 @@ export default defineConfig({ bindings: { HANDSHAKE_TIMEOUT_MS: 600_000, CLIENT_IDLE_TIMEOUT_MS: 600_000, - PAIR_TIMEOUT_MS: 250, + PAIR_TIMEOUT_MS: 600_000, // The directory's entry cap, bound small for the same reason the // deadlines above are bound at all: the only way to test a cap is to // reach it, and reaching the production 512 is 512 sequential round