Giter Club home page Giter Club logo

pact-workshop-js's Introduction

Pact JS workshop

Introduction

This workshop is aimed at demonstrating core features and benefits of contract testing with Pact.

Whilst contract testing can be applied retrospectively to systems, we will follow the consumer driven contracts approach in this workshop - where a new consumer and provider are created in parallel to evolve a service over time, especially where there is some uncertainty with what is to be built.

This workshop should take from 1 to 2 hours, depending on how deep you want to go into each topic.

Workshop outline:

NOTE: Each step is tied to, and must be run within, a git branch, allowing you to progress through each stage incrementally.

EG: Move to step 2:

git checkout step2

npm install


Learning objectives

If running this as a team workshop format, you may want to take a look through the learning objectives.

Requirements

Docker

Docker Compose

Node + NPM

Scenario

There are two components in scope for our workshop.

  1. Product Catalog website. It provides an interface to query the Product service for product information.
  2. Product Service (Provider). Provides useful things about products, such as listing all products and getting the details of an individual product.

Step 1 - Simple Consumer calling Provider

We need to first create an HTTP client to make the calls to our provider service:

Simple Consumer

The Consumer has implemented the product service client which has the following:

  • GET /products - Retrieve all products
  • GET /products/{id} - Retrieve a single product by ID

The diagram below highlights the interaction for retrieving a product with ID 10:

Sequence Diagram

You can see the client interface we created in consumer/src/api.js:

export class API {

    constructor(url) {
        if (url === undefined || url === "") {
            url = process.env.REACT_APP_API_BASE_URL;
        }
        if (url.endsWith("/")) {
            url = url.substr(0, url.length - 1)
        }
        this.url = url
    }

    withPath(path) {
        if (!path.startsWith("/")) {
            path = "/" + path
        }
        return `${this.url}${path}`
    }

    async getAllProducts() {
        return axios.get(this.withPath("/products"))
            .then(r => r.data);
    }

    async getProduct(id) {
        return axios.get(this.withPath("/products/" + id))
            .then(r => r.data);
    }
}

After forking or cloning the repository, we may want to install the dependencies npm install. We can run the client with npm start --prefix consumer - it should fail with the error below, because the Provider is not running.

Failed step1 page

Move on to step 2

Step 2 - Client Tested but integration fails

NOTE: Move to step 2:

git checkout step2

npm install


Now lets create a basic test for our API client. We're going to check 2 things:

  1. That our client code hits the expected endpoint
  2. That the response is marshalled into an object that is usable, with the correct ID

You can see the client interface test we created in consumer/src/api.spec.js:

import API from "./api";
import nock from "nock";

describe("API", () => {

    test("get all products", async () => {
        const products = [
            {
                "id": "9",
                "type": "CREDIT_CARD",
                "name": "GEM Visa",
                "version": "v2"
            },
            {
                "id": "10",
                "type": "CREDIT_CARD",
                "name": "28 Degrees",
                "version": "v1"
            }
        ];
        nock(API.url)
            .get('/products')
            .reply(200,
                products,
                {'Access-Control-Allow-Origin': '*'});
        const respProducts = await API.getAllProducts();
        expect(respProducts).toEqual(products);
    });

    test("get product ID 50", async () => {
        const product = {
            "id": "50",
            "type": "CREDIT_CARD",
            "name": "28 Degrees",
            "version": "v1"
        };
        nock(API.url)
            .get('/products/50')
            .reply(200, product, {'Access-Control-Allow-Origin': '*'});
        const respProduct = await API.getProduct("50");
        expect(respProduct).toEqual(product);
    });
});

Unit Test With Mocked Response

Let's run this test and see it all pass:

npm test --prefix consumer

PASS src/api.spec.js
  API
    ✓ get all products (15ms)
    ✓ get product ID 50 (3ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.03s
Ran all test suites.

If you encounter failing tests after running npm test --prefix consumer, make sure that the current branch is step2.

Meanwhile, our provider team has started building out their API in parallel. Let's run our website against our provider (you'll need two terminals to do this):

# Terminal 1npm start --prefix provider

Provider API listening on port 8080...
# Terminal 2
> npm start --prefix consumer

Compiled successfully!

You can now view pact-workshop-js in the browser.

  Local:            http://127.0.0.1:3000/
  On Your Network:  http://192.168.20.17:3000/

Note that the development build is not optimized.
To create a production build, use npm run build.

You should now see a screen showing 3 different products. There is a See more! button which should display detailed product information.

Let's see what happens!

Failed page

Doh! We are getting 404 everytime we try to view detailed product information. On closer inspection, the provider only knows about /product/{id} and /products.

We need to have a conversation about what the endpoint should be, but first...

Move on to step 3

Step 3 - Pact to the rescue

NOTE: Move to step 3:

git checkout step3

npm install


Unit tests are written and executed in isolation of any other services. When we write tests for code that talk to other services, they are built on trust that the contracts are upheld. There is no way to validate that the consumer and provider can communicate correctly.

An integration contract test is a test at the boundary of an external service verifying that it meets the contract expected by a consuming service — Martin Fowler

Adding contract tests via Pact would have highlighted the /product/{id} endpoint was incorrect.

Let us add Pact to the project and write a consumer pact test for the GET /products/{id} endpoint.

Provider states is an important concept of Pact that we need to introduce. These states help define the state that the provider should be in for specific interactions. For the moment, we will initially be testing the following states:

  • product with ID 10 exists
  • products exist

The consumer can define the state of an interaction using the given property.

Note how similar it looks to our unit test:

In consumer/src/api.pact.spec.js:

import path from "path";
import { PactV3, MatchersV3, SpecificationVersion, } from "@pact-foundation/pact";
import { API } from "./api";
const { eachLike, like } = MatchersV3;

const provider = new PactV3({
  consumer: "FrontendWebsite",
  provider: "ProductService",
  log: path.resolve(process.cwd(), "logs", "pact.log"),
  logLevel: "warn",
  dir: path.resolve(process.cwd(), "pacts"),
  spec: SpecificationVersion.SPECIFICATION_VERSION_V2,
  host: "127.0.0.1"
});

describe("API Pact test", () => {
  describe("getting all products", () => {
    test("products exists", async () => {
      // set up Pact interactions
      await provider.addInteraction({
        states: [{ description: "products exist" }],
        uponReceiving: "get all products",
        withRequest: {
          method: "GET",
          path: "/products",
        },
        willRespondWith: {
          status: 200,
          headers: {
            "Content-Type": "application/json; charset=utf-8",
          },
          body: eachLike({
            id: "09",
            type: "CREDIT_CARD",
            name: "Gem Visa",
          }),
        },
      });

      await provider.executeTest(async (mockService) => {
        const api = new API(mockService.url);

        // make request to Pact mock server
        const product = await api.getAllProducts();

        expect(product).toStrictEqual([
          { id: "09", name: "Gem Visa", type: "CREDIT_CARD" },
        ]);
      });
    });
  });

  describe("getting one product", () => {
    test("ID 10 exists", async () => {
      // set up Pact interactions
      await provider.addInteraction({
        states: [{ description: "product with ID 10 exists" }],
        uponReceiving: "get product with ID 10",
        withRequest: {
          method: "GET",
          path: "/products/10",
        },
        willRespondWith: {
          status: 200,
          headers: {
            "Content-Type": "application/json; charset=utf-8",
          },
          body: like({
            id: "10",
            type: "CREDIT_CARD",
            name: "28 Degrees",
          }),
        },
      });

      await provider.executeTest(async (mockService) => {
        const api = new API(mockService.url);

        // make request to Pact mock server
        const product = await api.getProduct("10");

        expect(product).toStrictEqual({
          id: "10",
          type: "CREDIT_CARD",
          name: "28 Degrees",
        });
      });
    });
  });
});

Test using Pact

This test starts a mock server a random port that acts as our provider service. To get this to work we update the URL in the Client that we create, after initialising Pact.

To simplify running the tests, add this to consumer/package.json:

// add it under scripts
"test:pact": "cross-env CI=true react-scripts test --testTimeout 30000 pact.spec.js",

Running this test still passes, but it creates a pact file which we can use to validate our assumptions on the provider side, and have conversation around.

npm run test:pact --prefix consumer

PASS src/api.spec.js
PASS src/api.pact.spec.js

Test Suites: 2 passed, 2 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        2.792s, estimated 3s
Ran all test suites.

A pact file should have been generated in consumer/pacts/FrontendWebsite-ProductService.json

NOTE: even if the API client had been graciously provided for us by our Provider Team, it doesn't mean that we shouldn't write contract tests - because the version of the client we have may not always be in sync with the deployed API - and also because we will write tests on the output appropriate to our specific needs.

Move on to step 4

Step 4 - Verify the provider

NOTE: Move to step 4:

git checkout step4

npm install


We need to make the pact file (the contract) that was produced from the consumer test available to the Provider module. This will help us verify that the provider can meet the requirements as set out in the contract. For now, we'll hard code the path to where it is saved in the consumer test, in step 11 we investigate a better way of doing this.

Now let's make a start on writing Pact tests to validate the consumer contract:

In provider/product/product.pact.test.js:

const { Verifier } = require('@pact-foundation/pact');
const path = require('path');

// Setup provider server to verify
const app = require('express')();
app.use(require('./product.routes'));
const server = app.listen("8080");

describe("Pact Verification", () => {
    it("validates the expectations of ProductService", () => {
        const opts = {
            logLevel: "INFO",
            providerBaseUrl: "http://127.0.0.1:8080",
            provider: "ProductService",
            providerVersion: "1.0.0",
            pactUrls: [
                path.resolve(__dirname, '../../consumer/pacts/FrontendWebsite-ProductService.json')
            ]
        };

        return new Verifier(opts).verifyProvider().then(output => {
            console.log(output);
        }).finally(() => {
            server.close();
        });
    })
});

To simplify running the tests, add this to provider/package.json:

// add it under scripts
"test:pact": "jest --testTimeout=30000 --testMatch \"**/*.pact.test.js\""

We now need to validate the pact generated by the consumer is valid, by executing it against the running service provider, which should fail:

npm run test:pact --prefix provider

Verifying a pact between FrontendWebsite and ProductService

  get product with ID 10
    returns a response which
      has status code 200 (FAILED)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (FAILED)
      has a matching body (FAILED)

  get all products
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (OK)


Failures:

1) Verifying a pact between FrontendWebsite and ProductService Given product with ID 10 exists - get product with ID 10
    1.1) has a matching body
           expected 'application/json;charset=utf-8' body but was 'text/html;charset=utf-8'
    1.2) has status code 200
           expected 200 but was 404
    1.3) includes header 'Content-Type' with value 'application/json; charset=utf-8'
           Expected header 'Content-Type' to have value 'application/json; charset=utf-8' but was 'text/html; charset=utf-8'

Pact Verification

The test has failed, as the expected path /products/{id} is returning 404. We incorrectly believed our provider was following a RESTful design, but the authors were too lazy to implement a better routing solution 🤷🏻‍♂️.

The correct endpoint which the consumer should call is /product/{id}.

Move on to step 5

Step 5 - Back to the client we go

NOTE: Move to step 5:

git checkout step5

npm install


We now need to update the consumer client and tests to hit the correct product path.

First, we need to update the GET route for the client:

In consumer/src/api.js:

async getProduct(id) {
  return axios.get(this.withPath("/product/" + id))
  .then(r => r.data);
}

Then we need to update the Pact test ID 10 exists to use the correct endpoint in path.

In consumer/src/api.pact.spec.js:

