Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify Content-Type header of the response when verifying HTTP provider #870

Open
avanmalleghem opened this issue May 30, 2022 · 8 comments
Labels
enhancement Indicates new feature requests

Comments

@avanmalleghem
Copy link

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/pact@9.17.3
  • Provider Pact library: @pact-foundation/pact@9.17.3
  • 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): pact-node@10.17.2: 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): pact-node@10.17.2: 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): pact-node@10.17.2: I, [2022-05-30T18:41:18.184099 #52326]  INFO -- response: Status 200

[2022-05-30 16:41:18.184 +0000] DEBUG (52303 on NGG568): pact-node@10.17.2: 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.

@avanmalleghem avanmalleghem added the bug Indicates an unexpected problem or unintended behavior label May 30, 2022
@YOU54F
Copy link
Member

YOU54F commented May 30, 2022

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(...)

@avanmalleghem
Copy link
Author

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 the pact-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.

@avanmalleghem
Copy link
Author

@YOU54F , any insight on this one ?

@mefellows
Copy link
Member

mefellows commented Jul 29, 2022

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).

@avanmalleghem
Copy link
Author

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.

@mefellows
Copy link
Member

Glad to hear! I'm going to leave this open as a feature request in the meantime.

@nicolasariza
Copy link

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?

image

@mefellows
Copy link
Member

@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.

@mefellows mefellows removed the bug Indicates an unexpected problem or unintended behavior label Mar 3, 2023
@mefellows mefellows added the enhancement Indicates new feature requests label Aug 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Indicates new feature requests
Projects
Status: New Issue
Development

No branches or pull requests

4 participants