From 3f3c35dd3dc4d1486145a69811b1c4fc539fc3f0 Mon Sep 17 00:00:00 2001 From: truffle Date: Tue, 16 Jun 2026 02:08:51 +0000 Subject: [PATCH] test(tenancy): un-skip reportFirstDmSent POST assertion (#129) The reportFirstDmSent "POSTs to /first_dm_sent" test was marked test.todo after a Bun 1.3.13 CI flake where a closure-captured-array recorder reported expect(count).toBe(1) as 0. The passing reportAgentReady sibling already avoids this by overriding globalThis.fetch with a plain async function that mutates only scalar locals, restored in a try/finally. Mirror that exact pattern here so the first_dm_sent body and headers are actually asserted rather than skipped. --- src/tenancy/__tests__/heartbeat.test.ts | 49 ++++++++++++++++++++----- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/tenancy/__tests__/heartbeat.test.ts b/src/tenancy/__tests__/heartbeat.test.ts index ba867e24..be4ec2d1 100644 --- a/src/tenancy/__tests__/heartbeat.test.ts +++ b/src/tenancy/__tests__/heartbeat.test.ts @@ -129,16 +129,45 @@ describe("reportAgentReady", () => { }); describe("reportFirstDmSent", () => { - // FIXME: this test fails on Bun 1.3.13 in CI with `expect(count).toBe(1)` - // receiving 0, despite the structurally-identical reportAgentReady - // "POSTs to ..." test passing on the same runtime. Two refactors did not - // fix it (removing the mock() cast, then switching to a globalThis.fetch - // override). The implementation is verified correct on local Bun 1.3.5 - // (8/8 pass) and the reportAgentReady sibling test exercises the same - // postBestEffort path on every CI run. Marked todo to unblock the merge; - // tracked in follow-up notes for a Bun 1.3.13 closure-mutation - // investigation. - test.todo("POSTs to /v1/tenant_status/first_dm_sent with the slack_message_ts", () => {}); + test("POSTs to /v1/tenant_status/first_dm_sent with the slack_message_ts", async () => { + // Mirrors the passing reportAgentReady sibling: override + // globalThis.fetch with a plain async function that mutates only + // scalar locals. The earlier closure-captured-array recorder was + // what surfaced the Bun 1.3.13 `expect(count).toBe(1)` -> 0 flake; + // scalar locals + the mandatory try/finally restore avoid it. + let count = 0; + let lastUrl = ""; + let lastBody = ""; + let lastMethod = ""; + let lastContentType = ""; + + const originalFetch = globalThis.fetch; + try { + globalThis.fetch = (async (input: string | URL | Request, init?: RequestInit) => { + count++; + lastUrl = typeof input === "string" ? input : input.toString(); + lastBody = String(init?.body ?? ""); + lastMethod = String(init?.method ?? ""); + const headers = init?.headers as Record | undefined; + lastContentType = headers?.["content-type"] ?? ""; + return new Response(null, { status: 204 }); + }) as typeof fetch; + + await reportFirstDmSent({ + metadataBaseUrl: "http://169.254.169.254", + slackMessageTs: "1715000000.000123", + // no fetchImpl: exercise the globalThis.fetch fallback path. + }); + + expect(count).toBe(1); + expect(lastUrl).toBe("http://169.254.169.254/v1/tenant_status/first_dm_sent"); + expect(lastMethod).toBe("POST"); + expect(lastContentType).toBe("application/json"); + expect(JSON.parse(lastBody)).toEqual({ slack_message_ts: "1715000000.000123" }); + } finally { + globalThis.fetch = originalFetch; + } + }); test("skips the POST when slack_message_ts is empty (caller must guard)", async () => { const { fn, calls } = makeFetchOk();