How are you verifying it? With Node or another lan...
# pact-js
m
How are you verifying it? With Node or another language?
t
With python, I use pact-python there. I installed it with:
Copy code
pip install pact-python
m
Yep that's the problem. The logs should specify it's verying a pact with a different spec and/or warn about matchers it doesn't understand
I'm on mobile, but you should be able to set the spec version on the constructor to V2 (there should be a type defined for that)
👀 1
It defaults to V3
t
Now I see that the pact file has pactSpecification v2 but I still have the same problem on the provider’s side. Also, I can see that matching rules were updated in the pact. It’s my pact right now:
Copy code
{
  "consumer": {
    "name": "consumer"
  },
  "interactions": [
    {
      "description": "a request to get all events",
      "providerState": "I can get all events",
      "request": {
        "method": "GET",
        "path": "/events",
        "query": "limit=10&name=&offset=0"
      },
      "response": {
        "body": {
          "items": [
            {
              "id": "1cb9eb9e",
              "name": "test event 1",
              "teamId": "dummy_tid"
            }
          ],
          "totalCount": 1
        },
        "headers": {
          "Content-Type": "application/json"
        },
        "matchingRules": {
          "$.body.items": {
            "match": "type"
          },
          "$.body.totalCount": {
            "match": "type"
          }
        },
        "status": 200
      }
    }
  ],
  "metadata": {
    "pact-js": {
      "version": "10.1.4"
    },
    "pactRust": {
      "ffi": "0.3.11",
      "models": "0.4.5"
    },
    "pactSpecification": {
      "version": "2.0.0"
    }
  },
  "provider": {
    "name": "provider"
  }
}
Also, I tried to change the
pactSpecification
version manually, just put 2 instead of 3. I could see the discrepancies between actual and expected results after test run, but it didn’t apply matchers 😞
m
You can't do that, the format of the JSON file and matchers are different. You need to generate it from code
What's the new error for the proper v2 generated pact look like? Please provide the whole log output so we can check for warnings etc
t
There is my full log with failures:
Copy code
Verifying a pact between consumer and provider
  Given I can get all events
    a request to get all events
      with GET /events?limit=10&name=&offset=0
        returns a response which
WARN: Skipping set up for provider state 'I can get all events' for consumer 'consumer' as there is no --provider-states-setup-url specified.
          has status code 200
          has a matching body (FAILED - 1)
          includes headers
            "Content-Type" which equals "application/json"


Failures:

  1) Verifying a pact between consumer and provider Given I can get all events a request to get all events with GET /events?limit=10&name=&offset=0 returns a response which has a matching body
     Failure/Error: expect(response_body).to match_term expected_response_body, diff_options, example

       Actual: {"items":[{"name":"test event da","start":"2021-02-21T11:42:21.954913+00:00","end":"2022-11-25T11:42:21.954913+00:00","ventureDuration":120,"location":"online","focus":[],"technology":[],"prerequisites":[],"challenges":[],"landingPage":null,"privacy":"private","branding":{"themeImage":"<https://www.arabnews.com/sites/default/files/main-image/2019/02/11/1456561-87422913.jpg>"},"testId":null,"id":"d5ac5cd4","teamId":"dummy_tid","createdAt":"2022-09-29T17:45:07.613494+00:00","updatedAt":"2022-09-29T17:45:07.613502+00:00","publishedAt":null,"status":"UNPUBLISHED","lifecycle":"STARTED","reportUrl":null},{"name":"test event dd","start":"2021-01-21T11:42:21.954913+00:00","end":"2022-10-25T11:42:21.954913+00:00","ventureDuration":120,"location":"online","focus":[],"technology":[],"prerequisites":[],"challenges":[],"landingPage":null,"privacy":"private","branding":{"themeImage":"<https://www.arabnews.com/sites/default/files/main-image/2019/02/11/1456561-87422913.jpg>"},"testId":null,"id":"1cb9eb9e","teamId":"dummy_tid","createdAt":"2022-09-21T15:44:43.831196+00:00","updatedAt":"2022-09-21T15:44:43.831203+00:00","publishedAt":null,"status":"UNPUBLISHED","lifecycle":"STARTED","reportUrl":null}],"totalCount":2}

       Diff
       --------------------------------------
       Key: - is expected 
            + is actual 
       Matching keys and values are not shown

        {
          "items": [
            ... ,
       -    Pact::UnexpectedIndex,
       +    Hash,
          ]
        }

       Description of differences
       --------------------------------------
       * Actual array is too long and should not contain a Hash at $.items[1]

1 interaction, 1 failure

Failed interactions:

