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 348 forks source link

[V3] Mock server fails with mismatches when using fromProviderState in iterated interaction #604

Open saw-jan opened 3 years ago

saw-jan commented 3 years ago

We tried to implement fromProviderState in an interaction that is iterated in the test (see code below) but running consumer test fails with;

Error: Mock server failed with the following mismatches: 

      1) The following request was expected but not received: 
          Method: GET
          Path: /users
          Query String: id=2
          Headers:
            Accept: application/hal+json
            Authorization: Basic YWRtaW46YWRtaW4=

      2) The following request was expected but not received: 
          Method: GET
          Path: /users
          Query String: id=3
          Headers:
            Accept: application/hal+json
            Authorization: Basic YWRtaW46YWRtaW4=

From above , it seems that interaction with id 1 gets passed

Note: All interactions pass with other Matchers except fromProviderState

Code:

  // interaction
  const aGETUserInfoByIdQueryInteraction = (provider, id, username, email) => {
    return provider
      .given('user is created', { username, email })
      .uponReceiving('a GET request for user by id as query ' + id)
      .withRequest({
        method: 'GET',
        path: '/users',
        query: { id: MatchersV3.fromProviderState('\${id}', id.toString()) },
        headers: {
          ...client.basicAuth,
          Accept: 'application/hal+json',
        },
      })
      .willRespondWith({
        status: 200,
        headers: {
          'content-type': 'application/hal+json',
        },
        body: {
          id: MatchersV3.number(id),
          username: MatchersV3.string(username),
          email: MatchersV3.string(email),
        },
      });
  };

  // test
  it('using in a loop: "fromProviderState" as query', async () => {
    for (let user of users) {
      aGETUserInfoByIdQueryInteraction(
        provider,
        user.id,
        user.username,
        user.email
      );
    }
    return provider.executeTest(async () => {
      for (let user of users) {
        await client.getUserByIdQuery(user.id);
      }
    });
  });

LOG:

