From dd3ec8d50f014b2a392ab2fbcdeb2f78830eab30 Mon Sep 17 00:00:00 2001 From: Nigro Simone Date: Fri, 31 Jul 2026 07:11:20 +0200 Subject: [PATCH] test: keep the suite runnable on Windows under Node 24+ Every test ends with process.exit() right after fetching, which on Windows with Node 24 and later trips a libuv assertion in undici's socket teardown (nodejs/node#56645) and kills the process with exit code 3221226505. The harness runs each test through exec(), so that exit code rejects and every test reports as failed. The crash also drops whatever stdout was still buffered, so the captured output is unreliable even when a test did its work correctly. Preload a small module on win32 that lets the loop settle before exiting for real. Linux and macOS, and therefore CI, are untouched. --- tests/index.js | 7 ++++++- tests/win-exit-delay.cjs | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/win-exit-delay.cjs diff --git a/tests/index.js b/tests/index.js index 95a24d2c..8a395461 100644 --- a/tests/index.js +++ b/tests/index.js @@ -12,6 +12,11 @@ const assert = require("node:assert"); const TEST_TIMEOUT = 60000; +// see tests/win-exit-delay.cjs: without it every test crashes on exit under Node 24+ on Windows +const NODE_ARGS = process.platform === 'win32' + ? `--require "${path.join(__dirname, 'win-exit-delay.cjs')}" ` + : ''; + const testPath = path.join(__dirname, 'tests'); let testCategories = fs.readdirSync(testPath).sort((a, b) => parseInt(a) - parseInt(b)); @@ -65,7 +70,7 @@ for (const testCategory of testCategories) { }; let execTest = async (testPath) => { - return (await exec(`node ${testPath}`, {maxBuffer: 1024 * 1024 * 100})).stdout + return (await exec(`node ${NODE_ARGS}${testPath}`, {maxBuffer: 1024 * 1024 * 100})).stdout } try { diff --git a/tests/win-exit-delay.cjs b/tests/win-exit-delay.cjs new file mode 100644 index 00000000..e90260b8 --- /dev/null +++ b/tests/win-exit-delay.cjs @@ -0,0 +1,19 @@ +// Node 24 and later crash on Windows when process.exit() runs while undici still has +// keep-alive sockets open: "Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), +// file src\win\async.c" (nodejs/node#56645). Every test here ends with process.exit() +// right after fetching, so they hit it, and the crash also loses whatever stdout was +// still buffered, which makes the captured output unreliable on top of the exit code. +// +// Letting the loop settle first avoids it. Preloaded by tests/index.js on win32 only, +// so nothing changes on Linux or macOS. + +const EXIT_DELAY_MS = 100; + +const realExit = process.exit.bind(process); + +process.exit = (code) => { + if (code !== undefined) { + process.exitCode = code; + } + setTimeout(() => realExit(process.exitCode), EXIT_DELAY_MS); +};