Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doesn't work with native Fetch API - The following request was expected but not received #1141

Closed
5 tasks done
andrzej-hanusek-tg opened this issue Dec 7, 2023 · 14 comments
Closed
5 tasks done
Labels
awaiting feedback Awaiting Feedback from OP bug Indicates an unexpected problem or unintended behavior

Comments

@andrzej-hanusek-tg
Copy link

andrzej-hanusek-tg commented Dec 7, 2023

Software versions

  • OS: _e.g. Mac OSX 14.1.2 (23B92)
  • Consumer Pact library: @pact-foundation/pact 12.1.1
  • Node Version: `v16.19.1

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

pact-js should correctly works with Fetch API, not only with axios.

BTW I don't know why in every example you are using axios. Right now, this is not a standard in web development.

Actual behaviour

Check below example. getMeDogsFetch method is the same like Axios implementation, but right now I'm always getting this error:


  Mock server failed with the following mismatches:

        0) The following request was expected but not received: 
            Method: GET

Steps to reproduce

import path from 'path';
import { MatchersV3, PactV3 } from '@pact-foundation/pact';
import axios from 'axios';
import { describe, expect, it } from 'vitest';

// Create a 'pact' between the two applications in the integration we are testing
const provider = new PactV3({
  dir: path.resolve(process.cwd(), 'pacts'),
  consumer: 'MyConsumer',
  provider: 'MyProvider',
});

class DogService {
  constructor(private url: string) {}

  public getMeDogs = (from: string) => {
    return axios.request({
      baseURL: this.url,
      params: { from },
      headers: { Accept: 'application/json' },
      method: 'GET',
      url: '/dogs',
    });
  };

  public getMeDogsFetch = async (from: string, url: string) => {
    const res = await fetch(`${url}/dogs?from=${from}`, {
      headers: { Accept: 'application/json' },
      method: 'GET',
    });

    return res.json();
  };
}
// API Client that will fetch dogs from the Dog API
// This is the target of our Pact test

const dogExample = { dog: 1 };
const EXPECTED_BODY = MatchersV3.eachLike(dogExample);

describe('GET /dogs', () => {
  it('[AXIOS] returns an HTTP 200 and a list of dogs', () => {
    // Arrange: Setup our expected interactions
    //
    // We use Pact to mock out the backend API
    provider
      .given('I have a list of dogs')
      .uponReceiving('a request for all dogs with the builder pattern')
      .withRequest({
        method: 'GET',
        path: '/dogs',
        query: { from: 'today' },
        headers: { Accept: 'application/json' },
      })
      .willRespondWith({
        status: 200,
        headers: { 'Content-Type': 'application/json' },
        body: EXPECTED_BODY,
      });

    return provider.executeTest(async (mockserver) => {
      // Act: test our API client behaves correctly
      //
      // Note we configure the DogService API client dynamically to
      // point to the mock service Pact created for us, instead of
      // the real one
      const dogService = new DogService(mockserver.url);
      const response = await dogService.getMeDogs('today');

      // Assert: check the result
      expect(response.data[0]).to.deep.eq(dogExample);
    });
  });
  it('[FETCH] returns an HTTP 200 and a list of dogs', () => {
    // Arrange: Setup our expected interactions
    //
    // We use Pact to mock out the backend API
    provider
      .given('I have a list of dogs')
      .uponReceiving('a request for all dogs with the builder pattern')
      .withRequest({
        method: 'GET',
        path: '/dogs',
        query: { from: 'today' },
        headers: { Accept: 'application/json' },
      })
      .willRespondWith({
        status: 200,
        headers: { 'Content-Type': 'application/json' },
        body: EXPECTED_BODY,
      });

    return provider.executeTest(async (mockserver) => {
      // Act: test our API client behaves correctly
      //
      // Note we configure the DogService API client dynamically to
      // point to the mock service Pact created for us, instead of
      // the real one
      const dogService = new DogService(mockserver.url);
      const response = await dogService.getMeDogsFetch('today');

      // Assert: check the result
      expect(response.data[0]).to.deep.eq(dogExample);
    });
  });
});

Relevant log files

[07:57:48.672] ERROR (74492): [email protected]: Test failed for the following reasons:

  Mock server failed with the following mismatches:

        0) The following request was expected but not received: 
            Method: GET
            
FetchError: invalid json response body at  reason: Unexpected end of JSON input
@andrzej-hanusek-tg andrzej-hanusek-tg added bug Indicates an unexpected problem or unintended behavior triage This issue is yet to be triaged by a maintainer labels Dec 7, 2023
@mefellows
Copy link
Member

Thanks for raising. I don't see why it wouldn't work with fetch, as long as it constructs a request that matches the expectations. Could you please attach trace level logs to your test - that will help.

BTW I don't know why in every example you are using axios. Right now, this is not a standard in web development.

I don't see the problem with axios, although I know the future of that project was/is sketchy. I don't think that really matters though, the API client that is used is irrelevant and I don't think we should have to go back and update all examples each time a new one comes in vogue.

What do you mean by "standard" here?

@mefellows
Copy link
Member

I've just pushed up a minor change to the TS example to show the use of fetch: https://github.com/pact-foundation/pact-js/compare/repro/issue-1141?expand=1

It works as I'd expect. Please if you can share a repro, I can help with your issue. I suspect it's just some test setup issue.

@mefellows mefellows added awaiting feedback Awaiting Feedback from OP and removed triage This issue is yet to be triaged by a maintainer labels Dec 7, 2023
@andrzej-hanusek-tg
Copy link
Author

Logs:

[08:42:12.971] INFO (81106): 0.4.9: pact native library successfully found, and the correct version
2023-12-07T07:42:12.978842Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact - ref = 1, keys = [1]
2023-12-07T07:42:12.979101Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact before - ref = 1, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [], metadata: {"pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:12.979408Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact after - ref = 1, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [], metadata: {"pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:12.979453Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact - ref = 1, keys = [1]
2023-12-07T07:42:12.979458Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact before - ref = 1, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [], metadata: {"pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:12.979484Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact after - ref = 1, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:12.982480Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact - ref = 1, keys = [1]
2023-12-07T07:42:12.982486Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact before - ref = 1, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:12.982586Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact after - ref = 1, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [], request: HttpRequest { method: "GET", path: "/", query: None, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:12.998962Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 1, interaction = 1
2023-12-07T07:42:12.998979Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [1]
2023-12-07T07:42:12.998983Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [], request: HttpRequest { method: "GET", path: "/", query: None, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:12.999228Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 1, interaction = 1
2023-12-07T07:42:12.999243Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [1]
2023-12-07T07:42:12.999247Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/", query: None, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:12.999652Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 1, interaction = 1
2023-12-07T07:42:12.999669Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [1]
2023-12-07T07:42:12.999693Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: None, headers: None, body: Missing, matching_rules: MatchingRules { rules: {PATH: MatchingRuleCategory { name: PATH, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:13.000405Z DEBUG ThreadId(01) pact_ffi::mock_server::handles: parsed header value: Left("application/json")
2023-12-07T07:42:13.000428Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Single header value received
2023-12-07T07:42:13.000432Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Existing header keys = []
2023-12-07T07:42:13.000450Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Index is 0
2023-12-07T07:42:13.000553Z TRACE ThreadId(01) pact_ffi::mock_server::handles: No existing key found, setting header 'Accept' = ["application/json"]
2023-12-07T07:42:13.000619Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 1, interaction = 1
2023-12-07T07:42:13.000623Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [1]
2023-12-07T07:42:13.000625Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: None, headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {PATH: MatchingRuleCategory { name: PATH, rules: {} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:13.000755Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 1, interaction = 1
2023-12-07T07:42:13.000758Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [1]
2023-12-07T07:42:13.000760Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {QUERY: MatchingRuleCategory { name: QUERY, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:13.000853Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 1, interaction = 1
2023-12-07T07:42:13.000860Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [1]
2023-12-07T07:42:13.000861Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {QUERY: MatchingRuleCategory { name: QUERY, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:13.001167Z DEBUG ThreadId(01) pact_ffi::mock_server::handles: parsed header value: Left("application/json")
2023-12-07T07:42:13.001170Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Single header value received
2023-12-07T07:42:13.001172Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Existing header keys = []
2023-12-07T07:42:13.001174Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Index is 0
2023-12-07T07:42:13.001176Z TRACE ThreadId(01) pact_ffi::mock_server::handles: No existing key found, setting header 'Content-Type' = ["application/json"]
2023-12-07T07:42:13.001231Z TRACE ThreadId(01) pact_ffi::mock_server::handles: >>> pactffi_with_body(InteractionHandle { interaction_ref: 65537 }, Response, 0x17055d338, 0x600003da2ac0)
2023-12-07T07:42:13.001257Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 1, interaction = 1
2023-12-07T07:42:13.001258Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [1]
2023-12-07T07:42:13.001260Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {QUERY: MatchingRuleCategory { name: QUERY, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: Some({"Content-Type": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:13.001318Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Setting up the response body
2023-12-07T07:42:13.001453Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: process_json
2023-12-07T07:42:13.001478Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: >>> process_object(obj={"min": Number(1), "pact:matcher:type": String("type"), "value": Array [Object {"dog": Number(1)}]}, matching_rules=MatchingRuleCategory { name: BODY, rules: {} }, generators=Generators { categories: {} }, path=$, type_matcher=false)
2023-12-07T07:42:13.001547Z DEBUG ThreadId(01) pact_ffi::mock_server::bodies: Path = $
2023-12-07T07:42:13.001549Z DEBUG ThreadId(01) pact_ffi::mock_server::bodies: detected pact:matcher:type, will configure a matcher
2023-12-07T07:42:13.001557Z TRACE ThreadId(01) pact_models::matchingrules: rule_type: type, attributes: {"min":1,"pact:matcher:type":"type","value":[{"dog":1}]}
2023-12-07T07:42:13.001604Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: >>> process_array(array=[Object {"dog": Number(1)}], matching_rules=MatchingRuleCategory { name: BODY, rules: {} }, generators=Generators { categories: {} }, path=$, type_matcher=true, skip_matchers=false)
2023-12-07T07:42:13.001607Z DEBUG ThreadId(01) pact_ffi::mock_server::bodies: Path = $
2023-12-07T07:42:13.001639Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: >>> process_object(obj={"dog": Number(1)}, matching_rules=MatchingRuleCategory { name: BODY, rules: {} }, generators=Generators { categories: {} }, path=$[*], type_matcher=false)
2023-12-07T07:42:13.001642Z DEBUG ThreadId(01) pact_ffi::mock_server::bodies: Path = $[*]
2023-12-07T07:42:13.001643Z DEBUG ThreadId(01) pact_ffi::mock_server::bodies: Configuring a normal object
2023-12-07T07:42:13.001714Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: -> result = Object {"dog": Number(1)}
2023-12-07T07:42:13.001717Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: matching_rule = Ok((Some(MinType(1)), Array [Object {"dog": Number(1)}]))
2023-12-07T07:42:13.001738Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: -> result = Array [Object {"dog": Number(1)}]
2023-12-07T07:42:13.002148Z TRACE ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 1 } addr_str=0x17055d028 tls=false}: pact_ffi::mock_server::handles: with_pact - ref = 1, keys = [1]
2023-12-07T07:42:13.002185Z TRACE ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 1 } addr_str=0x17055d028 tls=false}: pact_ffi::mock_server::handles: with_pact before - ref = 1, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {QUERY: MatchingRuleCategory { name: QUERY, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: Some({"Content-Type": ["application/json"]}), body: Present(b"[{\"dog\":1}]", Some(ContentType { main_type: "application", sub_type: "json", attributes: {}, suffix: None }), None), matching_rules: MatchingRules { rules: {BODY: MatchingRuleCategory { name: BODY, rules: {DocPath { path_tokens: [Root], expr: "$" }: RuleList { rules: [MinType(1)], rule_logic: And, cascaded: false }} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:13.002414Z TRACE ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 1 } addr_str=0x17055d028 tls=false}: pact_plugin_driver::catalogue_manager: register_core_entries([CatalogueEntry { entry_type: TRANSPORT, provider_type: CORE, plugin: None, key: "http", values: {} }, CatalogueEntry { entry_type: TRANSPORT, provider_type: CORE, plugin: None, key: "https", values: {} }])
2023-12-07T07:42:13.002712Z DEBUG ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 1 } addr_str=0x17055d028 tls=false}: pact_plugin_driver::catalogue_manager: Updated catalogue entries:
core/transport/http
core/transport/https
2023-12-07T07:42:13.002855Z TRACE ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 1 } addr_str=0x17055d028 tls=false}: pact_plugin_driver::catalogue_manager: register_core_entries([CatalogueEntry { entry_type: CONTENT_MATCHER, provider_type: CORE, plugin: None, key: "xml", values: {"content-types": "application/.*xml,text/xml"} }, CatalogueEntry { entry_type: CONTENT_MATCHER, provider_type: CORE, plugin: None, key: "json", values: {"content-types": "application/.*json,application/json-rpc,application/jsonrequest"} }, CatalogueEntry { entry_type: CONTENT_MATCHER, provider_type: CORE, plugin: None, key: "text", values: {"content-types": "text/plain"} }, CatalogueEntry { entry_type: CONTENT_MATCHER, provider_type: CORE, plugin: None, key: "multipart-form-data", values: {"content-types": "multipart/form-data,multipart/mixed"} }, CatalogueEntry { entry_type: CONTENT_GENERATOR, provider_type: CORE, plugin: None, key: "json", values: {"content-types": "application/.*json,application/json-rpc,application/jsonrequest"} }, CatalogueEntry { entry_type: CONTENT_GENERATOR, provider_type: CORE, plugin: None, key: "binary", values: {"content-types": "application/octet-stream"} }])
2023-12-07T07:42:13.002916Z DEBUG ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 1 } addr_str=0x17055d028 tls=false}: 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
 · |api| src/claim-money/example.test.ts (2)
   · GET /dogs (2)
     · [AXIOS] returns an HTTP 200 and a list of dogs
     · [FETCH] returns an HTTP 200 and a list of dogs
2023-12-07T07:42:13.002926Z TRACE ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 1 } addr_str=0x17055d028 tls=false}: pact_plugin_driver::catalogue_manager: register_core_entries([CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v2-regex", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v2-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-number-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-integer-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-decimal-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-date", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-time", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-datetime", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v2-min-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v2-max-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v2-minmax-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-includes", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-null", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-equals-ignore-order", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-min-equals-ignore-order", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-max-equals-ignore-order", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-minmax-equals-ignore-order", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-content-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-array-contains", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v1-equality", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-not-empty", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-semver", values: {} }])
2023-12-07T07:42:13.003440Z DEBUG ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 1 } addr_str=0x17055d028 tls=false}: 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-12-07T07:42:13.004215Z DEBUG ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 1 } addr_str=0x17055d028 tls=false}: pact_mock_server::mock_server: Started mock server on 127.0.0.1:62436
2023-12-07T07:42:13.004346Z TRACE ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 1 } addr_str=0x17055d028 tls=false}: pact_ffi::mock_server::handles: with_pact after - ref = 1, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {QUERY: MatchingRuleCategory { name: QUERY, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: Some({"Content-Type": ["application/json"]}), body: Present(b"[{\"dog\":1}]", Some(ContentType { main_type: "application", sub_type: "json", attributes: {}, suffix: None }), None), matching_rules: MatchingRules { rules: {BODY: MatchingRuleCategory { name: BODY, rules: {DocPath { path_tokens: [Root], expr: "$" }: RuleList { rules: [MinType(1)], rule_logic: And, cascaded: false }} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: true, specification_version: V3 } }
2023-12-07T07:42:13.036273Z TRACE tokio-runtime-worker hyper::proto::h1::conn: Conn::read_head
2023-12-07T07:42:13.036313Z TRACE tokio-runtime-worker hyper::proto::h1::conn: flushed({role=server}): State { reading: Init, writing: Init, keep_alive: Busy }
2023-12-07T07:42:13.037449Z TRACE tokio-runtime-worker hyper::proto::h1::conn: Conn::read_head
2023-12-07T07:42:13.037457Z TRACE tokio-runtime-worker hyper::proto::h1::io: received 309 bytes
2023-12-07T07:42:13.037483Z TRACE tokio-runtime-worker parse_headers: hyper::proto::h1::role: Request.parse bytes=309
2023-12-07T07:42:13.037514Z TRACE tokio-runtime-worker parse_headers: hyper::proto::h1::role: Request.parse Complete(309)
2023-12-07T07:42:13.037565Z DEBUG tokio-runtime-worker hyper::proto::h1::io: parsed 8 headers
2023-12-07T07:42:13.037580Z DEBUG tokio-runtime-worker hyper::proto::h1::conn: incoming body is empty
2023-12-07T07:42:13.037636Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Creating pact request from hyper request
2023-12-07T07:42:13.037652Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Extracting query from uri /dogs?from=today
2023-12-07T07:42:13.051708Z TRACE tokio-runtime-worker pact_mock_server::hyper_server: path_and_query -> /dogs?from=today
2023-12-07T07:42:13.051734Z TRACE tokio-runtime-worker pact_models::query_strings: kv = 'from=today'
2023-12-07T07:42:13.051752Z TRACE tokio-runtime-worker pact_models::query_strings: name_value = '["from", "today"]'
2023-12-07T07:42:13.051755Z TRACE tokio-runtime-worker pact_models::query_strings: ch = 'Some('f')'
2023-12-07T07:42:13.051758Z TRACE tokio-runtime-worker pact_models::query_strings: ch = 'Some('r')'
2023-12-07T07:42:13.051759Z TRACE tokio-runtime-worker pact_models::query_strings: ch = 'Some('o')'
2023-12-07T07:42:13.051760Z TRACE tokio-runtime-worker pact_models::query_strings: ch = 'Some('m')'
2023-12-07T07:42:13.051761Z TRACE tokio-runtime-worker pact_models::query_strings: ch = 'Some('t')'
2023-12-07T07:42:13.051762Z TRACE tokio-runtime-worker pact_models::query_strings: ch = 'Some('o')'
2023-12-07T07:42:13.051763Z TRACE tokio-runtime-worker pact_models::query_strings: ch = 'Some('d')'
2023-12-07T07:42:13.051800Z TRACE tokio-runtime-worker pact_models::query_strings: ch = 'Some('a')'
2023-12-07T07:42:13.051802Z TRACE tokio-runtime-worker pact_models::query_strings: ch = 'Some('y')'
2023-12-07T07:42:13.051804Z TRACE tokio-runtime-worker pact_models::query_strings: decoded: 'from' => 'today'
2023-12-07T07:42:13.051952Z  INFO tokio-runtime-worker pact_mock_server::hyper_server: Received request GET /dogs
2023-12-07T07:42:13.051968Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: 
      ----------------------------------------------------------------------------------------
       method: GET
       path: /dogs
       query: Some({"from": ["today"]})
       headers: Some({"host": ["127.0.0.1:62436"], "accept-language": ["en"], "connection": ["keep-alive"], "referer": ["http://localhost:3000/"], "accept": ["application/json"], "user-agent": ["Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML", "like Gecko) jsdom/22.1.0"], "origin": ["http://localhost:3000"], "accept-encoding": ["gzip", "deflate"]})
       body: Empty
      ----------------------------------------------------------------------------------------
      
2023-12-07T07:42:13.052070Z  INFO tokio-runtime-worker pact_matching: comparing to expected HTTP Request ( method: GET, path: /dogs, query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing )
2023-12-07T07:42:13.052089Z DEBUG tokio-runtime-worker pact_matching:      body: ''
2023-12-07T07:42:13.052091Z DEBUG tokio-runtime-worker pact_matching:      matching_rules: MatchingRules { rules: {QUERY: MatchingRuleCategory { name: QUERY, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }
2023-12-07T07:42:13.052094Z DEBUG tokio-runtime-worker pact_matching:      generators: Generators { categories: {} }
2023-12-07T07:42:13.052134Z TRACE tokio-runtime-worker pact_matching: plugin_data = {}
2023-12-07T07:42:13.052251Z TRACE tokio-runtime-worker matcher_is_defined{self=CoreMatchingContext { matchers: MatchingRuleCategory { name: PATH, rules: {} }, config: NoUnexpectedKeys, matching_spec: V3, plugin_configuration: {} } path=DocPath { path_tokens: [], expr: "" }}: pact_models::matchingrules: matcher_is_defined: for category path and path [] -> false
2023-12-07T07:42:13.052336Z DEBUG tokio-runtime-worker matches_with{self="/dogs" actual="/dogs" matcher=Equality cascaded=false}: pact_matching::matchers: String -> String: comparing '/dogs' to '/dogs' ==> true cascaded=false matcher=Equality
2023-12-07T07:42:13.052341Z DEBUG tokio-runtime-worker pact_matching: expected content type = '*/*', actual content type = '*/*'
2023-12-07T07:42:13.052395Z DEBUG tokio-runtime-worker pact_matching: content type header matcher = 'RuleList { rules: [], rule_logic: And, cascaded: false }'
2023-12-07T07:42:13.052448Z TRACE tokio-runtime-worker matcher_is_defined{self=CoreMatchingContext { matchers: MatchingRuleCategory { name: QUERY, rules: {} }, config: NoUnexpectedKeys, matching_spec: V3, plugin_configuration: {} } path=DocPath { path_tokens: [Root, Field("from")], expr: "$.from" }}: pact_models::matchingrules: matcher_is_defined: for category query and path ["$", "from"] -> false
2023-12-07T07:42:13.052466Z TRACE tokio-runtime-worker matcher_is_defined{self=CoreMatchingContext { matchers: MatchingRuleCategory { name: QUERY, rules: {} }, config: NoUnexpectedKeys, matching_spec: V3, plugin_configuration: {} } path=DocPath { path_tokens: [Root, Field("from"), Index(0)], expr: "$.from[0]" }}: pact_models::matchingrules: matcher_is_defined: for category query and path ["$", "from", "0"] -> false
2023-12-07T07:42:13.052471Z DEBUG tokio-runtime-worker matches_with{self="today" actual="today" matcher=Equality cascaded=false}: pact_matching::matchers: String -> String: comparing 'today' to 'today' ==> true cascaded=false matcher=Equality
2023-12-07T07:42:13.052535Z TRACE tokio-runtime-worker match_header_value{key="Accept" index=0 expected="application/json" actual="application/json" context=HeaderMatchingContext { inner_context: CoreMatchingContext { matchers: MatchingRuleCategory { name: HEADER, rules: {} }, config: NoUnexpectedKeys, matching_spec: V3, plugin_configuration: {} } } single_value=true}:matcher_is_defined{self=CoreMatchingContext { matchers: MatchingRuleCategory { name: HEADER, rules: {} }, config: NoUnexpectedKeys, matching_spec: V3, plugin_configuration: {} } path=DocPath { path_tokens: [Root, Field("accept")], expr: "$.accept" }}: pact_models::matchingrules: matcher_is_defined: for category header and path ["$", "accept"] -> false
2023-12-07T07:42:13.052539Z TRACE tokio-runtime-worker match_header_value{key="Accept" index=0 expected="application/json" actual="application/json" context=HeaderMatchingContext { inner_context: CoreMatchingContext { matchers: MatchingRuleCategory { name: HEADER, rules: {} }, config: NoUnexpectedKeys, matching_spec: V3, plugin_configuration: {} } } single_value=true}:matcher_is_defined{self=CoreMatchingContext { matchers: MatchingRuleCategory { name: HEADER, rules: {} }, config: NoUnexpectedKeys, matching_spec: V3, plugin_configuration: {} } path=DocPath { path_tokens: [Root, Field("accept"), Index(0)], expr: "$.accept[0]" }}: pact_models::matchingrules: matcher_is_defined: for category header and path ["$", "accept", "0"] -> false
2023-12-07T07:42:13.052588Z DEBUG tokio-runtime-worker pact_matching: --> Mismatches: []
2023-12-07T07:42:13.052635Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Test context = {"mockServer": Object {"port": Number(62436), "url": String("http://127.0.0.1:62436")}}
2023-12-07T07:42:13.052654Z TRACE tokio-runtime-worker pact_matching: generate_response response=HttpResponse { status: 200, headers: Some({"Content-Type": ["application/json"]}), body: Present(b"[{\"dog\":1}]", Some(ContentType { main_type: "application", sub_type: "json", attributes: {}, suffix: None }), None), matching_rules: MatchingRules { rules: {BODY: MatchingRuleCategory { name: BODY, rules: {DocPath { path_tokens: [Root], expr: "$" }: RuleList { rules: [MinType(1)], rule_logic: And, cascaded: false }} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } } mode=Consumer context={"mockServer": Object {"port": Number(62436), "url": String("http://127.0.0.1:62436")}}
2023-12-07T07:42:13.052683Z  INFO tokio-runtime-worker pact_mock_server::hyper_server: Request matched, sending response
2023-12-07T07:42:13.052686Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: 
          ----------------------------------------------------------------------------------------
           status: 200
           headers: Some({"Content-Type": ["application/json"]})
           body: Present(11 bytes, application/json) '[{"dog":1}]'
          ----------------------------------------------------------------------------------------
          
 ❯ |api| src/claim-money/example.test.ts (2)
   ❯ GET /dogs (2)
     ⠹ [AXIOS] returns an HTTP 200 and a list of dogs
     · [FETCH] returns an HTTP 200 and a list of dogs
2023-12-07T07:42:13.081254Z DEBUG ThreadId(01) pact_ffi::mock_server::handles: pact_ffi::mock_server::handles::pactffi_pact_handle_write_file FFI function invoked
2023-12-07T07:42:13.081274Z TRACE ThreadId(01) pact_ffi::mock_server::handles: @param pact = PactHandle { pact_ref: 1 }
2023-12-07T07:42:13.081276Z TRACE ThreadId(01) pact_ffi::mock_server::handles: @param directory = 0x600000b06ad0
2023-12-07T07:42:13.081277Z TRACE ThreadId(01) pact_ffi::mock_server::handles: @param overwrite = false
2023-12-07T07:42:13.081302Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact - ref = 1, keys = [1]
2023-12-07T07:42:13.081304Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact before - ref = 1, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {QUERY: MatchingRuleCategory { name: QUERY, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generat
 ❯ |api| src/claim-money/example.test.ts (2)
   ❯ GET /dogs (2)
     ⠸ [AXIOS] returns an HTTP 200 and a list of dogs
     · [FETCH] returns an HTTP 200 and a list of dogs
2023-12-07T07:42:13.086251Z TRACE ThreadId(01) pact_models::file_utils: Attempt 0 of 3 to get a shared lock on '"/Users/andrzej.hanusek/Documents/Projects/web/lie-frontend-v2/pacts/MyConsumer-MyProvider.json"'
2023-12-07T07:42:13.086275Z TRACE ThreadId(01) pact_models::file_utils: Got shared file lock
2023-12-07T07:42:13.086283Z TRACE ThreadId(01) pact_models::file_utils: Releasing shared file lock
2023-12-07T07:42:13.086361Z TRACE ThreadId(01) pact_models::pact: load_pact_from_json: found spec version V3 in metadata
2023-12-07T07:42:13.086364Z TRACE ThreadId(01) pact_models::pact: load_pact_from_json: loading JSON as a request/response pact
2023-12-07T07:42:13.086433Z TRACE ThreadId(01) pact_models::matchingrules: rule_type: type, attributes: {"match":"type","min":1}
2023-12-07T07:42:13.086446Z  WARN ThreadId(01) pact_models::pact: Note: Existing pact is an older specification version (V3), and will be upgraded
2023-12-07T07:42:13.086520Z TRACE ThreadId(01) pact_models::file_utils: Attempt 0 of 3 to get an exclusive lock on '"/Users/andrzej.hanusek/Documents/Projects/web/lie-frontend-v2/pacts/MyConsumer-MyProvider.json"'
2023-12-07T07:42:13.086525Z TRACE ThreadId(01) pact_models::file_utils: Got exclusive file lock
2023-12-07T07:42:13.086822Z TRACE ThreadId(01) pact_models::file_utils: Releasing exclusive file lock
2023-12-07T07:42:13.086920Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact after - ref = 1, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {QUERY: MatchingRuleCategory { name: QUERY, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: Some({"Content-Type": ["application/json"]}), body: Present(b"[{\"dog\":1}]", Some(ContentType { main_type: "application", sub_type: "json", attributes: {}, suffix: None }), None), matching_rules: MatchingRules { rules: {BODY: MatchingRuleCategory { name: BODY, rules: {DocPath { path_tokens: [Root], expr: "$" }: RuleList { rules: [MinType(1)], rule_logic: And, cascaded: false }} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: true, specification_version: V3 } }
2023-12-07T07:42:13.086964Z TRACE ThreadId(01) pact_ffi::mock_server::handles: pactffi_pact_handle_write_file FFI function completed output=0
2023-12-07T07:42:13.087048Z 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-12-07T07:42:13.087054Z DEBUG ThreadId(01) pact_mock_server::server_manager: Shutting down mock server with ID 7f64aa76-493f-4919-87c9-bdcd0b8a59bf - MockServerMetrics { requests: 1, requests_by_path: {"/dogs": 1} }
2023-12-07T07:42:13.087066Z DEBUG ThreadId(01) pact_mock_server::mock_server: Mock server 7f64aa76-493f-4919-87c9-bdcd0b8a59bf shutdown - MockServerMetrics { requests: 1, requests_by_path: {"/dogs": 1} }
2023-12-07T07:42:13.087084Z DEBUG tokio-runtime-worker hyper::server::shutdown: signal received, starting graceful shutdown
2023-12-07T07:42:13.087142Z TRACE tokio-runtime-worker hyper::proto::h1::conn: disable_keep_alive; closing idle connection
2023-12-07T07:42:13.087145Z TRACE tokio-runtime-worker hyper::proto::h1::conn: State::close()
2023-12-07T07:42:13.087146Z TRACE tokio-runtime-worker hyper::proto::h1::conn: State::close_read()
2023-12-07T07:42:13.087161Z TRACE tokio-runtime-worker hyper::proto::h1::conn: State::close_write()
2023-12-07T07:42:13.087166Z TRACE tokio-runtime-worker hyper::proto::h1::conn: flushed({role=server}): State { reading: Closed, writing: Closed, keep_alive: Disabled }
2023-12-07T07:42:13.087202Z TRACE tokio-runtime-worker hyper::proto::h1::conn: shut down IO complete
[08:42:13.087] TRACE (81106): [email protected]: Ffi has already been initialised, no need to repeat it
2023-12-07T07:42:13.088045Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact - ref = 2, keys = [2, 1]
2023-12-07T07:42:13.088048Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact before - ref = 2, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [], metadata: {"pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:13.088052Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact after - ref = 2, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [], metadata: {"pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
 ❯ |api| src/claim-money/example.test.ts (2)
   ❯ GET /dogs (2)
     ⠼ [AXIOS] returns an HTTP 200 and a list of dogs
     · [FETCH] returns an HTTP 200 and a list of dogs
2023-12-07T07:42:13.116846Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact - ref = 2, keys = [2, 1]
2023-12-07T07:42:13.116850Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact before - ref = 2, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:13.116858Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact after - ref = 2, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [], request: HttpRequest { method: "GET", path: "/", query: None, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:13.117019Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 2, interaction = 1
2023-12-07T07:42:13.117025Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [2, 1]
2023-12-07T07:42:13.117027Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [], request: HttpRequest { method: "GET", path: "/", query: None, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:13.117065Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 2, interaction = 1
2023-12-07T07:42:13.117067Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [2, 1]
2023-12-07T07:42:13.117069Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/", query: None, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:13.117162Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 2, interaction = 1
2023-12-07T07:42:13.117165Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [2, 1]
 ❯ |api| src/claim-money/example.test.ts (2)
   ❯ GET /dogs (2)
     ⠴ [AXIOS] returns an HTTP 200 and a list of dogs
     · [FETCH] returns an HTTP 200 and a list of dogs
2023-12-07T07:42:13.117270Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Single header value received
2023-12-07T07:42:13.117273Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Existing header keys = []
2023-12-07T07:42:13.117275Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Index is 0
2023-12-07T07:42:13.117278Z TRACE ThreadId(01) pact_ffi::mock_server::handles: No existing key found, setting header 'Accept' = ["application/json"]
2023-12-07T07:42:13.117287Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 2, interaction = 1
2023-12-07T07:42:13.117288Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [2, 1]
2023-12-07T07:42:13.117289Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: None, headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {HEADER: MatchingRuleCategory { name: HEADER, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:13.117558Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 2, interaction = 1
2023-12-07T07:42:13.117559Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [2, 1]
2023-12-07T07:42:13.117560Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {HEADER: MatchingRuleCategory { name: HEADER, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, QUERY: MatchingRuleCategory { name: QUERY, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:13.117615Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 2, interaction = 1
2023-12-07T07:42:13.117617Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [2, 1]
2023-12-07T07:42:13.117618Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {HEADER: MatchingRuleCategory { name: HEADER, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, QUERY: MatchingRuleCategory { name: QUERY, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 201, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:13.117774Z DEBUG ThreadId(01) pact_ffi::mock_server::handles: parsed header value: Left("application/json")
2023-12-07T07:42:13.117776Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Single header value received
2023-12-07T07:42:13.117777Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Existing header keys = []
2023-12-07T07:42:13.117779Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Index is 0
2023-12-07T07:42:13.117780Z TRACE ThreadId(01) pact_ffi::mock_server::handles: No existing key found, setting header 'Content-Type' = ["application/json"]
2023-12-07T07:42:13.117853Z TRACE ThreadId(01) pact_ffi::mock_server::handles: >>> pactffi_with_body(InteractionHandle { interaction_ref: 131073 }, Response, 0x17055ce28, 0x600003d0d1c0)
2023-12-07T07:42:13.117856Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - index = 2, interaction = 1
2023-12-07T07:42:13.117857Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - keys = [2, 1]
2023-12-07T07:42:13.117858Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {HEADER: MatchingRuleCategory { name: HEADER, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, QUERY: MatchingRuleCategory { name: QUERY, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 201, headers: Some({"Content-Type": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {HEADER: MatchingRuleCategory { name: HEADER, rules: {} }} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 }
2023-12-07T07:42:13.117919Z TRACE ThreadId(01) pact_ffi::mock_server::handles: Setting up the response body
2023-12-07T07:42:13.117927Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: process_json
2023-12-07T07:42:13.117933Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: >>> process_object(obj={"min": Number(1), "pact:matcher:type": String("type"), "value": Array [Object {"dog": Number(1)}]}, matching_rules=MatchingRuleCategory { name: BODY, rules: {} }, generators=Generators { categories: {} }, path=$, type_matcher=false)
2023-12-07T07:42:13.117984Z DEBUG ThreadId(01) pact_ffi::mock_server::bodies: Path = $
2023-12-07T07:42:13.117986Z DEBUG ThreadId(01) pact_ffi::mock_server::bodies: detected pact:matcher:type, will configure a matcher
2023-12-07T07:42:13.117989Z TRACE ThreadId(01) pact_models::matchingrules: rule_type: type, attributes: {"min":1,"pact:matcher:type":"type","value":[{"dog":1}]}
2023-12-07T07:42:13.118000Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: >>> process_array(array=[Object {"dog": Number(1)}], matching_rules=MatchingRuleCategory { name: BODY, rules: {} }, generators=Generators { categories: {} }, path=$, type_matcher=true, skip_matchers=false)
2023-12-07T07:42:13.118002Z DEBUG ThreadId(01) pact_ffi::mock_server::bodies: Path = $
2023-12-07T07:42:13.118071Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: >>> process_object(obj={"dog": Number(1)}, matching_rules=MatchingRuleCategory { name: BODY, rules: {} }, generators=Generators { categories: {} }, path=$[*], type_matcher=false)
2023-12-07T07:42:13.118073Z DEBUG ThreadId(01) pact_ffi::mock_server::bodies: Path = $[*]
2023-12-07T07:42:13.118075Z DEBUG ThreadId(01) pact_ffi::mock_server::bodies: Configuring a normal object
2023-12-07T07:42:13.118079Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: -> result = Object {"dog": Number(1)}
2023-12-07T07:42:13.118082Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: matching_rule = Ok((Some(MinType(1)), Array [Object {"dog": Number(1)}]))
2023-12-07T07:42:13.118087Z TRACE ThreadId(01) pact_ffi::mock_server::bodies: -> result = Array [Object {"dog": Number(1)}]
2023-12-07T07:42:13.118120Z TRACE ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 2 } addr_str=0x17055cb18 tls=false}: pact_ffi::mock_server::handles: with_pact - ref = 2, keys = [2, 1]
2023-12-07T07:42:13.118134Z TRACE ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 2 } addr_str=0x17055cb18 tls=false}: pact_ffi::mock_server::handles: with_pact before - ref = 2, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {HEADER: MatchingRuleCategory { name: HEADER, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, QUERY: MatchingRuleCategory { name: QUERY, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 201, headers: Some({"Content-Type": ["application/json"]}), body: Present(b"[{\"dog\":1}]", Some(ContentType { main_type: "application", sub_type: "json", attributes: {}, suffix: None }), None), matching_rules: MatchingRules { rules: {HEADER: MatchingRuleCategory { name: HEADER, rules: {} }, BODY: MatchingRuleCategory { name: BODY, rules: {DocPath { path_tokens: [Root], expr: "$" }: RuleList { rules: [MinType(1)], rule_logic: And, cascaded: false }} }} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:13.118230Z TRACE ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 2 } addr_str=0x17055cb18 tls=false}: pact_plugin_driver::catalogue_manager: register_core_entries([CatalogueEntry { entry_type: TRANSPORT, provider_type: CORE, plugin: None, key: "http", values: {} }, CatalogueEntry { entry_type: TRANSPORT, provider_type: CORE, plugin: None, key: "https", values: {} }])
2023-12-07T07:42:13.118282Z TRACE ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 2 } addr_str=0x17055cb18 tls=false}: pact_plugin_driver::catalogue_manager: register_core_entries([CatalogueEntry { entry_type: CONTENT_MATCHER, provider_type: CORE, plugin: None, key: "xml", values: {"content-types": "application/.*xml,text/xml"} }, CatalogueEntry { entry_type: CONTENT_MATCHER, provider_type: CORE, plugin: None, key: "json", values: {"content-types": "application/.*json,application/json-rpc,application/jsonrequest"} }, CatalogueEntry { entry_type: CONTENT_MATCHER, provider_type: CORE, plugin: None, key: "text", values: {"content-types": "text/plain"} }, CatalogueEntry { entry_type: CONTENT_MATCHER, provider_type: CORE, plugin: None, key: "multipart-form-data", values: {"content-types": "multipart/form-data,multipart/mixed"} }, CatalogueEntry { entry_type: CONTENT_GENERATOR, provider_type: CORE, plugin: None, key: "json", values: {"content-types": "application/.*json,application/json-rpc,application/jsonrequest"} }, CatalogueEntry { entry_type: CONTENT_GENERATOR, provider_type: CORE, plugin: None, key: "binary", values: {"content-types": "application/octet-stream"} }])
2023-12-07T07:42:13.118373Z TRACE ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 2 } addr_str=0x17055cb18 tls=false}: pact_plugin_driver::catalogue_manager: register_core_entries([CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v2-regex", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v2-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-number-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-integer-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-decimal-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-date", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-time", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-datetime", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v2-min-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v2-max-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v2-minmax-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-includes", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-null", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-equals-ignore-order", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-min-equals-ignore-order", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-max-equals-ignore-order", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-minmax-equals-ignore-order", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v3-content-type", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-array-contains", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v1-equality", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-not-empty", values: {} }, CatalogueEntry { entry_type: MATCHER, provider_type: CORE, plugin: None, key: "v4-semver", values: {} }])
2023-12-07T07:42:13.118709Z DEBUG ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 2 } addr_str=0x17055cb18 tls=false}: pact_mock_server::mock_server: Started mock server on 127.0.0.1:62438
2023-12-07T07:42:13.118721Z TRACE ThreadId(01) pactffi_create_mock_server_for_pact{pact=PactHandle { pact_ref: 2 } addr_str=0x17055cb18 tls=false}: pact_ffi::mock_server::handles: with_pact after - ref = 2, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for all dogs with the builder pattern", provider_states: [ProviderState { name: "I have a list of dogs", params: {} }], request: HttpRequest { method: "GET", path: "/dogs", query: Some({"from": ["today"]}), headers: Some({"Accept": ["application/json"]}), body: Missing, matching_rules: MatchingRules { rules: {HEADER: MatchingRuleCategory { name: HEADER, rules: {} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, QUERY: MatchingRuleCategory { name: QUERY, rules: {} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 201, headers: Some({"Content-Type": ["application/json"]}), body: Present(b"[{\"dog\":1}]", Some(ContentType { main_type: "application", sub_type: "json", attributes: {}, suffix: None }), None), matching_rules: MatchingRules { rules: {HEADER: MatchingRuleCategory { name: HEADER, rules: {} }, BODY: MatchingRuleCategory { name: BODY, rules: {DocPath { path_tokens: [Root], expr: "$" }: RuleList { rules: [MinType(1)], rule_logic: And, cascaded: false }} }} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: true, specification_version: V3 } }
2023-12-07T07:42:13.121711Z 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-12-07T07:42:13.121720Z DEBUG ThreadId(01) pact_mock_server::server_manager: Shutting down mock server with ID ccc21f38-14c7-470c-b8bf-af687b518235 - MockServerMetrics { requests: 0, requests_by_path: {} }
2023-12-07T07:42:13.121727Z DEBUG ThreadId(01) pact_mock_server::mock_server: Mock server ccc21f38-14c7-470c-b8bf-af687b518235 shutdown - MockServerMetrics { requests: 0, requests_by_path: {} }
2023-12-07T07:42:13.121738Z DEBUG tokio-runtime-worker hyper::server::shutdown: signal received, starting graceful shutdown
[08:42:13.121] TRACE (81106): [email protected]: Ffi has already been initialised, no need to repeat it
2023-12-07T07:42:13.122183Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact - ref = 3, keys = [3, 2, 1]
2023-12-07T07:42:13.122186Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact before - ref = 3, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [], metadata: {"pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:13.122192Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact after - ref = 3, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [], metadata: {"pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:13.166495Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact - ref = 3, keys = [3, 2, 1]
2023-12-07T07:42:13.166506Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact before - ref = 3, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [], metadata: {"pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
2023-12-07T07:42:13.166516Z TRACE ThreadId(01) pact_ffi::mock_server::handles: with_pact after - ref = 3, inner = RefCell { value: PactHandleInner { pact: V4Pact { consumer: Consumer { name: "MyConsumer" }, provider: Provider { name: "MyProvider" }, interactions: [], metadata: {"pact-js": Object {"version": String("12.1.1")}, "pactRust": Object {"ffi": String("0.4.9")}}, plugin_data: [] }, mock_server_started: false, specification_version: V3 } }
[08:42:13.166] ERROR (81106): [email protected]: Test failed for the following reasons:

  Mock server failed with the following mismatches:

        0) The following request was expected but not received: 
            Method: GET

@andrzej-hanusek-tg
Copy link
Author

Reproduction:

https://github.com/andrzej-hanusek-tg/pactjs-fetch-example

Here error is a little bit different:

 ❯ tests/example.test.ts (2)
   ❯ GET /dogs (2)
     ✓ [AXIOS] returns an HTTP 200 and a list of dogs
     × [FETCH] returns an HTTP 200 and a list of dogs

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 FAIL  tests/example.test.ts > GET /dogs > [FETCH] returns an HTTP 200 and a list of dogs
Error: Error in native callback

@mefellows
Copy link
Member

Thanks for the repro.

So, the issue is that your function is returning an object that contains a wrapped promise, and I think that's causing issues in the test itself:

  public getMeDogsFetch = (from: string, url: string) => {
    console.log('url', `${url}/dogs?from=${from}`);
    return fetch(`${url}/dogs?from=${from}`, {
      headers: { Accept: 'application/json' },
      method: 'GET',
    }).then((res) => {
      console.log('response', res, res.headers);
      return {
        data: res.json(), // <- that is a promise
      } as const;
    });
  };

Here's a version of that method that works:

  public getMeDogsFetch = async (from: string, url: string) => {
    const res = await fetch(`${url}/dogs?from=${from}`, {
      headers: { Accept: 'application/json' },
      method: 'GET',
    })

    return res.json()
  }

(note you'll need to adjust the test to not address the .data[0] anymore)

@andrzej-hanusek-tg
Copy link
Author

andrzej-hanusek-tg commented Dec 7, 2023

@mefellows

Sorry, my fault when I copied code from my repo to example, but this won't change anything.
I'm still getting error. Right now repro is updated.

@mefellows
Copy link
Member

mefellows commented Dec 7, 2023

The issue is that the test is not checking that it's an array. I've submitted a PR here: andrzej-hanusek-tg/pactjs-fetch-example#1

It should be printing out the error, so I suspect there is something else going on. Perhaps vitest swallows the error?

UPDATE: I've updated the TS example it was copied from to improve the test, and it prints the error as follows


  0 passing (103ms)
  1 failing

  1) The Dog API
       get /dogs using builder pattern
         returns the correct response:
     AssertionError: expected [ { dog: 1 } ] to deeply equal { dog: 1 }

@andrzej-hanusek-tg
Copy link
Author

Ok I'll fetch your example and try. Right now assertion doesn't matter, because always getting:

Error: Error in native callback

or

 Mock server failed with the following mismatches:

        0) The following request was expected but not received: 
            Method: GET

Cannot understand why the same test case with same config works for axios.

   ❯ GET /dogs (2)
     ✓ [AXIOS] returns an HTTP 200 and a list of dogs
     × [FETCH] returns an HTTP 200 and a list of dogs
describe('GET /dogs', () => {
  it('[AXIOS] returns an HTTP 200 and a list of dogs', () => {
    // Arrange: Setup our expected interactions
    //
    // We use Pact to mock out the backend API
    provider
      .given('I have a list of dogs')
      .uponReceiving('a request for all dogs with the builder pattern')
      .withRequest({
        method: 'GET',
        path: '/dogs',
        query: { from: 'today' },
        headers: { Accept: 'application/json' },
      })
      .willRespondWith({
        status: 200,
        headers: { 'Content-Type': 'application/json' },
        body: EXPECTED_BODY,
      });

    return provider.executeTest(async (mockserver) => {
      // Act: test our API client behaves correctly
      //
      // Note we configure the DogService API client dynamically to
      // point to the mock service Pact created for us, instead of
      // the real one
      const dogService = new DogService(mockserver.url);
      const response = await dogService.getMeDogs('today');


      // Assert: check the result
      expect(response).toBeDefined();
    });
  });
  it('[FETCH] returns an HTTP 200 and a list of dogs', () => {
    // Arrange: Setup our expected interactions
    //
    // We use Pact to mock out the backend API
    provider
      .given('I have a list of dogs #2')
      .uponReceiving('a request for all dogs with the builder pattern')
      .withRequest({
        method: 'GET',
        path: '/dogs',
        query: { from: 'today' },
        headers: { Accept: 'application/json' },
      })
      .willRespondWith({
        status: 200,
        headers: { 'Content-Type': 'application/json' },
        body: EXPECTED_BODY,
      });

    return provider.executeTest(async (mockserver) => {
      // Act: test our API client behaves correctly
      //
      // Note we configure the DogService API client dynamically to
      // point to the mock service Pact created for us, instead of
      // the real one
      const dogService = new DogService(mockserver.url);
      const response = await dogService.getMeDogsFetch('today', mockserver.url);

      console.log('response', response)

      // Assert: check the result
      expect(response).toBeDefined();
    });
  });
});

@andrzej-hanusek-tg
Copy link
Author

andrzej-hanusek-tg commented Dec 7, 2023

@mefellows

Also I'm a little bit lost in examples because sometimes you're using .addInteraction and sometimes directly provider. What is a difference and recommended way to do this?


  before(() =>
    provider.setup().then((opts) => {
      dogService = new DogService({ url, port: opts.port });
    })
  );

  after(() => provider.finalize());

  afterEach(() => provider.verify());

  describe('get /dogs using builder pattern', () => {
    before(() => {
      const interaction = new Interaction()
        .given('I have a list of dogs')
        .uponReceiving('a request for all dogs with the builder pattern')
        .withRequest({
          method: 'GET',
          path: '/dogs',
          headers: {
            Accept: 'application/json',
          },
        })
        .willRespondWith({
          status: 200,
          headers: {
            'Content-Type': 'application/json',
          },
          body: EXPECTED_BODY,
        });

      return provider.addInteraction(interaction);
    });

Example without addInteraction one comment above.

@YOU54F
Copy link
Member

YOU54F commented Dec 7, 2023

It seems that the assertions from vite, are causing issues in the providerExecute block.

  it.only('[FETCH] returns an HTTP 200 and a list of dogs', () => {
    // Arrange: Setup our expected interactions
    //
    // We use Pact to mock out the backend API
    provider
      .given('I have a list of dogs #2')
      .uponReceiving('a request for all dogs with the builder pattern')
      .withRequest({
        method: 'GET',
        path: '/dogs',
        query: { from: 'today' },
        headers: { Accept: 'application/json' },
      })
      .willRespondWith({
        status: 200,
        headers: { 'Content-Type': 'application/json' },
        body: EXPECTED_BODY,
      });

    return provider.executeTest(async (mockserver) => {
      // Act: test our API client behaves correctly
      //
      // Note we configure the DogService API client dynamically to
      // point to the mock service Pact created for us, instead of
      // the real one
      const dogService = new DogService(mockserver.url);
      return await dogService.getMeDogsFetch('today', mockserver.url);
    }).then((response) => {expect(response).to.deep.eq(dogExample)});
  });

The above code gives me the following output from Pact for mismatched client routes (if I change the pact request expectation to be the path /dog

Screenshot 2023-12-07 at 13 28 06

And this output from vites assertion engine, for an incorrect response returned from the dogService.getMeDogsFetch

Screenshot 2023-12-07 at 13 28 39

BTW I don't know why in every example you are using axios. Right now, this is not a standard in web development.

This is quite a sweeping statement, have you got any evidence to back this up as GitHub suggests 10.8 million projects are using Axios.

Screenshot 2023-12-07 at 13 30 23

@YOU54F
Copy link
Member

YOU54F commented Dec 7, 2023

Also thanks for the repro repo. That was useful to take a look.

You can also remove jest / jest-pact from the package.json devDependencies, and the jest top level key as they are not needed or used in your examples from what I can see. You also don't need "@pact-foundation/pact-core" as it is not a peer dep and is included in the pact-foundation/pact package

@andrzej-hanusek-tg
Copy link
Author

This is quite a sweeping statement, have you got any evidence to back this up as GitHub suggests 10.8 million projects are using Axios.

Ok, that was a little to offensive. Sorry for that. Basically I meant that right now the default http client for web app usually (especially frontend) is Fetch API (react-query, next.js and many different tools and frameworks can confirm my words). In the past we need to use things like axios or superagent, but right now it doesn't seem like the best option, especially axios which handling errors in very strange way and sometimes behave very sketchy (like adding extra 300kB to your Lambdas after upgrade the lib). The frustration comes from fact the very examples for me works pretty fine with axios, but still I'm not able setup pact with fetch/wretch which is my opinion should be a default option.

Also thanks for the repro repo. That was useful to take a look.

You can also remove jest / jest-pact from the package.json devDependencies, and the jest top level key as they are not needed or used in your examples from what I can see. You also don't need "@pact-foundation/pact-core" as it is not a peer dep and is included in the pact-foundation/pact package

I know, because basically I've mixed app two different setup to provide quick repro.

@YOU54F
Copy link
Member

YOU54F commented Dec 7, 2023

thank you, that helps! I'm not adverse to an example using native fetch if you wanted to add on to the pact-js repo. it could also be coupled with vitest as in your example, as we've seen at least 5 requests ( not huge I know ) in any many months for an example of Pact with Vitest.

I've added a pr to your repo which shows the changes I had to make to give you the correct feedback for incorrect routes and incorrect assertions.

I've got other examples using fetch and Pact, one such is here generated from Swagger codegen templates, https://github.com/SmartBear-DevRel/swaggerhub-pactflow/blob/main/client-codegen-pact/api_test.spec.ts

@andrzej-hanusek-tg
Copy link
Author

andrzej-hanusek-tg commented Dec 12, 2023

@YOU54F

Thanks for PR. I've merged your changes, but still have the same results for fetch test:

Error: Error in native callback
❯ mockServerMismatches node_modules/@pact-foundation/pact/node_modules/@pact-foundation/pact-core/src/consumer/internals.ts:10:9

I see that fetch test is skipped, do you able run this test with success result?

UPDATE: switching node from v16 to v19 helps! I switched before to v16 and forgot about it. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting feedback Awaiting Feedback from OP bug Indicates an unexpected problem or unintended behavior
Projects
None yet
Development

No branches or pull requests

3 participants