describe("getting one product", () => {
  test("ID 10 exists", async () => {

    // set up Pact interactions
    await provider.addInteraction({
      state: 'product with ID 10 exists',
      uponReceiving: 'get product with ID 10',
      withRequest: {
        method: 'GET',
        path: '/product/10'
      },

...

Pact Verification

Let's run and generate an updated pact file on the client:

npm run test:pact --prefix consumer

PASS src/api.pact.spec.js
  API Pact test
    getting all products
      ✓ products exists (18ms)
    getting one product
      ✓ ID 10 exists (8ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.106s
Ran all test suites matching /pact.spec.js/i.

Now we run the provider tests again with the updated contract:

Run the command:

npm run test:pact --prefix provider

Verifying a pact between FrontendWebsite and ProductService

  get product with ID 10
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (OK)

  get all products
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (OK)

Yay - green ✅!

Move on to step 6

Step 6 - Consumer updates contract for missing products

NOTE: Move to step 6:

git checkout step6

npm install


We're now going to add 2 more scenarios for the contract

  • What happens when we make a call for a product that doesn't exist? We assume we'll get a 404.

  • What happens when we make a call for getting all products but none exist at the moment? We assume a 200 with an empty array.

Let's write a test for these scenarios, and then generate an updated pact file.

In consumer/src/api.pact.spec.js:

// within the 'getting all products' group
test("no products exists", async () => {

  // set up Pact interactions
  await provider.addInteraction({
    state: 'no products exist',
    uponReceiving: 'get all products',
    withRequest: {
      method: 'GET',
      path: '/products'
    },
    willRespondWith: {
      status: 200,
      headers: {
        'Content-Type': 'application/json; charset=utf-8'
      },
      body: []
    },
  });

  const api = new API(provider.mockService.baseUrl);

  // make request to Pact mock server
  const product = await api.getAllProducts();

  expect(product).toStrictEqual([]);
});

// within the 'getting one product' group
test("product does not exist", async () => {

  // set up Pact interactions
  await provider.addInteraction({
    state: 'product with ID 11 does not exist',
    uponReceiving: 'get product with ID 11',
    withRequest: {
      method: 'GET',
      path: '/product/11'
    },
    willRespondWith: {
      status: 404
    },
  });

  await provider.executeTest(async (mockService) => {
    const api = new API(mockService.url);

    // make request to Pact mock server
    await expect(api.getProduct("11")).rejects.toThrow(
    "Request failed with status code 404"
    );
  });
});

Notice that our new tests look almost identical to our previous tests, and only differ on the expectations of the response - the HTTP request expectations are exactly the same.

npm run test:pact --prefix consumer

PASS src/api.pact.spec.js
  API Pact test
    getting all products
      ✓ products exists (24ms)
      ✓ no products exists (13ms)
    getting one product
      ✓ ID 10 exists (14ms)
      ✓ product does not exist (14ms)

Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        2.437s, estimated 3s
Ran all test suites matching /pact.spec.js/i.

What does our provider have to say about this new test:

npm run test:pact --prefix provider

Verifying a pact between FrontendWebsite and ProductService

  get all products
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (FAILED)

  get product with ID 10
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (OK)

  get product with ID 11
    returns a response which
      has status code 404 (FAILED)
      has a matching body (OK)

  get all products
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (OK)


Failures:

1) Verifying a pact between FrontendWebsite and ProductService Given no products exist - get all products
    1.1) has a matching body
           $ -> Expected an empty List but received [{"id":"09","name":"Gem Visa","type":"CREDIT_CARD","version":"v1"},{"id":"10","name":"28 Degrees","type":"CREDIT_CARD","version":"v1"},{"id":"11","name":"MyFlexiPay","type":"PERSONAL_LOAN","version":"v2"}]
2) Verifying a pact between FrontendWebsite and ProductService Given product with ID 11 does not exist - get product with ID 11
    2.1) has status code 404
           expected 404 but was 200

We expected this failure, because the product we are requesing does in fact exist! What we want to test for, is what happens if there is a different state on the Provider. This is what is referred to as "Provider states", and how Pact gets around test ordering and related issues.

We could resolve this by updating our consumer test to use a known non-existent product, but it's worth understanding how Provider states work more generally.

Move on to step 7

Step 7 - Adding the missing states

NOTE: Move to step 7:

git checkout step7

npm install


Our code already deals with missing users and sends a 404 response, however our test data fixture always has product ID 10 and 11 in our database.

In this step, we will add a state handler (stateHandlers) to our provider Pact verifications, which will update the state of our data store depending on which states the consumers require.

States are invoked prior to the actual test function is invoked. You can see the full lifecycle here.

We're going to add handlers for all our states:

  • products exist
  • no products exist
  • product with ID 10 exists
  • product with ID 11 does not exist

Let's open up our provider Pact verifications in provider/product/product.pact.test.js:

// add this to the Verifier opts
stateHandlers: {
  "product with ID 10 exists": () => {
    controller.repository.products = new Map([
      ["10", new Product("10", "CREDIT_CARD", "28 Degrees", "v1")]
    ]);
  },
  "products exist": () => {
    controller.repository.products = new Map([
      ["09", new Product("09", "CREDIT_CARD", "Gem Visa", "v1")],
      ["10", new Product("10", "CREDIT_CARD", "28 Degrees", "v1")]
    ]);
  },
  "no products exist": () => {
    controller.repository.products = new Map();
  },
  "product with ID 11 does not exist": () => {
    controller.repository.products = new Map();
  },
}

Let's see how we go now:

npm run test:pact --prefix provider

Verifying a pact between FrontendWebsite and ProductService

  get all products
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (OK)

  get product with ID 10
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (OK)

  get product with ID 11
    returns a response which
      has status code 404 (OK)
      has a matching body (OK)

  get all products
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (OK)

NOTE: The states are not necessarily a 1 to 1 mapping with the consumer contract tests. You can reuse states amongst different tests. In this scenario we could have used no products exist for both tests which would have equally been valid.

Move on to step 8

Step 8 - Authorization

NOTE: Move to step 8:

git checkout step8

npm install


It turns out that not everyone should be able to use the API. After a discussion with the team, it was decided that a time-bound bearer token would suffice. The token must be in yyyy-MM-ddTHHmm format and within 1 hour of the current time.

In the case a valid bearer token is not provided, we expect a 401. Let's update the consumer to pass the bearer token, and capture this new 401 scenario.

In consumer/src/api.js:

    generateAuthToken() {
        return "Bearer " + new Date().toISOString()
    }

    async getAllProducts() {
        return axios.get(this.withPath("/products"), {
            headers: {
                "Authorization": this.generateAuthToken()
            }
        })
            .then(r => r.data);
    }

    async getProduct(id) {
        return axios.get(this.withPath("/product/" + id), {
            headers: {
                "Authorization": this.generateAuthToken()
            }
        })
            .then(r => r.data);
    }

In consumer/src/api.pact.spec.js we add authentication headers to the request setup for the existing tests:

      await provider.addInteraction({
        states: [{ description: "no products exist" }],
        uponReceiving: "get all products",
        withRequest: {
          method: "GET",
          path: "/products",
          headers: {
            Authorization: like("Bearer 2019-01-14T11:34:18.045Z"),
          },
        },
        willRespondWith: {
          status: 200,
          headers: {
            "Content-Type": "application/json; charset=utf-8",
          },
          body: [],
        },
      });

and we also add two new tests for the "no auth token" use case:

    // ...
    test("no auth token", async () => {

      // set up Pact interactions
      await provider.addInteraction({
        states: [{ description: "product with ID 10 exists" }],
        uponReceiving: "get product by ID 10 with no auth token",
        withRequest: {
          method: "GET",
          path: "/product/10",
        },
        willRespondWith: {
          status: 401,
        },
      });

      await provider.executeTest(async (mockService) => {
        const api = new API(mockService.url);

        // make request to Pact mock server
        await expect(api.getProduct("10")).rejects.toThrow(
          "Request failed with status code 401"
        );
      });
    });

Generate a new Pact file:

npm run test:pact --prefix consumer

PASS src/api.pact.spec.js
  API Pact test
    getting all products
      ✓ products exists (23ms)
      ✓ no products exists (13ms)
      ✓ no auth token (14ms)
    getting one product
      ✓ ID 10 exists (12ms)
      ✓ product does not exist (12ms)
      ✓ no auth token (14ms)

Test Suites: 1 passed, 1 total
Tests:       6 passed, 6 total
Snapshots:   0 total
Time:        2.469s, estimated 3s
Ran all test suites matching /pact.spec.js/i.

We should now have two new interactions in our pact file.

Let's test the provider:

npm run test:pact --prefix provider

Verifying a pact between FrontendWebsite and ProductService

  get all products
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (OK)

  get product by ID 10 with no auth token
    returns a response which
      has status code 401 (FAILED)
      has a matching body (OK)

  get product with ID 10
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (OK)

  get product with ID 11
    returns a response which
      has status code 404 (OK)
      has a matching body (OK)

  get all products
    returns a response which
      has status code 401 (FAILED)
      has a matching body (OK)


Failures:

1) Verifying a pact between FrontendWebsite and ProductService Given product with ID 10 exists - get product by ID 10 with no auth token
    1.1) has status code 401
           expected 401 but was 200
2) Verifying a pact between FrontendWebsite and ProductService Given products exist - get all products
    2.1) has status code 401
           expected 401 but was 200

Now with the most recently added interactions where we are expecting a response of 401 when no authorization header is sent, we are getting 200...

Move on to step 9*

Step 9 - Implement authorisation on the provider

NOTE: Move to step 9:

git checkout step9

npm install


We will add a middleware to check the Authorization header and deny the request with 401 if the token is older than 1 hour.

In provider/middleware/auth.middleware.js

// 'Token' should be a valid ISO 8601 timestamp within the last hour
const isValidAuthTimestamp = (timestamp) => {
    let diff = (new Date() - new Date(timestamp)) / 1000;
    return diff >= 0 && diff <= 3600
};

const authMiddleware = (req, res, next) => {
    if (!req.headers.authorization) {
        return res.status(401).json({ error: "Unauthorized" });
    }
    const timestamp = req.headers.authorization.replace("Bearer ", "")
    if (!isValidAuthTimestamp(timestamp)) {
        return res.status(401).json({ error: "Unauthorized" });
    }
    next();
};

module.exports = authMiddleware;

In provider/server.js

const authMiddleware = require('./middleware/auth.middleware');

// add this into your init function
app.use(authMiddleware);

We also need to add the middleware to the server our Pact tests use.

In provider/product/product.pact.test.js:

const authMiddleware = require('../middleware/auth.middleware');
app.use(authMiddleware);

This means that a client must present an HTTP Authorization header that looks as follows:

Authorization: Bearer 2006-01-02T15:04

Let's test this out:

npm run test:pact --prefix provider

Verifying a pact between FrontendWebsite and ProductService

  get all products
    returns a response which
      has status code 200 (FAILED)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (FAILED)

  get product by ID 10 with no auth token
    returns a response which
      has status code 401 (OK)
      has a matching body (OK)

  get product with ID 10
    returns a response which
      has status code 200 (FAILED)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (FAILED)

  get product with ID 11
    returns a response which
      has status code 404 (FAILED)
      has a matching body (OK)

  get all products
    returns a response which
      has status code 401 (OK)
      has a matching body (OK)


Failures:

1) Verifying a pact between FrontendWebsite and ProductService Given no products exist - get all products
    1.1) has a matching body
           $ -> Type mismatch: Expected List [] but received Map {"error":"Unauthorized"}
    1.2) has status code 200
           expected 200 but was 401
2) Verifying a pact between FrontendWebsite and ProductService Given product with ID 10 exists - get product with ID 10
    2.1) has a matching body
           $ -> Actual map is missing the following keys: id, name, type
    2.2) has status code 200
           expected 200 but was 401
3) Verifying a pact between FrontendWebsite and ProductService Given product with ID 11 does not exist - get product with ID 11
    3.1) has status code 404
           expected 404 but was 401

There were 3 pact failures

Oh, dear. More tests are failing. Can you understand why?

Move on to step 10

Step 10 - Request Filters on the Provider

NOTE: Move to step 10:

git checkout step10

npm install


Because our pact file has static data in it, our bearer token is now out of date, so when Pact verification passes it to the Provider we get a 401. There are multiple ways to resolve this - mocking or stubbing out the authentication component is a common one. In our use case, we are going to use a process referred to as Request Filtering, using a RequestFilter.

NOTE: This is an advanced concept and should be used carefully, as it has the potential to invalidate a contract by bypassing its constraints. See https://github.com/DiUS/pact-jvm/blob/master/provider/junit/README.md#modifying-the-requests-before-they-are-sent for more details on this.

The approach we are going to take to inject the header is as follows:

  1. If we receive any Authorization header, we override the incoming request with a valid (in time) Authorization header, and continue with whatever call was being made
  2. If we don't receive an Authorization header, we do nothing

NOTE: We are not considering the 403 scenario in this example.

In provider/product/product.pact.test.js:

// add this to the Verifier opts
requestFilter: (req, res, next) => {
  if (!req.headers["authorization"]) {
    next();
    return;
  }
  req.headers["authorization"] = `Bearer ${ new Date().toISOString() }`;
  next();
},

We can now run the Provider tests

npm run test:pact --prefix provider

Move on to step 11

Step 11 - Using a Pact Broker

NOTE: Move to step 11:

git checkout step11

npm install


Broker collaboration Workflow

We've been publishing our pacts from the consumer project by essentially sharing the file system with the provider. But this is not very manageable when you have multiple teams contributing to the code base, and pushing to CI. We can use a Pact Broker to do this instead.

Using a broker simplifies the management of pacts and adds a number of useful features, including some safety enhancements for continuous delivery which we'll see shortly.

In this workshop we will be using the open source Pact broker.

Running the Pact Broker with docker-compose

In the root directory, run:

docker-compose up

Publish contracts from consumer

First, in the consumer project we need to tell Pact about our broker. We can use the in built pact-broker CLI command to do this:

// add this under scripts
"pact:publish": "pact-broker publish ./pacts --consumer-app-version=\"1.0.0\" --auto-detect-version-properties --broker-base-url=http://127.0.0.1:8000 --broker-username pact_workshop --broker-password pact_workshop"

Now run

npm run test:pact --prefix consumer

PASS src/api.pact.spec.js
  API Pact test
    getting all products
      ✓ products exists (22ms)
      ✓ no products exists (12ms)
      ✓ no auth token (13ms)
    getting one product
      ✓ ID 10 exists (11ms)
      ✓ product does not exist (12ms)
      ✓ no auth token (14ms)

Test Suites: 1 passed, 1 total
Tests:       6 passed, 6 total
Snapshots:   0 total
Time:        2.653s
Ran all test suites matching /pact.spec.js/i.

To publish the pacts:

❯ npm run pact:publish --prefix consumer

Created FrontendWebsite version 24c0e1-step11+24c0e1.SNAPSHOT.SB-AS-G7GM9F7 with branch step11
Pact successfully published for FrontendWebsite version 24c0e1-step11+24c0e1.SNAPSHOT.SB-AS-G7GM9F7 and provider ProductService.
  View the published pact at http://127.0.0.1:8000/pacts/provider/ProductService/consumer/FrontendWebsite/version/24c0e1-step11%2B24c0e1.SNAPSHOT.SB-AS-G7GM9F7
  Events detected: contract_published (pact content is the same as previous versions with tags  and no new tags were applied)
  Next steps:
    * Configure separate ProductService pact verification build and webhook to trigger it when the pact content changes. See https://docs.pact.io/go/webhooks