[2021-02-10T07:16:52Z DEBUG pact_js_v3] Initialising Pact native library version 0.0.6
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Request = Request ( method: GET, path: /users, query: Some({"id": ["1"]}), headers: Some({"Authorization": ["Basic YWRtaW46YWRtaW4="], "Accept": ["application/hal+json"]}), body: Missing )
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Request matching rules = MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Request generators = Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Response = Response ( status: 200, headers: Some({"content-type": ["application/hal+json"]}), body: Present(50 bytes, application/json) )
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Response matching rules = MatchingRules { rules: {"body": MatchingRuleCategory { name: "body", rules: {"$.email": RuleList { rules: [Type], rule_logic: And }, "$.username": RuleList { rules: [Type], rule_logic: And }, "$.id": RuleList { rules: [Number], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Response generators = Generators { categories: {} }
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Request = Request ( method: GET, path: /users, query: Some({"id": ["2"]}), headers: Some({"Authorization": ["Basic YWRtaW46YWRtaW4="], "Accept": ["application/hal+json"]}), body: Missing )
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Request matching rules = MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Request generators = Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Response = Response ( status: 200, headers: Some({"content-type": ["application/hal+json"]}), body: Present(48 bytes, application/json) )
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Response matching rules = MatchingRules { rules: {"body": MatchingRuleCategory { name: "body", rules: {"$.id": RuleList { rules: [Number], rule_logic: And }, "$.username": RuleList { rules: [Type], rule_logic: And }, "$.email": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Response generators = Generators { categories: {} }
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Request = Request ( method: GET, path: /users, query: Some({"id": ["3"]}), headers: Some({"Accept": ["application/hal+json"], "Authorization": ["Basic YWRtaW46YWRtaW4="]}), body: Missing )
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Request matching rules = MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Request generators = Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Response = Response ( status: 200, headers: Some({"content-type": ["application/hal+json"]}), body: Present(50 bytes, application/json) )
[2021-02-10T07:16:52Z DEBUG pact_js_v3] Response matching rules = MatchingRules { rules: {"body": MatchingRuleCategory { name: "body", rules: {"$.id": RuleList { rules: [Number], rule_logic: And }, "$.username": RuleList { rules: [Type], rule_logic: And }, "$.email": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52.522Z] DEBUG: pact@10.0.0-beta.30/72236 on JT101: Initialised native library 0.0.6

 RUNS  tests/pactV3.test.js
[2021-02-10T07:16:52Z DEBUG pact_mock_server::hyper_server] Creating pact request from hyper request
[2021-02-10T07:16:52Z INFO  pact_mock_server::hyper_server] Received request Request ( method: GET, path: /users, query: Some({"id": ["1"]}), headers: Some({"accept": ["application/hal+json"], "user-agent": ["axios/0.21.1"], "connection": ["close"], "host": ["127.0.0.1:1234"], "authorization": ["Basic YWRtaW46YWRtaW4="]}), body: Empty )
[2021-02-10T07:16:52Z INFO  pact_matching] comparing to expected Request ( method: GET, path: /users, query: Some({"id": ["1"]}), headers: Some({"Authorization": ["Basic YWRtaW46YWRtaW4="], "Accept": ["application/hal+json"]}), body: Missing )
[2021-02-10T07:16:52Z DEBUG pact_matching]      body: ''
[2021-02-10T07:16:52Z DEBUG pact_matching]      matching_rules: MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_matching]      generators: Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '/users' to '/users' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] expected content type = '*/*', actual content type = '*/*'
[2021-02-10T07:16:52Z DEBUG pact_matching] content type header matcher = 'None'
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '1' to '1' using Type
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing 'Basic YWRtaW46YWRtaW4=' to 'Basic YWRtaW46YWRtaW4=' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] --> Mismatches: []
[2021-02-10T07:16:52Z INFO  pact_matching] comparing to expected Request ( method: GET, path: /users, query: Some({"id": ["2"]}), headers: Some({"Authorization": ["Basic YWRtaW46YWRtaW4="], "Accept": ["application/hal+json"]}), body: Missing )
[2021-02-10T07:16:52Z DEBUG pact_matching]      body: ''
[2021-02-10T07:16:52Z DEBUG pact_matching]      matching_rules: MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_matching]      generators: Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '/users' to '/users' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] expected content type = '*/*', actual content type = '*/*'
[2021-02-10T07:16:52Z DEBUG pact_matching] content type header matcher = 'None'
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '2' to '1' using Type
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing 'Basic YWRtaW46YWRtaW4=' to 'Basic YWRtaW46YWRtaW4=' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] --> Mismatches: []
[2021-02-10T07:16:52Z INFO  pact_matching] comparing to expected Request ( method: GET, path: /users, query: Some({"id": ["3"]}), headers: Some({"Accept": ["application/hal+json"], "Authorization": ["Basic YWRtaW46YWRtaW4="]}), body: Missing )
[2021-02-10T07:16:52Z DEBUG pact_matching]      body: ''
[2021-02-10T07:16:52Z DEBUG pact_matching]      matching_rules: MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_matching]      generators: Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '/users' to '/users' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] expected content type = '*/*', actual content type = '*/*'
[2021-02-10T07:16:52Z DEBUG pact_matching] content type header matcher = 'None'
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '3' to '1' using Type
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing 'Basic YWRtaW46YWRtaW4=' to 'Basic YWRtaW46YWRtaW4=' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] --> Mismatches: []
[2021-02-10T07:16:52Z DEBUG pact_mock_server::hyper_server] Test context = {"mockServer": Object({"href": String("http://127.0.0.1:1234"), "port": Number(1234)})}
[2021-02-10T07:16:52Z INFO  pact_mock_server::hyper_server] Request matched, sending response Response ( status: 200, headers: Some({"content-type": ["application/hal+json"]}), body: Present(50 bytes, application/json) )
[2021-02-10T07:16:52Z DEBUG pact_mock_server::hyper_server]      body: '{"email":"adam@mail.com","id":1,"username":"Adam"}'
[2021-02-10T07:16:52Z DEBUG pact_mock_server::hyper_server] Creating pact request from hyper request
[2021-02-10T07:16:52Z INFO  pact_mock_server::hyper_server] Received request Request ( method: GET, path: /users, query: Some({"id": ["2"]}), headers: Some({"accept": ["application/hal+json"], "authorization": ["Basic YWRtaW46YWRtaW4="], "connection": ["close"], "host": ["127.0.0.1:1234"], "user-agent": ["axios/0.21.1"]}), body: Empty )
[2021-02-10T07:16:52Z INFO  pact_matching] comparing to expected Request ( method: GET, path: /users, query: Some({"id": ["1"]}), headers: Some({"Authorization": ["Basic YWRtaW46YWRtaW4="], "Accept": ["application/hal+json"]}), body: Missing )
[2021-02-10T07:16:52Z DEBUG pact_matching]      body: ''
[2021-02-10T07:16:52Z DEBUG pact_matching]      matching_rules: MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_matching]      generators: Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '/users' to '/users' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] expected content type = '*/*', actual content type = '*/*'
[2021-02-10T07:16:52Z DEBUG pact_matching] content type header matcher = 'None'
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '1' to '2' using Type
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing 'Basic YWRtaW46YWRtaW4=' to 'Basic YWRtaW46YWRtaW4=' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] --> Mismatches: []
[2021-02-10T07:16:52Z INFO  pact_matching] comparing to expected Request ( method: GET, path: /users, query: Some({"id": ["2"]}), headers: Some({"Authorization": ["Basic YWRtaW46YWRtaW4="], "Accept": ["application/hal+json"]}), body: Missing )
[2021-02-10T07:16:52Z DEBUG pact_matching]      body: ''
[2021-02-10T07:16:52Z DEBUG pact_matching]      matching_rules: MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_matching]      generators: Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '/users' to '/users' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] expected content type = '*/*', actual content type = '*/*'
[2021-02-10T07:16:52Z DEBUG pact_matching] content type header matcher = 'None'
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '2' to '2' using Type
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing 'Basic YWRtaW46YWRtaW4=' to 'Basic YWRtaW46YWRtaW4=' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] --> Mismatches: []
[2021-02-10T07:16:52Z INFO  pact_matching] comparing to expected Request ( method: GET, path: /users, query: Some({"id": ["3"]}), headers: Some({"Accept": ["application/hal+json"], "Authorization": ["Basic YWRtaW46YWRtaW4="]}), body: Missing )
[2021-02-10T07:16:52Z DEBUG pact_matching]      body: ''
[2021-02-10T07:16:52Z DEBUG pact_matching]      matching_rules: MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_matching]      generators: Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '/users' to '/users' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] expected content type = '*/*', actual content type = '*/*'
[2021-02-10T07:16:52Z DEBUG pact_matching] content type header matcher = 'None'
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '3' to '2' using Type
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing 'Basic YWRtaW46YWRtaW4=' to 'Basic YWRtaW46YWRtaW4=' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] --> Mismatches: []
[2021-02-10T07:16:52Z DEBUG pact_mock_server::hyper_server] Test context = {"mockServer": Object({"href": String("http://127.0.0.1:1234"), "port": Number(1234)})}
[2021-02-10T07:16:52Z INFO  pact_mock_server::hyper_server] Request matched, sending response Response ( status: 200, headers: Some({"content-type": ["application/hal+json"]}), body: Present(50 bytes, application/json) )
[2021-02-10T07:16:52Z DEBUG pact_mock_server::hyper_server]      body: '{"email":"adam@mail.com","id":1,"username":"Adam"}'
[2021-02-10T07:16:52Z DEBUG pact_mock_server::hyper_server] Creating pact request from hyper request
[2021-02-10T07:16:52Z INFO  pact_mock_server::hyper_server] Received request Request ( method: GET, path: /users, query: Some({"id": ["3"]}), headers: Some({"connection": ["close"], "host": ["127.0.0.1:1234"], "accept": ["application/hal+json"], "authorization": ["Basic YWRtaW46YWRtaW4="], "user-agent": ["axios/0.21.1"]}), body: Empty )
[2021-02-10T07:16:52Z INFO  pact_matching] comparing to expected Request ( method: GET, path: /users, query: Some({"id": ["1"]}), headers: Some({"Authorization": ["Basic YWRtaW46YWRtaW4="], "Accept": ["application/hal+json"]}), body: Missing )
[2021-02-10T07:16:52Z DEBUG pact_matching]      body: ''
[2021-02-10T07:16:52Z DEBUG pact_matching]      matching_rules: MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_matching]      generators: Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '/users' to '/users' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] expected content type = '*/*', actual content type = '*/*'
[2021-02-10T07:16:52Z DEBUG pact_matching] content type header matcher = 'None'
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '1' to '3' using Type
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing 'Basic YWRtaW46YWRtaW4=' to 'Basic YWRtaW46YWRtaW4=' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] --> Mismatches: []
[2021-02-10T07:16:52Z INFO  pact_matching] comparing to expected Request ( method: GET, path: /users, query: Some({"id": ["2"]}), headers: Some({"Authorization": ["Basic YWRtaW46YWRtaW4="], "Accept": ["application/hal+json"]}), body: Missing )
[2021-02-10T07:16:52Z DEBUG pact_matching]      body: ''
[2021-02-10T07:16:52Z DEBUG pact_matching]      matching_rules: MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_matching]      generators: Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '/users' to '/users' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] expected content type = '*/*', actual content type = '*/*'
[2021-02-10T07:16:52Z DEBUG pact_matching] content type header matcher = 'None'
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '2' to '3' using Type
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing 'Basic YWRtaW46YWRtaW4=' to 'Basic YWRtaW46YWRtaW4=' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] --> Mismatches: []
[2021-02-10T07:16:52Z INFO  pact_matching] comparing to expected Request ( method: GET, path: /users, query: Some({"id": ["3"]}), headers: Some({"Accept": ["application/hal+json"], "Authorization": ["Basic YWRtaW46YWRtaW4="]}), body: Missing )
[2021-02-10T07:16:52Z DEBUG pact_matching]      body: ''
[2021-02-10T07:16:52Z DEBUG pact_matching]      matching_rules: MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T07:16:52Z DEBUG pact_matching]      generators: Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '/users' to '/users' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] expected content type = '*/*', actual content type = '*/*'
[2021-02-10T07:16:52Z DEBUG pact_matching] content type header matcher = 'None'
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing '3' to '3' using Type
[2021-02-10T07:16:52Z DEBUG pact_matching::matchers] String -> String: comparing 'Basic YWRtaW46YWRtaW4=' to 'Basic YWRtaW46YWRtaW4=' using Equality
[2021-02-10T07:16:52Z DEBUG pact_matching] --> Mismatches: []
[2021-02-10T07:16:52Z DEBUG pact_mock_server::hyper_server] Test context = {"mockServer": Object({"href": String("http://127.0.0.1:1234"), "port": Number(1234)})}
[2021-02-10T07:16:52Z INFO  pact_mock_server::hyper_server] Request matched, sending response Response ( status: 200, headers: Some({"content-type": ["application/hal+json"]}), body: Present(50 bytes, applic
saw-jan commented 3 years ago

@mefellows

for (let user of users) {
      aGETUserInfoByIdQueryInteraction(
        provider,
        user.id,
        user.username,
        user.email
      );
    }

this is expected to create 3 interactions

mefellows commented 3 years ago

Yes, I wrote the message and as I hit send I saw my mistake and deleted it - but I didn't sneak it past you it seems! :)

The strange thing is, the log looks good to me.

This does seem like a bug, but a quick workaround would be to enclose the whole thing in a loop (rather than have the executeTest function run all 3).

saw-jan commented 3 years ago
it('using in a loop: "fromProviderState" as query', async () => {
    for (let user of users) {
      aGETUserInfoByIdQueryInteraction(
        provider,
        user.id,
        user.username,
        user.email
      );
      await provider.executeTest(async () => {        
       await client.getUserByIdQuery(user.id);
      });
   }
  });

Tried this one too but no luck

Log is different though

Error: Mock server failed with the following mismatches: 

      1) The following request was expected but not received: 
          Method: GET
          Path: /users
          Query String: id=2
          Headers:
            Accept: application/hal+json
            Authorization: Basic YWRtaW46YWRtaW4=

LOG

[2021-02-10T10:04:14Z DEBUG pact_js_v3] Initialising Pact native library version 0.0.6
[2021-02-10T10:04:14Z DEBUG pact_js_v3] Request = Request ( method: GET, path: /users, query: Some({"id": ["1"]}), headers: Some({"Authorization": ["Basic YWRtaW46YWRtaW4="], "Accept": ["application/hal+json"]}), body: Missing )
[2021-02-10T10:04:14Z DEBUG pact_js_v3] Request matching rules = MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T10:04:14Z DEBUG pact_js_v3] Request generators = Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T10:04:14Z DEBUG pact_js_v3] Response = Response ( status: 200, headers: Some({"content-type": ["application/hal+json"]}), body: Present(50 bytes, application/json) )
[2021-02-10T10:04:14Z DEBUG pact_js_v3] Response matching rules = MatchingRules { rules: {"body": MatchingRuleCategory { name: "body", rules: {"$.id": RuleList { rules: [Number], rule_logic: And }, "$.username": RuleList { rules: [Type], rule_logic: And }, "$.email": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T10:04:14.157Z] DEBUG: pact@10.0.0-beta.30/95931 on JT101: Initialised native library 0.0.6

 RUNS  tests/pactV3.test.js
[2021-02-10T10:04:14Z DEBUG pact_mock_server::hyper_server] Creating pact request from hyper request
[2021-02-10T10:04:14Z INFO  pact_mock_server::hyper_server] Received request Request ( method: GET, path: /users, query: Some({"id": ["1"]}), headers: Some({"authorization": ["Basic YWRtaW46YWRtaW4="], "user-agent": ["axios/0.21.1"], "host": ["127.0.0.1:1234"], "connection": ["close"], "accept": ["application/hal+json"]}), body: Empty )
[2021-02-10T10:04:14Z INFO  pact_matching] comparing to expected Request ( method: GET, path: /users, query: Some({"id": ["1"]}), headers: Some({"Authorization": ["Basic YWRtaW46YWRtaW4="], "Accept": ["application/hal+json"]}), body: Missing )
[2021-02-10T10:04:14Z DEBUG pact_matching]      body: ''
[2021-02-10T10:04:14Z DEBUG pact_matching]      matching_rules: MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T10:04:14Z DEBUG pact_matching]      generators: Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T10:04:14Z DEBUG pact_matching::matchers] String -> String: comparing '/users' to '/users' using Equality
[2021-02-10T10:04:14Z DEBUG pact_matching] expected content type = '*/*', actual content type = '*/*'
[2021-02-10T10:04:14Z DEBUG pact_matching] content type header matcher = 'None'
[2021-02-10T10:04:14Z DEBUG pact_matching::matchers] String -> String: comparing '1' to '1' using Type
[2021-02-10T10:04:14Z DEBUG pact_matching::matchers] String -> String: comparing 'Basic YWRtaW46YWRtaW4=' to 'Basic YWRtaW46YWRtaW4=' using Equality
[2021-02-10T10:04:14Z DEBUG pact_matching] --> Mismatches: []
[2021-02-10T10:04:14Z DEBUG pact_mock_server::hyper_server] Test context = {"mockServer": Object({"href": String("http://127.0.0.1:1234"), "port": Number(1234)})}
[2021-02-10T10:04:14Z INFO  pact_mock_server::hyper_server] Request matched, sending response Response ( status: 200, headers: Some({"content-type": ["application/hal+json"]}), body: Present(50 bytes, application/json) )
[2021-02-10T10:04:14Z DEBUG pact_mock_server::hyper_server]      body: '{"email":"adam@mail.com","id":1,"username":"Adam"}'
[2021-02-10T10:04:14Z INFO  pact_mock_server::mock_server] Writing pact out to '/mnt/sda1/www/pactv3/tests/pacts/client-server.json'
[2021-02-10T10:04:14Z DEBUG pact_matching::models] Merging pact with file "/mnt/sda1/www/pactv3/tests/pacts/client-server.json"
[2021-02-10T10:04:14Z DEBUG pact_js_v3] Request = Request ( method: GET, path: /users, query: Some({"id": ["2"]}), headers: Some({"Accept": ["application/hal+json"], "Authorization": ["Basic YWRtaW46YWRtaW4="]}), body: Missing )
[2021-02-10T10:04:14Z DEBUG pact_js_v3] Request matching rules = MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T10:04:14Z DEBUG pact_js_v3] Request generators = Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T10:04:14Z DEBUG pact_js_v3] Response = Response ( status: 200, headers: Some({"content-type": ["application/hal+json"]}), body: Present(48 bytes, application/json) )
[2021-02-10T10:04:14Z DEBUG pact_js_v3] Response matching rules = MatchingRules { rules: {"body": MatchingRuleCategory { name: "body", rules: {"$.email": RuleList { rules: [Type], rule_logic: And }, "$.username": RuleList { rules: [Type], rule_logic: And }, "$.id": RuleList { rules: [Number], rule_logic: And }} }} }
[2021-02-10T10:04:14Z DEBUG pact_js_v3] Response generators = Generators { categories: {} }
[2021-02-10T10:04:14Z DEBUG pact_mock_server::mock_server] Started mock server on 0.0.0.0:1234
[2021-02-10T10:04:14Z DEBUG pact_mock_server::hyper_server] Creating pact request from hyper request
[2021-02-10T10:04:14Z INFO  pact_mock_server::hyper_server] Received request Request ( method: GET, path: /users, query: Some({"id": ["2"]}), headers: Some({"user-agent": ["axios/0.21.1"], "authorization": ["Basic YWRtaW46YWRtaW4="], "accept": ["application/hal+json"], "host": ["127.0.0.1:1234"], "connection": ["close"]}), body: Empty )
[2021-02-10T10:04:14Z INFO  pact_matching] comparing to expected Request ( method: GET, path: /users, query: Some({"id": ["1"]}), headers: Some({"Authorization": ["Basic YWRtaW46YWRtaW4="], "Accept": ["application/hal+json"]}), body: Missing )
[2021-02-10T10:04:14Z DEBUG pact_matching]      body: ''
[2021-02-10T10:04:14Z DEBUG pact_matching]      matching_rules: MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T10:04:14Z DEBUG pact_matching]      generators: Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T10:04:14Z DEBUG pact_matching::matchers] String -> String: comparing '/users' to '/users' using Equality
[2021-02-10T10:04:14Z DEBUG pact_matching] expected content type = '*/*', actual content type = '*/*'
[2021-02-10T10:04:14Z DEBUG pact_matching] content type header matcher = 'None'
[2021-02-10T10:04:14Z DEBUG pact_matching::matchers] String -> String: comparing '1' to '2' using Type
[2021-02-10T10:04:14Z DEBUG pact_matching::matchers] String -> String: comparing 'Basic YWRtaW46YWRtaW4=' to 'Basic YWRtaW46YWRtaW4=' using Equality
[2021-02-10T10:04:14Z DEBUG pact_matching] --> Mismatches: []
[2021-02-10T10:04:14Z INFO  pact_matching] comparing to expected Request ( method: GET, path: /users, query: Some({"id": ["2"]}), headers: Some({"Accept": ["application/hal+json"], "Authorization": ["Basic YWRtaW46YWRtaW4="]}), body: Missing )
[2021-02-10T10:04:14Z DEBUG pact_matching]      body: ''
[2021-02-10T10:04:14Z DEBUG pact_matching]      matching_rules: MatchingRules { rules: {"query": MatchingRuleCategory { name: "query", rules: {"id": RuleList { rules: [Type], rule_logic: And }} }} }
[2021-02-10T10:04:14Z DEBUG pact_matching]      generators: Generators { categories: {QUERY: {"id": ProviderStateGenerator("${id}", None)}} }
[2021-02-10T10:04:14Z DEBUG pact_matching::matchers] String -> String: comparing '/users' to '/users' using Equality
[2021-02-10T10:04:14Z DEBUG pact_matching] expected content type = '*/*', actual content type = '*/*'
[2021-02-10T10:04:14Z DEBUG pact_matching] content type header matcher = 'None'
[2021-02-10T10:04:14Z DEBUG pact_matching::matchers] String -> String: comparing '2' to '2' using Type
[2021-02-10T10:04:14Z DEBUG pact_matching::matchers] String -> String: comparing 'Basic YWRtaW46YWRtaW4=' to 'Basic YWRtaW46YWRtaW4=' using Equality
[2021-02-10T10:04:14Z DEBUG pact_matching] --> Mismatches: []
[2021-02-10T10:04:14Z DEBUG pact_mock_server::hyper_server] Test context = {"mockServer": Object({"href": String("http://127.0.0.1:1234"), "port": Number(1234)})}
[2021-02-10T10:04:14Z INFO  pact_mock_server::hyper_server] Request matched, sending response Response ( status: 200, headers: Some({"content-type": ["application/hal+json"]}), body: Present(50 bytes
individual-it commented 3 years ago

I reproduced the issue with the provider-state-injected example see https://gist.github.com/47213e2386aefeeb3cea31663b9c90c1 it seems to me that for the mock-server its not a new interaction if the query uses fromProviderState

both response are send and answered, but the answer is always the one coming from the first interaction

GET /accounts/search/findOneByAccountNumberId?accountNumber=100 HTTP/1.1
Accept: application/hal+json
Referer: http://localhost/
User-Agent: Mozilla/5.0 (linux) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/16.4.0
Accept-Language: en
Origin: http://localhost
host: 127.0.0.1:1234
accept-encoding: gzip, deflate
Connection: keep-alive

HTTP/1.1 200 OK
access-control-allow-origin: *
access-control-allow-headers: *
access-control-allow-methods: GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH
access-control-expose-headers: Location, Link
content-type: application/hal+json
content-length: 301
date: Mon, 22 Feb 2021 05:19:42 GMT

{"_links":{"account":{"href":"http://localhost:8080/accounts/100"},"self":{"href":"http://localhost:8080/accounts/100"}},"accountNumber":{"id":100},"accountRef":"Test001","createdDate":"2021-02-22T11:04:42.490+05:45","id":1,"lastModifiedDate":"2021-02-22T11:04:42.490+05:45","name":"Test","version":8}GET /accounts/search/findOneByAccountNumberId?accountNumber=200 HTTP/1.1
Accept: application/hal+json
Referer: http://localhost/
User-Agent: Mozilla/5.0 (linux) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/16.4.0
Accept-Language: en
Origin: http://localhost
host: 127.0.0.1:1234
accept-encoding: gzip, deflate
Connection: keep-alive

HTTP/1.1 200 OK
access-control-allow-origin: *
access-control-allow-headers: *
access-control-allow-methods: GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH
access-control-expose-headers: Location, Link
content-type: application/hal+json
content-length: 301
date: Mon, 22 Feb 2021 05:19:42 GMT

{"_links":{"account":{"href":"http://localhost:8080/accounts/100"},"self":{"href":"http://localhost:8080/accounts/100"}},"accountNumber":{"id":100},"accountRef":"Test001","createdDate":"2021-02-22T11:04:42.504+05:45","id":1,"lastModifiedDate":"2021-02-22T11:04:42.504+05:45","name":"Test","version":7}

using a query without fromProviderState makes the mock server to distinguish between the interactions and give the correct response.

I guess the issue is rather in the mock server than in the js lib

mefellows commented 1 year ago

Looks to be an upstream issue. @uglyog mind casting your eyes over this one?

rholshausen commented 1 year ago

Looks like only one interaction is being setup, i.e., it is seeing the 3 with different example values as the same interaction.