pact-foundation / pact-js

JS version of Pact. Pact is a contract testing framework for HTTP APIs and non-HTTP asynchronous messaging systems.
https://pact.io
Other
1.59k stars 343 forks source link

Request validation fails after upgrade to v12 #1106

Open pieter-stek opened 11 months ago

pieter-stek commented 11 months ago

Software versions

Issue Checklist

Please confirm the following:

Expected behaviour

I updated PactJS from 11.0.2 to 12.1.0 and without any other changes the contract test started failing.

Actual behaviour

Mock server failed with the following mismatches:

        0) The following request was incorrect:

                PUT /picking-capacity/capacities/1

                      'Content-Type': Expected value 'application/json' at index 1 but was missing

Steps to reproduce

import { PactV3 } from "@pact-foundation/pact";
import axios from "axios";
import path from "path";

const provider = new PactV3({
    port: 8082,
    dir: path.resolve(process.cwd(), "pacts"),
    cors: true,
    pactfileWriteMode: "update",
    consumer: "application",
    provider: "applicationApi",
    logLevel: "debug",
  });

  describe("serviceUnderTest", () => {

    it("should validate PUT request", async () => {
      await provider.addInteraction({
        states: [{ description: "resource exists" }],
        uponReceiving: "update",
        withRequest: {
          method: "PUT",
          path: "/resource/1",
          headers: {
            "Content-Type": "application/json",
          },
          body: {},
        },
        willRespondWith: {
          status: 200,
          headers: {
            "Content-Type": "application/json",
          },
          body: {},
        },
      });

      await provider.executeTest(async () => {
        await axios.put("http://localhost:8082/resource/1", {});
      });
    });
  })

Relevant log files

pact-debug.log

mefellows commented 11 months ago

It looks like the core is expecting two headers, as can be seen by it expecting it at index 1 (not index 0, for 1 value for content-type header)

It received the right request:

2023-07-27T12:05:59.358269Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Creating pact request from hyper request
2023-07-27T12:05:59.358285Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Extracting query from uri /resource/1
2023-07-27T12:05:59.358364Z  INFO tokio-runtime-worker pact_mock_server::hyper_server: Received request PUT /resource/1
2023-07-27T12:05:59.358373Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server:
      ----------------------------------------------------------------------------------------
       method: PUT
       path: /resource/1
       query: None
       headers: Some({"host": ["localhost:8082"], "user-agent": ["axios/1.4.0"], "connection": ["keep-alive"], "accept": ["application/json", "text/plain", "*/*"], "content-length": ["8"], "content-type": ["application/json"], "accept-encoding": ["gzip", "compress", "deflate", "br"]})
       body: Present(8 bytes, application/json) '{"id":1}'
      ----------------------------------------------------------------------------------------

But you can see it's expecting two:

2023-07-27T12:04:10.904720Z  INFO tokio-runtime-worker pact_matching: comparing to expected HTTP Request ( method: PUT, path: /resource/1, query: None, headers: Some({"Content-Type": ["application/json", "application/json"]}), body: Present(8 bytes, application/json) )

It works in v0.4.5 of libpact_ffi so something between that and v0.4.6 is causing it. I'll continue to investigate.

mefellows commented 11 months ago

I've traced it to this commit. I'll raise this issue upstream to track it, thanks for reporting.

github-actions[bot] commented 11 months ago

👋 Hi! The 'smartbear-supported' label has just been added to this issue, which will create an internal tracking ticket in PactFlow's Jira (PACT-1219). We will use this to prioritise and assign a team member to this task. All activity will be public on this ticket. For now, sit tight and we'll update this ticket once we have more information on the next steps.

See our documentation for more information.

mefellows commented 11 months ago

I should say, in this case, I think because the Content-Type is a special header and it's defaulting to application/json (possibly why it's expecting two header values) you can simply omit that header from your expectations and things will work. It doesn't seem to affect other headers, which strengthens that theory. This test for example should work:

import { PactV3 } from '@pact-foundation/pact';
import axios from 'axios';
import path from 'path';

const provider = new PactV3({
  port: 8082,
  dir: path.resolve(process.cwd(), 'pacts'),
  cors: true,
  consumer: 'application',
  provider: 'applicationApi',
  logLevel: 'debug',
});

describe('serviceUnderTest', () => {
  it('should validate PUT request', async () => {
    await provider.addInteraction({
      states: [{ description: 'resource exists' }],
      uponReceiving: 'update',
      withRequest: {
        method: 'PUT',
        path: '/resource/1',
        headers: {
          Foo: 'bar',
        },
        body: {
          id: 1,
        },
      },
      willRespondWith: {
        status: 200,
        headers: {
          'Content-Type': 'application/json',
        },
        body: {
          id: 1,
        },
      },
    });

    await provider.executeTest(async () => {
      await axios.put(
        'http://localhost:8082/resource/1',
        {
          id: 1,
        },
        {
          headers: {
            Foo: 'bar',
          },
        }
      );
    });
  });
});
pieter-stek commented 11 months ago

I can confirm that if I omit the Content-Type header from the request expectation, the test succeeds. The corresponding provider tests also still succeed if I do that.

mefellows commented 11 months ago

Thanks for confirming.