*NOTE: you would usually only publish pacts from CI. *

Have a browse around the broker on http://127.0.0.1:8000 (with username/password: pact_workshop/pact_workshop) and see your newly published contract!

Verify contracts on Provider

All we need to do for the provider is update where it finds its pacts, from local URLs, to one from a broker.

In provider/product/product.pact.test.js:

//replace
pactUrls: [
  path.resolve(__dirname, '../pacts/FrontendWebsite-ProductService.json')
],

// with
pactBrokerUrl: process.env.PACT_BROKER_BASE_URL || "http://127.0.0.1:8000",
pactBrokerUsername: process.env.PACT_BROKER_USERNAME || "pact_workshop",
pactBrokerPassword: process.env.PACT_BROKER_PASSWORD || "pact_workshop",
// add to the opts {...}
publishVerificationResult: process.env.CI || process.env.PACT_BROKER_PUBLISH_VERIFICATION_RESULTS

Let's run the provider verification one last time after this change. It should print a few notices showing which pact(s) it has found from the broker, and why they were selected:

PACT_BROKER_PUBLISH_VERIFICATION_RESULTS=true npm run test:pact --prefix provider

The pact at http://127.0.0.1:8000/pacts/provider/ProductService/consumer/FrontendWebsite/pact-version/80d8e7379fc7d5cfe503665ec1776bfb139aa8cf is being verified because the pact content belongs to the consumer version matching the following criterion:
    * latest version of FrontendWebsite that has a pact with ProductService (9cd950-step10+9cd950.SNAPSHOT.SB-AS-G7GM9F7)

Verifying a pact between FrontendWebsite and ProductService

  get all products
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (OK)

  get product by ID 10 with no auth token
    returns a response which
      has status code 401 (OK)
      has a matching body (OK)

  get product with ID 10
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "application/json; charset=utf-8" (OK)
      has a matching body (OK)

  get product with ID 11
    returns a response which
      has status code 404 (OK)
      has a matching body (OK)

  get all products
    returns a response which
      has status code 401 (OK)
      has a matching body (OK)

As part of this process, the results of the verification - the outcome (boolean) and the detailed information about the failures at the interaction level - are published to the Broker also.

This is one of the Broker's more powerful features. Referred to as Verifications, it allows providers to report back the status of a verification to the broker. You'll get a quick view of the status of each consumer and provider on a nice dashboard. But, it is much more important than this!

Can I deploy?

With just a simple use of the pact-broker can-i-deploy tool - the Broker will determine if a consumer or provider is safe to release to the specified environment.

In this example, we will use the pact-cli tools which are contained in the pact-js package.

This is why we use npx in our example. Ensure you are in the consumer or provider folder. Alternatively you can download the cli tools, to your machine and make it globally available or use it from a Docker container.

You can run the pact-broker can-i-deploy checks as follows:

cd consumernpx pact-broker can-i-deploy \
               --pacticipant FrontendWebsite \
               --broker-base-url http://127.0.0.1:8000 \
               --broker-username pact_workshop \
               --broker-password pact_workshop \
               --latest

Computer says yes \o/

CONSUMER        | C.VERSION | PROVIDER       | P.VERSION | SUCCESS?
----------------|-----------|----------------|-----------|---------
FrontendWebsite | fe0b6a3   | ProductService | 1.0.0     | true

All required verification results are published and successful

----------------------------cd consumernpx pact-broker can-i-deploy \
                --pacticipant ProductService \
                --broker-base-url http://127.0.0.1:8000 \
                --broker-username pact_workshop \
                --broker-password pact_workshop \
                --latest

Computer says yes \o/

CONSUMER        | C.VERSION | PROVIDER       | P.VERSION | SUCCESS?
----------------|-----------|----------------|-----------|---------
FrontendWebsite | fe0b6a3   | ProductService | 1.0.0     | true

All required verification results are published and successful

That's it - you're now a Pact pro. Go build 🔨

If you have extra time, why not try out Pact Webhooks

Move on to step 12

Step 12 - Using Webhooks

NOTE: Move to step 12:

git checkout step12

npm install


honours course

When a consumer contract is published, we want to trigger a provider build, in order to verify the contract.

We can simulate this locally and explore the techniques involved.

Update your docker-compose file to support webhooks running from your local machine

  1. in docker-compose.yaml
      PACT_BROKER_WEBHOOK_SCHEME_WHITELIST: http
      PACT_BROKER_WEBHOOK_HOST_WHITELIST: host.docker.internal

Recreate or Start the Pact Broker if its not already running

  1. docker compose up -d

Start the fake broker webhook service

  1. npm install - we need to install the dependencies for a fake broker-webhook service.
  2. npm run start --prefix broker-webhook
> [email protected] start
> node server.js

## CI Simulator ## Broker webhook is listening on port 9090...

Publish a webhook to our Pact broker

  1. npm run create-webhook --prefix broker-webhook
curl http://host.docker.internal:8000/webhooks \
    -X POST --user pact_workshop:pact_workshop \
    -H "Content-Type: application/json" -d @broker-create-body.json -v

This will send the following payload.

{
"events": [
  {
    "name": "contract_content_changed"
  }
],
"request": {
  "method": "POST",
  "url": "http://host.docker.internal:9090",
  "headers": {
    "Content-Type": "application/json"
  },
  "body": {
    "state": "${pactbroker.githubVerificationStatus}",
    "description": "Pact Verification Tests ${pactbroker.providerVersionTags}",
    "context": "${pactbroker.providerName}",
    "target_url": "${pactbroker.verificationResultUrl}"
  }
}
}
> [email protected] create-webhook
> ./create_webhook.sh

Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
* Server auth using Basic with user 'pact_workshop'
> POST /webhooks HTTP/1.1
> Host: localhost:8000
> Authorization: Basic cGFjdF93b3Jrc2hvcDpwYWN0X3dvcmtzaG9w
> User-Agent: curl/8.1.2
> Accept: */*
> Content-Type: application/json
> Content-Length: 511
> 
< HTTP/1.1 201 Created
< Vary: Accept
< Content-Type: application/hal+json;charset=utf-8
< Location: http://localhost:8000/webhooks/QxdSU5uCDllJTLDS_iLbNg
< Date: Fri, 29 Sep 2023 14:28:43 GMT
< Server: Webmachine-Ruby/2.0.0 Rack/1.3
< X-Pact-Broker-Version: 2.107.1
< X-Content-Type-Options: nosniff
< Content-Length: 926
< 
* Connection #0 to host localhost left intact
{"uuid":"QxdSU5uCDllJTLDS_iLbNg","description":"POST host.docker.internal","enabled":true,"request":{"method":"POST","url":"http://host.docker.internal:9090","headers":{"Content-Type":"application/json"},"body":{"state":"${pactbroker.githubVerificationStatus}","description":"Pact Verification Tests ${pactbroker.providerVersionTags}","context":"${pactbroker.providerName}","target_url":"${pactbroker.verificationResultUrl}"}},"events":[{"name":"contract_content_changed"}],"createdAt":"2023-09-29T14:28:43+00:00","_links":{"self":{"title":"POST host.docker.internal","href":"http://localhost:8000/webhooks/QxdSU5uCDllJTLDS_iLbNg"},"pb:execute":{"title":"Test the execution of the webhook with the latest matching pact or verification by sending a POST request to this URL","href":"http://localhost:8000/webhooks/QxdSU5uCDllJTLDS_iLbNg/execute"},"pb:webhooks":{"title":"All webhooks","href":"http://localhost:8000/webhooks"}}}% 

Run the consumer pact tests

  1. npm run test:pact --prefix consumer

Publish the consumer pact tests

  1. npm run pact:publish --prefix consumer
> [email protected] pact:publish
> pact-broker publish ./pacts --consumer-app-version="1.0.1" --auto-detect-version-properties --broker-base-url=http://127.0.0.1:8000 --broker-username pact_workshop --broker-password pact_workshop

Created FrontendWebsite version 1.0.1 with branch all_steps
Pact successfully published for FrontendWebsite version 1.0.1 and provider ProductService.
  View the published pact at http://127.0.0.1:8000/pacts/provider/ProductService/consumer/FrontendWebsite/version/1.0.1
  Events detected: contract_published, contract_content_changed (first time untagged pact published)
  Webhook QxdSU5uCDllJTLDS_iLbNg triggered for event contract_content_changed.
    View logs at http://127.0.0.1:8000/triggered-webhooks/f8299b7a-53c4-4f5f-b4a8-7f87dbee1bdf/logs
  Next steps:
    * Add Pact verification tests to the ProductService build. See https://docs.pact.io/go/provider_verification

This will trigger the provider tests.

## CI Simulator ## Broker webhook is listening on port 9090...
Got webhook {"state":"pending","description":"Pact Verification Tests ","context":"ProductService","target_url":""} 
 Triggering provider tests...
provider-verification: 
> [email protected] test:pact
> jest --testTimeout 30000 --testMatch "**/*.pact.test.js"

Try publishing the contract again. You'll note the broker webhook does not trigger a 2nd time as the content has not changed.

> [email protected] pact:publish
> pact-broker publish ./pacts --consumer-app-version="1.0.1" --auto-detect-version-properties --broker-base-url=http://127.0.0.1:8000 --broker-username pact_workshop --broker-password pact_workshop

Updated FrontendWebsite version 1.0.1 with branch step12
Pact successfully republished for FrontendWebsite version 1.0.1 and provider ProductService with no content changes.
  View the published pact at http://127.0.0.1:8000/pacts/provider/ProductService/consumer/FrontendWebsite/version/1.0.1
  Events detected: contract_published
  No enabled webhooks found for the detected events

Try updating the version of the contract, in consumer/package.json

    "pact:publish": "pact-broker publish ./pacts --consumer-app-version=\"1.0.2\" --auto-detect-version-properties --broker-base-url=http://127.0.0.1:8000 --broker-username pact_workshop --broker-password pact_workshop",

You'll again note, that as the contract version has changed, but the contents have not changed, since the last verification, the Pact Broker is aware of this, pre-verifying the Pact without needing to trigger the provider build.

> [email protected] pact:publish
> pact-broker publish ./pacts --consumer-app-version="1.0.2" --auto-detect-version-properties --broker-base-url=http://127.0.0.1:8000 --broker-username pact_workshop --broker-password pact_workshop

Created FrontendWebsite version 1.0.2 with branch step12
Pact successfully published for FrontendWebsite version 1.0.2 and provider ProductService.
  View the published pact at http://127.0.0.1:8000/pacts/provider/ProductService/consumer/FrontendWebsite/version/1.0.2
  Events detected: contract_published (pact content is the same as previous versions with tags  and no new tags were applied)
  No enabled webhooks found for the detected events

If you update the Pact contracts and attempt to republish under an existing version number, you will be stopped by the Pact Broker.

This ensures contracts remain consistent once published.

> [email protected] pact:publish
> pact-broker publish ./pacts --consumer-app-version="1.0.2" --auto-detect-version-properties --broker-base-url=http://127.0.0.1:8000 --broker-username pact_workshop --broker-password pact_workshop

Cannot change the content of the pact for ProductService version 1.0.2 and provider ProductService, as race conditions will cause unreliable results for can-i-deploy. Each pact must be published with a unique consumer version number. Some Pact libraries generate random data when a concrete value for a type matcher is not specified, and this can cause the contract to mutate - ensure you have given example values for all type matchers. For more information see https://docs.pact.io/go/versioning
     ... ,
     {
       "request": {
-        "path": "/product/10"
+        "path": "/product/11"
       }
     },
     ... ,

Optional - Move on to step 13 for integrating with a PactFlow Broker

Step 13 - Using a PactFlow Broker

NOTE: Move to step 13:

git checkout step13

npm install


In step 11 we've been publishing our pacts from the consumer and provider projects to our locally hosted open source Pact broker.

We can use a managed Pact Broker from PactFlow to do this instead.

Using a hosted pact broker with pactflow, will allow you to concentrate on testing your application without having to worry about managing infrastructure, along with a number of other useful features.

Creating a pactflow account

Create a new PactFlow account and signup to the free Starter Plan. You will be emailed a set of credentials to access your account, these credentials are only for accessing the UI.

Grab your API Token(Click on settings -> API Tokens -> Read/write token -> COPY ENV VARS) and set the environment variables in your terminal as follows:

export PACT_BROKER_BASE_URL=https://<your_broker_name>.pactflow.io
export PACT_BROKER_TOKEN=exampleToken

Update your scripts to use the pact broker token based authentication method

First, in the consumer project we need to tell Pact about our broker.

In consumer/publish.pact.js:

const pact = require('@pact-foundation/pact-node');

if (!process.env.CI && !process.env.PUBLISH_PACT) {
    console.log("skipping Pact publish...");
    process.exit(0)
}

const pactBrokerUrl = process.env.PACT_BROKER_BASE_URL || 'https://<your_broker_name>.pactflow.io';
const pactBrokerToken = process.env.PACT_BROKER_TOKEN || 'pact_workshop';

const gitHash = require('child_process')
    .execSync('git rev-parse --short HEAD')
    .toString().trim();

const opts = {
    pactFilesOrDirs: ['./pacts/'],
    pactBroker: pactBrokerUrl,
    pactBrokerToken: pactBrokerToken,
    tags: ['prod', 'test'],
    consumerVersion: gitHash
};

