PACT-JS - 提供商合同验证 error/failure - PactFlow

PACT-JS - Provider contract verification error/failure - PactFlow

我正在拔头发。我创建了一个 Pact-JS 测试框架来测试我的简单 NodeJS 应用程序。我将在下面显示所有代码。我使用 PactFlow 来托管我的合同文件(由 运行 我的消费者测试生成)。问题是当我 运行 我的提供商代码失败时。我收到以下错误(我将 post 下面更详细的错误)) - `assert_success!': Error retrieving https://zaizi.pactflow.io/pacts/provider/Clients%20Service/for-verification status=404

消费者测试代码 - pact.consumer.spec.js

const { Matchers, Publisher } = require("@pact-foundation/pact")
const Pact = require("@pact-foundation/pact").Pact
const { getClients, getClient, postClient } = require("../../src/consumer")
const path = require("path")

// mock provider setup
const mockProvider = new Pact({
    port: 8081,
    log: path.resolve(process.cwd(), "__tests__/logs", "mock-provider.log"), // log file location and name
    dir: path.resolve(process.cwd(), "__tests__/pacts"), // location where pact contract will be saved
    spec: 2,
    logLevel: 'INFO',
    pactfileWriteMode: "overwrite",
    consumer: "pact-consumer",
    provider: "pact-provider"
 })

 describe("Pact for Clients Service API", () => {
    beforeAll(() => mockProvider.setup());  // Start the Mock Server and wait for it to be available
    afterEach(() => mockProvider.verify()); // Verifies that all interactions specified
    afterAll(() => mockProvider.finalize()); // Records the interactions between the Mock Server into the pact file and shuts it down


    describe('given there are clients', () => {
        describe('when a request is made to GET all clients', () => {

            // GET_EXPECTED_BODY = [
            //     { "firstName": "Lisa", "lastName": "Simpson", "age": 8, "id": 1 }, 
            //     { "firstName": "Wonder", "lastName": "Woman", "age": 30, "id": 2 },
            //     { "firstName": "Homer", "lastName": "Simpson", "age": 39, "id": 3 }]

            const GET_EXPECTED_BODY = [{
                "firstName": "Lisa",
                "lastName": "Simpson",
                "age": 8,
                "id": 1
            },
            {
                "firstName": "Wonder",
                "lastName": "Woman",
                "age": 30,
                "id": 2
            },
            {
                "firstName": "Homer",
                "lastName": "Simpson",
                "age": 39,
                "id": 3
            }]

            beforeEach(() => {
                const interaction = {
                    state: "i have a list of clients",
                    uponReceiving: "a request for all clients",
                    withRequest: {
                      method: "GET",
                      path: "/clients",
                      headers: {
                        Accept: "application/json, text/plain, */*",
                      },
                    },
                    willRespondWith: {
                      status: 200,
                      headers: {
                        "Content-Type": "application/json; charset=utf-8",
                      },
                      body: GET_EXPECTED_BODY,
                    },
                  }
                return mockProvider.addInteraction(interaction)  
            })

            it('will return a list of clients', async() => {

                // make request to the Pact mock server to get all clients
                const response = await getClients()
                expect(response.headers['content-type']).toBe("application/json; charset=utf-8")
                expect(response.data).toEqual(GET_EXPECTED_BODY)
                expect(response.status).toEqual(200)
            })
        })
    })

 })

提供商测试代码 - pact.provider.spec.js

const path = require("path")
const { Verifier } = require("@pact-foundation/pact")
const { server, importData } = require("../../src/provider")
require('dotenv').config()

jest.setTimeout(30000);

describe("Clients Service Pact verification", () => {

    const port = 8081
    let opts = {
        provider: "Clients Service",
        logLevel: "DEBUG",
        providerBaseUrl: `http://localhost:${port}`,
        // pactUrls: [path.resolve(process.cwd(), "./__tests__/pacts/pact-consumer-pact-provider.json")],
        pactBrokerUrl: process.env.PACT_BROKER_BASE_URL,
        pactBrokerToken: process.env.PACT_BROKER_TOKEN,
        consumerVersionTags: ["pact-consumer"],
        providerVersionTags: ["pact-provider"],
        publishVerificationResult: false,
        providerVersion: "1.0.0"
    }

    // Start real provider server
    beforeAll(async () => {
        server.listen(port, () => {
            importData()
            console.log(`Provider listening on http://localhost:${port}`)
        })
    })

    it("validates the expectations of the clients service", () => {

        return new Verifier(opts).verifyProvider().then(output => {
            console.log("Pact Verification Complete!")
            console.log(output)
        })
    })

    afterAll(async ()=> {
        server.close();
    })
    // it('should validate the expectations of Order Web', () => {
    //     return new Verifier().verifyProvider(opts)
    //   })
})

