pact-foundation/pact-js

Incorrect response with header Content-Type application/xml in PactV3

donche129 opened this issue · 2 comments

Software versions

  • OS: Mac OSX 13.5
  • Consumer Pact library: Pact JS v12.0.0
  • Node Version: v18.16.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

Able to set the expected response Content-Type header to application/xml and be able to match the response to valid xml with the complete data.

Actual behaviour

When setting the interactions expected response Content-Type header to application/xml, the returned response from the pact mock server is always only <?xml version='1.0'?> and does not include the rest of the xml data.

Steps to reproduce

Example repo here: https://github.com/donche129/pact-xml-issue

Example test code:

import chai from "chai";
import chaiAsPromised from "chai-as-promised";
import {
  SpecificationVersion,
  PactV3,
  LogLevel,
  MatchersV3,
} from "@pact-foundation/pact";
import axios from "axios";

chai.use(chaiAsPromised);

const { expect } = chai;

describe("Pact Consumer Test", () => {
  const pact = new PactV3({
    consumer: "myconsumer",
    provider: "myprovider",
    spec: SpecificationVersion.SPECIFICATION_VERSION_V3,
    logLevel: (process.env.LOG_LEVEL as LogLevel) || "debug",
    dir: "./logs"
  });

  const xmlBody = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <images>
    <sized>
      <service id="1234">
        <image lastModified="00:00:00 1 Jan 2023" placement="square:x-small">https://google.com/image1.svg</image>
        <image lastModified="00:00:00 1 Jan 2023" placement="square:small">https://google.com/imag2.svg</image>
      </service>
    </sized>
    <presentationmap>
      <service id="1234">
        <image lastModified="00:00:00 1 Jan 2023" placement="PlacementOne">https://google.com/image3.svg</image>
        <image lastModified="00:00:00 1 Jan 2023" placement="PlacementTwo">https://google.com/image4.svg</image>
      </service>
    </presentationmap>
  </images>`

  it("creates a pact to verify", async () => {
    await pact
      .addInteraction({
        uponReceiving: "a request for a foo",
        withRequest: {
          method: "GET",
          path: "/",
          headers: {
            Accept: ["application/xml", "text/xml"],
          },
        },
        willRespondWith: {
          status: 200,
          headers: {
            "Content-Type": "application/xml; charset=utf-8",
          },
          body: MatchersV3.like(xmlBody),
        },
      })
      .executeTest(async (mockserver) => {
        const res = await axios.request({
          baseURL: mockserver.url,
          method: "GET",
          url: "/",
          headers: {
            Accept: ["application/xml", "text/xml"],
          },
        });

        console.log({ res: res.data })

        expect(res.data).to.equal(xmlBody);
      });
  });
});

Relevant log files


> pact-repro-project@1.0.0 test:consumer
> jest consumer.spec.ts

[17:37:41.279] INFO (40827): 0.4.0: pact native library successfully found, and the correct version
2023-08-11T21:37:41.295330Z DEBUG ThreadId(01) pact_plugin_driver::catalogue_manager: Updated catalogue entries:
core/transport/http
core/transport/https
2023-08-11T21:37:41.310545Z DEBUG ThreadId(01) pact_plugin_driver::catalogue_manager: Updated catalogue entries:
core/content-generator/binary
core/content-generator/json
core/content-matcher/json
core/content-matcher/multipart-form-data
core/content-matcher/text
core/content-matcher/xml
2023-08-11T21:37:41.310633Z DEBUG ThreadId(01) pact_plugin_driver::catalogue_manager: Updated catalogue entries:
core/matcher/v1-equality
core/matcher/v2-max-type
core/matcher/v2-min-type
core/matcher/v2-minmax-type
core/matcher/v2-regex
core/matcher/v2-type
core/matcher/v3-content-type
core/matcher/v3-date
core/matcher/v3-datetime
core/matcher/v3-decimal-type
core/matcher/v3-includes
core/matcher/v3-integer-type
core/matcher/v3-null
core/matcher/v3-number-type
core/matcher/v3-time
core/matcher/v4-array-contains
core/matcher/v4-equals-ignore-order
core/matcher/v4-max-equals-ignore-order
core/matcher/v4-min-equals-ignore-order
core/matcher/v4-minmax-equals-ignore-order
core/matcher/v4-not-empty
core/matcher/v4-semver
2023-08-11T21:37:41.310925Z DEBUG ThreadId(01) pact_mock_server::mock_server: Started mock server on 127.0.0.1:53518
2023-08-11T21:37:41.322235Z DEBUG tokio-runtime-worker hyper::proto::h1::io: parsed 6 headers
2023-08-11T21:37:41.322286Z DEBUG tokio-runtime-worker hyper::proto::h1::conn: incoming body is empty
2023-08-11T21:37:41.322332Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Creating pact request from hyper request
2023-08-11T21:37:41.322360Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Extracting query from uri /
2023-08-11T21:37:41.322442Z  INFO tokio-runtime-worker pact_mock_server::hyper_server: Received request HTTP Request ( method: GET, path: /, query: None, headers: Some({"accept": ["application/xml", "text/xml"], "accept-encoding": ["gzip", "compress", "deflate", "br"], "user-agent": ["axios/1.4.0"], "host": ["127.0.0.1:53518"], "connection": ["close"]}), body: Empty )
2023-08-11T21:37:41.322541Z  INFO tokio-runtime-worker pact_matching: comparing to expected HTTP Request ( method: GET, path: /, query: None, headers: Some({"Accept": ["application/xml", "text/xml"]}), body: Missing )
2023-08-11T21:37:41.322563Z DEBUG tokio-runtime-worker pact_matching:      body: ''
2023-08-11T21:37:41.322580Z DEBUG tokio-runtime-worker pact_matching:      matching_rules: MatchingRules { rules: {HEADER: MatchingRuleCategory { name: HEADER, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }} }
2023-08-11T21:37:41.322604Z DEBUG tokio-runtime-worker pact_matching:      generators: Generators { categories: {} }
2023-08-11T21:37:41.322658Z DEBUG tokio-runtime-worker pact_matching::matchers: String -> String: comparing '/' to '/' ==> true cascaded=false matcher=Equality
2023-08-11T21:37:41.322690Z DEBUG tokio-runtime-worker pact_matching: expected content type = '*/*', actual content type = '*/*'
2023-08-11T21:37:41.322739Z DEBUG tokio-runtime-worker pact_matching: content type header matcher = 'RuleList { rules: [], rule_logic: And, cascaded: false }'
2023-08-11T21:37:41.322827Z DEBUG tokio-runtime-worker pact_matching: --> Mismatches: []
2023-08-11T21:37:41.322898Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Test context = {"mockServer": Object {"href": String("http://127.0.0.1:53518"), "port": Number(53518)}}
2023-08-11T21:37:41.322940Z  INFO tokio-runtime-worker pact_mock_server::hyper_server: Request matched, sending response HTTP Response ( status: 200, headers: Some({"Content-Type": ["application/xml; charset=utf-8"]}), body: Present(21 bytes, application/xml) )
2023-08-11T21:37:41.322965Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server:      body: '<?xml version='1.0'?>'
2023-08-11T21:37:41.323046Z DEBUG tokio-runtime-worker hyper::proto::h1::io: flushed 387 bytes
  console.log
    { res: "<?xml version='1.0'?>" }

      at consumer.spec.ts:69:17

2023-08-11T21:37:41.353606Z DEBUG ThreadId(01) pact_matching::metrics: Could not get the tokio runtime, will not send metrics - there is no reactor running, must be called from the context of a Tokio 1.x runtime
2023-08-11T21:37:41.353653Z DEBUG ThreadId(01) pact_mock_server::server_manager: Shutting down mock server with ID a14896b9-c907-4c2a-b302-340be7050f6e - MockServerMetrics { requests: 1 }
2023-08-11T21:37:41.353688Z DEBUG ThreadId(01) pact_mock_server::mock_server: Mock server a14896b9-c907-4c2a-b302-340be7050f6e shutdown - MockServerMetrics { requests: 1 }
2023-08-11T21:37:41.353705Z DEBUG tokio-runtime-worker hyper::server::shutdown: signal received, starting graceful shutdown

Fixed a typo in the behavior description to make it clear the response is somehow returning only <?xml version='1.0'?> instead of the complete XML. @mefellows apologies if there was any confusion!