diff --git a/packages/runtime-host/src/__tests__/event-wait-manager.test.ts b/packages/runtime-host/src/__tests__/event-wait-manager.test.ts index a420ccff8dc..57c3daa0122 100644 --- a/packages/runtime-host/src/__tests__/event-wait-manager.test.ts +++ b/packages/runtime-host/src/__tests__/event-wait-manager.test.ts @@ -22,6 +22,7 @@ import { mkdtemp, rm, readFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { test, type TestContext } from 'node:test'; +import { setTimeout as delay } from 'node:timers/promises'; import { EVENT_WAIT_LIMITS, eventWaitDeliveryKey, @@ -55,9 +56,12 @@ async function settle() { for (let n = 0; n < 40; n++) await new Promise((resolve) => setImmediate(resolve)); } async function eventually(check: () => Promise | boolean) { - for (let n = 0; n < 100; n++) { + // Host I/O needs wall-clock time; immediate turns can exhaust before it completes. + // Keep the injected observation clock fixed so timeout/Retry-After assertions remain exact. + const deadline = performance.now() + 5_000; + while (performance.now() < deadline) { if (await check()) return; - await settle(); + await delay(5); } assert.fail('Condition did not settle without advancing the observation clock'); } @@ -94,6 +98,26 @@ class Clock implements EventWaitClock { await settle(); } } +test('eventually waits for delayed host work without advancing the observation clock', async () => { + const clock = new Clock(); + let observationTimerRan = false; + clock.schedule(() => { + observationTimerRan = true; + }, 1); + let completed = false; + const work = delay(200).then(() => { + completed = true; + }); + try { + await eventually(() => completed); + assert.equal(clock.now(), 1_000); + assert.equal(observationTimerRan, false); + assert.equal(clock.timers.size, 1); + } finally { + await work; + } +}); + const satisfied = (key = 'event-1'): EventWaitObservationResult => ({ kind: 'satisfied', receiptKey: key,