我的契约在这里:-

{
  "consumer": {
    "name": "pact-consumer"
  },
  "provider": {
    "name": "pact-provider"
  },
  "interactions": [
    {
      "description": "a request for all clients",
      "providerState": "i have a list of clients",
      "request": {
        "method": "GET",
        "path": "/clients",
        "headers": {
          "Accept": "application/json, text/plain, */*"
        }
      },
      "response": {
        "status": 200,
        "headers": {
          "Content-Type": "application/json; charset=utf-8"
        },
        "body": [
          {
            "firstName": "Lisa",
            "lastName": "Simpson",
            "age": 8,
            "id": 1
          },
          {
            "firstName": "Wonder",
            "lastName": "Woman",
            "age": 30,
            "id": 2
          },
          {
            "firstName": "Homer",
            "lastName": "Simpson",
            "age": 39,
            "id": 3
          }
        ]
      }
    }
  ],
  "metadata": {
    "pactSpecification": {
      "version": "2.0.0"
    }
  }
}

这是我的 PactFlow:-

详细错误日志:-

[2022-04-13 16:57:12.453 +0000] INFO (15316 on DESKTOP-TJVM91P): pact@9.17.3: Verifying provider
  console.log
    Provider listening on http://localhost:8081

      at Server.<anonymous> (__tests__/provider/pact.provider.spec.js:28:21)

