pact-foundation/pact-js

Request validation fails after upgrade to v12

pieter-stek opened this issue ยท 6 comments

Software versions

  • OS: Windows 11
  • Consumer Pact library: PactJS 12.1.0
  • Provider Pact library: au.com.dius.pact.provider:junit5:4.6.1
  • Node Version: 16.18.0

Issue Checklist

Please confirm the following:

  • I have upgraded to the latest
  • I have the read the FAQs in the Readme
  • I have triple checked, that there are no unhandled promises in my code and have read the section on intermittent test failures
  • I have set my log level to debug and attached a log file showing the complete request/response cycle
  • For bonus points and virtual high fives, I have created a reproduceable git repository (see below) to illustrate the problem

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

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.

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

๐Ÿ‘‹ 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.

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',
          },
        }
      );
    });
  });
});

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.

Thanks for confirming.