From 2f52291805849a1e9202d23ea1913219c7f9b325 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 11 Sep 2026 13:15:01 +0200 Subject: [PATCH 01/12] test: reduce ZIP64 stress test I/O A leading member just over 4 GiB followed by a small member exercises Zip64 sizes and offsets with less I/O. Reuse source buffers and verify raw Zip64 fields while retaining full readback and file-backed reserialization. Signed-off-by: Filip Skokan Assisted-by: Codex --- test/pummel/test-zlib-zip-slow.js | 94 +++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 30 deletions(-) diff --git a/test/pummel/test-zlib-zip-slow.js b/test/pummel/test-zlib-zip-slow.js index c3e334c06dec..ab023456890b 100644 --- a/test/pummel/test-zlib-zip-slow.js +++ b/test/pummel/test-zlib-zip-slow.js @@ -20,33 +20,32 @@ const { test } = require('node:test'); tmpdir.refresh(); const GiB = 1024 * 1024 * 1024; -const MEMBER_SIZE = 500 * 1024 * 1024; // Four ~500 MiB stored members... -const STORED_MEMBER_COUNT = 4; -const STREAMED_MEMBER_SIZE = 4.5 * GiB; // ...plus one >4 GiB streamed member: -// the total archive size (~6.5 GiB) pushes offsets over the 4 GiB Zip64 -// threshold, and the streamed member's own sizes exceed 32 bits too, so the -// per-entry Zip64 size fields (central header and data descriptor) are -// exercised as well as the offset promotion. Required free space includes -// generous slack over that total. -const REQUIRED_FREE_BYTES = 12 * GiB; const CHUNK_SIZE = 16 * 1024 * 1024; - -function fillChunk(seed) { - const chunk = Buffer.allocUnsafe(CHUNK_SIZE); - chunk.fill(seed & 0xff); - return chunk; -} +const STREAMED_MEMBER_SIZE = 4 * GiB + CHUNK_SIZE; +const TAIL_MEMBER_SIZE = 64 * 1024; +// The leading member needs Zip64 sizes; the small member after it needs a +// Zip64 offset. Only one member has to be large to exercise both paths. +const REQUIRED_FREE_BYTES = 8 * GiB; +const CENTRAL_FILE_HEADER_SIGNATURE = Buffer.from([0x50, 0x4b, 0x01, 0x02]); async function* repeatingChunks(totalSize, seed) { + const chunk = Buffer.alloc(Math.min(CHUNK_SIZE, totalSize), seed); let remaining = totalSize; while (remaining > 0) { - const size = Math.min(CHUNK_SIZE, remaining); - const chunk = fillChunk(seed); + const size = Math.min(chunk.length, remaining); remaining -= size; yield size === chunk.length ? chunk : chunk.subarray(0, size); } } +function assertZip64Extra(buffer, offset, values) { + assert.strictEqual(buffer.readUInt16LE(offset), 0x0001); + assert.strictEqual(buffer.readUInt16LE(offset + 2), values.length * 8); + for (let i = 0; i < values.length; i++) { + assert.strictEqual(buffer.readBigUInt64LE(offset + 4 + i * 8), BigInt(values[i])); + } +} + test('an archive larger than 4 GiB round-trips and triggers Zip64 via offset', async () => { let free; try { @@ -63,15 +62,14 @@ test('an archive larger than 4 GiB round-trips and triggers Zip64 via offset', a const dir = await fs.mkdtemp(path.join(tmpdir.path, 'zlib-zip-slow-')); const archivePath = path.join(dir, 'large.zip'); try { - const entries = []; - for (let i = 0; i < STORED_MEMBER_COUNT; i++) { - entries.push(zlib.ZipEntry.createStream(`stored-${i}.bin`, repeatingChunks(MEMBER_SIZE, i), { + const entries = [ + zlib.ZipEntry.createStream('streamed.bin', repeatingChunks(STREAMED_MEMBER_SIZE, 0xaa), { method: 'store', - })); - } - entries.push(zlib.ZipEntry.createStream('streamed.bin', repeatingChunks(STREAMED_MEMBER_SIZE, 0xaa), { - method: 'store', - })); + }), + zlib.ZipEntry.createStream('tail.bin', repeatingChunks(TAIL_MEMBER_SIZE, 2), { + method: 'store', + }), + ]; const handle = await fs.open(archivePath, 'w'); try { @@ -85,9 +83,38 @@ test('an archive larger than 4 GiB round-trips and triggers Zip64 via offset', a const stat = await fs.stat(archivePath); assert.ok(stat.size > 4 * GiB, `archive is only ${stat.size} bytes`); + const reader = await fs.open(archivePath, 'r'); + try { + // Read only the leading local header and the archive tail. The two + // central headers and the trailer fit comfortably in these 1024 bytes. + const local = Buffer.alloc(30); + const tail = Buffer.alloc(1024); + await reader.read(local, 0, local.length, 0); + await reader.read(tail, 0, tail.length, stat.size - tail.length); + assert.strictEqual(local.readUInt32LE(0), 0x04034b50); + const tailOffset = local.length + local.readUInt16LE(26) + + local.readUInt16LE(28) + STREAMED_MEMBER_SIZE + 24; // Zip64 data descriptor. + assert.ok(tailOffset > 0xffffffff); + + const bigCentral = tail.indexOf(CENTRAL_FILE_HEADER_SIGNATURE); + assert.notStrictEqual(bigCentral, -1); + assert.strictEqual(tail.readUInt32LE(bigCentral + 20), 0xffffffff); + assert.strictEqual(tail.readUInt32LE(bigCentral + 24), 0xffffffff); + assertZip64Extra(tail, bigCentral + 46 + tail.readUInt16LE(bigCentral + 28), + [STREAMED_MEMBER_SIZE, STREAMED_MEMBER_SIZE]); + + const tailCentral = tail.indexOf(CENTRAL_FILE_HEADER_SIGNATURE, bigCentral + 4); + assert.notStrictEqual(tailCentral, -1); + assert.strictEqual(tail.readUInt32LE(tailCentral + 42), 0xffffffff); + assertZip64Extra(tail, tailCentral + 46 + tail.readUInt16LE(tailCentral + 28), + [tailOffset]); + } finally { + await reader.close(); + } + const zip = await zlib.ZipFile.open(archivePath); try { - assert.strictEqual(zip.size, STORED_MEMBER_COUNT + 1); + assert.strictEqual(zip.size, 2); let seen = 0; for await (const chunk of await zip.stream('streamed.bin')) { @@ -96,12 +123,12 @@ test('an archive larger than 4 GiB round-trips and triggers Zip64 via offset', a } assert.strictEqual(seen, STREAMED_MEMBER_SIZE); - let storedSeen = 0; - for await (const chunk of await zip.stream('stored-2.bin')) { - storedSeen += chunk.length; + let tailSeen = 0; + for await (const chunk of await zip.stream('tail.bin')) { + tailSeen += chunk.length; assert.strictEqual(chunk[0], 2); } - assert.strictEqual(storedSeen, MEMBER_SIZE); + assert.strictEqual(tailSeen, TAIL_MEMBER_SIZE); // The streamed member's sizes genuinely exceed 32 bits (stored, so // compressed === uncompressed), which the reader must have resolved @@ -116,6 +143,13 @@ test('an archive larger than 4 GiB round-trips and triggers Zip64 via offset', a // descriptor) without needing a second copy on disk. let reserialized = 0; for await (const chunk of zlib.createZipArchive([big])) { + if (reserialized === 0) { + assert.strictEqual(chunk.readUInt16LE(6) & 0x08, 0); // No data descriptor. + assert.strictEqual(chunk.readUInt32LE(18), 0xffffffff); + assert.strictEqual(chunk.readUInt32LE(22), 0xffffffff); + assertZip64Extra(chunk, 30 + chunk.readUInt16LE(26), + [STREAMED_MEMBER_SIZE, STREAMED_MEMBER_SIZE]); + } reserialized += chunk.length; } assert.ok(reserialized > STREAMED_MEMBER_SIZE, From 638fb885a33036a9741f50a5bcd8a0643506fa36 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 11 Sep 2026 13:15:01 +0200 Subject: [PATCH 02/12] test: use named parameters in DH stress test Named modp14 parameters avoid generating and repeatedly validating a custom prime. Keep the existing exchange counts and FIPS rejection assertion. The separate deterministic padding test continues to cover imported prime parameters. Signed-off-by: Filip Skokan Assisted-by: Codex --- test/pummel/test-dh-regr.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/test/pummel/test-dh-regr.js b/test/pummel/test-dh-regr.js index c442fbc3a809..a3e7b42a452f 100644 --- a/test/pummel/test-dh-regr.js +++ b/test/pummel/test-dh-regr.js @@ -32,9 +32,8 @@ if (common.isPi()) { const assert = require('assert'); const crypto = require('crypto'); -const { hasOpenSSL, hasFIPS } = require('../common/crypto'); +const { hasFIPS } = require('../common/crypto'); -let p; let iterations = 2000; if (hasFIPS(3)) { assert.throws(() => crypto.createDiffieHellman(1024), { @@ -42,22 +41,14 @@ if (hasFIPS(3)) { name: 'TypeError', }); - // Use a precomputed approved group instead of generating a 2048-bit prime - // for every test run. Its larger keys also make each pummel iteration more - // expensive, so use enough iterations to exercise the regression without - // making the FIPS job excessively slow. - p = crypto.getDiffieHellman('modp14').getPrime(); + // Keep a lower iteration count for FIPS jobs. iterations = 100; -} else { - // FIPS requires length >= 1024, but small parameters keep this pummel test - // from timing out in ordinary CI. - const length = crypto.getFips() === 1 ? 1024 : (hasOpenSSL(3) ? 512 : 256); - p = crypto.createDiffieHellman(length).getPrime(); } for (let i = 0; i < iterations; i++) { - const a = crypto.createDiffieHellman(p); - const b = crypto.createDiffieHellman(p); + // A named group avoids generating and validating custom parameters. + const a = crypto.getDiffieHellman('modp14'); + const b = crypto.getDiffieHellman('modp14'); a.generateKeys(); b.generateKeys(); From 57f6cf7df0e8d8708747dc8b702166afab2fbdc5 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 11 Sep 2026 13:15:01 +0200 Subject: [PATCH 03/12] test: close WebAssembly test HTTP servers Unreferencing listeners leaves accepted connections alive until the keep-alive timeout. Close each single-use connection and its server when the response closes, including intentionally destroyed responses. Signed-off-by: Filip Skokan Assisted-by: Codex --- test/es-module/test-wasm-web-api.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/es-module/test-wasm-web-api.js b/test/es-module/test-wasm-web-api.js index ee1971133be5..6177c909e56c 100644 --- a/test/es-module/test-wasm-web-api.js +++ b/test/es-module/test-wasm-web-api.js @@ -16,7 +16,13 @@ const simpleWasmBytes = fixtures.readSync('simple.wasm'); // Sets up an HTTP server with the given response handler and calls fetch() to // obtain a Response from the newly created server. async function testRequest(handler) { - const server = createServer((_, res) => handler(res)).unref().listen(0); + const server = createServer(common.mustCall((_, res) => { + res.setHeader('Connection', 'close'); + res.once('close', common.mustCall(() => { + server.close(common.mustCall()); + })); + handler(res); + })).listen(0); await events.once(server, 'listening'); const { port } = server.address(); return fetch(`http://127.0.0.1:${port}/foo.wasm`); From 0b65929c0d0a2aa8a6810fefc698e9ae1dd8edc6 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 11 Sep 2026 13:15:01 +0200 Subject: [PATCH 04/12] tools: reduce test runner timing overhead RunProcess sleeps after polling even when the child has already exited. Skip that sleep, saving up to 100 ms per test. Sort --time results in descending order to display the 20 slowest tests. Signed-off-by: Filip Skokan Assisted-by: Codex --- tools/test.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/test.py b/tools/test.py index 2c2a4d78d80a..a20c44e52516 100755 --- a/tools/test.py +++ b/tools/test.py @@ -741,10 +741,11 @@ def RunProcess(context, timeout, args, **rest): timed_out = True else: exit_code = process.poll() - time.sleep(sleep_time) - sleep_time = sleep_time * SLEEP_TIME_FACTOR - if sleep_time > MAX_SLEEP_TIME: - sleep_time = MAX_SLEEP_TIME + if exit_code is None: + time.sleep(sleep_time) + sleep_time = sleep_time * SLEEP_TIME_FACTOR + if sleep_time > MAX_SLEEP_TIME: + sleep_time = MAX_SLEEP_TIME return (process, exit_code, timed_out) @@ -1849,7 +1850,7 @@ def should_keep(case): print() sys.stderr.write("--- Total time: %s ---\n" % FormatTime(duration)) timed_tests = [ t for t in cases_to_run if not t.duration is None ] - timed_tests.sort(key=lambda x: x.duration) + timed_tests.sort(key=lambda x: x.duration, reverse=True) for i, entry in enumerate(timed_tests[:20], start=1): t = FormatTimedelta(entry.duration) sys.stderr.write("%4i (%s) %s\n" % (i, t, entry.GetLabel())) From 092524b9f48731ae3e963d7ab49914c2d5852d51 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 11 Sep 2026 13:32:54 +0200 Subject: [PATCH 05/12] test: avoid idle HTTP/HTTPS connections Single-use requests otherwise wait for the keep-alive timeout. Use nonpersistent agents where agent selection is unrelated to coverage. For default-agent tests, close the server after consuming the response. Signed-off-by: Filip Skokan Assisted-by: Codex --- test/parallel/test-http-buffer-sanity.js | 1 + test/parallel/test-http-byteswritten.js | 2 +- test/parallel/test-http-client-check-http-token.js | 2 +- test/parallel/test-http-client-encoding.js | 1 + test/parallel/test-http-client-response-domain.js | 1 + test/parallel/test-http-decoded-auth.js | 2 +- test/parallel/test-http-default-port.js | 2 +- .../test-http-dont-set-default-headers-with-setHost.js | 1 + test/parallel/test-http-dont-set-default-headers.js | 1 + test/parallel/test-http-early-hints-invalid-argument.js | 4 ++-- test/parallel/test-http-head-request.js | 1 + test/parallel/test-http-hex-write.js | 2 +- test/parallel/test-http-outgoing-end-types.js | 2 +- test/parallel/test-http-outgoing-finish-writable.js | 1 + test/parallel/test-http-outgoing-finish.js | 1 + test/parallel/test-http-outgoing-properties.js | 2 ++ test/parallel/test-http-outgoing-write-types.js | 2 +- test/parallel/test-http-request-arguments.js | 2 +- test/parallel/test-http-request-large-payload.js | 1 + test/parallel/test-http-server-connection-list-when-close.js | 1 + test/parallel/test-http-server-delete-parser.js | 1 + test/parallel/test-http-server-multiheaders.js | 1 + test/parallel/test-http-server-multiheaders2.js | 1 + .../test-http-url.parse-auth-with-header-in-request.js | 1 + test/parallel/test-http-url.parse-auth.js | 1 + test/parallel/test-http-url.parse-basic.js | 5 ++++- test/parallel/test-http-url.parse-https.request.js | 5 ++++- test/parallel/test-http-url.parse-path.js | 1 + test/parallel/test-http-url.parse-post.js | 1 + test/parallel/test-http-url.parse-search.js | 1 + test/parallel/test-http-write-callbacks.js | 1 + test/parallel/test-http-write-empty-string.js | 2 +- test/parallel/test-http-zero-length-write.js | 2 +- test/parallel/test-https-drain.js | 1 + test/parallel/test-https-request-arguments.js | 1 + test/parallel/test-https-truncate.js | 2 +- test/parallel/test-https-unix-socket-self-signed.js | 1 + 37 files changed, 45 insertions(+), 15 deletions(-) diff --git a/test/parallel/test-http-buffer-sanity.js b/test/parallel/test-http-buffer-sanity.js index 4a4435b20abd..919118ea53b5 100644 --- a/test/parallel/test-http-buffer-sanity.js +++ b/test/parallel/test-http-buffer-sanity.js @@ -55,6 +55,7 @@ const server = new http.Server(common.mustCallAtLeast(function(req, res) { server.listen(0, common.mustCall(() => { const req = http.request({ + agent: false, port: server.address().port, method: 'POST', path: '/', diff --git a/test/parallel/test-http-byteswritten.js b/test/parallel/test-http-byteswritten.js index 003b7dfbd049..475176e6c976 100644 --- a/test/parallel/test-http-byteswritten.js +++ b/test/parallel/test-http-byteswritten.js @@ -51,5 +51,5 @@ const httpServer = http.createServer(common.mustCall(function(req, res) { })); httpServer.listen(0, function() { - http.get({ port: this.address().port }); + http.get({ port: this.address().port, agent: false }); }); diff --git a/test/parallel/test-http-client-check-http-token.js b/test/parallel/test-http-client-check-http-token.js index ef2445ec66e5..7ab9aa83d761 100644 --- a/test/parallel/test-http-client-check-http-token.js +++ b/test/parallel/test-http-client-check-http-token.js @@ -29,6 +29,6 @@ server.listen(0, common.mustCall(() => { }); expectedSuccesses.forEach((method) => { - http.request({ method, port: server.address().port }).end(); + http.request({ method, port: server.address().port, agent: false }).end(); }); })); diff --git a/test/parallel/test-http-client-encoding.js b/test/parallel/test-http-client-encoding.js index a4701cdbd0ab..253496307286 100644 --- a/test/parallel/test-http-client-encoding.js +++ b/test/parallel/test-http-client-encoding.js @@ -29,6 +29,7 @@ const server = http.createServer((req, res) => { server.close(); }).listen(0, common.mustCall(() => { http.request({ + agent: false, port: server.address().port, encoding: 'utf8' }, common.mustCall((res) => { diff --git a/test/parallel/test-http-client-response-domain.js b/test/parallel/test-http-client-response-domain.js index 9975ca3f9498..da3d3a09ff0f 100644 --- a/test/parallel/test-http-client-response-domain.js +++ b/test/parallel/test-http-client-response-domain.js @@ -49,6 +49,7 @@ function test() { })); const req = http.get({ + agent: false, socketPath: common.PIPE, headers: { 'Content-Length': '1' }, method: 'POST', diff --git a/test/parallel/test-http-decoded-auth.js b/test/parallel/test-http-decoded-auth.js index 076c056253b6..4f7847133f58 100644 --- a/test/parallel/test-http-decoded-auth.js +++ b/test/parallel/test-http-decoded-auth.js @@ -43,6 +43,6 @@ for (const testCase of testCases) { server.listen(0, function() { // make the request const url = new URL(`http://${testCase.username}:${testCase.password}@localhost:${this.address().port}`); - http.request(url).end(); + http.request(url, { agent: false }).end(); }); } diff --git a/test/parallel/test-http-default-port.js b/test/parallel/test-http-default-port.js index 2005487502fe..874affcdf23c 100644 --- a/test/parallel/test-http-default-port.js +++ b/test/parallel/test-http-default-port.js @@ -44,7 +44,6 @@ for (const { mod, createServer } of [ assert.strictEqual(req.headers['x-port'], `${server.address().port}`); res.writeHead(200); res.end('ok'); - server.close(); })).listen(0, common.mustCall(() => { mod.globalAgent.defaultPort = server.address().port; mod.get({ @@ -54,6 +53,7 @@ for (const { mod, createServer } of [ 'x-port': server.address().port } }, common.mustCall((res) => { + res.on('end', common.mustCall(() => server.close())); res.resume(); })); })); diff --git a/test/parallel/test-http-dont-set-default-headers-with-setHost.js b/test/parallel/test-http-dont-set-default-headers-with-setHost.js index e2a4e39c24b8..418051127859 100644 --- a/test/parallel/test-http-dont-set-default-headers-with-setHost.js +++ b/test/parallel/test-http-dont-set-default-headers-with-setHost.js @@ -14,6 +14,7 @@ const server = http.createServer(common.mustCall(function(req, res) { })); server.listen(0, common.localhostIPv4, function() { http.request({ + agent: false, method: 'POST', host: common.localhostIPv4, port: this.address().port, diff --git a/test/parallel/test-http-dont-set-default-headers.js b/test/parallel/test-http-dont-set-default-headers.js index 3f73c11e5112..0b8e4c58f56b 100644 --- a/test/parallel/test-http-dont-set-default-headers.js +++ b/test/parallel/test-http-dont-set-default-headers.js @@ -17,6 +17,7 @@ const server = http.createServer(common.mustCall(function(req, res) { })); server.listen(0, common.localhostIPv4, function() { http.request({ + agent: false, method: 'POST', host: common.localhostIPv4, port: this.address().port, diff --git a/test/parallel/test-http-early-hints-invalid-argument.js b/test/parallel/test-http-early-hints-invalid-argument.js index edf613614bc7..b426ca3e840f 100644 --- a/test/parallel/test-http-early-hints-invalid-argument.js +++ b/test/parallel/test-http-early-hints-invalid-argument.js @@ -38,7 +38,7 @@ const testResBody = 'response content\n'; server.listen(0, common.mustCall(() => { const req = http.request({ - port: server.address().port, path: '/' + port: server.address().port, path: '/', agent: false }); req.end(); @@ -79,7 +79,7 @@ const testResBody = 'response content\n'; server.listen(0, common.mustCall(() => { const req = http.request({ - port: server.address().port, path: '/' + port: server.address().port, path: '/', agent: false }); req.end(); diff --git a/test/parallel/test-http-head-request.js b/test/parallel/test-http-head-request.js index 26d490d357dc..a9fcb2c166b3 100644 --- a/test/parallel/test-http-head-request.js +++ b/test/parallel/test-http-head-request.js @@ -35,6 +35,7 @@ function test(headers) { server.listen(0, common.mustCall(function() { const request = http.request({ + agent: false, port: this.address().port, method: 'HEAD', path: '/' diff --git a/test/parallel/test-http-hex-write.js b/test/parallel/test-http-hex-write.js index a3cbec6b36c0..4162811276d9 100644 --- a/test/parallel/test-http-hex-write.js +++ b/test/parallel/test-http-hex-write.js @@ -34,7 +34,7 @@ http.createServer(function(q, s) { s.end(); this.close(); }).listen(0, common.mustCall(function() { - http.request({ port: this.address().port }) + http.request({ port: this.address().port, agent: false }) .on('response', common.mustCall(function(res) { let data = ''; diff --git a/test/parallel/test-http-outgoing-end-types.js b/test/parallel/test-http-outgoing-end-types.js index 20b443bff2c1..48372a98e81b 100644 --- a/test/parallel/test-http-outgoing-end-types.js +++ b/test/parallel/test-http-outgoing-end-types.js @@ -14,5 +14,5 @@ const httpServer = http.createServer(common.mustCall(function(req, res) { })); httpServer.listen(0, common.mustCall(function() { - http.get({ port: this.address().port }); + http.get({ port: this.address().port, agent: false }); })); diff --git a/test/parallel/test-http-outgoing-finish-writable.js b/test/parallel/test-http-outgoing-finish-writable.js index e3c870164bac..e0d9b73702cf 100644 --- a/test/parallel/test-http-outgoing-finish-writable.js +++ b/test/parallel/test-http-outgoing-finish-writable.js @@ -25,6 +25,7 @@ server.listen(0); server.on('listening', common.mustCall(function() { const clientRequest = http.request({ + agent: false, port: server.address().port, method: 'GET', path: '/' diff --git a/test/parallel/test-http-outgoing-finish.js b/test/parallel/test-http-outgoing-finish.js index 0f71cccdf810..f2378d9e05b2 100644 --- a/test/parallel/test-http-outgoing-finish.js +++ b/test/parallel/test-http-outgoing-finish.js @@ -33,6 +33,7 @@ http.createServer(function(req, res) { this.close(); }).listen(0, function() { const req = http.request({ + agent: false, port: this.address().port, method: 'PUT' }); diff --git a/test/parallel/test-http-outgoing-properties.js b/test/parallel/test-http-outgoing-properties.js index 85c5b659a36d..a831765322b8 100644 --- a/test/parallel/test-http-outgoing-properties.js +++ b/test/parallel/test-http-outgoing-properties.js @@ -36,6 +36,7 @@ const OutgoingMessage = http.OutgoingMessage; server.on('listening', common.mustCall(function() { const clientRequest = http.request({ + agent: false, port: server.address().port, method: 'GET', path: '/' @@ -62,6 +63,7 @@ const OutgoingMessage = http.OutgoingMessage; server.on('listening', common.mustCall(() => { const req = http.request({ + agent: false, port: server.address().port, method: 'GET', path: '/' diff --git a/test/parallel/test-http-outgoing-write-types.js b/test/parallel/test-http-outgoing-write-types.js index 6257b87eea8e..0f2c686d5a7a 100644 --- a/test/parallel/test-http-outgoing-write-types.js +++ b/test/parallel/test-http-outgoing-write-types.js @@ -20,5 +20,5 @@ const httpServer = http.createServer(common.mustCall(function(req, res) { })); httpServer.listen(0, common.mustCall(function() { - http.get({ port: this.address().port }); + http.get({ port: this.address().port, agent: false }); })); diff --git a/test/parallel/test-http-request-arguments.js b/test/parallel/test-http-request-arguments.js index 5cdd514fd506..b08da9bc5256 100644 --- a/test/parallel/test-http-request-arguments.js +++ b/test/parallel/test-http-request-arguments.js @@ -18,7 +18,7 @@ const http = require('http'); common.mustCall(() => { http.get( 'http://example.com/testpath', - { hostname: 'localhost', port: server.address().port }, + { hostname: 'localhost', port: server.address().port, agent: false }, common.mustCall((res) => { res.resume(); }) diff --git a/test/parallel/test-http-request-large-payload.js b/test/parallel/test-http-request-large-payload.js index 3be100b74041..08fada1381f2 100644 --- a/test/parallel/test-http-request-large-payload.js +++ b/test/parallel/test-http-request-large-payload.js @@ -16,6 +16,7 @@ const server = http.createServer(function(req, res) { server.listen(0, function() { const req = http.request({ + agent: false, method: 'POST', port: this.address().port }); diff --git a/test/parallel/test-http-server-connection-list-when-close.js b/test/parallel/test-http-server-connection-list-when-close.js index a530b710c490..0c8308b63c53 100644 --- a/test/parallel/test-http-server-connection-list-when-close.js +++ b/test/parallel/test-http-server-connection-list-when-close.js @@ -5,6 +5,7 @@ const http = require('http'); function request(server) { http.get({ + agent: false, port: server.address().port, path: '/', }, (res) => { diff --git a/test/parallel/test-http-server-delete-parser.js b/test/parallel/test-http-server-delete-parser.js index 4215ee2f9df7..6b5a3e13f503 100644 --- a/test/parallel/test-http-server-delete-parser.js +++ b/test/parallel/test-http-server-delete-parser.js @@ -14,6 +14,7 @@ const server = http.createServer(common.mustCall((req, res) => { server.listen(0, '127.0.0.1', common.mustCall(() => { const req = http.request({ + agent: false, port: server.address().port, host: '127.0.0.1', method: 'GET', diff --git a/test/parallel/test-http-server-multiheaders.js b/test/parallel/test-http-server-multiheaders.js index fea84a8d4a7e..e15dbd0fcaed 100644 --- a/test/parallel/test-http-server-multiheaders.js +++ b/test/parallel/test-http-server-multiheaders.js @@ -48,6 +48,7 @@ const server = http.createServer(common.mustCall((req, res) => { server.listen(0, function() { http.get({ + agent: false, host: 'localhost', port: this.address().port, path: '/', diff --git a/test/parallel/test-http-server-multiheaders2.js b/test/parallel/test-http-server-multiheaders2.js index 0408afa1b13e..85f2fb09f931 100644 --- a/test/parallel/test-http-server-multiheaders2.js +++ b/test/parallel/test-http-server-multiheaders2.js @@ -100,6 +100,7 @@ const headers = [] server.listen(0, function() { http.get({ + agent: false, host: 'localhost', port: this.address().port, path: '/', diff --git a/test/parallel/test-http-url.parse-auth-with-header-in-request.js b/test/parallel/test-http-url.parse-auth-with-header-in-request.js index ea5793ee18ae..e4834c32a644 100644 --- a/test/parallel/test-http-url.parse-auth-with-header-in-request.js +++ b/test/parallel/test-http-url.parse-auth-with-header-in-request.js @@ -41,6 +41,7 @@ const server = http.createServer(function(request, response) { server.listen(0, function() { const testURL = url.parse(`http://asdf:qwer@localhost:${this.address().port}`); + testURL.agent = false; // The test here is if you set a specific authorization header in the // request we should not override that with basic auth testURL.headers = { diff --git a/test/parallel/test-http-url.parse-auth.js b/test/parallel/test-http-url.parse-auth.js index 2bb531158645..287c27b9eb91 100644 --- a/test/parallel/test-http-url.parse-auth.js +++ b/test/parallel/test-http-url.parse-auth.js @@ -42,6 +42,7 @@ server.listen(0, function() { const port = this.address().port; // username = "user", password = "pass:" const testURL = url.parse(`http://user:pass%3A@localhost:${port}`); + testURL.agent = false; // make the request http.request(testURL).end(); diff --git a/test/parallel/test-http-url.parse-basic.js b/test/parallel/test-http-url.parse-basic.js index d0c230977178..223d1d7af25a 100644 --- a/test/parallel/test-http-url.parse-basic.js +++ b/test/parallel/test-http-url.parse-basic.js @@ -43,7 +43,6 @@ const server = http.createServer(function(request, response) { check(request); response.writeHead(200, {}); response.end('ok'); - server.close(); }); server.listen(0, common.mustCall(function() { @@ -54,5 +53,9 @@ server.listen(0, common.mustCall(function() { // Since there is a little magic with the agent // make sure that an http request uses the http.Agent assert.ok(clientRequest.agent instanceof http.Agent); + clientRequest.on('response', common.mustCall((response) => { + response.on('end', common.mustCall(() => server.close())); + response.resume(); + })); clientRequest.end(); })); diff --git a/test/parallel/test-http-url.parse-https.request.js b/test/parallel/test-http-url.parse-https.request.js index ff819adc2b84..e20c3a0ec7bc 100644 --- a/test/parallel/test-http-url.parse-https.request.js +++ b/test/parallel/test-http-url.parse-https.request.js @@ -45,7 +45,6 @@ const server = https.createServer(httpsOptions, function(request, response) { check(request); response.writeHead(200, {}); response.end('ok'); - server.close(); }); server.listen(0, common.mustCall(function() { @@ -57,5 +56,9 @@ server.listen(0, common.mustCall(function() { // Since there is a little magic with the agent // make sure that the request uses the https.Agent assert.ok(clientRequest.agent instanceof https.Agent); + clientRequest.on('response', common.mustCall((response) => { + response.on('end', common.mustCall(() => server.close())); + response.resume(); + })); clientRequest.end(); })); diff --git a/test/parallel/test-http-url.parse-path.js b/test/parallel/test-http-url.parse-path.js index 25e4838c4afa..04fe12a4ff1f 100644 --- a/test/parallel/test-http-url.parse-path.js +++ b/test/parallel/test-http-url.parse-path.js @@ -40,6 +40,7 @@ const server = http.createServer(function(request, response) { server.listen(0, function() { const testURL = url.parse(`http://localhost:${this.address().port}/asdf`); + testURL.agent = false; // make the request http.request(testURL).end(); diff --git a/test/parallel/test-http-url.parse-post.js b/test/parallel/test-http-url.parse-post.js index db5ee78fe6eb..447a1b6a3fdc 100644 --- a/test/parallel/test-http-url.parse-post.js +++ b/test/parallel/test-http-url.parse-post.js @@ -47,6 +47,7 @@ const server = http.createServer(function(request, response) { server.listen(0, function() { testURL = url.parse(`http://localhost:${this.address().port}/asdf?qwer=zxcv`); + testURL.agent = false; testURL.method = 'POST'; // make the request diff --git a/test/parallel/test-http-url.parse-search.js b/test/parallel/test-http-url.parse-search.js index 0759c779d3ff..80f435a6789a 100644 --- a/test/parallel/test-http-url.parse-search.js +++ b/test/parallel/test-http-url.parse-search.js @@ -41,6 +41,7 @@ const server = http.createServer(function(request, response) { server.listen(0, function() { const port = this.address().port; const testURL = url.parse(`http://localhost:${port}/asdf?qwer=zxcv`); + testURL.agent = false; // make the request http.request(testURL).end(); diff --git a/test/parallel/test-http-write-callbacks.js b/test/parallel/test-http-write-callbacks.js index 1f90e5135be6..3b29f7c2f5d6 100644 --- a/test/parallel/test-http-write-callbacks.js +++ b/test/parallel/test-http-write-callbacks.js @@ -71,6 +71,7 @@ server.on('checkContinue', common.mustCall((req, res) => { server.listen(0, common.mustCall(function() { const req = http.request({ + agent: false, port: this.address().port, method: 'PUT', headers: { 'expect': '100-continue' } diff --git a/test/parallel/test-http-write-empty-string.js b/test/parallel/test-http-write-empty-string.js index 88eff08f7666..05e97a4865c3 100644 --- a/test/parallel/test-http-write-empty-string.js +++ b/test/parallel/test-http-write-empty-string.js @@ -39,7 +39,7 @@ const server = http.createServer(function(request, response) { }); server.listen(0, common.mustCall(() => { - http.get({ port: server.address().port }, common.mustCall((res) => { + http.get({ port: server.address().port, agent: false }, common.mustCall((res) => { let response = ''; assert.strictEqual(res.statusCode, 200); diff --git a/test/parallel/test-http-zero-length-write.js b/test/parallel/test-http-zero-length-write.js index dfaa7b92fb7d..92905fd9755a 100644 --- a/test/parallel/test-http-zero-length-write.js +++ b/test/parallel/test-http-zero-length-write.js @@ -75,7 +75,7 @@ const server = http.createServer(common.mustCall((req, res) => { })); server.listen(0, common.mustCall(function() { - const req = http.request({ port: this.address().port, method: 'POST' }); + const req = http.request({ port: this.address().port, method: 'POST', agent: false }); let actual = ''; req.on('response', common.mustCall((res) => { res.setEncoding('utf8'); diff --git a/test/parallel/test-https-drain.js b/test/parallel/test-https-drain.js index 5d7bf9736458..b9a5c3d3bda8 100644 --- a/test/parallel/test-https-drain.js +++ b/test/parallel/test-https-drain.js @@ -45,6 +45,7 @@ const server = https.createServer(options, function(req, res) { server.listen(0, common.mustCall(function() { let resumed = false; const req = https.request({ + agent: false, method: 'POST', port: this.address().port, rejectUnauthorized: false diff --git a/test/parallel/test-https-request-arguments.js b/test/parallel/test-https-request-arguments.js index 9dc80094be0d..e68f757be81b 100644 --- a/test/parallel/test-https-request-arguments.js +++ b/test/parallel/test-https-request-arguments.js @@ -32,6 +32,7 @@ const options = { 'https://example.com/testpath', { + agent: false, hostname: 'localhost', port: server.address().port, rejectUnauthorized: false diff --git a/test/parallel/test-https-truncate.js b/test/parallel/test-https-truncate.js index beed36cd7c08..eaaedea1afc7 100644 --- a/test/parallel/test-https-truncate.js +++ b/test/parallel/test-https-truncate.js @@ -47,7 +47,7 @@ function httpsTest() { }); server.listen(0, function() { - const opts = { port: this.address().port, rejectUnauthorized: false }; + const opts = { port: this.address().port, rejectUnauthorized: false, agent: false }; https.get(opts).on('response', function(res) { test(res); }); diff --git a/test/parallel/test-https-unix-socket-self-signed.js b/test/parallel/test-https-unix-socket-self-signed.js index 9db92ac2aed4..5a3b76e11794 100644 --- a/test/parallel/test-https-unix-socket-self-signed.js +++ b/test/parallel/test-https-unix-socket-self-signed.js @@ -21,6 +21,7 @@ const server = https.createServer(options, common.mustCall((req, res) => { server.listen(common.PIPE, common.mustCall(() => { https.get({ + agent: false, socketPath: common.PIPE, rejectUnauthorized: false }); From cbb5bf256b16ce7f06222b937fbeb3c35398a601 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 11 Sep 2026 13:32:54 +0200 Subject: [PATCH 06/12] test: reuse fixed primes in DH tests Use the modp14 prime instead of generating fresh parameters for tests of constructors, key setters, and memory retention. Keep generic DiffieHellman instances and the existing setter and leak assertions. Signed-off-by: Filip Skokan Assisted-by: Codex --- test/parallel/test-crypto-dh-constructor.js | 9 +++------ test/parallel/test-crypto-dh-generate-keys.js | 6 ++---- test/parallel/test-crypto-dh-leak.js | 6 ++---- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/test/parallel/test-crypto-dh-constructor.js b/test/parallel/test-crypto-dh-constructor.js index 28747ac3a726..edf7ab08e44c 100644 --- a/test/parallel/test-crypto-dh-constructor.js +++ b/test/parallel/test-crypto-dh-constructor.js @@ -5,17 +5,14 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); -const { hasOpenSSL, hasFIPS } = require('../common/crypto'); +const { hasFIPS } = require('../common/crypto'); -const size = hasFIPS(3) ? - 2048 : (crypto.getFips() === 1 || hasOpenSSL(3) ? 1024 : 256); -const dh1 = crypto.createDiffieHellman(size); -const p1 = dh1.getPrime('buffer'); +const prime = crypto.getDiffieHellman('modp14').getPrime('buffer'); { const DiffieHellman = crypto.DiffieHellman; - const dh = DiffieHellman(p1, 'buffer'); + const dh = DiffieHellman(prime, 'buffer'); assert(dh instanceof DiffieHellman, 'DiffieHellman is expected to return a ' + 'new instance when called without `new`'); } diff --git a/test/parallel/test-crypto-dh-generate-keys.js b/test/parallel/test-crypto-dh-generate-keys.js index d074ba957516..65efb369c08e 100644 --- a/test/parallel/test-crypto-dh-generate-keys.js +++ b/test/parallel/test-crypto-dh-generate-keys.js @@ -6,11 +6,9 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); -const { hasOpenSSL, hasFIPS } = require('../common/crypto'); { - const size = hasFIPS(3) ? - 2048 : (crypto.getFips() === 1 || hasOpenSSL(3) ? 1024 : 256); + const prime = crypto.getDiffieHellman('modp14').getPrime(); function unlessInvalidState(f) { try { @@ -23,7 +21,7 @@ const { hasOpenSSL, hasFIPS } = require('../common/crypto'); } function testGenerateKeysChangesKeys(setup, expected) { - const dh = crypto.createDiffieHellman(size); + const dh = crypto.createDiffieHellman(prime); setup(dh); const firstPublicKey = unlessInvalidState(() => dh.getPublicKey()); const firstPrivateKey = unlessInvalidState(() => dh.getPrivateKey()); diff --git a/test/parallel/test-crypto-dh-leak.js b/test/parallel/test-crypto-dh-leak.js index 8d5141eef4b1..f58a5a152b78 100644 --- a/test/parallel/test-crypto-dh-leak.js +++ b/test/parallel/test-crypto-dh-leak.js @@ -9,13 +9,11 @@ if (common.isASan) const assert = require('assert'); const crypto = require('crypto'); -const { hasOpenSSL, hasFIPS } = require('../common/crypto'); const before = process.memoryUsage.rss(); { - const size = hasFIPS(3) ? - 2048 : (crypto.getFips() === 1 || hasOpenSSL(3) ? 1024 : 256); - const dh = crypto.createDiffieHellman(size); + const prime = crypto.getDiffieHellman('modp14').getPrime(); + const dh = crypto.createDiffieHellman(prime); const publicKey = dh.generateKeys(); const privateKey = dh.getPrivateKey(); for (let i = 0; i < 5e4; i += 1) { From 7181c3cc439163b503a8e634f825f637c099a4b1 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 11 Sep 2026 13:32:54 +0200 Subject: [PATCH 07/12] test: synchronize ordered runner events Release the slow fixture over a local socket after the fast fixture emits its bypassed completion event. This removes the fixed 30-second delay while preserving event-order assertions and a bounded failure timeout. Signed-off-by: Filip Skokan Assisted-by: Codex --- .../execution-ordered-bypass/slow.mjs | 12 +++++---- .../test-runner-execution-ordered-bypass.mjs | 25 ++++++++++++++++--- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/test/fixtures/test-runner/execution-ordered-bypass/slow.mjs b/test/fixtures/test-runner/execution-ordered-bypass/slow.mjs index 4ee60ffe8537..21f6f2b68676 100644 --- a/test/fixtures/test-runner/execution-ordered-bypass/slow.mjs +++ b/test/fixtures/test-runner/execution-ordered-bypass/slow.mjs @@ -1,9 +1,11 @@ import { test } from 'node:test'; -import { setTimeout as sleep } from 'node:timers/promises'; +import { once } from 'node:events'; +import { connect } from 'node:net'; test('slow', async () => { - // Long enough that fast-fail's process can spawn, run, and round-trip its - // bypassed test:complete to the host on slow CI, but short enough that the - // test does not waste much time when the bypass is working. - await sleep(30_000); + // The host closes this connection after receiving fast-fail's bypassed + // test:complete event, so this test cannot finish before that event arrives. + const socket = connect(Number(process.argv[2]), '127.0.0.1'); + socket.resume(); + await once(socket, 'end'); }); diff --git a/test/parallel/test-runner-execution-ordered-bypass.mjs b/test/parallel/test-runner-execution-ordered-bypass.mjs index ac1c97ee007d..75e9eb51d7d9 100644 --- a/test/parallel/test-runner-execution-ordered-bypass.mjs +++ b/test/parallel/test-runner-execution-ordered-bypass.mjs @@ -1,8 +1,10 @@ // Flags: --no-warnings -import '../common/index.mjs'; +import { mustCall, platformTimeout } from '../common/index.mjs'; import * as fixtures from '../common/fixtures.mjs'; import assert from 'node:assert'; +import { once } from 'node:events'; +import { createServer } from 'node:net'; import { test, run } from 'node:test'; const files = [ @@ -10,15 +12,29 @@ const files = [ fixtures.path('test-runner', 'execution-ordered-bypass', 'fast-fail.mjs'), ]; -test('execution-ordered events bypass FileTest declaration-order buffer', async () => { +test('execution-ordered events bypass FileTest declaration-order buffer', { + timeout: platformTimeout(30_000), +}, async (t) => { + const { promise: fastCompleted, resolve: releaseSlow } = Promise.withResolvers(); + const server = createServer(mustCall((socket) => { + t.after(() => socket.destroy()); + fastCompleted.then(mustCall(() => { + socket.end(); + })); + })); + t.after(() => server.close()); + await once(server.listen(0, '127.0.0.1'), 'listening'); + // Concurrency must be a number so the runner does not collapse it to 1 on // single-core CI runners (where `concurrency: true` resolves to // `availableParallelism() - 1`). Without two slots the runner spawns the - // files sequentially and fast-fail never starts while slow is sleeping. + // files sequentially and fast-fail never starts while slow is waiting. const stream = run({ files, isolation: 'process', concurrency: 2, + argv: [String(server.address().port)], + signal: t.signal, }); const events = []; @@ -27,6 +43,9 @@ test('execution-ordered events bypass FileTest declaration-order buffer', async if (data.name === 'slow' || data.name === 'fast-fail') { events.push(`complete:${data.name}`); } + if (data.name === 'fast-fail') { + releaseSlow(); + } }); stream.on('test:fail', (data) => { From a088fbf671a726217c78fd840b82bb815317f20a Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 11 Sep 2026 13:32:55 +0200 Subject: [PATCH 08/12] test: skip retries in DNS timeout coverage A single query attempt exercises the configured timeout without the default retry backoff. Retry behavior has separate coverage. Signed-off-by: Filip Skokan Assisted-by: Codex --- test/parallel/test-dns-channel-timeout.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-dns-channel-timeout.js b/test/parallel/test-dns-channel-timeout.js index 1e4dac548973..0c9c7c31caee 100644 --- a/test/parallel/test-dns-channel-timeout.js +++ b/test/parallel/test-dns-channel-timeout.js @@ -22,10 +22,11 @@ for (const ctor of [dns.Resolver, dns.promises.Resolver]) { for (const timeout of [-1, 0, 1]) new ctor({ timeout }); // OK } +// One attempt is enough to exercise the timeout without retry backoff. for (const timeout of [0, 1, 2]) { const server = dgram.createSocket('udp4'); server.bind(0, '127.0.0.1', common.mustCall(() => { - const resolver = new dns.Resolver({ timeout }); + const resolver = new dns.Resolver({ timeout, tries: 1 }); resolver.setServers([`127.0.0.1:${server.address().port}`]); resolver.resolve4('nodejs.org', common.mustCall((err) => { assert.throws(() => { throw err; }, { @@ -40,7 +41,7 @@ for (const timeout of [0, 1, 2]) { for (const timeout of [0, 1, 2]) { const server = dgram.createSocket('udp4'); server.bind(0, '127.0.0.1', common.mustCall(() => { - const resolver = new dns.promises.Resolver({ timeout }); + const resolver = new dns.promises.Resolver({ timeout, tries: 1 }); resolver.setServers([`127.0.0.1:${server.address().port}`]); resolver.resolve4('nodejs.org').catch(common.mustCall((err) => { assert.throws(() => { throw err; }, { From f008c10008a0e9ee976ee0e2da902aee63fbf339 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 11 Sep 2026 13:32:55 +0200 Subject: [PATCH 09/12] test: collect timeout signals explicitly Force collection on a later turn while the timeout sources are only retained by AbortSignal.any(). Shorten the first timeout and clear the watchdog after the assertion. This preserves the source-retention regression check without waiting ten seconds on successful runs. Signed-off-by: Filip Skokan Assisted-by: Codex --- .../test-abort-controller-any-timeout.js | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/test/parallel/test-abort-controller-any-timeout.js b/test/parallel/test-abort-controller-any-timeout.js index 2d94afaa63d9..675be3af703c 100644 --- a/test/parallel/test-abort-controller-any-timeout.js +++ b/test/parallel/test-abort-controller-any-timeout.js @@ -1,28 +1,42 @@ +// Flags: --expose-gc 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const { once } = require('node:events'); const { describe, it } = require('node:test'); describe('AbortSignal.any() with timeout signals', () => { it('should abort when the first timeout signal fires', async () => { - const signal = AbortSignal.any([AbortSignal.timeout(9000), AbortSignal.timeout(110000)]); + const signal = AbortSignal.any([ + AbortSignal.timeout(common.platformTimeout(1000)), + AbortSignal.timeout(110000), + ]); + let timeout; const abortPromise = Promise.race([ once(signal, 'abort').then(() => { throw signal.reason; }), - new Promise((resolve) => setTimeout(resolve, 10000)), + new Promise((resolve) => { + timeout = setTimeout(resolve, common.platformTimeout(10000)); + }), ]); - // The promise should be aborted by the 9000ms timeout - await assert.rejects( - () => abortPromise, - { - name: 'TimeoutError', - message: 'The operation was aborted due to timeout' - } - ); + // Collect after this turn so the WeakRefs no longer keep the timeout + // signals alive by themselves. + setImmediate(common.mustCall(() => globalThis.gc())); + + try { + await assert.rejects( + () => abortPromise, + { + name: 'TimeoutError', + message: 'The operation was aborted due to timeout' + } + ); + } finally { + clearTimeout(timeout); + } }); }); From d4184b92f93c0c2ea222263bbeace36b725bf512 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 11 Sep 2026 13:32:55 +0200 Subject: [PATCH 10/12] test: unref cancelled broadcast source timer The source delay should not keep the process alive after cancellation. Keep the blocked-source cancellation assertions and unref its timer. Signed-off-by: Filip Skokan Assisted-by: Codex --- test/parallel/test-stream-iter-broadcast-from.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-stream-iter-broadcast-from.js b/test/parallel/test-stream-iter-broadcast-from.js index 39d92c2aef49..928d4d472f06 100644 --- a/test/parallel/test-stream-iter-broadcast-from.js +++ b/test/parallel/test-stream-iter-broadcast-from.js @@ -122,8 +122,8 @@ async function testBroadcastFromCancelWhileBlocked() { async function* slowSource() { const enc = new TextEncoder(); yield [enc.encode('chunk1')]; - // Simulate a long delay - the cancel should unblock this - await new Promise((resolve) => setTimeout(resolve, 10000)); + // Simulate a long delay without keeping the cancelled source alive. + await new Promise((resolve) => setTimeout(resolve, 10000).unref()); yield [enc.encode('chunk2')]; sourceFinished = true; } From 6c2d3c1718d874bff1cf0e1577c5bc4b590da8a9 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 11 Sep 2026 13:40:19 +0200 Subject: [PATCH 11/12] test: overlap SLH-DSA signature checks Start each asynchronous signature before the synchronous checks for the same algorithm. Keep every sign, verify, and invalid-digest assertion, with only one asynchronous signature outstanding. This reduces elapsed time when CPU capacity is available without reducing coverage. Signed-off-by: Filip Skokan Assisted-by: Codex --- test/pummel/test-crypto-pqc-sign-verify-slh-dsa.mjs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/pummel/test-crypto-pqc-sign-verify-slh-dsa.mjs b/test/pummel/test-crypto-pqc-sign-verify-slh-dsa.mjs index 772d9bab6f61..52b018e7028d 100644 --- a/test/pummel/test-crypto-pqc-sign-verify-slh-dsa.mjs +++ b/test/pummel/test-crypto-pqc-sign-verify-slh-dsa.mjs @@ -13,6 +13,9 @@ import { promisify } from 'node:util'; import { randomBytes, sign, verify } from 'node:crypto'; import fixtures from '../common/fixtures.js'; +const pSign = promisify(sign); +const pVerify = promisify(verify); + function getKeyFileName(type, suffix) { return `${type.replaceAll('-', '_')}_${suffix}.pem`; } @@ -37,6 +40,8 @@ for (const [asymmetricKeyType, sigLen] of [ }; const data = randomBytes(32); + // Start the async signature before the sync work to overlap the two. + const signaturePromise = pSign(undefined, data, keys.private); // sync { @@ -48,9 +53,7 @@ for (const [asymmetricKeyType, sigLen] of [ // async { - const pSign = promisify(sign); - const pVerify = promisify(verify); - const signature = await pSign(undefined, data, keys.private); + const signature = await signaturePromise; assert.strictEqual(signature.byteLength, sigLen); assert.strictEqual(await pVerify(undefined, randomBytes(32), keys.public, signature), false); assert.strictEqual(await pVerify(undefined, data, keys.public, signature), true); From d72070f6eb909b63d1e8759a178d1a244a124a5b Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 11 Sep 2026 16:28:27 +0200 Subject: [PATCH 12/12] fixup! test: use named parameters in DH stress test Retain the original small imported parameters when the OpenSSL 3 provider shortcut is unavailable. BoringSSL checks the prime on every named-group construction, making 4,000 modp14 constructions too slow. Preserve the existing exchange counts and FIPS assertions. Signed-off-by: Filip Skokan Assisted-by: Codex --- test/pummel/test-dh-regr.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/test/pummel/test-dh-regr.js b/test/pummel/test-dh-regr.js index a3e7b42a452f..8a2e71745a38 100644 --- a/test/pummel/test-dh-regr.js +++ b/test/pummel/test-dh-regr.js @@ -32,7 +32,7 @@ if (common.isPi()) { const assert = require('assert'); const crypto = require('crypto'); -const { hasFIPS } = require('../common/crypto'); +const { hasOpenSSL, hasFIPS } = require('../common/crypto'); let iterations = 2000; if (hasFIPS(3)) { @@ -45,10 +45,20 @@ if (hasFIPS(3)) { iterations = 100; } +let createDH; +if (hasOpenSSL(3)) { + // OpenSSL 3 recognizes named groups without validating their primes. + createDH = () => crypto.getDiffieHellman('modp14'); +} else { + // Other backends validate each peer's parameters, so keep them small. + const length = crypto.getFips() === 1 ? 1024 : 256; + const prime = crypto.createDiffieHellman(length).getPrime(); + createDH = () => crypto.createDiffieHellman(prime); +} + for (let i = 0; i < iterations; i++) { - // A named group avoids generating and validating custom parameters. - const a = crypto.getDiffieHellman('modp14'); - const b = crypto.getDiffieHellman('modp14'); + const a = createDH(); + const b = createDH(); a.generateKeys(); b.generateKeys();