PACT_DESCRIPTION='a request to get all events' PACT_PROVIDER_STATE='I can get all events' /Users/tatiana/projects/events/.venv/bin/pact-verifier --provider-base-url=<http://localhost:8000> --pact-url=./pacts/consumer-provider.json # A request to get all events given I can get all events
t
The consumer tests looks like:
Can you share the
import
statements from your tests?
t
Sure, I use the following imports for my consumer test
Copy code
import React from 'react';
import { Provider } from 'react-redux';
import { renderHook } from '@testing-library/react-hooks'
import path from "path";
import { PactV3, SpecificationVersion } from '@pact-foundation/pact';
import { like, eachLike } from '@pact-foundation/pact/src/v3/matchers';
import { useGetEventQuery, useGetEventsQuery } from "../../../src/services/events";
import { makeStore } from "../../../src/redux";
Just in case, I am running the provider-verifier as:
Copy code
pact-verifier --provider-base-url=<http://localhost:8000> --pact-url=./pacts/consumer-provider.json
Please let me know if you have any ideas what might be wrong
t
I don’t know, sorry. The pact test looks right to me. I’m not sure what Hash means in the context of this error message- one for @uglyog, maybe
m
Hi Tatiana. We’re probably at diminishing returns it terms of asking questions/responses. To be able to help now I think we’ll need an example repository that demonstrates the issue
It looks like the Ruby verifier to me (
Hash
is a Ruby class). The consumer was generated by the Rust core, the provider looks like a Python one via the Ruby core.
t
Ah, right. Either way, the error message is not as helpful as it could be
t
Unfortunately, I cannot share a repository, it’s a private one :( And yes, provider uses Python and FastApi. Generally, do you know why such error might happen? Might be the problem with nested matchers? Also, I saw this answer in the Internet, that it might happen that the received array is longer than was expected. But I’m not sure that’s my case. Maybe I can send you the whole response from my Api, maybe it can help somehow?
just in case this is full response from my API, when I am trying to get all events:
Copy code
{
  "items": [
    {
      "name": "test event dd",
      "start": "2021-01-21T11:42:21.954913+00:00",
      "end": "2022-10-25T11:42:21.954913+00:00",
      "ventureDuration": 120,
      "location": "online",
      "focus": [],
      "technology": [],
      "prerequisites": [],
      "challenges": [],
      "landingPage": null,
      "privacy": "private",
      "branding": {
        "themeImage": "<https://www.arabnews.com/sites/default/files/main-image/2019/02/11/1456561-87422913.jpg>"
      },
      "testId": null,
      "id": "1cb9eb9e-4529-41da-bb51-d92c5649ebca",
      "teamId": "dummy_tid",
      "createdAt": "2022-09-21T15:44:43.831196+00:00",
      "updatedAt": "2022-09-21T15:44:43.831203+00:00",
      "publishedAt": null,
      "status": "UNPUBLISHED",
      "lifecycle": "STARTED",
      "reportUrl": null
    },
    {
      "name": "test event dd",
      "start": "2021-01-21T11:42:21.954913+00:00",
      "end": "2022-10-25T11:42:21.954913+00:00",
      "ventureDuration": 120,
      "location": "online",
      "focus": [],
      "technology": [],
      "prerequisites": [],
      "challenges": [],
      "landingPage": null,
      "privacy": "private",
      "branding": {
        "themeImage": "<https://www.arabnews.com/sites/default/files/main-image/2019/02/11/1456561-87422913.jpg>"
      },
      "testId": null,
      "id": "1cb9eb9e-4529-41da-bb51-d92c5649ebcd",
      "teamId": "dummy_tid",
      "createdAt": "2022-09-21T15:44:43.831196+00:00",
      "updatedAt": "2022-09-21T15:44:43.831203+00:00",
      "publishedAt": null,
      "status": "UNPUBLISHED",
      "lifecycle": "STARTED",
      "reportUrl": null
    }
  ],
  "totalCount": 2
}
if it matters, the provider test passes with the current set up if there is just one record in the DB
if all above is still useless without the repo, please let me know 🙏
t
Just to confirm, this is a pact file that is generated by Pact-JS, without modification?
You mentioned above about manually editing the pact file - you’re not supposed to do this, and it might result in unexpected behaviour
Also, the pact file doesn’t look like it should for the test you gave - it doesn’t look like the
eachLike
matcher is being applied. Are you sure you are testing with the right pact file? Are you sure another test isn’t updating it with different data?
t
Just to confirm, this is a pact file that is generated by Pact-JS, without modification?
Yes, it was generated with pact-js after test run. I didn’t change anything there manually. I generated the pact and shared it with my provider. Provider uses Python and fast api.
Yes, I am sure that I use a correct pact file. And I don’t update any data within my tests, I just test GET endpoints for now.
Maybe I use matchers incorrectly in my Consumer test?
Also, maybe there is a workaround how I can test GET all events? I was thinking to use provider states and create a consumer test without matchers, it should work in pipeline because the DB will be empty every time. But if you want to run the contract test locally you might have existing data in DB and test will crash then….
t
@Matt (pactflow.io / pact-js / pact-go) I put the example above in a repo here: https://github.com/TimothyJones/jest-pact/commit/2a6ab176f353011e882050ad077de1cf9d2db274
It generates the following pact file, which (if I am reading the spec correctly) is not correct
Copy code
{
  "consumer": {
    "name": "MyConsumer"
  },
  "interactions": [
    {
      "description": "A get request to get a pet 1845563262948980200",
      "providerState": "A pet 1845563262948980200 exists",
      "request": {
        "headers": {
          "api_key": "[]"
        },
        "method": "GET",
        "path": "/v2/pet/1845563262948980200"
      },
      "response": {
        "body": {
          "items": [
            {
              "id": "1cb9eb9e",
              "name": "test event 1",
              "teamId": "dummy_tid"
            }
          ]
        },
        "headers": {
          "Content-Type": "application/json"
        },
        "matchingRules": {
          "$.body.items": {
            "match": "type"
          }
        },
        "status": 200
      }
    },
    {
      "description": "A get request to get a pet 1845563262948980200",
      "providerState": "No pets exist",
      "request": {
        "headers": {
          "api_key": "[]"
        },
        "method": "GET",
        "path": "/v2/pet/1845563262948980200"
      },
      "response": {
        "status": 404
      }
    }
  ],
  "metadata": {
    "pact-js": {
      "version": "10.1.4"
    },
    "pactRust": {
      "ffi": "0.3.12",
      "models": "0.4.5"
    },
    "pactSpecification": {
      "version": "2.0.0"
    }
  },
  "provider": {
    "name": "pactWith v3"
  }
}
The problem is this bit:
Copy code
"matchingRules": {
          "$.body.items": {
            "match": "type"
          }
        },
which is supposed to have a
min
item
ah, right! The problem is that you have to use the V2 matcher
Right - yes. If you use
spec: SpecificationVersion.SPECIFICATION_VERSION_V2,
, then it still generates the wrong pact file.
in order to get the right results, you have to import the old
eachLike
matcher from
dsl/matchers
👀 1
That will fix your problem @Tatiana
👀 1
t
yes, I am using spec, my setup looks like:
Copy code
const provider = new PactV3({
  dir: path.resolve(process.cwd(), 'contract-tests/pacts'),
  consumer: 'mousedeer-events-consumer',
  provider: 'eventhub-provider',
  port: 8001,
  spec: SpecificationVersion.SPECIFICATION_VERSION_V2
});
let me try
it helped!!!
👍 1
t
@Matt (pactflow.io / pact-js / pact-go) - I’m not sure what the best fix is here. We have
PactV3
which takes the V3 matchers, but it takes an option called
spec
where you can tell it to use V2, but when you do that you need to use the V2 matchers. But they’re not called V2 matchers, they’re called
Matchers
. And, it’s not obvious that the V3 matchers might not be appropriate to pass to PactV3.
Really this should be a concern of the core. The core should just figure it out for you, and users don’t have to know.
t
It works as I expected. Thank you very much! you helped me to understand pact and how it works, you helped me a lot!
t
Anyway, there’s the reproducible example 🙂
👍 1
You’re welcome! Sorry about this, but also thanks for highlighting an area where we can improve the interface
🙌 1
t
Really this should be a concern of the core. The core should just figure it out for you, and users don’t have to know.
+1, totally agree 👍
m
> Really this should be a concern of the core. The core should just figure it out for you, and users don’t have to know.
+1 also
BUT, the
eachLike
for both V2 and V3 should actually be the same (they are both going to the same core!). I’m surprised that not passing in
min
to that matcher changes the behaviour. This makes me believe it probably never worked in the way it was intended (you can look at the definition of both variants of the code).
t
It’s different according to the spec, I think
m
Sure, maybe. But the passing of a matcher to the core should be spec agnostic is my point (i.e. it’s a bug in the rust core, or at least confusing)
t
Yes.
m
or put another way - the way matchers are passed to the rust core as it stands is deliberately agnostic to the spec version. The serialisation to the pact file of the matcher is based on the spec version
I think we’re saying the same thing?
In any case, THANK YOU. This exposed / reminded me of 2 problems that need to be solved
🙌 1
party parrot 1
t
Copy code
'pact:matcher:type': 'type',
^ This means “shaped like this”
it needs the
min
to know that it means “repeatedly shaped like this”
m
I thought Ron actually created that matcher 🤔 But any case, will look tomorow
(reminder set, thank you!)
👍 1
I’ll look tomorrow, I’m distracting myself from the presentation I’m supposed to be delivering at 7am 😬
t
Good luck!
🙏 1