[2022-04-13 16:57:12.481 +0000] INFO (15316 on DESKTOP-TJVM91P): pact-node@10.17.2: Verifying Pacts.
[2022-04-13 16:57:12.483 +0000] INFO (15316 on DESKTOP-TJVM91P): pact-node@10.17.2: Verifying Pact Files
[2022-04-13 16:57:12.484 +0000] DEBUG (15316 on DESKTOP-TJVM91P): pact-node@10.17.2: Starting pact binary 'D:\p\pact-nodejs-demo\node_modules\@pact-foundation\pact-node\standalone\win32-1.88.83\pact\bin\pact-provider-verifier.bat', with arguments [--provider-states-setup-url http://localhost:59761/_pactSetup --provider Clients Service --log-level DEBUG --provider-base-url http://localhost:59761 --pact-broker-base-url https://******.pactflow.io --broker-token 1iB-FQbUuvpxce6QX-DBTQ --consumer-version-tag pact-consumer --provider-version-tag pact-provider --publish-verification-results false --provider-app-version 1.0.0 --verbose true]
[2022-04-13 16:57:12.491 +0000] DEBUG (15316 on DESKTOP-TJVM91P): pact-node@10.17.2: Created 'D:\p\pact-nodejs-demo\node_modules\@pact-foundation\pact-node\standalone\win32-1.88.83\pact\bin\pact-provider-verifier.bat' process with PID: 10524
FAIL __tests__/provider/pact.provider.spec.js (9.938s)
  Clients Service Pact verification
    × validates the expectations of the clients service (8022ms)

  ● Clients Service Pact verification › validates the expectations of the clients service

    D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-1.62.0/lib/pact/hal/entity.rb:102:in `assert_success!': Error retrieving https://******.pactflow.io/pacts/provider/Clients%20Service/for-verification status=404  (Pact::Hal::ErrorResponseReturned)
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-1.62.0/lib/pact/hal/link.rb:65:in `post!'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-1.62.0/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb:75:in `pacts_for_verification_entity'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-1.62.0/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb:61:in `pacts_for_verification'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-1.62.0/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb:44:in `call'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-1.62.0/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb:38:in `call'

        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-1.62.0/lib/pact/pact_broker.rb:18:in `fetch_pact_uris_for_verification'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-provider-verifier-1.36.1/lib/pact/provider_verifier/aggregate_pact_configs.rb:46:in `pacts_for_verification'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-provider-verifier-1.36.1/lib/pact/provider_verifier/aggregate_pact_configs.rb:39:in `pacts_urls_from_broker'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-provider-verifier-1.36.1/lib/pact/provider_verifier/aggregate_pact_configs.rb:26:in `call'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-provider-verifier-1.36.1/lib/pact/provider_verifier/aggregate_pact_configs.rb:10:in `call'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-provider-verifier-1.36.1/lib/pact/provider_verifier/app.rb:211:in `all_pact_urls'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-provider-verifier-1.36.1/lib/pact/provider_verifier/app.rb:225:in `warn_empty_pact_set'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-provider-verifier-1.36.1/lib/pact/provider_verifier/app.rb:40:in `call'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-provider-verifier-1.36.1/lib/pact/provider_verifier/app.rb:35:in `call'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-provider-verifier-1.36.1/lib/pact/provider_verifier/cli/verify.rb:49:in `verify'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/thor-1.2.1/lib/thor/command.rb:27:in 
`run'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/thor-1.2.1/lib/thor/invocation.rb:127:in `invoke_command'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/thor-1.2.1/lib/thor.rb:392:in `dispatch'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/thor-1.2.1/lib/thor/base.rb:485:in `start'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/vendor/ruby/2.2.0/gems/pact-provider-verifier-1.36.1/lib/pact/provider_verifier/cli/custom_thor.rb:17:in `start'
        from D:/p/pact-nodejs-demo/node_modules/@pact-foundation/pact-node/standalone/win32-1.88.83/pact/lib/app/pact-provider-verifier.rb:33:in `<main>'

    opening connection to ******.pactflow.io:443...
    opened
    starting SSL for ******.pactflow.io:443...
    SSL established
    <- "GET / HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: application/hal+json\r\nUser-Agent: Ruby\r\nAuthorization: [redacted]\r\n" 
    -> "HTTP/1.1 200 OK\r\n"
    -> "Date: Wed, 13 Apr 2022 16:57:19 GMT\r\n"
    -> "Content-Type: application/hal+json;charset=utf-8\r\n"
    -> "Content-Length: 5555\r\n"
    -> "Connection: keep-alive\r\n"
    -> "Vary: Accept\r\n"
    -> "Server: Webmachine-Ruby/1.6.0 Rack/1.3\r\n"
    -> "X-Pact-Broker-Version: 2.98.0\r\n"
    -> "X-Pact-Broker-Git-Sha: 3a08d128\r\n"
    -> "X-Pactflow-Git-Sha: 85e7754e3\r\n"
    -> "X-Content-Type-Options: nosniff\r\n"
    -> "Strict-Transport-Security: max-age=63072000; includeSubDomains; preload\r\n"
    -> "X-Request-Id: cb35bba1d72c207abc7d787cbb5f4473\r\n"
    -> "\r\n"
    reading 5555 bytes...
    -> "{\"_links\":{\"self\":{\"href\":\"https://******.pactflow.io\",\"title\":\"Index\",\"templated\":false},\"pb:publish-pact\":{\"href\":\"https://******.pactflow.io/pacts/provider/{provider}/consumer/{consumer}/version/{consumerApplicationVersion}\",\"title\":\"Publish a pact\",\"templated\":true},\"pb:publish-contracts\":{\"href\":\"https://******.pactflow.io/contracts/publish\",\"title\":\"Publish contracts\",\"templated\":false},\"pb:latest-pact-versions\":{\"href\":\"https://******.pactflow.io/pacts/latest\",\"title\":\"Latest pact versions\",\"templated\":false},\"pb:tagged-pact-versions\":{\"href\":\"https://******.pactflow.io/pacts/provider/{provider}/consumer/{consumer}/tag/{tag}\",\"title\":\"All versions of a pact for a given consumer, provider and consumer version tag\",\"templated\":false},\"pb:pacticipants\":{\"href\":\"https://******.pactflow.io/pacticipants\",\"title\":\"Pacticipants\",\"templated\":false},\"pb:pacticipant\":{\"href\":\"https://******.pactflow.io/pacticipants/{pacticipant}\",\"title\":\"Fetch pacticipant by name\",\"templated\":true},\"pb:latest-provider-pacts\":{\"href\":\"https://******.pactflow.io/pacts/provider/{provider}/latest\",\"title\":\"Latest pacts by provider\",\"templated\":true},\"pb:latest-provider-pacts-with-tag\":{\"href\":\"https://******.pactflow.io/pacts/provider/{provider}/latest/{tag}\",\"title\":\"Latest pacts for provider with the specified tag\",\"templated\":true},\"pb:provider-pacts-with-tag\":{\"href\":\"https://******.pactflow.io/pacts/provider/{provider}/tag/{tag}\",\"title\":\"All pact versions for the provider with the specified consumer version tag\",\"templated\":true},\"pb:provider-pacts\":{\"href\":\"https://******.pactflow.io/pacts/provider/{provider}\",\"title\":\"All pact versions for the specified provider\",\"templated\":true},\"pb:latest-version\":{\"href\":\"https://******.pactflow.io/pacticipants/{pacticipant}/latest-version\",\"title\":\"Latest pacticipant version\",\"templated\":true},\"pb:latest-tagged-version\":{\"href\":\"https://******.pactflow.io/pacticipants/{pacticipant}/latest-version/{tag}\",\"title\":\"Latest pacticipant version with the specified tag\",\"templated\":true},\"pb:webhooks\":{\"href\":\"https://******.pactflow.io/webhooks\",\"title\":\"Webhooks\",\"templated\":false},\"pb:webhook\":{\"href\":\"https://******.pactflow.io/webhooks/{uuid}\",\"title\":\"Webhook\",\"templated\":true},\"pb:integrations\":{\"href\":\"https://******.pactflow.io/integrations\",\"title\":\"Integrations\",\"templated\":false},\"pb:pacticipant-version-tag\":{\"href\":\"https://******.pactflow.io/pacticipants/{pacticipant}/versions/{version}/tags/{tag}\",\"title\":\"Get, create or delete a tag for a pacticipant version\",\"templated\":true},\"pb:pacticipant-branch-version\":{\"href\":\"https://******.pactflow.io/pacticipants/{pacticipant}/branches/{branch}/versions/{version}\",\"title\":\"Get or add/create a pacticipant version for a branch\",\"templated\":true},\"pb:pacticipant-version\":{\"href\":\"https://******.pactflow.io/pacticipants/{pacticipant}/versions/{version}\",\"title\":\"Get, create or delete a pacticipant version\",\"templated\":true},\"pb:metrics\":{\"href\":\"https://******.pactflow.io/metrics\",\"title\":\"Get Pact Broker metrics\"},\"pb:can-i-deploy-pacticipant-version-to-tag\":{\"href\":\"https://******.pactflow.io/can-i-deploy?pacticipant={pacticipant}\u0026version={version}\u0026to={tag}\",\"title\":\"Determine if an application version can be safely deployed to an environment identified by the given tag\",\"templated\":true},\"pb:can-i-deploy-pacticipant-version-to-environment\":{\"href\":\"https://******.pactflow.io/can-i-deploy?pacticipant={pacticipant}\u0026version={version}\u0026environment={environment}\",\"title\":\"Determine if an application version can be safely deployed to an environment\",\"templated\":true},\"pb:provider-pacts-for-verification\":{\"href\":\"https://******.pactflow.io/pacts/provider/{provider}/for-verification\",\"title\":\"Pact versions to be verified for the specified provider\",\"templated\":true},\"beta:provider-pacts-for-verification\":{\"name\":\"beta\",\"href\":\"https://******.pactflow.io/pacts/provider/{provider}/for-verification\",\"title\":\"DEPRECATED - please use pb:provider-pacts-for-verification\",\"templated\":true},\"curies\":[{\"name\":\"pb\",\"href\":\"https://******.pactflow.io/doc/{rel}?context=index\",\"templated\":true},{\"name\":\"beta\",\"href\":\"https://******.pactflow.io/doc/{rel}?context=index\",\"templated\":true}],\"pb:environments\":{\"title\":\"Environments\",\"href\":\"https://******.pactflow.io/environments\",\"templated\":false},\"pb:environment\":{\"title\":\"Environment\",\"href\":\"https://******.pactflow.io/environments/{uuid}\",\"templated\":true},\"pb:api-tokens\":{\"href\":\"https://******.pactflow.io/settings/tokens\",\"title\":\"API tokens\",\"templated\":false},\"pb:audit\":{\"href\":\"https://******.pactflow.io/audit\",\"title\":\"Audit trail\",\"templated\":false},\"pb:secrets\":{\"href\":\"https://******.pactflow.io/secrets\",\"title\":\"Secrets\",\"templated\":false},\"pf:admin-users\":{\"href\":\"https://******.pactflow.io/admin/users\",\"title\":\"Users\",\"templated\":false},\"pf:admin-teams\":{\"href\":\"https://******.pactflow.io/admin/teams\",\"title\":\"Teams\",\"templated\":false},\"pf:admin-system-accounts\":{\"href\":\"https://******.pactflow.io/admin/system-accounts\",\"title\":\"System accounts\",\"templated\":false},\"pf:admin-roles\":{\"href\":\"https://******.pactflow.io/admin/roles\",\"title\":\"Roles\",\"templated\":false},\"pf:admin-permissions\":{\"href\":\"https://******.pactflow.io/admin/permissions\",\"title\":\"Permissions\",\"templated\":false},\"pf:admin-authentication-settings\":{\"href\":\"https://******.pactflow.io/admin/tenant/authentication-settings\",\"title\":\"Authentication Settings\",\"templated\":false},\"pf:user-allocations\":{\"href\":\"https://******.pactflow.io/tenant/limits\",\"title\":\"User Allocations\",\"templated\":false}}}"

很简单。您已将消费者测试中的提供者(以及合同中的内容)定义为 pact-provider.

然而,在您的提供商测试中,它被称为 Clients Service

当您的提供者测试试图发现合同时,它使用提供者名称作为键。因为没有名为 Clients Service 的提供程序,所以它得到一个 404.

两者都

  1. 将消费者中的提供商名称更新为 Clients Service(并删除旧集成)
  2. 将提供商名称更新为 pact-provider

(1. 似乎是正确的做法)