pact
    .publishPacts(opts)
    .then(() => {
        console.log('Pact contract publishing complete!');
        console.log('');
        console.log(`Head over to ${pactBrokerUrl}`);
        console.log('to see your published contracts.')
    })
    .catch(e => {
        console.log('Pact contract publishing failed: ', e)
    });

Now run

npm run test:pact --prefix consumer

> [email protected] test:pact /Users/you54f/dev/saf/dev/pact-workshop-clone/consumer
> react-scripts test --testTimeout 30000 pact.spec.js

PASS src/api.pact.spec.js
  API Pact test
    getting all products
      ✓ products exists (19ms)
      ✓ no products exists (10ms)
      ✓ no auth token (10ms)
    getting one product
      ✓ ID 10 exists (10ms)
      ✓ product does not exist (8ms)
      ✓ no auth token (12ms)

Then publish your pacts:

❯ npm run pact:publish --prefix consumer

> pact-broker publish ./pacts --consumer-app-version="1.0.0" --auto-detect-version-properties

Updated FrontendWebsite version 71c1b7-step12+71c1b7.SNAPSHOT.SB-AS-G7GM9F7 with branch step12
Pact successfully published for FrontendWebsite version 71c1b7-step12+71c1b7.SNAPSHOT.SB-AS-G7GM9F7 and provider ProductService.
  View the published pact at https://testdemo.pactflow.io/pacts/provider/ProductService/consumer/FrontendWebsite/version/71c1b7-step12%2B71c1b7.SNAPSHOT.SB-AS-G7GM9F7
  Events detected: contract_published, contract_requiring_verification_published, contract_content_changed (first time untagged pact published)
  Webhook "Automatically trigger pact verification on contract change." triggered for event contract_requiring_verification_published.
    View logs at https://testdemo.pactflow.io/triggered-webhooks/fa8d571e-8b61-41f8-9955-79a6fa9481fd/logs

Have a browse around your pactflow broker and see your newly published contract

Verify contracts on Provider

All we need to do for the provider is update where it finds its pacts, from local broker, to one from a hosted pactflow broker

In provider/product/product.pact.test.js:

//replace
pactBrokerUrl: process.env.PACT_BROKER_BASE_URL || "http://127.0.0.1:8000",
pactBrokerUsername: process.env.PACT_BROKER_USERNAME || "pact_workshop",
pactBrokerPassword: process.env.PACT_BROKER_PASSWORD || "pact_workshop",

// with
pactBrokerUrl :process.env.PACT_BROKER_BASE_URL || 'https://<your_broker_name>.pactflow.io',
pactBrokerToken: process.env.PACT_BROKER_TOKEN || 'pact_workshop',

Let's run the provider verification one last time after this change:

npm run test:pact --prefix provider

> [email protected] test:pact /Users/you54f/dev/saf/dev/pact-workshop-clone/provider
> jest --testTimeout 30000 --testMatch "**/*.pact.test.js"

INFO: [email protected]/84537 on safmac.local: Verifying provider
INFO: [email protected]/84537 on safmac.local: Verifying Pacts.
INFO: [email protected]/84537 on safmac.local: Verifying Pact Files
 PASS  product/product.pact.test.js (6.786s)
  Pact Verification
    ✓ validates the expectations of ProductService (6006ms)
    INFO: Verification results published to https://you54f.pactflow.io/pacts/provider/ProductService/consumer/FrontendWebsite/pact-version/c4b62aae734255d00eba62ced76594343a148e29/verification-results/256

Can I deploy?

As per step 11, we can use the can-i-deploy command to gate releases.

You can run the pact-broker can-i-deploy checks as follows:

cd consumernpx pact-broker can-i-deploy \
               --pacticipant FrontendWebsite \
               --latest

Computer says yes \o/

CONSUMER        | C.VERSION | PROVIDER       | P.VERSION | SUCCESS?
----------------|-----------|----------------|-----------|---------
FrontendWebsite | fe0b6a3   | ProductService | 1.0.0     | true

All required verification results are published and successful

----------------------------cd providernpx pact-broker can-i-deploy \
                --pacticipant ProductService \
                --latest

Computer says yes \o/

CONSUMER        | C.VERSION | PROVIDER       | P.VERSION | SUCCESS?
----------------|-----------|----------------|-----------|---------
FrontendWebsite | fe0b6a3   | ProductService | 1.0.0     | true

All required verification results are published and successful

NOTE: Because we have exported the PACT_* environment variables, we can omit the necessary flags on the command.

That's it - you're now a Pact pro. Go build 🔨

pact-workshop-js's People

Contributors

bengglim avatar bethesque avatar dependabot[bot] avatar edouard-lopez avatar mefellows avatar shilgam avatar timothyjones avatar uglyog avatar williaminfante avatar you54f avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

pact-workshop-js's Issues

Mismatch between instructions and sample code

The instructions say the Pact file should be copied from consumer/pacts to provider/pacts...
BUT
product.pact.test.js looks for the Pact file in consumer/pacts.

I'm happy to fix, but which was intended?

(Personally I favour copying the Pact file & modifying product.pact.test.js to look in provider/pacts)

Errors when running the tests in step 4

Hi there,

Whilst following the tutorial, I am getting the following errors when i run the tests using the command: npm run test:pact --prefix consumer. I have all the node_modules installed, im using node version 10.8.0

