Skip to content

Make the full test suite pass on Node.js LTS and latest - #2

Merged
Kikobeats merged 5 commits into
v11from
v11-full-test-suite
Sep 14, 2026
Merged

Kikobeats merged 5 commits into
v11from
v11-full-test-suite

Conversation

@Kikobeats

@Kikobeats Kikobeats commented Sep 14, 2026 •

Copy link
Copy Markdown
Owner

Makes the complete v11 test suite pass on Node.js LTS and latest and runs it on CI. That's the release gate for @kikobeats/got.

Before this PR, 63 of 534 tests failed on Node.js 24.20.0. Two of those failures were real bugs in got v11 on modern Node.js, not outdated tests.

Bug fixes (source/)

1. stream.pipeline() into a Got stream throws an uncaught TypeError

pipeline(request, got.stream(''), response) crashed the process with The payload has been already provided on Node.js 18 and later.

The stream lock swapped write/end for a throwing function and relied on pipe listener order to unlock right before the source ended. Since Node.js 17.3, pipeline() calls end() from its own listener after the lock is back on.

Fix: backport of upstream bb8eca9 ("Remove stream lock - unreliable since Node 17.3.0"), including removal of the two tests that asserted the lock.

2. Cached responses never resolve with keep-alive agents

got(url, {cache}) never settles, on the first request as well as later ones, on Node.js 20 and later, where keep-alive is the default. Repro: 18 resolves; 20, 22, 24.20.0 and 26 hang, with stock [email protected] too.

clone-response copies complete onto the clone before the body is piped, so it stays false. By the time the promise API checks request.aborted (the ClientRequest is destroyed and complete is false), the keep-alive ClientRequest has already been released. So aborted is wrongly true and the body is never read.

Fix: on the clone-response PassThrough, complete becomes a getter backed by writableEnded. Real IncomingMessage objects and cache hits don't change. Upstream replaced this logic entirely in the Got 12 rewrite, so there's nothing smaller to backport. The existing cache.ts and cancel.ts tests cover it: 10 of them hang without the fix.

Test updates (test/)

These tests are outdated for current Node.js and OpenSSL. Most of the updates match upstream's own changes to the same tests.

Test Change
arguments, helpers, error (Invalid URL) invalid-url helper accepts Invalid URL with input as well as the old Invalid URL: <input>
promise (response IP) 127.0.0.1 or ::1
error (properties) connection: keep-alive
response-parse (ParseErrors) /^Unexpected token/ for V8's new message
http (DNS auto) IPv4 or IPv6
http (deprecated family) numeric family: 4; Node.js now rejects '4' before Got's DeprecationWarning can be observed
timeout (early lookup) the fake lookup honors options.all
gzip (premature close) also accepts The server aborted pending request
error (DNS errors) ENOTFOUND or EAI_AGAIN; GitHub runners return EAI_AGAIN
stream (pipeline) assert code: 'ECONNREFUSED', since localhost now fails with an empty-message AggregateError
https helper CA and server certificates get the extensions OpenSSL 3 requires (unsuitable certificate purpose otherwise)
https (invalid passphrase) ERR_OSSL_BAD_DECRYPT or ERR_OSSL_EVP_BAD_DECRYPT
https (key passphrases) skipped on macOS, as upstream does
https (expired certificate) static fixture in test/fixtures/expired-certificate (expired 2000-01-02, CA valid until 2100); OpenSSL 3.5 refuses negative -days

The fixture includes a private key for a certificate that expired in 2000. It's test-only, and the CA's private key isn't committed.

CI

.github/workflows/test.yml runs build and the full ava suite on lts/* and latest.

  • Dev dependencies: resolved as of 2021-06-15, because TypeScript 4.0.3 can't compile newer typings. @types/[email protected] and [email protected] (OpenSSL 3 support) are installed without being saved.
  • Runtime dependencies: the newest in their ranges, which is what consumers install.
  • Lint: not in CI. xo 0.34's eslint-plugin-ava calls util.isDate, which was removed in Node.js 23, so it can't run on LTS or latest.

Verification

Environment Result
GitHub Actions lts/* (Node.js 24.20.0) and latest (Node.js 26.8.2), OpenSSL 3.0.13 529 pass, 0 fail, 2 skipped, 1 known failure (test.failing from v11.8.6)
Linux node:24.20.0 (OpenSSL 3.0.20), CI steps 528 pass, 0 fail, 4 skipped
Linux node:26 = 26.8.2 (OpenSSL 3.5.6), CI steps 528 pass, 0 fail, 4 skipped
macOS Node.js 24.20.0 528 pass, 1 fail: client certificate PFX
  • Skips: 2 are test.skip in the source; 2 are IPv6 tests that skip without an external IPv6 interface.
  • macOS PFX failure: macOS's /usr/bin/openssl is LibreSSL, which creates legacy PKCS#12 that Node.js can't read. It passes with OPENSSL_BIN=$(brew --prefix openssl@3)/bin/openssl.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VZiY7VMgg1nEMkCezwMXEC

Kikobeats and others added 5 commits September 14, 2026 23:30
Backport of bb8eca9 (Got 12) to v11.

The lock replaced `write`/`end` with a function throwing
`The payload has been already provided` and relied on the order of
`pipe` listeners to unlock right before a piped source ends. Since
Node.js 17.3, `stream.pipeline()` calls `end()` from its own listener
after the lock is re-applied, so `pipeline(request, got.stream(''),
response)` throws that TypeError as an uncaught exception.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01VZiY7VMgg1nEMkCezwMXEC
`clone-response` copies `complete` onto the cloned response before the
body has been piped, so it stays `false`. With keep-alive agents (the
default since Node.js 20), the ClientRequest is already destroyed when
the promise API checks `request.aborted`, which is derived from both
values, so it returned early and the request never settled.

Make `complete` on the clone reflect whether the original response has
been fully piped (`writableEnded`).

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01VZiY7VMgg1nEMkCezwMXEC
- Accept the `Invalid URL` error format with `input` (invalid-url helper)
- Accept `::1` for localhost, `keep-alive` connections and V8's JSON
  parse error message
- Accept IPv4 or IPv6 for `dnsLookupIpVersion: 'auto'`
- Pass a numeric `family` in the deprecation test, since Node.js now
  rejects `'4'` before Got's warning is observed
- Honor `options.all` in the fake `lookup`
- Accept `The server aborted pending request` for broken gzip streams
- Create test certificates with CA and server extensions required by
  OpenSSL 3, and accept `ERR_OSSL_BAD_DECRYPT`
- Use a static expired certificate fixture, since OpenSSL 3.5 no longer
  creates certificates with negative validity
- Skip key passphrase tests on macOS, as upstream does

Most of these match the upstream test changes.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01VZiY7VMgg1nEMkCezwMXEC
Dev dependencies stay resolved as of 2021-06-15 (TypeScript 4.0.3
tooling), runtime dependencies are the latest in range like consumers
get, plus pem 1.14.8 for OpenSSL 3.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01VZiY7VMgg1nEMkCezwMXEC
GitHub Actions runners resolve unknown hosts with EAI_AGAIN. The message
assertion already allowed it; upstream accepts both codes too.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01VZiY7VMgg1nEMkCezwMXEC
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant