From 00c975c00ee8febb08389639b96bb0b20fc31625 Mon Sep 17 00:00:00 2001 From: Gihoon1123 Date: Sun, 12 Jul 2026 16:20:25 +0900 Subject: [PATCH] test(interceptors): add cache/dedupe + dns re-dispatch integration tests The underlying normalizeHeaders bug was fixed in #5536, which covers the flat-header handling with unit tests. These integration tests exercise the path the bug was actually reported through - a dns interceptor re-dispatch landing in a composed cache() (and deduplicate(), which shares the same cache-key utility) - so the compose-chain scenario stays covered end to end. --- test/interceptors/dns.js | 89 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/test/interceptors/dns.js b/test/interceptors/dns.js index f4acdf72277..5dada0fb2e0 100644 --- a/test/interceptors/dns.js +++ b/test/interceptors/dns.js @@ -12,7 +12,7 @@ const { tspl } = require('@matteo.collina/tspl') const pem = require('@metcoder95/https-pem') const { interceptors, Agent, Client, Pool, request } = require('../..') -const { dns } = interceptors +const { dns, cache, deduplicate } = interceptors // Helper to check if IPv6 is available for localhost // This is called synchronously at test definition time @@ -2156,6 +2156,93 @@ test('#4444 - Should preserve iterable headers', async t => { t.equal(await response.body.text(), 'hello world!') }) +test('#5522 - Should not throw when dns re-dispatches into a composed cache interceptor', async t => { + t = tspl(t, { plan: 2 }) + + const server = createServer() + + server.on('request', (req, res) => { + res.writeHead(200, { 'content-type': 'text/plain' }) + res.end('hello world!') + }) + + server.listen(0) + + await once(server, 'listening') + + // dns is the outer interceptor here, so its re-dispatch after resolving + // lands back in cache's normalizeHeaders() with a flat header array. + const client = new Agent().compose(cache(), dns({ + lookup: (_origin, _opts, cb) => { + cb(null, [ + { + address: '127.0.0.1', + family: 4 + } + ]) + } + })) + + after(async () => { + await client.close() + server.close() + + await once(server, 'close') + }) + + const response = await request(`http://localhost:${server.address().port}`, { + dispatcher: client, + headers: [] + }) + + t.equal(response.statusCode, 200) + t.equal(await response.body.text(), 'hello world!') +}) + +test('#5522 - Should not throw when dns re-dispatches into a composed deduplicate interceptor', async t => { + t = tspl(t, { plan: 2 }) + + const server = createServer() + + server.on('request', (req, res) => { + res.writeHead(200, { 'content-type': 'text/plain' }) + res.end('hello world!') + }) + + server.listen(0) + + await once(server, 'listening') + + // Same root cause as the cache-interceptor case above, but through + // deduplicate() -- it shares the same normalizeHeaders()/makeCacheKey() + // utility from lib/util/cache.js, so it was equally affected. + const client = new Agent().compose(deduplicate(), dns({ + lookup: (_origin, _opts, cb) => { + cb(null, [ + { + address: '127.0.0.1', + family: 4 + } + ]) + } + })) + + after(async () => { + await client.close() + server.close() + + await once(server, 'close') + }) + + const response = await request(`http://localhost:${server.address().port}`, { + dispatcher: client, + headers: [] + }) + + t.equal(response.statusCode, 200) + t.equal(await response.body.text(), 'hello world!') +}) + test('#3951 - Should handle lookup errors correctly', async t => { const suite = tspl(t, { plan: 1 })