diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3f9949745..937816102 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,70 +18,44 @@ jobs: matrix: pact_provider: [ - # "pactflow-example-provider-dredd", - # "pactflow-example-provider-restassured", - # "pactflow-example-provider-postman", + # "pactflow-example-bi-directional-provider-dredd", + # "pactflow-example-bi-directional-provider-restassured", + # "pactflow-example-bi-directional-provider-postman", "pactflow-example-provider", ] - pact_consumer: - [ - "pactflow-example-consumer", - # "pactflow-example-consumer-nock" - ] steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2-beta with: node-version: "12" - - name: Install for ${{ matrix.pact_consumer }} & ${{ matrix.pact_provider }} + - name: Install for ${{ matrix.pact_provider }} run: npm i - - name: Test for ${{ matrix.pact_consumer }} & ${{ matrix.pact_provider }} + - name: Test for ${{ matrix.pact_provider }} env: - PACTICIPANT: ${{ matrix.pact_consumer }} PACT_PROVIDER: ${{ matrix.pact_provider }} - run: | - [[ $PACTICIPANT = pactflow-example-consumer-nock ]] && make test_nock || make test - - name: Publish pacts for ${{ matrix.pact_consumer }} & ${{ matrix.pact_provider }} + run: make test + - name: Publish pacts for${{ matrix.pact_provider }} run: GIT_BRANCH=${GIT_REF:11} make publish_pacts env: - PACTICIPANT: ${{ matrix.pact_consumer }} PACT_PROVIDER: ${{ matrix.pact_provider }} # Runs on branches as well, so we know the status of our PRs can-i-deploy: runs-on: ubuntu-latest needs: test - strategy: - matrix: - pact_consumer: - [ - "pactflow-example-consumer", - # "pactflow-example-consumer-nock" - ] steps: - uses: actions/checkout@v2 - run: docker pull pactfoundation/pact-cli:latest - - name: Can I deploy? for ${{ matrix.pact_consumer }} - env: - PACTICIPANT: ${{ matrix.pact_consumer }} + - name: Can I deploy? run: GIT_BRANCH=${GIT_REF:11} make can_i_deploy # Only deploy from master deploy: runs-on: ubuntu-latest needs: can-i-deploy - strategy: - matrix: - pact_consumer: - [ - "pactflow-example-consumer", - # "pactflow-example-consumer-nock" - ] steps: - uses: actions/checkout@v2 - run: docker pull pactfoundation/pact-cli:latest - - name: Deploy for ${{ matrix.pact_consumer }} + - name: Deploy run: GIT_BRANCH=${GIT_REF:11} make deploy if: github.ref == 'refs/heads/master' - env: - PACTICIPANT: ${{ matrix.pact_consumer }} \ No newline at end of file diff --git a/Makefile b/Makefile index 3c93f5d3c..3ff322cb0 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ # Default to the read only token - the read/write token will be present on Travis CI. # It's set as a secure environment variable in the .travis.yml file GITHUB_ORG="pactflow" -PACTICIPANT ?= ${PACTICIPANT} || "pactflow-example-consumer" +PACTICIPANT="pactflow-example-consumer" GITHUB_WEBHOOK_UUID := "04510dc1-7f0a-4ed2-997d-114bfa86f8ad" PACT_CHANGED_WEBHOOK_UUID := "8e49caaa-0498-4cc1-9368-325de0812c8a" PACT_CLI="docker run --rm -v ${PWD}:${PWD} -e PACT_BROKER_BASE_URL -e PACT_BROKER_TOKEN pactfoundation/pact-cli" @@ -24,7 +24,6 @@ all: test ## ==================== ci: test publish_pacts can_i_deploy $(DEPLOY_TARGET) -ci_nock: test_nock publish_pacts can_i_deploy $(DEPLOY_TARGET) # Run the ci target from a developer machine with the environment variables # set as if it was on CI. @@ -36,14 +35,6 @@ fake_ci: .env REACT_APP_API_BASE_URL=http://localhost:8080 \ make ci -fake_ci_nock: .env - @CI=true \ - GIT_COMMIT=`git rev-parse --short HEAD`+`date +%s` \ - GIT_BRANCH=`git rev-parse --abbrev-ref HEAD` \ - REACT_APP_API_BASE_URL=http://localhost:8080 \ - PACTICIPANT=pactflow-example-consumer-nock \ - make ci_nock - publish_pacts: .env @echo "\n========== STAGE: publish pacts ==========\n" @"${PACT_CLI}" publish ${PWD}/pacts --consumer-app-version ${GIT_COMMIT} --tag ${GIT_BRANCH} @@ -56,10 +47,6 @@ test: .env @echo "\n========== STAGE: test (pact) ==========\n" npm run test:pact -test_nock: .env - @echo "\n========== STAGE: test (nock) ==========\n" - PACTICIPANT=pactflow-example-consumer-nock npm run test:nock - ## ===================== ## Deploy tasks ## ===================== diff --git a/README-bi-directional.md b/README-bi-directional.md deleted file mode 100644 index 5abaf90f0..000000000 --- a/README-bi-directional.md +++ /dev/null @@ -1,65 +0,0 @@ -## Example JS Nock Consumer - - - -![Build](https://github.com/pactflow/example-consumer/workflows/Build/badge.svg) - -[![Can I deploy Status](https://testdemo.pactflow.io/pacticipants/pactflow-example-consumer/branches/master/latest-version/can-i-deploy/to-environment/production/badge)](https://testdemo.pactflow.io/pacticipants/pactflow-example-consumer/branches/master/latest-version/can-i-deploy/to-environment/production/badge) - -This is an example of a Node consumer using Pact to create a consumer driven contract, and sharing it via [Pactflow](https://pactflow.io). - -It implements a "Product" website, to demonstrate the new bi-directional contract capability of Pactflow (previously referred to as Provider driven contracts, or collaborative contracts). See the [Provider](https://github.com/pactflow/example-pactflow-example-provider-dredd) counterpart. - -It is using a private tenant on Pactflow, which you can access [here](https://testdemo.pactflow.io/). The latest version of the Example Pactflow Consumer/Example Pactflow Provider (Dredd) pact is published [here](https://testdemo.pactflow.io/overview/provider/pactflow-example-provider-dredd/consumer/pactflow-example-consumer). - -In the following diagram, you can see how the consumer testing process works - it's the same as the current Pact process! (We do show an alternative using Nock's record/replay functionality) - -When we call "can-i-deploy" the cross-contract validation process kicks off on Pactflow, to ensure any consumer consumes a valid subset of the OAS for the provider. - -![Consumer Test](docs/consumer-scope.png "Consumer Test") - -When you run the CI pipeline (see below for doing this), the pipeline should perform the following activities (simplified): - -![Consumer Pipeline](docs/consumer-pipeline.png "Consumer Pipeline") - -### Pre-requisites - -**Software**: - -* Tools listed at: https://docs.pactflow.io/docs/workshops/ci-cd/set-up-ci/prerequisites/ -* A pactflow.io account with an valid [API token](https://docs.pactflow.io/docs/getting-started/#configuring-your-api-token) - - -#### Environment variables - -To be able to run some of the commands locally, you will need to export the following environment variables into your shell: - -* `PACT_BROKER_TOKEN`: a valid [API token](https://docs.pactflow.io/docs/getting-started/#configuring-your-api-token) for Pactflow -* `PACT_BROKER_BASE_URL`: a fully qualified domain name with protocol to your pact broker e.g. https://testdemo.pactflow.io -* `PACT_PROVIDER=pactflow-example-provider-dredd`: this changes the default provider to the Dredd based provider (https://github.com/pactflow/example-provider-dredd) -* `PACT_PROVIDER=pactflow-example-provider-postman`: ... Postman (https://github.com/pactflow/example-provider-postman) -* `PACT_PROVIDER=pactflow-example-provider-restassured`: ... Rest Assured (https://github.com/pactflow/example-provider-restassured) -### Usage - -#### Pact use case - -* `make test` - run the pact test locally -* `make fake_ci` - run the CI process locally - -#### BYO Tool use case with Nock (record/replay example) - -NOTE: The nock recordings are already in the project, in the `./fixtures` directory, see below for how to obtain these recordings. - -* `make clean` - ensure previous pacts are cleared -* `make test_nock` - run the nock test locally -* `make fake_ci_nock` - run the nock version of the CI process locally - -*Re-record nock fixtures* - -You first need to start up the provider API in order to obtain nock recordings. The API must be running on `http://localhost:3001` for this step to work. - -For the default [Provider](https://github.com/pactflow/example-provider-dredd) designed for this workshop, you can simply start it up by running `npm start` in the root directory of the provider project, as per its README. - - -* `npm run test:record` - this will run nock in record mode, and your api client will issue real requests to the API -* `npm run test:nock` - run the nock tests in replay only mode, validating all stubs were used in the process, and writing a pact file if successful diff --git a/README.md b/README.md index b820d39bb..465abc336 100644 --- a/README.md +++ b/README.md @@ -24,30 +24,6 @@ The project uses a Makefile to simulate a very simple build pipeline with two st See the [Pactflow CI/CD Workshop](https://github.com/pactflow/ci-cd-workshop). -## Bi-directional Contracts Feature (BYO Tools) - -![Build](https://github.com/pactflow/example-consumer/workflows/Build/badge.svg) - -[![Can I deploy Status](https://testdemo.pactflow.io/pacticipants/pactflow-example-consumer-msw/branches/master/latest-version/can-i-deploy/to-environment/production/badge)](https://testdemo.pactflow.io/pacticipants/pactflow-example-consumer-nock/branches/master/latest-version/can-i-deploy/to-environment/production/badge) - -_NOTE: if you're running the CI/CD workshop, you can ignore this section. This is an extension to the example that demonstrates a new [feature](https://github.com/pactflow/roadmap/issues/4) in developer preview._ - -This is an example of a Node consumer using Pact to create a consumer driven contract, and sharing it via [Pactflow](https://pactflow.io). - -It implements a "Product" website, to demonstrate the new bi-directional contract capability of Pactflow (previously referred to as Provider driven contracts, or collaborative contracts). See the [Provider](https://github.com/pactflow/example-pactflow-example-provider-dredd) counterpart. - -It is using a private tenant on Pactflow. The latest version of the Example Pactflow Consumer/Example Pactflow Provider (Dredd) pact is published [here](https://testdemo.pactflow.io/overview/provider/pactflow-example-provider-dredd/consumer/pactflow-example-consumer). - -In the following diagram, you can see how the consumer testing process works - it's the same as the current Pact process! (We do show an alternative using Nock's record/replay functionality) - -When we call "can-i-deploy" the cross-contract validation process kicks off on Pactflow, to ensure any consumer consumes a valid subset of the OAS for the provider. - -![Consumer Test](docs/consumer-scope.png "Consumer Test") - -When you run the CI pipeline (see below for doing this), the pipeline should perform the following activities (simplified): - -![Consumer Pipeline](docs/consumer-pipeline.png "Consumer Pipeline") - ### Pre-requisites **Software**: @@ -62,30 +38,10 @@ To be able to run some of the commands locally, you will need to export the foll * `PACT_BROKER_TOKEN`: a valid [API token](https://docs.pactflow.io/docs/getting-started/#configuring-your-api-token) for Pactflow * `PACT_BROKER_BASE_URL`: a fully qualified domain name with protocol to your pact broker e.g. https://testdemo.pactflow.io -* `PACT_PROVIDER=pactflow-example-provider-dredd`: this changes the default provider to the Dredd based provider (https://github.com/pactflow/example-provider-dredd) -* `PACT_PROVIDER=pactflow-example-provider-postman`: ... Postman (https://github.com/pactflow/example-provider-postman) -* `PACT_PROVIDER=pactflow-example-provider-restassured`: ... Rest Assured (https://github.com/pactflow/example-provider-restassured) + ### Usage #### Pact use case * `make test` - run the pact test locally -* `make fake_ci` - run the CI process locally - -#### BYO Tool use case with Nock (record/replay example) - -NOTE: The nock recordings are already in the project, in the `./fixtures` directory, see below for how to obtain these recordings. - -* `make clean` - ensure previous pacts are cleared -* `make test_nock` - run the nock test locally -* `make fake_ci_nock` - run the nock version of the CI process locally - -*Re-record nock fixtures* - -You first need to start up the provider API in order to obtain nock recordings. The API must be running on `http://localhost:3001` for this step to work. - -For the default [Provider](https://github.com/pactflow/example-provider-dredd) designed for this workshop, you can simply start it up by running `npm start` in the root directory of the provider project, as per its README. - - -* `npm run test:record` - this will run nock in record mode, and your api client will issue real requests to the API -* `npm run test:nock` - run the nock tests in replay only mode, validating all stubs were used in the process, and writing a pact file if successful \ No newline at end of file +* `make fake_ci` - run the CI process locally \ No newline at end of file diff --git a/fixtures/nock.json b/fixtures/nock.json deleted file mode 100644 index e0af3ef89..000000000 --- a/fixtures/nock.json +++ /dev/null @@ -1,80 +0,0 @@ -[ - { - "scope": "http://localhost:3001", - "method": "GET", - "path": "/products", - "body": "", - "status": 200, - "response": [ - { - "id": "09", - "type": "CREDIT_CARD", - "name": "Gem Visa", - "version": "v1", - "price": 99.99 - }, - { - "id": "10", - "type": "CREDIT_CARD", - "name": "28 Degrees", - "version": "v1", - "price": 49.49 - }, - { - "id": "11", - "type": "PERSONAL_LOAN", - "name": "MyFlexiPay", - "version": "v2", - "price": 16.5 - } - ], - "rawHeaders": [ - "X-Powered-By", - "Express", - "Access-Control-Allow-Origin", - "*", - "Content-Type", - "application/json; charset=utf-8", - "Content-Length", - "246", - "ETag", - "W/\"f6-w3kK4moDvOGs6FOpA/LePl4PlXg\"", - "Date", - "Wed, 17 Feb 2021 13:20:37 GMT", - "Connection", - "close" - ], - "responseIsBinary": false - }, - { - "scope": "http://localhost:3001", - "method": "GET", - "path": "/product/10", - "body": "", - "status": 200, - "response": { - "id": "10", - "type": "CREDIT_CARD", - "name": "28 Degrees", - "version": "v1", - "price": 49.49 - }, - "rawHeaders": [ - "X-Powered-By", - "Express", - "Access-Control-Allow-Origin", - "*", - "Content-Type", - "application/json; charset=utf-8", - "Content-Length", - "81", - "ETag", - "W/\"51-/fCT2fY74qwFWk1CnZqdJTmzvZg\"", - "Date", - "Wed, 17 Feb 2021 13:20:37 GMT", - "Connection", - "close" - ], - "responseIsBinary": false - } -] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c076861c4..35b913f86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,8 +33,7 @@ "eslint-plugin-import": "^2.22.1", "eslint-plugin-jsx-a11y": "^6.3.1", "eslint-plugin-react": "^7.21.3", - "eslint-plugin-react-hooks": "^1.7.0", - "nock": "^11.9.1" + "eslint-plugin-react-hooks": "^1.7.0" } }, "node_modules/@apidevtools/json-schema-ref-parser": { @@ -12343,22 +12342,6 @@ "tslib": "^1.10.0" } }, - "node_modules/nock": { - "version": "11.9.1", - "resolved": "https://registry.npmjs.org/nock/-/nock-11.9.1.tgz", - "integrity": "sha512-U5wPctaY4/ar2JJ5Jg4wJxlbBfayxgKbiAeGh+a1kk6Pwnc2ZEuKviLyDSG6t0uXl56q7AALIxoM6FJrBSsVXA==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.13", - "mkdirp": "^0.5.0", - "propagate": "^2.0.0" - }, - "engines": { - "node": ">= 8.0" - } - }, "node_modules/node-forge": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", @@ -14737,15 +14720,6 @@ "react-is": "^16.8.1" } }, - "node_modules/propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/proxy-addr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", @@ -30423,19 +30397,6 @@ "tslib": "^1.10.0" } }, - "nock": { - "version": "11.9.1", - "resolved": "https://registry.npmjs.org/nock/-/nock-11.9.1.tgz", - "integrity": "sha512-U5wPctaY4/ar2JJ5Jg4wJxlbBfayxgKbiAeGh+a1kk6Pwnc2ZEuKviLyDSG6t0uXl56q7AALIxoM6FJrBSsVXA==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.13", - "mkdirp": "^0.5.0", - "propagate": "^2.0.0" - } - }, "node-forge": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", @@ -32365,12 +32326,6 @@ "react-is": "^16.8.1" } }, - "propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true - }, "proxy-addr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", diff --git a/package.json b/package.json index 436054e00..13ac38f13 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,7 @@ "build": "react-scripts build", "test": "cross-env CI=true react-scripts test", "eject": "react-scripts eject", - "test:pact": "cross-env CI=true react-scripts test --testTimeout 30000 pact.spec.js", - "test:nock": "cross-env CI=true react-scripts test --testTimeout 30000 nock.spec.js", - "test:record": "cross-env CI=true react-scripts test --testTimeout 30000 record.spec.js" + "test:pact": "cross-env CI=true react-scripts test --testTimeout 30000 pact.spec.js" }, "eslintConfig": { "extends": "react-app" @@ -52,7 +50,6 @@ "eslint-plugin-import": "^2.22.1", "eslint-plugin-jsx-a11y": "^6.3.1", "eslint-plugin-react": "^7.21.3", - "eslint-plugin-react-hooks": "^1.7.0", - "nock": "^11.9.1" - } + "eslint-plugin-react-hooks": "^1.7.0" + } } diff --git a/src/api.nock.spec.js b/src/api.nock.spec.js deleted file mode 100644 index 6796bbe62..000000000 --- a/src/api.nock.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -import { API } from "./api"; -import { convertNockToPact } from "./api.record.spec"; - -const nockBack = require("nock").back; -nockBack.setMode("lockdown"); // don't allow new recording! -nockBack.fixtures = "fixtures"; - -describe("API Nock Tests", () => { - // convert the fixture file into a pact contract after testing - afterAll(() => convertNockToPact()) - - test("nock replay tests", (done) => { - // reuse the nock fixture - nockBack( - "nock.json", - async function(nockDone ) { - const api = new API("http://localhost:3001"); - - // Test 1 - const products = await api.getAllProducts(); - expect(products.length).toBeGreaterThan(0); - - // Test 2 - // Comment this out, and the test should fail, not serialising the contract - const product = await api.getProduct("10"); - expect(product.id).toBe("10"); - - // This will fail if not all mocks were tested - // meaning we won't serialise unused items into the contract - this.assertScopesFinished(); - - nockDone(); - done() - } - ); - }); -}); diff --git a/src/api.record.spec.js b/src/api.record.spec.js deleted file mode 100644 index 9a93a27ea..000000000 --- a/src/api.record.spec.js +++ /dev/null @@ -1,76 +0,0 @@ -import { API } from "./api"; -import * as path from "path"; -import * as fs from "fs"; - -const nockBack = require("nock").back; -nockBack.setMode("record"); // record interactions -nockBack.fixtures = "fixtures"; // fixture files will be stored in ./fixtures/.json -const filename = "nock.json"; - -describe("API Nock Tests", () => { - test("nock recordings", () => { - // recording of the fixture - return nockBack(filename).then(async ({ nockDone }) => { - const api = new API("http://localhost:3001"); - - // Record all of the data - await api.getAllProducts(); - await api.getProduct("10"); - - nockDone(); - }); - }); -}); - -// Crude converter from a nock fixture to a pactfile -export const convertNockToPact = () => { - const scopes = require(path.join( - __dirname, - "..", - nockBack.fixtures, - filename - )); - - const pact = { - consumer: { name: "pactflow-example-consumer-nock" }, - provider: { - name: process.env.PACT_PROVIDER - ? process.env.PACT_PROVIDER - : "pactflow-example-provider-dredd", - }, - interactions: [], - metadata: { - pactSpecification: { - version: "2.0.0", - }, - }, - }; - - pact.interactions = scopes.map((interaction) => { - return { - description: `nock_${interaction.method}_${interaction.path}_${interaction.status}`, - request: { - method: interaction.method, - path: interaction.path, - body: interaction.body, - }, - response: { - status: interaction.status, - body: interaction.response, - }, - }; - }); - - try { - fs.mkdirSync(path.join(__dirname, "..", "pacts")); - } catch (e) { - // writeFileSync fails if dir doesnt exist - // mkdirSync fails if dir exists - } - fs.writeFileSync( - path.join(__dirname, "..", "pacts", "nock-contract.json"), - JSON.stringify(pact) - ); - - return scopes; -};