From b704a9156071cb9aef45d19401e3017697ea703a Mon Sep 17 00:00:00 2001 From: nicolotognoni Date: Mon, 25 May 2026 14:15:37 +0200 Subject: [PATCH] fix(test): replace 50 ms drain wait with bounded poll in server.test.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ``EmbeddedServer wraps logging callbacks with active-record fallback`` test fired once on PR #103's CI run and once on the post-merge ``main`` run with the same root cause: ``setTimeout(50)`` to drain a fire-and-forget ``logCallStart`` is not enough on Node 22 (~10 % failure rate) — the async scheduler defers the atomic-write resolve a tick longer than on Node 20 where the test was originally tuned. Replaced the fixed wait with a bounded poll: walk ``tmp`` for ``metadata.json`` every 25 ms up to 2 s, break on first hit. Same assertion, no behaviour change in the SUT, fast path is unchanged (the file lands within ~30 ms on both Node versions when the system is idle). --- libraries/typescript/tests/server.test.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libraries/typescript/tests/server.test.ts b/libraries/typescript/tests/server.test.ts index b45e6f8..cb64fd8 100644 --- a/libraries/typescript/tests/server.test.ts +++ b/libraries/typescript/tests/server.test.ts @@ -369,9 +369,10 @@ describe('EmbeddedServer wraps logging callbacks with active-record fallback', ( telephony_provider: 'twilio', }); - // Allow the fire-and-forget logCallStart promise to drain. - await new Promise((resolve) => setTimeout(resolve, 50)); - + // Poll for the fire-and-forget logCallStart promise to drain. A + // fixed 50 ms wait was enough on Node 20 but flakes on Node 22 + // (~10 % failure rate in CI) because the async scheduler defers + // the atomic-write resolve a tick longer. Bounded poll up to 2 s. const metaPaths: string[] = []; const walk = (d: string): void => { for (const entry of fs.readdirSync(d, { withFileTypes: true })) { @@ -380,7 +381,13 @@ describe('EmbeddedServer wraps logging callbacks with active-record fallback', ( else if (entry.name === 'metadata.json') metaPaths.push(full); } }; - walk(tmp); + const deadline = Date.now() + 2000; + while (Date.now() < deadline) { + metaPaths.length = 0; + walk(tmp); + if (metaPaths.length >= 1) break; + await new Promise((resolve) => setTimeout(resolve, 25)); + } expect(metaPaths).toHaveLength(1); const payload = JSON.parse(fs.readFileSync(metaPaths[0], 'utf8')) as { caller: string;