From d89b2438c300ea89490fff55f37c7bef07559cdf Mon Sep 17 00:00:00 2001 From: ephraimduncan Date: Mon, 14 Sep 2026 18:04:46 +0000 Subject: [PATCH] fix(health): stop expired probes before work starts --- CHANGES.md | 5 +++++ packages/health/src/run.test.ts | 21 +++++++++++++++++++++ packages/health/src/run.ts | 1 + 3 files changed, 27 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 2099aa4..2ebed3a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased + +- `@openstatus/health`: Probes no longer start work if an async `skip` + returns `false` after the timeout. The completed timeout report stays unchanged. + ## 0.1.2 - Every hosting package (`fly`, `koyeb`, `railway`, `vercel`, `cloudflare`) diff --git a/packages/health/src/run.test.ts b/packages/health/src/run.test.ts index e3d6f73..724cd5b 100644 --- a/packages/health/src/run.test.ts +++ b/packages/health/src/run.test.ts @@ -175,6 +175,27 @@ test("runProbes() times out a hanging skip()", async () => { assert.equal(report.checks[0].status, "timeout"); }); +test("runProbes() does not start work when skip() resolves false after timeout", async () => { + const skip = Promise.withResolvers(); + let ran = false; + const report = await runProbes([{ + name: "a", + timeoutMs: 10, + skip: () => skip.promise, + run: () => { + ran = true; + }, + }]); + assert.equal(report.checks[0].status, "timeout"); + const completed = structuredClone(report); + + skip.resolve(false); + await delay(0); + + assert.equal(ran, false); + assert.deepEqual(report, completed); +}); + test("runProbes() passes name, critical and timeoutMs to run()", async () => { let seen: { name: string; critical: boolean; timeoutMs: number } | undefined; await runProbes([{ diff --git a/packages/health/src/run.ts b/packages/health/src/run.ts index bae9a3a..0089ddf 100644 --- a/packages/health/src/run.ts +++ b/packages/health/src/run.ts @@ -63,6 +63,7 @@ async function runProbe( const work = Promise.resolve().then(async () => { if (await probe.skip?.()) return "skipped"; + controller.signal.throwIfAborted(); await probe.run(controller.signal, ctx); return "ok"; });