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.63k stars 347 forks source link

[V3] pact matchers on namespaced xml elments fail on provider test #632

Open dpakach opened 3 years ago

dpakach commented 3 years ago

Software versions

Please provide at least OS and version of pact-js

Issue Checklist

Please confirm the following:

Expected behaviour

Matchers on namespaced xml should work eg.

       new XmlBuilder('1.0', '', 'root').build(root => {
          root.setAttributes({"xmlns:h":"http://www.w3.org/TR/html4/"})
          root.appendElement('data', '', data => {
            data.appendElement("h:data", "", MatchersV3.string("random"))
              .appendElement('id', '', MatchersV3.fromProviderState("${id}", "42"))
        })

when the server handler is

<?xml version="1.0" encoding="UTF-8"?>
  <root xmlns:h="http://www.w3.org/TR/html4/">
      <data>
          <h:data>testData</h:data>
          <id>42</id>
      </data>
  </root>

Actual behaviour

The provider test fail with this error

Failures:

1) Verifying a pact between TransactionService and AccountService Given set id - a request to get the plain data returns a response which 
    1.1) has a matching body
           $.root.data.http://www.w3.org/TR/html4/:data.#text -> Expected 'random' to be equal to 'testData'

Steps to reproduce

See the bug reproduced in https://github.com/dpakach/pact-js/pull/2 Test on https://github.com/dpakach/pact-js/pull/2/files#diff-656f935c93a4d4ea910cb4961f3463c434eb6ea7cbe8d51e2c7b0b3fd4c5ae0cR68

individual-it commented 3 years ago

I believe this will be fixed by #636 / #672

individual-it commented 3 years ago

@dpakach please retest with beta36 and close if the issue is solved

dpakach commented 3 years ago

@individual-it the issue is not fixed yet, the tests doesn't catch it because the interaction names are same in following tests https://github.com/pact-foundation/pact-js/blob/feat/v3.0.0/examples/v3/provider-state-injected/consumer/transaction-service.test.js#L79 https://github.com/pact-foundation/pact-js/blob/feat/v3.0.0/examples/v3/provider-state-injected/consumer/transaction-service.test.js#L105

To reproduce the issue, change L105 in the file to a request to get the XML data and run the tests



Failures:

1) Verifying a pact between TransactionService and AccountService Given set id - a request to get the xml data returns a response which 
    1.1) has a matching body
           $.root.data.http://www.w3.org/TR/html4/:data.#text -> Expected 'random' to be equal to 'testData'
individual-it commented 3 years ago

I think we have multiple issues here:

  1. the matcher in the pact json file needs to have the identifier of the namespace mentioned not the name this json works:
       "response": {
        "body": "<?xml version='1.0'?><root xmlns:h='myns'><data><h:data>random</h:data><id>42</id></data></root>",
        "headers": {
          "Content-Type": "application/xml; charset=utf-8"
        },
        "matchingRules": {
          "body": {
            "$.root.data.myns:data.#text": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },

    this does not work:

      "response": {
        "body": "<?xml version='1.0'?><root xmlns:h='myns'><data><h:data>random</h:data><id>42</id></data></root>",
        "headers": {
          "Content-Type": "application/xml; charset=utf-8"
        },
        "matchingRules": {
          "body": {
            "$.root.data.h:data.#text": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },

    only difference is $.root.data.myns:data.#text" vs. "$.root.data.h:data.#text"

  2. namespace identifiers cannot contain dots <?xml version='1.0'?><root xmlns:h='my.ns'><data><h:data>random</h:data><id>42</id></data></root> would become $.root.data.my.ns:data.#text , but the dot is used to seperate the elements and by that breaks the system
individual-it commented 3 years ago

this notation works $.root.data['my.ns:data'].#text also with complex namespaces "$.root.data['http://www.w3.org/TR/html4/:data'].#text"

this is also the notation pact-reference uses e.g.: https://github.com/pact-foundation/pact-reference/blob/ebb11df0da1e8424fc5853b953b724ffb0e87e48/rust/pact_matching/src/xml.rs#L1020

individual-it commented 3 years ago

I think the namespaces are not taken in account at all xml.rs:create_element_from_json has a namespace attribute https://github.com/pact-foundation/pact-js/blob/34cf2f56c29f3c4d80ed30e7c6eecbe28082ec02/native/src/xml.rs#L55 but its not used and when called it set to an empty hashmap https://github.com/pact-foundation/pact-js/blob/34cf2f56c29f3c4d80ed30e7c6eecbe28082ec02/native/src/xml.rs#L25

individual-it commented 3 years ago

more debugging: pact-reference seems not to understand that the matcher is attached to that element. The element from the XML comes with resolved namespace, but in the matching rule only the name-space name is mentioned so it does not join those [2021-06-04T04:56:21Z TRACE pact_matching::models::matchingrules] matcher_is_defined: for category body and path ["$", "root", "data", "http://www.w3.org/TR/html4/:data", "#text"] -> false