pact-js
pact-js copied to clipboard
Modify Content-Type header of the response when verifying HTTP provider
Hello everyone,
Seems there is an issue when you try to modify the 'Content-Type' header using a requestFilter.
Software versions
- OS: MacOS Monterey 12.4
- Consumer Pact library: @pact-foundation/[email protected]
- Provider Pact library: @pact-foundation/[email protected]
- Node Version: v16.15.0
Expected behaviour
The test should pass, the 'Content-Type' header of the response should be set to 'application/json'.
Actual behaviour
Expected header "content-type" to equal "application/json", but was "text/html; charset=utf-8". It seems there is an issue with this content-type header. I can add any header but this one.
Steps to reproduce
I added a requestFilter in the options of the Verifier
constructor.
requestFilter: (req, res, next) => {
next();
res.header('test', 'valuetest'); // example to show it works
res.header('Content-Type', 'application/json');
}
Logs :
[2022-05-30 16:41:18.123 +0000] DEBUG (52303 on NGG568): [email protected]: I, [2022-05-30T18:41:18.123823 #52326] INFO -- request: POST http://localhost:52088/_pactSetup
[2022-05-30 16:41:18.124 +0000] DEBUG (52303 on NGG568): [email protected]: D, [2022-05-30T18:41:18.123870 #52326] DEBUG -- request: User-Agent: "Faraday v0.17.5"
Content-Type: "application/json"
[2022-05-30 16:41:18.184 +0000] DEBUG (52303 on NGG568): [email protected]: I, [2022-05-30T18:41:18.184099 #52326] INFO -- response: Status 200
[2022-05-30 16:41:18.184 +0000] DEBUG (52303 on NGG568): [email protected]: D, [2022-05-30T18:41:18.184170 #52326] DEBUG -- response: x-powered-by: "Express"
test: "valuetest"
content-type: "text/plain; charset=utf-8"
content-length: "2"
etag: "W/\"2-nOO9QiTIwXgNtWtBJezz8kv3SLc\""
date: "Mon, 30 May 2022 16:41:18 GMT"
connection: "close"
You can see that the test
header has been added but the content-type header hasn't been set correctly.
Thanks in advance for your answer.
Hey, what happens if you set res.header('content-type', 'application/json');
to match the case.
also I would have expected next() to have been called after setting your options, as its a callback
see this example
https://github.com/pact-foundation/pact-js#modify-requests-prior-to-verification-request-filters
let token
let opts = {
provider: 'Animal Profile Service',
...
stateHandlers: {
"is authenticated": () => {
token = "1234"
Promise.resolve(`Valid bearer token generated`)
},
"is not authenticated": () => {
token = ""
Promise.resolve(`Expired bearer token generated`)
}
},
// this middleware is executed for each request, allowing `token` to change between invocations
// it is common to pair this with `stateHandlers` as per above, that can set/expire the token
// for different test cases
requestFilter: (req, res, next) => {
req.headers["Authorization"] = `Bearer: ${token}`
next()
},
// This header will always be sent for each and every request, and can't be dynamic
// (i.e. passing a variable instead of the bearer token)
customProviderHeaders: ["Authorization: Bearer 1234"]
}
return new Verifier(opts).verifyProvider().then(...)
Hey, what happens if you set
res.header('content-type', 'application/json');
to match the case.also I would have expected next() to have been called after setting your options, as its a callback
see this example
https://github.com/pact-foundation/pact-js#modify-requests-prior-to-verification-request-filters
let token let opts = { provider: 'Animal Profile Service', ... stateHandlers: { "is authenticated": () => { token = "1234" Promise.resolve(`Valid bearer token generated`) }, "is not authenticated": () => { token = "" Promise.resolve(`Expired bearer token generated`) } }, // this middleware is executed for each request, allowing `token` to change between invocations // it is common to pair this with `stateHandlers` as per above, that can set/expire the token // for different test cases requestFilter: (req, res, next) => { req.headers["Authorization"] = `Bearer: ${token}` next() }, // This header will always be sent for each and every request, and can't be dynamic // (i.e. passing a variable instead of the bearer token) customProviderHeaders: ["Authorization: Bearer 1234"] } return new Verifier(opts).verifyProvider().then(...)
I tried but same result ! As suggested, I forked the pact-js repository and modified an example in order to reproduce the issue. You can find my fork here. To reproduce it :
- You have first to generate the pact contract using
npm run test:consumer
in thepact-js/examples/e2e
directory. - You can then execute
npm run test:provider
(same directory). You will see that the test fails because the header is not correctly set.
@YOU54F , any insight on this one ?
Can I ask - why do you want to change the content type header of the provider? This is a fairly crucial header as far as matching is concerned, so overriding it is quite dangerous.
I'm not sure why we can't override the content-type header here, it's probably something to do with the way express works. You can certainly add others.
I don't have time to look into this specific issue. If you really need to do this and can't find a way to modify the current Pact JS code / use the express interface provided, I'd suggest you add a test only middleware on your provider code base and modify it there (or roll your own proxy).
That's indeed a good question ! In fact, this api will run in an AWS lambda. Consequently, I deploy the code on localstack during my CI step and run the Pact contract on it. The problem is that by default AWS Lambda returns this header correctly when you don't specify anything BUT an AWS lambda on localstack doesn't. I don't want to modify the code for a test purpose. Fortunately, I opened a ticket on the localstack project and they solved the issue on their side.
Glad to hear! I'm going to leave this open as a feature request in the meantime.
I have my consumer in java and my provider in Nestjs, the problem is with the content type uppercase, any idea how to solve that?

@nicolasariza this is a different issue to the one reported here. You could add a regex matcher on the response header that was case insensitive or ignored the charset component.