FAIL src/api.pact.spec.js (6.973s)
  API Pact test
    getting all products
      ✕ products exists (6ms)
      ✕ no products exists (1ms)
      ✕ no auth token (1ms)
    getting one product
      ✕ ID 10 exists (1ms)
      ✕ product does not exist (1ms)
      ✕ no auth token

  ● API Pact test › getting all products › products exists

    Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Error: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

      at mapper (node_modules/jest-jasmine2/build/queueRunner.js:25:45)

  ● API Pact test › getting all products › products exists

    TypeError: Cannot read property 'addInteraction' of undefined

      30 |
      31 |             // set up Pact interactions
    > 32 |             await provider.addInteraction({
         |                            ^
      33 |                 state: 'products exist',
      34 |                 uponReceiving: 'get all products',
      35 |                 withRequest: {

      at Pact.addInteraction (node_modules/@pact-foundation/src/httpPact.ts:102:29)
      at Object.test (src/api.pact.spec.js:32:28)

  ● API Pact test › getting all products › products exists

    TypeError: Cannot read property 'verify' of undefined

      19 |
      20 |     afterEach(async () => {
    > 21 |         await provider.verify();
         |                        ^
      22 |     });
      23 |
      24 |     afterAll(async () => {

      at Pact.verify (node_modules/@pact-foundation/src/httpPact.ts:113:8)
      at Object.afterEach (src/api.pact.spec.js:21:24)

  ● API Pact test › getting all products › no products exists

    Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Error: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

      at mapper (node_modules/jest-jasmine2/build/queueRunner.js:25:45)

  ● API Pact test › getting all products › no products exists

    TypeError: Cannot read property 'addInteraction' of undefined

      66 |
      67 |             // set up Pact interactions
    > 68 |             await provider.addInteraction({
         |                            ^
      69 |                 state: 'no products exist',
      70 |                 uponReceiving: 'get all products',
      71 |                 withRequest: {

      at Pact.addInteraction (node_modules/@pact-foundation/src/httpPact.ts:102:29)
      at Object.test (src/api.pact.spec.js:68:28)

  ● API Pact test › getting all products › no products exists

    TypeError: Cannot read property 'verify' of undefined

      19 |
      20 |     afterEach(async () => {
    > 21 |         await provider.verify();
         |                        ^
      22 |     });
      23 |
      24 |     afterAll(async () => {

      at Pact.verify (node_modules/@pact-foundation/src/httpPact.ts:113:8)
      at Object.afterEach (src/api.pact.spec.js:21:24)

  ● API Pact test › getting all products › no auth token

    Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Error: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

      at mapper (node_modules/jest-jasmine2/build/queueRunner.js:25:45)

  ● API Pact test › getting all products › no auth token

    TypeError: Cannot read property 'addInteraction' of undefined

       96 |
       97 |             // set up Pact interactions
    >  98 |             await provider.addInteraction({
          |                            ^
       99 |                 state: 'products exist',
      100 |                 uponReceiving: 'get all products with no auth token',
      101 |                 withRequest: {

      at Pact.addInteraction (node_modules/@pact-foundation/src/httpPact.ts:102:29)
      at Object.test (src/api.pact.spec.js:98:28)

  ● API Pact test › getting all products › no auth token

    TypeError: Cannot read property 'verify' of undefined

      19 |
      20 |     afterEach(async () => {
    > 21 |         await provider.verify();
         |                        ^
      22 |     });
      23 |
      24 |     afterAll(async () => {

      at Pact.verify (node_modules/@pact-foundation/src/httpPact.ts:113:8)
      at Object.afterEach (src/api.pact.spec.js:21:24)

  ● API Pact test › getting one product › ID 10 exists

    Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Error: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

      at mapper (node_modules/jest-jasmine2/build/queueRunner.js:25:45)

  ● API Pact test › getting one product › ID 10 exists

    TypeError: Cannot read property 'addInteraction' of undefined

      119 |
      120 |             // set up Pact interactions
    > 121 |             await provider.addInteraction({
          |                            ^
      122 |                 state: 'product with ID 10 exists',
      123 |                 uponReceiving: 'get product with ID 10',
      124 |                 withRequest: {

      at Pact.addInteraction (node_modules/@pact-foundation/src/httpPact.ts:102:29)
      at Object.test (src/api.pact.spec.js:121:28)

  ● API Pact test › getting one product › ID 10 exists

    TypeError: Cannot read property 'verify' of undefined

      19 |
      20 |     afterEach(async () => {
    > 21 |         await provider.verify();
         |                        ^
      22 |     });
      23 |
      24 |     afterAll(async () => {

      at Pact.verify (node_modules/@pact-foundation/src/httpPact.ts:113:8)
      at Object.afterEach (src/api.pact.spec.js:21:24)

  ● API Pact test › getting one product › product does not exist

    Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Error: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

      at mapper (node_modules/jest-jasmine2/build/queueRunner.js:25:45)

  ● API Pact test › getting one product › product does not exist

    TypeError: Cannot read property 'addInteraction' of undefined

      157 |
      158 |             // set up Pact interactions
    > 159 |             await provider.addInteraction({
          |                            ^
      160 |                 state: 'product with ID 11 does not exist',
      161 |                 uponReceiving: 'get product with ID 11',
      162 |                 withRequest: {

      at Pact.addInteraction (node_modules/@pact-foundation/src/httpPact.ts:102:29)
      at Object.test (src/api.pact.spec.js:159:28)

  ● API Pact test › getting one product › product does not exist

    TypeError: Cannot read property 'verify' of undefined

      19 |
      20 |     afterEach(async () => {
    > 21 |         await provider.verify();
         |                        ^
      22 |     });
      23 |
      24 |     afterAll(async () => {

      at Pact.verify (node_modules/@pact-foundation/src/httpPact.ts:113:8)
      at Object.afterEach (src/api.pact.spec.js:21:24)

  ● API Pact test › getting one product › no auth token

    Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Error: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

      at mapper (node_modules/jest-jasmine2/build/queueRunner.js:25:45)

  ● API Pact test › getting one product › no auth token

    TypeError: Cannot read property 'addInteraction' of undefined

      181 |
      182 |             // set up Pact interactions
    > 183 |             await provider.addInteraction({
          |                            ^
      184 |                 state: 'product with ID 10 exists',
      185 |                 uponReceiving: 'get product by ID 10 with no auth token',
      186 |                 withRequest: {

      at Pact.addInteraction (node_modules/@pact-foundation/src/httpPact.ts:102:29)
      at Object.test (src/api.pact.spec.js:183:28)

  ● API Pact test › getting one product › no auth token

    TypeError: Cannot read property 'verify' of undefined

      19 |
      20 |     afterEach(async () => {
    > 21 |         await provider.verify();
         |                        ^
      22 |     });
      23 |
      24 |     afterAll(async () => {

      at Pact.verify (node_modules/@pact-foundation/src/httpPact.ts:113:8)
      at Object.afterEach (src/api.pact.spec.js:21:24)

Test Suites: 1 failed, 1 total
Tests:       6 failed, 6 total
Snapshots:   0 total
Time:        7.528s
Ran all test suites matching /pact.spec.js/i.
Jest did not exit one second after the test run has completed.

Windows installation issues

Using node 12.18.2 and the bundled version of npm, executing npm install on Windows 10 results in the following output:

[0] > [email protected] install C:\Users\nope\git\pact-workshop-js\consumer\node_modules\chokidar\node_modules\fsevents
[0] > node-gyp rebuild
[0]
[0]
[0] C:\Users\nope\git\pact-workshop-js\consumer\node_modules\chokidar\node_modules\fsevents>if not defined npm_config_node_gyp (node "C:\ProgramData\nvm\v12.18.2\node_modules\npm\node_modules\npm-lifecycle\node-gyp-bin\\..\..\node_modules\node-gyp\bin\node-gyp.js" rebuild )  else (node "C:\ProgramData\nvm\v12.18.2\node_modules\npm\node_modules\node-gyp\bin\node-gyp.js" rebuild )
[0] Traceback (most recent call last):
[0]   File "C:\ProgramData\nvm\v12.18.2\node_modules\npm\node_modules\node-gyp\gyp\gyp_main.py", line 50, in <module>
[0]     sys.exit(gyp.script_main())
[0]   File "C:\ProgramData\nvm\v12.18.2\node_modules\npm\node_modules\node-gyp\gyp\pylib\gyp\__init__.py", line 554, in script_main
[0]     return main(sys.argv[1:])
[0]   File "C:\ProgramData\nvm\v12.18.2\node_modules\npm\node_modules\node-gyp\gyp\pylib\gyp\__init__.py", line 547, in main
[0]     return gyp_main(args)
[0]   File "C:\ProgramData\nvm\v12.18.2\node_modules\npm\node_modules\node-gyp\gyp\pylib\gyp\__init__.py", line 532, in gyp_main
[0]     generator.GenerateOutput(flat_list, targets, data, params)
[0]   File "C:\ProgramData\nvm\v12.18.2\node_modules\npm\node_modules\node-gyp\gyp\pylib\gyp\generator\msvs.py", line 2030, in GenerateOutput
[0]     sln_projects, project_objects, flat=msvs_version.FlatSolution())
[0]   File "C:\ProgramData\nvm\v12.18.2\node_modules\npm\node_modules\node-gyp\gyp\pylib\gyp\generator\msvs.py", line 1787, in _GatherSolutionFolders
[0]     return _DictsToFolders('', root, flat)
[0]   File "C:\ProgramData\nvm\v12.18.2\node_modules\npm\node_modules\node-gyp\gyp\pylib\gyp\generator\msvs.py", line 1740, in _DictsToFolders
[0]     for folder, contents in bucket.items():
[0] AttributeError: 'MSVSProject' object has no attribute 'items'
[0] gyp ERR! configure error
[0] gyp ERR! stack Error: `gyp` failed with exit code: 1
[0] gyp ERR! stack     at ChildProcess.onCpExit (C:\ProgramData\nvm\v12.18.2\node_modules\npm\node_modules\node-gyp\lib\configure.js:351:16)
[0] gyp ERR! stack     at ChildProcess.emit (events.js:315:20)
[0] gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:275:12)
[0] gyp ERR! System Windows_NT 10.0.19041
[0] gyp ERR! command "C:\\Program Files\\nodejs\\node.exe" "C:\\ProgramData\\nvm\\v12.18.2\\node_modules\\npm\\node_modules\\node-gyp\\bin\\node-gyp.js" "rebuild"
[0] gyp ERR! cwd C:\Users\nope\git\pact-workshop-js\consumer\node_modules\chokidar\node_modules\fsevents
[0] gyp ERR! node -v v12.18.2
[0] gyp ERR! node-gyp -v v5.1.0
[0] gyp ERR! not ok

What the heck is going on here? Why is fsevents even attempting to build on Windows?

Alway meet error when run npm run test:pact --prefix consumer

Thank you for reporting a bug! We appreciate it very much. Issues are a big input into the priorities for Pact-JS development

Software versions

Please provide at least OS and version of pact-js

  • OS: _e.g. Mac Catalina 10.15.5
  • Consumer Pact library: . "@pact-foundation/pact": "^9.11.1",
  • Provider Pact library:
  • Node Version: v14.17.5

Issue Checklist

Please confirm the following:

  • [Confirm ] I have upgraded to the latest
  • [Confirm ] I have the read the FAQs in the Readme
  • [Confirm ] I have triple checked, that there are no unhandled promises in my code and have read the section on intermittent test failures
  • [Confirm ] I have set my log level to debug and attached a log file showing the complete request/response cycle
  • [ Confirm] For bonus points and virtual high fives, I have created a reproduceable git repository (see below) to illustrate the problem

Expected behaviour

Now I following this tutorial https://docs.pactflow.io/docs/workshops/introduction/readme , at step 3 , I checkout code by 'git checkout step 3', then run command 'npm run test:pact --prefix consumer', so it should
return
'❯ npm run test:pact --prefix consumer

PASS src/api.spec.js
PASS src/api.pact.spec.js

Test Suites: 2 passed, 2 total
Tests: 4 passed, 4 total
Snapshots: 0 total
Time: 2.792s, estimated 3s
Ran all test suites.'

Actual behaviour

'MacBooks-MacBook-Air:pact-workshop-js macbookair$ npm run test:pact --prefix consumer

[email protected] test:pact /Users/macbookair/Desktop/Automation project/pact-workshop-js/consumer
CI=true react-scripts test --testTimeout=30000 pact.spec.js

[2021-09-11T07:14:08.642Z] ERROR: [email protected]/39337 on MacBooks-MacBook-Air.local:
Pact Binary Error: /Users/macbookair/Desktop/Automation project/pact-workshop-js/consumer/node_modules/@pact-foundation/pact-node/standalone/darwin-1.86.0/pact/lib/ruby/bin/ruby: line 14: project/pact-workshop-js/consumer/node_modules/@pact-foundation/pact-node/standalone/darwin-1.86.0/pact/lib/ruby/lib/ruby/gems/2.2.0: No such file or directory

[2021-09-11T07:14:08.646Z] WARN: [email protected]/39337 on MacBooks-MacBook-Air.local: Pact exited with code 1.
FAIL src/api.pact.spec.js (32.635s)
API Pact test
getting all products
✕ products exists (6ms)
getting one product
✕ ID 10 exists (2ms)

● API Pact test › getting all products › products exists

Pact startup failed; tried calling service 10 times with no result.

  at retry (node_modules/@pact-foundation/pact-node/src/service.ts:219:6)
  at Server.check (node_modules/@pact-foundation/pact-node/src/service.ts:236:5)
  at Timeout.callback [as _onTimeout] (node_modules/jsdom/lib/jsdom/browser/Window.js:678:19)

● API Pact test › getting all products › products exists

TypeError: Cannot read property 'addInteraction' of undefined

  24 | 
  25 |             // set up Pact interactions
> 26 |             await provider.addInteraction({
     |                            ^
  27 |                 state: 'products exist',
  28 |                 uponReceiving: 'get all products',
  29 |                 withRequest: {

  at Pact.addInteraction (node_modules/@pact-foundation/src/httpPact.ts:102:29)
  at Object.<anonymous> (src/api.pact.spec.js:26:28)

● API Pact test › getting all products › products exists

TypeError: Cannot read property 'verify' of undefined

  17 | 
  18 |     beforeAll(() => provider.setup());
> 19 |     afterEach(() => provider.verify());
     |                              ^
  20 |     afterAll(() => provider.finalize());
  21 | 
  22 |     describe("getting all products", () => {

  at Pact.verify (node_modules/@pact-foundation/src/httpPact.ts:113:8)
  at Object.<anonymous> (src/api.pact.spec.js:19:30)

● API Pact test › getting one product › ID 10 exists

Pact startup failed; tried calling service 10 times with no result.

  at retry (node_modules/@pact-foundation/pact-node/src/service.ts:219:6)
  at Server.check (node_modules/@pact-foundation/pact-node/src/service.ts:236:5)
  at Timeout.callback [as _onTimeout] (node_modules/jsdom/lib/jsdom/browser/Window.js:678:19)

● API Pact test › getting one product › ID 10 exists

TypeError: Cannot read property 'addInteraction' of undefined

  59 | 
  60 |             // set up Pact interactions
> 61 |             await provider.addInteraction({
     |                            ^
  62 |                 state: 'product with ID 10 exists',
  63 |                 uponReceiving: 'get product with ID 10',
  64 |                 withRequest: {

  at Pact.addInteraction (node_modules/@pact-foundation/src/httpPact.ts:102:29)
  at Object.<anonymous> (src/api.pact.spec.js:61:28)

● API Pact test › getting one product › ID 10 exists

TypeError: Cannot read property 'verify' of undefined

  17 | 
  18 |     beforeAll(() => provider.setup());
> 19 |     afterEach(() => provider.verify());
     |                              ^
  20 |     afterAll(() => provider.finalize());
  21 | 
  22 |     describe("getting all products", () => {

  at Pact.verify (node_modules/@pact-foundation/src/httpPact.ts:113:8)
  at Object.<anonymous> (src/api.pact.spec.js:19:30)

Test Suites: 1 failed, 1 total
Tests: 2 failed, 2 total
Snapshots: 0 total
Time: 34.134s
Ran all test suites matching /pact.spec.js/i.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] test:pact: CI=true react-scripts test --testTimeout=30000 pact.spec.js
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] test:pact script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /Users/macbookair/.npm/_logs/2021-09-11T07_14_37_522Z-debug.log'

Steps to reproduce

Relevant log files

"0 info it worked if it ends with ok
1 verbose cli [
1 verbose cli '/usr/local/bin/node',
1 verbose cli '/usr/local/bin/npm',
1 verbose cli 'run',
1 verbose cli 'test:pact',
1 verbose cli '--prefix',
1 verbose cli 'consumer'
1 verbose cli ]
2 info using [email protected]
3 info using [email protected]
4 verbose run-script [ 'pretest:pact', 'test:pact', 'posttest:pact' ]
5 info lifecycle [email protected]pretest:pact: [email protected]
6 info lifecycle [email protected]
test:pact: [email protected]
7 verbose lifecycle [email protected]test:pact: unsafe-perm in lifecycle true
8 verbose lifecycle [email protected]
test:pact: PATH: /usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/macbookair/Desktop/Automation project/pact-workshop-js/consumer/node_modules/.bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin
9 verbose lifecycle [email protected]test:pact: CWD: /Users/macbookair/Desktop/Automation project/pact-workshop-js/consumer
10 silly lifecycle [email protected]
test:pact: Args: [ '-c', 'CI=true react-scripts test --testTimeout=30000 pact.spec.js' ]
11 silly lifecycle [email protected]test:pact: Returned: code: 1 signal: null
12 info lifecycle [email protected]
test:pact: Failed to exec test:pact script
13 verbose stack Error: [email protected] test:pact: CI=true react-scripts test --testTimeout=30000 pact.spec.js
13 verbose stack Exit status 1
13 verbose stack at EventEmitter. (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/index.js:332:16)
13 verbose stack at EventEmitter.emit (events.js:400:28)
13 verbose stack at ChildProcess. (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/lib/spawn.js:55:14)
13 verbose stack at ChildProcess.emit (events.js:400:28)
13 verbose stack at maybeClose (internal/child_process.js:1055:16)
13 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:288:5)
14 verbose pkgid [email protected]
15 verbose cwd /Users/macbookair/Desktop/Automation project/pact-workshop-js
16 verbose Darwin 19.5.0
17 verbose argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "test:pact" "--prefix" "consumer"
18 verbose node v14.17.5
19 verbose npm v6.14.14
20 error code ELIFECYCLE
21 error errno 1
22 error [email protected] test:pact: CI=true react-scripts test --testTimeout=30000 pact.spec.js
22 error Exit status 1
23 error Failed at the [email protected] test:pact script.
23 error This is probably not a problem with npm. There is likely additional logging output above.
24 verbose exit [ 1, true ]"

Pact.json is not created / No failures in console

Hello,
Im writing a small programm for a University project.
I'd like to test it with the pact framewok. Unfortunately, no Pact.json file is created for me, although there are no errors.
Below you can see the source code of my javascript file and the console output:

`const {Pact} = require('@pact-foundation/pact');
const axios = require('axios');
const path = require('path');

const provider = new Pact({
consumer: 'consumer',
provider: 'producer',
port: 1234,
log: path.resolve(process.cwd(), 'logs', 'pact.log'),
dir: path.resolve(process.cwd(), 'pacts'),
logLevel: 'INFO',
});

describe('Pact Consumer', () => {

beforeAll(() => {
    return provider.setup();
});

afterEach(async () => {
    await provider.verify();
});

afterAll(async () => {
    return provider.finalize();
});

describe('consumer', () => {

beforeEach
(() =>

        provider.addInteraction({
            state: "valid date",
            uponReceiving: "a request for JSON data",
            withRequest: {
                method: "GET",
                path: "/test",
                headers: { Accept: "application/json" },
            },
            willRespondWith: {
                status: 200,
                headers: { "Content-Type": "application/json" },
                body:
                    {
                        name: 'Scherr',
                        surname: 'Valerian',
                        age: 28,
                    },

            },


    }),


);

});

describe('test', () => {
it('should return the correct data', () => {

  axios.get('localhost:1234/test').then(response => {
    expect(response[0].name).toBe('Scherr');
    expect(response[0].surname).toBe('Valerian');
    expect(response[0].age).toBe(28);
})

});
});
});
});

`

Console:
"C:\Program Files\nodejs\node.exe" C:\Users\vsche\IdeaProjects\testscenario\webservice\node_modules\jasmine\bin\jasmine.js --reporter=C:\Users\vsche\.IntelliJIdea2019.3\config\plugins\IdeaJasmine\lib\intellij_reporter.js C:/Users/vsche/IdeaProjects/testscenario/webservice/spec/WebService.spec.js --filter=test [2020-06-05T15:01:45.085Z] INFO: [email protected]/16548 on DESKTOP-FH2DU0O: Creating Pact Server with options: {"consumer":"consumer","cors":false,"dir":"C:\\Users\\vsche\\IdeaProjects\\testscenario\\pacts","host":"127.0.0.1","log":"C:\\Users\\vsche\\IdeaProjects\\testscenario\\logs\\pact.log","pactFileWriteMode":"overwrite","port":1234,"provider":"producer","spec":2,"ssl":false} [2020-06-05T15:01:46.128Z] INFO: [email protected]/16548 on DESKTOP-FH2DU0O: Setting up Pact with Consumer "consumer" and Provider "producer" using mock service on Port: "1234" [2020-06-05T15:01:46.168Z] INFO: [email protected]/16548 on DESKTOP-FH2DU0O: Pact File Written [2020-06-05T15:01:46.168Z] INFO: [email protected]/16548 on DESKTOP-FH2DU0O: Removing Pact process with PID: 7944 [2020-06-05T15:01:48.313Z] INFO: [email protected]/16548 on DESKTOP-FH2DU0O: Deleting Pact Server with options: {"consumer":"consumer","cors":false,"dir":"C:\\Users\\vsche\\IdeaProjects\\testscenario\\pacts","host":"127.0.0.1","log":"C:\\Users\\vsche\\IdeaProjects\\testscenario\\logs\\pact.log","pactFileWriteMode":"overwrite","port":1234,"provider":"producer","spec":2,"ssl":false}

I am grateful for any help and thanks in advance

Step9 npm install failes

Scenario
Checkout the repo
checkout step9 branch
run npm install

ci --prefix provider exited with code 0
npm ERR! code 1
npm ERR! path /Users/amitabak/dev/tutorials/pact-workshop-js
npm ERR! command failed
npm ERR! command sh -c concurrently "npm ci --prefix consumer" "npm ci --prefix provider"

npm ERR! A complete log of this run can be found in:

Note that step 7 completes OK
Seems the package-lock.json has some breaking changes in step9

Step11 pactBrokerUrl requires one of the following properties

Have followed all steps up to step11 without issue and getting the following error...

pactBrokerUrl requires one of the following properties: pactUrls,consumerVersionSelectors,consumerVersionTags

when running the following command >PACT_BROKER_PUBLISH_VERIFICATION_RESULTS=true npm run test:pact --prefix provider

Running the workshop on a MAC with M1 chip

Step 3 - Running pact tests with 'npm run test:pact --prefix consumer' command tests fail with 'Error in native callback'

Using a Mac OS Monterey 12.5.1
NPM version 9.6.4
Node version 18.15.0

In step3
Running pact tests with 'npm run test:pact --prefix consumer' command tests fail with 'Error in native callback'

`Error in native callback

  at Object.mockServerMismatches (node_modules/@pact-foundation/pact-core/src/consumer/index.ts:94:13)
  at PactV3.<anonymous> (node_modules/@pact-foundation/src/v3/pact.ts:207:39)
  at step (node_modules/@pact-foundation/pact/src/v3/pact.js:33:23)
  at Object.throw (node_modules/@pact-foundation/pact/src/v3/pact.js:14:53)
  at rejected (node_modules/@pact-foundation/pact/src/v3/pact.js:6:65)`

https://github.com/druoid/pact-workshop-js (no changes made to code)

issue running npm i on M1 Mac

If you clone the pact-workshop-js repo and try to install using a Mac m1 computer then you get the following error:

npm ERR! code EBADPLATFORM
[0] npm ERR! notsup Unsupported platform for @pact-foundation/[email protected]: wanted {"os":"darwin,linux,win32","arch":"x64,ia32"} (current: {"os":"darwin","arch":"arm64"})
[0] npm ERR! notsup Valid OS: darwin,linux,win32
[0] npm ERR! notsup Valid Arch: x64,ia32
[0] npm ERR! notsup Actual OS: darwin
[0] npm ERR! notsup Actual Arch: arm64

step1 npm install -- Maximum call stack size exceeded

I am doing a PoC for pact for work, tried to follow this workshop and ran into this issue. Can someone help here?

node 14.16.1 npm 6.14.12
Steps I tried multiple times:

  1. git clone
  2. git check out step1
  3. npm install

Tried to install pact global before running npm install, didn't work either.

Verifier verifyProvider took 17 min to run test

Hi there,

I try to run a test on src/provider but it always timeout so I had to setTimeout on Jest and Verifier(opts) to 30 sec but it not work so I decided to take 20 min and then it works! It took 17 min to process a test.

Any suggestions on this.
(I'm using node v12.16.1, MacBook air)

Debug logs here...

Click to expand!
 $ yarn test:pact
yarn run v1.22.4
$ npx jest --testMatch "**/*.pact.test.js"
[2020-05-25T03:27:31.429Z]  INFO: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: Verifying provider
[2020-05-25T03:27:31.445Z]  INFO: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: Verifying Pacts.
[2020-05-25T03:27:31.446Z]  INFO: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: Verifying Pact Files
[2020-05-25T03:27:31.447Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: Starting pact binary 'standalone/darwin-1.84.0/pact/bin/pact-provider-verifier', with arguments [--provider-states-setup-url http://localhost:51993/_pactSetup --provider-base-url http://localhost:51993 --provider ProductService --provider-app-version 1.0.0 --pact-broker-base-url http://localhost:8081 --broker-username pact_workshop --broker-password pact_workshop], and environment: {"cwd":"/Users/thuchchai/Github/pact-workshop-js/provider/node_modules/@pact-foundation/pact-node","detached":true,"env":{"rvm_use_flag":"","TERM_PROGRAM":"vscode","rvm_bin_path":"/Users/thuchchai/.rvm/bin","NODE":"/usr/local/bin/node","npm_package_devDependencies__pact_foundation_pact":"^9.6.0","INIT_CWD":"/Users/thuchchai/Github/pact-workshop-js/provider","rvm_quiet_flag":"","GEM_HOME":"/Users/thuchchai/.rvm/gems/ruby-2.4.2","ANDROID_HOME":"/Users/thuchchai/Library/Android/sdk","npm_config_version_git_tag":"true","rvm_gemstone_url":"","SHELL":"/bin/zsh","TERM":"xterm-256color","npm_package_devDependencies_jest":"^24.9.0","rvm_docs_type":"","IRBRC":"/Users/thuchchai/.rvm/rubies/ruby-2.4.2/.irbrc","TMPDIR":"/var/folders/qn/5ldvls554cq7rjy0h0fwpr6r0000gp/T/","npm_config_init_license":"MIT","npm_config_email":"[email protected]","TERM_PROGRAM_VERSION":"1.45.1","rvm_hook":"","MY_RUBY_HOME":"/Users/thuchchai/.rvm/rubies/ruby-2.4.2","TERM_SESSION_ID":"***","npm_config_git_tag_version":"","npm_config_registry":"https://registry.yarnpkg.com","LC_ALL":"en_US.UTF-8","ZSH":"/Users/thuchchai/.oh-my-zsh","USER":"thuchchai","npm_package_scripts_test_pact":"npx jest --testMatch \"**/*.pact.test.js\"","npm_package_description":"","npm_config_python":"/usr/bin/python","rvm_gemstone_package_file":"","npm_package_license":"ISC","_system_type":"Darwin","COMMAND_MODE":"unix2003","rvm_path":"/Users/thuchchai/.rvm","SSH_AUTH_SOCK":"/private/tmp/com.apple.launchd.HR9T4hvDBh/Listeners","__CF_USER_TEXT_ENCODING":"0x1F6:0x0:0x0","npm_package_devDependencies_eslint":"^6.8.0","rvm_proxy":"","npm_execpath":"/usr/local/Cellar/yarn/1.22.4/libexec/bin/yarn.js","rvm_ruby_file":"","PAGER":"less","npm_package_dependencies_cors":"^2.8.5","LSCOLORS":"Gxfxcxdxbxegedabagacad","rvm_silent_flag":"","rvm_prefix":"/Users/thuchchai","rvm_ruby_make":"","PATH":"/Users/thuchchai/Github/pact-workshop-js/provider/node_modules/.bin:/var/folders/qn/5ldvls554cq7rjy0h0fwpr6r0000gp/T/yarn--1590377241825-0.35979851382387706:/Users/thuchchai/Github/pact-workshop-js/provider/node_modules/.bin:/Users/thuchchai/.config/yarn/link/node_modules/.bin:/usr/local/libexec/lib/node_modules/npm/bin/node-gyp-bin:/usr/local/lib/node_modules/npm/bin/node-gyp-bin:/usr/local/bin/node_modules/npm/bin/node-gyp-bin:/Users/thuchchai/.rvm/gems/ruby-2.4.2/bin:/Users/thuchchai/.rvm/gems/ruby-2.4.2@global/bin:/Users/thuchchai/.rvm/rubies/ruby-2.4.2/bin:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/bin:/Users/thuchchai/ImageMagick-7.0.8/bin:/Users/thuchchai/.fastlane/bin:/Users/thuchchai/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/Users/thuchchai/.rvm/gems/ruby-2.4.2/bin:/Users/thuchchai/.rvm/gems/ruby-2.4.2@global/bin:/Users/thuchchai/.rvm/rubies/ruby-2.4.2/bin:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/bin:/Users/thuchchai/ImageMagick-7.0.8/bin:/Users/thuchchai/.fastlane/bin:/Users/thuchchai/bin:/Users/thuchchai/Library/Android/sdk/tools:/Users/thuchchai/Library/Android/sdk/tools/bin:/Users/thuchchai/Library/Android/sdk/platform-tools:/Users/thuchchai/.rvm/bin:/Users/thuchchai/Library/Android/sdk/tools:/Users/thuchchai/Library/Android/sdk/tools/bin:/Users/thuchchai/Library/Android/sdk/platform-tools:/Users/thuchchai/.rvm/bin","npm_config_argv":"{\"remain\":[],\"cooked\":[\"run\",\"test:pact\"],\"original\":[\"test:pact\"]}","_":"/usr/local/bin/npx","LaunchInstanceID":"5D1006F6-8044-460F-B7E6-D42D15C92426","PWD":"/Users/thuchchai/Github/pact-workshop-js/provider","JAVA_HOME":"/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home","npm_lifecycle_event":"test:pact","rvm_sdk":"","LANG":"en_US.UTF-8","npm_package_name":"product-service","ITERM_PROFILE":"Default","npm_package_scripts_start":"node server.js","npm_config_version_commit_hooks":"true","_system_arch":"x86_64","XPC_FLAGS":"0x0","npm_config_username":"thuchchai","npm_config_bin_links":"true","_system_version":"10.15","MAGICK_HOME":"/Users/thuchchai/ImageMagick-7.0.8","npm_package_main":"app.js","XPC_SERVICE_NAME":"0","npm_package_version":"1.0.0","rvm_version":"1.29.7 (latest)","rvm_script_name":"","rvm_pretty_print_flag":"","COLORFGBG":"15;0","SHLVL":"4","HOME":"/Users/thuchchai","rvm_ruby_mode":"","VSCODE_GIT_ASKPASS_MAIN":"/Applications/Visual Studio Code.app/Contents/Resources/app/extensions/git/dist/askpass-main.js","APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL":"true","npm_package_scripts_test":"npx jest","LC_TERMINAL_VERSION":"3.3.7","npm_config_save_prefix":"^","npm_config_strict_ssl":"true","npm_config_version_git_message":"v%s","NPM_CONFIG_PYTHON":"/usr/bin/python","ITERM_SESSION_ID":"****","LOGNAME":"thuchchai","LESS":"-R","YARN_WRAP_OUTPUT":"false","PREFIX":"/usr/local","rvm_alias_expanded":"","npm_lifecycle_script":"npx jest --testMatch \"**/*.pact.test.js\"","LC_CTYPE":"en_US.UTF-8","VSCODE_GIT_IPC_HANDLE":"/var/folders/qn/5ldvls554cq7rjy0h0fwpr6r0000gp/T/vscode-git-3548be83de.sock","GEM_PATH":"/Users/thuchchai/.rvm/gems/ruby-2.4.2:/Users/thuchchai/.rvm/gems/ruby-2.4.2@global","rvm_nightly_flag":"","npm_config_version_git_sign":"","npm_config_ignore_scripts":"","npm_config_user_agent":"yarn/1.22.4 npm/? node/v12.16.1 darwin x64","rvm_ruby_make_install":"","VSCODE_GIT_ASKPASS_NODE":"/Applications/Visual Studio Code.app/Contents/Frameworks/Code Helper (Renderer).app/Contents/MacOS/Code Helper (Renderer)","GIT_ASKPASS":"/Applications/Visual Studio Code.app/Contents/Resources/app/extensions/git/dist/askpass.sh","rvm_niceness":"","LC_TERMINAL":"iTerm2","rvm_ruby_bits":"","npm_package_dependencies_express":"^4.17.1","rvm_bin_flag":"","npm_config_init_version":"1.0.0","npm_config_ignore_optional":"","rvm_only_path_flag":"","SECURITYSESSIONID":"186a6","RUBY_VERSION":"ruby-2.4.2","_system_name":"OSX","COLORTERM":"truecolor","npm_node_execpath":"/usr/local/bin/node","npm_config_version_tag_prefix":"v","NODE_ENV":"test","JEST_WORKER_ID":"1"}}
[2020-05-25T03:27:31.457Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: Created 'standalone/darwin-1.84.0/pact/bin/pact-provider-verifier' process with PID: 5469
[2020-05-25T03:32:35.094Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
    INFO: Fetching pacts for ProductService from http://localhost:8081
    INFO: Reading pact at http://pact_workshop:*****@localhost:8081/pacts/provider/ProductService/consumer/FrontendWebsite/version/86a4112
    
    
[2020-05-25T03:32:35.097Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
    Verifying a pact between FrontendWebsite and ProductService
      Given products exist
        get all products
          with GET /products
            returns a response which
    

 RUNS  product/product.pact.test.js
[2020-05-25T03:35:05.646Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: Proxing /products
[2020-05-25T03:35:05.681Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
              has status code 200
              has a matching body
              includes headers
                "Content-Type" which equals "application/json; charset=utf-8"
      Given no products exist
        get all products
          with GET /products
            returns a response which
    
[2020-05-25T03:37:36.678Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: Proxing /products
[2020-05-25T03:37:36.717Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
              has status code 200
    
[2020-05-25T03:37:36.719Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
              has a matching body
    
[2020-05-25T03:37:36.720Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
              includes headers
    
[2020-05-25T03:37:36.721Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
                "Content-Type" which equals "application/json; charset=utf-8"
      Given products exist
    
[2020-05-25T03:37:36.726Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
        get all products with no auth token
    
[2020-05-25T03:37:36.726Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
          with GET /products
    
[2020-05-25T03:37:36.726Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
            returns a response which
    
[2020-05-25T03:40:07.511Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: Proxing /products
[2020-05-25T03:40:07.532Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
              has status code 401
    
[2020-05-25T03:40:07.537Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
      Given product with ID 10 exists
    
[2020-05-25T03:40:07.537Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
        get product with ID 10
          with GET /product/10
    
[2020-05-25T03:40:07.537Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
            returns a response which
    
[2020-05-25T03:42:38.557Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: Proxing /product/10
[2020-05-25T03:42:38.596Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
              has status code 200
    
[2020-05-25T03:42:38.602Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
              has a matching body
    
[2020-05-25T03:42:38.602Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
              includes headers
    
[2020-05-25T03:42:38.610Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
                "Content-Type" which equals "application/json; charset=utf-8"
    
[2020-05-25T03:42:38.611Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
      Given product with ID 11 does not exist
    
[2020-05-25T03:42:38.611Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
        get product with ID 11
    
[2020-05-25T03:42:38.612Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
          with GET /product/11
    
[2020-05-25T03:42:38.612Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
            returns a response which
    
[2020-05-25T03:45:09.544Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: Proxing /product/11
[2020-05-25T03:45:09.578Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
              has status code 404
    
[2020-05-25T03:45:09.579Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
      Given product with ID 10 exists
    
[2020-05-25T03:45:09.579Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
        get product by ID 10 with no auth token
          with GET /product/10
            returns a response which
    
[2020-05-25T03:45:09.589Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: Proxing /product/10
[2020-05-25T03:45:09.595Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
              has status code 401
    
[2020-05-25T03:45:09.598Z] DEBUG: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: 
    
    6 interactions, 0 failures

Last Result

 PASS  product/product.pact.test.js (1062.351s)
  Pact Verification
    ✓ validates the expectations of ProductService (1058327ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1064.617s
Ran all test suites.
[2020-05-25T03:45:09.682Z]  INFO: [email protected]/5449 on THUCHCHAIs-MacBook-Air.local: Pact Verification succeeded.
  console.log product/product.pact.test.js:66
    Pact Verification Complete!

  console.log product/product.pact.test.js:67
    INFO: Fetching pacts for ProductService from http://localhost:8081
    INFO: Reading pact at http://pact_workshop:*****@localhost:8081/pacts/provider/ProductService/consumer/FrontendWebsite/version/86a4112
    
    
    Verifying a pact between FrontendWebsite and ProductService
      Given products exist
        get all products
          with GET /products
            returns a response which
    
              has status code 200
              has a matching body
              includes headers
                "Content-Type" which equals "application/json; charset=utf-8"
      Given no products exist
        get all products
          with GET /products
            returns a response which
    
              has status code 200
    
              has a matching body
    
              includes headers
    
                "Content-Type" which equals "application/json; charset=utf-8"
      Given products exist
    
        get all products with no auth token
    
          with GET /products
    
            returns a response which
    
              has status code 401
    
      Given product with ID 10 exists
    
        get product with ID 10
          with GET /product/10
    
            returns a response which
    
              has status code 200
    
              has a matching body
    
              includes headers
    
                "Content-Type" which equals "application/json; charset=utf-8"
    
      Given product with ID 11 does not exist
    
        get product with ID 11
    
          with GET /product/11
    
            returns a response which
    
              has status code 404
    
      Given product with ID 10 exists
    
        get product by ID 10 with no auth token
          with GET /product/10
            returns a response which
    
              has status code 401
    
    6 interactions, 0 failures
    
✨  Done in 1068.36s.

Step 4 test does not fail as described

Summary

The instructions in step 4 read:

"validate the pact generated by the consumer is valid, by executing it against the running service provider, which should fail"

Also: what is meant by, "the running service provider"?

Instead, the test passes with output different from the README file.

Steps to reproduce

  1. Checkout branch, step4.
  2. Run the test, npm run test:pact --prefix provider

The test script is https://github.com/pact-foundation/pact-workshop-js/blob/step4/provider/product/product.pact.test.js
The documentation at https://github.com/pact-foundation/pact-workshop-js/blob/step4/README.md#step-4---verify-the-provider says that the test should fail, but it succeeds with the output:

$ npm run test:pact --prefix provider

> [email protected] test:pact
> npx jest --testTimeout=30000 --testMatch "**/*.pact.test.js"


 RUNS  product/product.pact.test.js
2023-04-19T12:35:17.633449Z  WARN ThreadId(21) pact_matching::metrics: 

Please note:
We are tracking events anonymously to gather important usage statistics like Pact version and operating system. To disable tracking, set the 'PACT_DO_NOT_TRACK' environment variable to 'true'.

[2023-04-19 12:35:17.626 +0000] INFO (27052 on blarn): [email protected]: Verifying provider
[2023-04-19 12:35:17.630 +0000] INFO (27052 on blarn): [email protected]: Verifying Pacts.
[2023-04-19 12:35:17.630 +0000] INFO (27052 on blarn): [email protected]: Verifying Pact Files
 PASS  product/product.pact.test.js
  Pact Verification
    ✓ validates the expectations of ProductService (111ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.828s, estimated 1s
Ran all test suites.
[2023-04-19 12:35:17.729 +0000] INFO (27052 on blarn): [email protected]: Verification successful
  console.log product/product.pact.test.js:22
    finished: 0

TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string

Hi,

Thanks for all the work.

Step 1, I had the following issue in the browser :

TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined at validateString (internal/validators.js:112:11) at Object.join (path.js:375:7) at noopServiceWorkerMiddleware (C:\Devs\work\pact-workshop-js\consumer\node_modules\react-dev-utils\noopServiceWorkerMiddleware.js:14:26) at Layer.handle [as handle_request] (C:\Devs\work\pact-workshop-js\consumer\node_modules\express\lib\router\layer.js:95:5) at trim_prefix (C:\Devs\work\pact-workshop-js\consumer\node_modules\express\lib\router\index.js:317:13) at C:\Devs\work\pact-workshop-js\consumer\node_modules\express\lib\router\index.js:284:7 at Function.process_params (C:\Devs\work\pact-workshop-js\consumer\node_modules\express\lib\router\index.js:335:12) at next (C:\Devs\work\pact-workshop-js\consumer\node_modules\express\lib\router\index.js:275:10) at launchEditorMiddleware (C:\Devs\work\pact-workshop-js\consumer\node_modules\react-dev-utils\errorOverlayMiddleware.js:20:7) at Layer.handle [as handle_request] (C:\Devs\work\pact-workshop-js\consumer\node_modules\express\lib\router\layer.js:95:5) at trim_prefix (C:\Devs\work\pact-workshop-js\consumer\node_modules\express\lib\router\index.js:317:13) at C:\Devs\work\pact-workshop-js\consumer\node_modules\express\lib\router\index.js:284:7 at Function.process_params (C:\Devs\work\pact-workshop-js\consumer\node_modules\express\lib\router\index.js:335:12) at next (C:\Devs\work\pact-workshop-js\consumer\node_modules\express\lib\router\index.js:275:10) at handleWebpackInternalMiddleware (C:\Devs\work\pact-workshop-js\consumer\node_modules\react-dev-utils\evalSourceMapMiddleware.js:42:7) at Layer.handle [as handle_request] (C:\Devs\work\pact-workshop-js\consumer\node_modules\express\lib\router\layer.js:95:5)

  • Windows 10
  • npm 6.12.0
  • node 12.13

To fix this, I had to upgrade to react-scripts 3.4.0 (see this)

Pact broker docker compose fail to start

Using 92a5b93, I wasn't able to start a broker after git checkout step11

❯ docker compose up
[+] Running 3/3
 ⠿ Network pact-workshop-js_default         Created                                                                                                                                                           0.0s
 ⠿ Container pact-workshop-js-postgres-1    Created                                                                                                                                                           0.0s
 ⠿ Container pact-workshop-js-broker_app-1  Created                                                                                                                                                           0.0s
Attaching to pact-workshop-js-broker_app-1, pact-workshop-js-postgres-1
pact-workshop-js-postgres-1    | The files belonging to this database system will be owned by user "postgres".
pact-workshop-js-postgres-1    | This user must also own the server process.
pact-workshop-js-postgres-1    | 
pact-workshop-js-postgres-1    | The database cluster will be initialized with locale "en_US.utf8".
pact-workshop-js-postgres-1    | The default database encoding has accordingly been set to "UTF8".
pact-workshop-js-postgres-1    | The default text search configuration will be set to "english".
pact-workshop-js-postgres-1    | 
pact-workshop-js-postgres-1    | Data page checksums are disabled.
pact-workshop-js-postgres-1    | 
pact-workshop-js-postgres-1    | fixing permissions on existing directory /var/lib/postgresql/data ... ok
pact-workshop-js-postgres-1    | creating subdirectories ... ok
pact-workshop-js-postgres-1    | selecting dynamic shared memory implementation ... posix
pact-workshop-js-postgres-1    | selecting default max_connections ... 100
pact-workshop-js-postgres-1    | selecting default shared_buffers ... 128MB
pact-workshop-js-postgres-1    | selecting default time zone ... Etc/UTC
pact-workshop-js-postgres-1    | creating configuration files ... ok
pact-workshop-js-postgres-1    | running bootstrap script ... ok
pact-workshop-js-broker_app-1  | Puma starting in single mode...
pact-workshop-js-broker_app-1  | * Puma version: 5.5.2 (ruby 2.6.8-p205) ("Zawgyi")
pact-workshop-js-broker_app-1  | *  Min threads: 0
pact-workshop-js-broker_app-1  | *  Max threads: 5
pact-workshop-js-broker_app-1  | *  Environment: production
pact-workshop-js-broker_app-1  | *          PID: 8
pact-workshop-js-postgres-1    | performing post-bootstrap initialization ... ok
pact-workshop-js-postgres-1    | syncing data to disk ... ok
pact-workshop-js-postgres-1    | 
pact-workshop-js-postgres-1    | 
pact-workshop-js-postgres-1    | Success. You can now start the database server using:
pact-workshop-js-postgres-1    | 
pact-workshop-js-postgres-1    |     pg_ctl -D /var/lib/postgresql/data -l logfile start
pact-workshop-js-postgres-1    | 
pact-workshop-js-postgres-1    | initdb: warning: enabling "trust" authentication for local connections
pact-workshop-js-postgres-1    | You can change this by editing pg_hba.conf or using the option -A, or
pact-workshop-js-postgres-1    | --auth-local and --auth-host, the next time you run initdb.
pact-workshop-js-postgres-1    | waiting for server to start....2021-12-02 21:20:54.230 UTC [47] LOG:  starting PostgreSQL 13.4 (Debian 13.4-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.231 UTC [47] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.234 UTC [48] LOG:  database system was shut down at 2021-12-02 21:20:54 UTC
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.237 UTC [47] LOG:  database system is ready to accept connections
pact-workshop-js-postgres-1    |  done
pact-workshop-js-postgres-1    | server started
pact-workshop-js-postgres-1    | 
pact-workshop-js-postgres-1    | /usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/*
pact-workshop-js-postgres-1    | 
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.355 UTC [47] LOG:  received fast shutdown request
pact-workshop-js-postgres-1    | waiting for server to shut down....2021-12-02 21:20:54.356 UTC [47] LOG:  aborting any active transactions
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.357 UTC [47] LOG:  background worker "logical replication launcher" (PID 54) exited with exit code 1
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.357 UTC [49] LOG:  shutting down
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.365 UTC [47] LOG:  database system is shut down
pact-workshop-js-broker_app-1  | 2021-12-02 21:20:54.340654 I [8:70132409424380] pact-broker -- Connecting to database: -- "{:adapter=>\"postgres\", :user=>\"postgres\", :password=>\"*****\", :host=>\"postgres\", :database=>\"postgres\", :encoding=>\"utf8\", :sql_log_level=>:debug, :enable_caller_logging=>false, :log_warn_duration=>5.0, :pool_timeout=>5, :driver_options=>{:options=>\"-c statement_timeout=15s\"}, :connect_max_retries=>0}"
pact-workshop-js-broker_app-1  | ! Unable to load application: Sequel::DatabaseConnectionError: PG::ConnectionBad: could not connect to server: Connection refused
pact-workshop-js-broker_app-1  | 	Is the server running on host "postgres" (172.22.0.2) and accepting
pact-workshop-js-broker_app-1  | 	TCP/IP connections on port 5432?
pact-workshop-js-broker_app-1  | bundler: failed to load command: puma (/pact_broker/vendor/bundle/ruby/2.6.0/bin/puma)
pact-workshop-js-postgres-1    |  done
pact-workshop-js-postgres-1    | server stopped
pact-workshop-js-postgres-1    | 
pact-workshop-js-postgres-1    | PostgreSQL init process complete; ready for start up.
pact-workshop-js-postgres-1    | 
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.498 UTC [1] LOG:  starting PostgreSQL 13.4 (Debian 13.4-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.498 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.498 UTC [1] LOG:  listening on IPv6 address "::", port 5432
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.500 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.503 UTC [66] LOG:  database system was shut down at 2021-12-02 21:20:54 UTC
pact-workshop-js-postgres-1    | 2021-12-02 21:20:54.507 UTC [1] LOG:  database system is ready to accept connections
pact-workshop-js-broker_app-1  | /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/adapters/postgres.rb:210:in `initialize': PG::ConnectionBad: could not connect to server: Connection refused (Sequel::DatabaseConnectionError)
pact-workshop-js-broker_app-1  | 	Is the server running on host "postgres" (172.22.0.2) and accepting
pact-workshop-js-broker_app-1  | 	TCP/IP connections on port 5432?
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/adapters/postgres.rb:210:in `new'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/adapters/postgres.rb:210:in `connect'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/connection_pool.rb:122:in `make_new'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/connection_pool/threaded.rb:209:in `assign_connection'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/connection_pool/threaded.rb:139:in `acquire'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/connection_pool/threaded.rb:91:in `hold'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/database/connecting.rb:269:in `synchronize'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/database/connecting.rb:278:in `test_connection'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/database/misc.rb:175:in `initialize'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/database/connecting.rb:57:in `new'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/database/connecting.rb:57:in `connect'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/core.rb:124:in `connect'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/pact_broker-2.92.0/lib/pact_broker/initializers/database_connection.rb:17:in `block in create_database_connection'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/pact_broker-2.92.0/lib/pact_broker/initializers/database_connection.rb:31:in `with_retries'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/pact_broker-2.92.0/lib/pact_broker/initializers/database_connection.rb:16:in `create_database_connection'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/pact_broker-2.92.0/lib/pact_broker/app.rb:135:in `configure_database_connection'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/pact_broker-2.92.0/lib/pact_broker/app.rb:99:in `post_configure'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/pact_broker-2.92.0/lib/pact_broker/app.rb:50:in `initialize'
pact-workshop-js-broker_app-1  | 	from config.ru:4:in `new'
pact-workshop-js-broker_app-1  | 	from config.ru:4:in `block in <main>'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/builder.rb:116:in `eval'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/builder.rb:116:in `new_from_string'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/builder.rb:105:in `load_file'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/builder.rb:66:in `parse_file'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/lib/puma/configuration.rb:345:in `load_rackup'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/lib/puma/configuration.rb:267:in `app'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/lib/puma/runner.rb:149:in `load_and_bind'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/lib/puma/single.rb:44:in `run'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/lib/puma/launcher.rb:181:in `run'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/lib/puma/cli.rb:80:in `run'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/bin/puma:10:in `<top (required)>'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/bin/puma:23:in `load'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/bin/puma:23:in `<top (required)>'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/cli/exec.rb:63:in `load'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/cli/exec.rb:63:in `kernel_load'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/cli/exec.rb:28:in `run'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/cli.rb:494:in `exec'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/cli.rb:30:in `dispatch'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/cli.rb:24:in `start'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/exe/bundle:49:in `block in <top (required)>'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/friendly_errors.rb:130:in `with_friendly_errors'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/exe/bundle:37:in `<top (required)>'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/bin/bundle:23:in `load'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/bin/bundle:23:in `<main>'
pact-workshop-js-broker_app-1  | /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/adapters/postgres.rb:210:in `initialize': could not connect to server: Connection refused (PG::ConnectionBad)
pact-workshop-js-broker_app-1  | 	Is the server running on host "postgres" (172.22.0.2) and accepting
pact-workshop-js-broker_app-1  | 	TCP/IP connections on port 5432?
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/adapters/postgres.rb:210:in `new'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/adapters/postgres.rb:210:in `connect'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/connection_pool.rb:122:in `make_new'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/connection_pool/threaded.rb:209:in `assign_connection'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/connection_pool/threaded.rb:139:in `acquire'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/connection_pool/threaded.rb:91:in `hold'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/database/connecting.rb:269:in `synchronize'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/database/connecting.rb:278:in `test_connection'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/database/misc.rb:175:in `initialize'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/database/connecting.rb:57:in `new'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/database/connecting.rb:57:in `connect'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/sequel-5.50.0/lib/sequel/core.rb:124:in `connect'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/pact_broker-2.92.0/lib/pact_broker/initializers/database_connection.rb:17:in `block in create_database_connection'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/pact_broker-2.92.0/lib/pact_broker/initializers/database_connection.rb:31:in `with_retries'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/pact_broker-2.92.0/lib/pact_broker/initializers/database_connection.rb:16:in `create_database_connection'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/pact_broker-2.92.0/lib/pact_broker/app.rb:135:in `configure_database_connection'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/pact_broker-2.92.0/lib/pact_broker/app.rb:99:in `post_configure'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/pact_broker-2.92.0/lib/pact_broker/app.rb:50:in `initialize'
pact-workshop-js-broker_app-1  | 	from config.ru:4:in `new'
pact-workshop-js-broker_app-1  | 	from config.ru:4:in `block in <main>'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/builder.rb:116:in `eval'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/builder.rb:116:in `new_from_string'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/builder.rb:105:in `load_file'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/rack-2.2.3/lib/rack/builder.rb:66:in `parse_file'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/lib/puma/configuration.rb:345:in `load_rackup'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/lib/puma/configuration.rb:267:in `app'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/lib/puma/runner.rb:149:in `load_and_bind'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/lib/puma/single.rb:44:in `run'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/lib/puma/launcher.rb:181:in `run'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/lib/puma/cli.rb:80:in `run'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/gems/puma-5.5.2/bin/puma:10:in `<top (required)>'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/bin/puma:23:in `load'
pact-workshop-js-broker_app-1  | 	from /pact_broker/vendor/bundle/ruby/2.6.0/bin/puma:23:in `<top (required)>'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/cli/exec.rb:63:in `load'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/cli/exec.rb:63:in `kernel_load'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/cli/exec.rb:28:in `run'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/cli.rb:494:in `exec'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/cli.rb:30:in `dispatch'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/cli.rb:24:in `start'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/exe/bundle:49:in `block in <top (required)>'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/lib/bundler/friendly_errors.rb:130:in `with_friendly_errors'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/gems/bundler-2.2.10/exe/bundle:37:in `<top (required)>'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/bin/bundle:23:in `load'
pact-workshop-js-broker_app-1  | 	from /usr/local/bundle/bin/bundle:23:in `<main>'
pact-workshop-js-broker_app-1 exited with code 1

Separate and uplift provider code

While discussing this issue, I noticed that the provider startup code is included in the test file.

// Setup provider server to verify
const app = express();
app.use(apiRoutes);

const server = app.listen('8080');

The example would be clearer if we pulled it out to a file that exports a start() function or similar, highlighting that the server is the application, rather than a separate bit of code that only lives at test time.

While we're there:

  • The port number in that method signature is a number, not a string (although I don't think it matters)
  • We could also modernise the JS at the same time (es6 imports, maybe typescript, not doing Object.assign, etc).

step 11 - npx pact-broker package is not available, ideally replace with docker cli version of can-i-deploy

yo dudes/dudettes, in the pact-workshop-js guide, it mentions an npx package called pact-broker which doesn’t exist

https://github.com/pact-foundation/pact-workshop-js#can-i-deploy
Should it point to the docker image of the ruby cli here?

https://github.com/pact-foundation/pact-ruby-cli#can-i-deploy

the image is here

https://hub.docker.com/r/pactfoundation/pact-cli

two ways we can run it via docker from the CLI

Attached to our host network

docker run --rm \
  --network='host' \
  pactfoundation/pact-cli:latest \
  broker can-i-deploy \
  --pacticipant FrontendWebsite \
               --broker-base-url http://localhost:8000 \
               --broker-username pact_workshop \
               --broker-password pact_workshop \
               --latest

Attached to our the default network started by the docker-compose setup

docker run --rm \
  --network='pact-workshop-js_default' \
  pactfoundation/pact-cli:latest \
  broker can-i-deploy \
  --pacticipant FrontendWebsite \
               --broker-base-url http://broker_app:80 \
               --broker-username pact_workshop \
               --broker-password pact_workshop \
               --latest

pact-broker publish not responding.

I'm currently working through the workshop and on step 11, I seem to be unable to publish the consumer pact successfully. "pact:publish": "pact-broker publish ./pacts --auto-detect-version-properties --broker-base-url=http://localhost:8000 --broker-username pact_workshop --broker-password pact_workshop " just seems to hang indefinitely. It neither errors, times out, nor does it display the confirmation message.
I am able to navigate to the broker on a browser and connect using curl after running docker-compose up. I have had the same tried on a colleague's machine and confirms the same issue happens. Working off HEAD on step11 branch.

npm ci fails with latest commit

I was using this repo some time around the 20th July (before commits on 27th), and didn't have any issues.
I'm using node version v14.18.2, but my colleague had the same issues with the most recent version of node.
Bit confusing for newbies trying to get going with Pact! I was helping my colleague, and couldn't see what the difference was with her setup until I pulled the latest version.

Some time after the 20th July, commits have been added that cause this error on npm ci

[1] npm ERR! uglify-js not accessible from handlebars
[1] 
[1] npm ERR! A complete log of this run can be found in:
[1] npm ERR!     /Users/andrewmccafferty/.npm/_cacache/_logs/2022-08-19T08_48_53_862Z-debug.log
[1] npm ci --prefix provider exited with code 1
[0] npm ERR! @types/node not accessible from @types/needle
[0] 
[0] npm ERR! A complete log of this run can be found in:
[0] npm ERR!     /Users/andrewmccafferty/.npm/_cacache/_logs/2022-08-19T08_49_05_730Z-debug.log
[0] npm ci --prefix consumer exited with code 1
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] postinstall: `concurrently "npm ci --prefix consumer" "npm ci --prefix provider"`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] postinstall script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/andrewmccafferty/.npm/_logs/2022-08-19T08_49_05_804Z-debug.log

When I then do the first step in the tutorial, namely npm start --prefix consumer , it fails because the first step didn't install any packages in the consumer or provider folders.

> [email protected] start /Users/andrewmccafferty/Documents/git/bench/pact-workshop-js/consumer
> react-scripts start

sh: react-scripts: command not found
npm ERR! code ELIFECYCLE
npm ERR! syscall spawn
npm ERR! file sh
npm ERR! errno ENOENT
npm ERR! [email protected] start: `react-scripts start`
npm ERR! spawn ENOENT
npm ERR! 
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:

Step 12 (Pactflow) Error when executing Pact verifier (even without broker)

I encountered a handful of broken changes in each "step branches" following the JS workshop but managed to get around/fix most of them by googling. When I get to Step 12, I was dealing with SSL error publishing to Pactlow. Spent the entire day googling, generating cert, setting 'SSL_CERT_FILE' environment var - without much luck. l finally gave up and set 'PACT_DISABLE_SSL_VERIFICATION = true'. I was able to publish PACT to pactflow.

However, when I run 'npm run test:pact --prefix provider ' to execute PACT verifier, I started getting the error below.

thread '' panicked at 'Client::new(): reqwest::Error { kind: Builder, source: Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." } }', C:\Users\runneradmin.cargo\registry\src\github.com-1ecc6299db9ec823\reqwest-0.11.11\src\async_impl\client.r FAIL product/product.pact.test.js

I have never seen before prior to Step 12. I even updated the configuration to use local PACT file instead of broker and still getting the same error.

Note: Provider verifies with local Pact was working prior to Step 12.

Please help!

ERROR Message

See attached error

Or below:

npm run test:pact --prefix provider

[email protected] test:pact
npx jest --testTimeout 30000 --testMatch "**/product/product.pact.test.js" --verbose

RUNS product/product.pact.test.js
2022-11-06T02:32:11.435005Z WARN ThreadId(09) pact_matching::metrics:

Please note:
We are tracking events anonymously to gather important usage statistics like Pact version and operating system. To disable tracking, set the 'PACT_DO_NOT_TRACK' environment variable to 'true'.

thread '' panicked at 'Client::new(): reqwest::Error { kind: Builder, source: Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." } }', C:\Users\runneradmin.cargo\registry\src\github.com-1ecc6299db9ec823\reqwest-0.11.11\src\async_impl\client.r FAIL product/product.pact.test.js
Pact Verification
× validates the expectations of ProductService (31ms)

● Pact Verification › validates the expectations of ProductService

Verfication failed

  at node_modules/@pact-foundation/pact-core/src/verifier/nativeVerifier.ts:50:20

[2022-11-06 02:32:11.415 +0000] INFO (4036 on NWLPT597): [email protected]: Verifying provider
[2022-11-06 02:32:11.426 +0000] INFO (4036 on NWLPT597): [email protected]: Verifying Pacts.
[2022-11-06 02:32:11.428 +0000] INFO (4036 on NWLPT597): [email protected]: Verifying Pact Files
[2022-11-06 02:32:11.438 +0000] ERROR (4036 on NWLPT597): [email protected]: Verification unsuccessful
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 5.275s, estimated 6s
Ran all test suites.

provider\product\product.pact.test.js

See attached JS (renamed as txt): product.pact.test.js.txt

consumer\pacts\FrontendWebsite-ProductService.json

See attached JSON (renamed as txt): FrontendWebsite-ProductService.json.txt

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.