Bug Description
MockAgent allows us to generate a reply dynamically. The request's options are passed into that function. Undici's type definitions specify that a Headers object will be used for options.headers, but instead a flattened array is given. This is entirely unusable without splitting it into two-tuple chunks first and manually wrapping it in a new Headers() call.
Note that it is also possible to access the headers inside a matcher function passed to intercept(). This was a useful workaround for me, but keep in mind that for some reason, the matcher gets called twice which you can also see in the output below.
Reproducible By
import { fetch, MockAgent, setGlobalDispatcher } from 'undici'
const mockAgent = new MockAgent()
const mockPool = mockAgent.get('http://localhost')
setGlobalDispatcher(mockAgent)
mockPool.intercept({
path: '/foo',
method: 'GET',
headers: (headers) => {
console.log('headers in intercept:', Object.prototype.toString.call(headers), headers)
return true
}
}).reply(200, (options) => {
console.log('headers in reply:', Object.prototype.toString.call(options.headers), options.headers)
return {}
})
await fetch('http://localhost/foo')
Expected Behavior
The included type definitions specify that the parameter to the headers matcher in intercept() should receive Record<string, string>. For reply(), they specify options.headers to be a Headers object.
As such I would expect the following output:
headers in intercept: [object Object] {
accept: '*/*',
'accept-language': '*',
'sec-fetch-mode': 'cors',
'user-agent': 'undici',
'accept-encoding': 'gzip, deflate'
}
headers in reply: [object Headers] HeadersList {
[Symbol(headers map)]: Map(5) {
'accept' => '*/*',
'accept-language' => '*',
'sec-fetch-mode' => 'cors',
'user-agent' => 'undici',
'accept-encoding' => 'gzip, deflate'
},
[Symbol(headers map sorted)]: null
}
Logs & Screenshots
Instead of the above, the following is logged:
headers in intercept: [object Object] {
accept: '*/*',
'accept-language': '*',
'sec-fetch-mode': 'cors',
'user-agent': 'undici',
'accept-encoding': 'gzip, deflate'
}
headers in reply: [object Array] [
'accept',
'*/*',
'accept-language',
'*',
'sec-fetch-mode',
'cors',
'user-agent',
'undici',
'accept-encoding',
'gzip, deflate'
]
headers in intercept: [object Object] {
accept: '*/*',
'accept-language': '*',
'sec-fetch-mode': 'cors',
'user-agent': 'undici',
'accept-encoding': 'gzip, deflate'
}
Environment
- Windows 11, additionally tried in WSL running Ubuntu 20.04.4 LTS with same behavior
- Node v18.6.0
- npm 8.13.2
- Undici 5.8.0
Additional context
- Relevant type definitions for
options.headers in reply():
- I didn't yet have the time to figure out where in the source code the headers array is coming from, unfortunately.
Bug Description
MockAgentallows us to generate a reply dynamically. The request'soptionsare passed into that function. Undici's type definitions specify that aHeadersobject will be used foroptions.headers, but instead a flattened array is given. This is entirely unusable without splitting it into two-tuple chunks first and manually wrapping it in anew Headers()call.Note that it is also possible to access the headers inside a matcher function passed to
intercept(). This was a useful workaround for me, but keep in mind that for some reason, the matcher gets called twice which you can also see in the output below.Reproducible By
Expected Behavior
The included type definitions specify that the parameter to the
headersmatcher inintercept()should receiveRecord<string, string>. Forreply(), they specifyoptions.headersto be aHeaders object.As such I would expect the following output:
Logs & Screenshots
Instead of the above, the following is logged:
Environment
Additional context
options.headersinreply():undici/types/mock-interceptor.d.ts
Line 77 in 784c6b4