diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 48a292f..95061de 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,6 +47,23 @@ jobs: PACT_BROKER_TOKEN_PACT_FOUNDATION: ${{ secrets.PACT_BROKER_TOKEN_PACT_FOUNDATION }} PACT_BROKER_FEATURES: ${{ matrix.feature }} TEST_FEATURE: ${{ matrix.feature }} + pact-v2: + runs-on: "ubuntu-latest" + continue-on-error: true + strategy: + fail-fast: false + matrix: + feature: [""] + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.4" + - run: "bundle install" + - name: Generate and publish pacts + run: | + rm -rf spec/pacts/* + bundle exec rake pact:v2:spec can-i-deploy: runs-on: "ubuntu-latest" needs: pact diff --git a/Gemfile b/Gemfile index c0d08e4..6b5cefe 100644 --- a/Gemfile +++ b/Gemfile @@ -15,15 +15,24 @@ group :development do gem 'fakefs', '~> 3.0' gem 'webmock', '~> 3.0' gem 'conventional-changelog', '~>1.3' - gem 'pact', '~> 1.16' gem 'pact-support', '~> 1.16' gem 'approvals', '0.1.7' gem 'rspec-its', '~> 2.0' gem 'pry-byebug' + + if ENV['X_PACT_DEVELOPMENT'] == 'true' + gem 'pact', path: '../pact-ruby' + gem 'pact-ffi', path: '../pact-ffi' + else + gem 'pact' + gem 'pact-ffi' + end + # for pact/v2 with non rail apps + gem 'activesupport' end group :test do gem 'faraday', '~>2.0' gem 'faraday-retry', '~>2.0' - gem 'rackup', '~> 2.1' + gem 'rack', '~> 2.1' end diff --git a/Rakefile b/Rakefile index d9b0374..8460925 100644 --- a/Rakefile +++ b/Rakefile @@ -38,3 +38,8 @@ task 'pact:list_provider_states' do JSON.parse(File.read(pact_file))['interactions'].collect{ | interaction| interaction['providerState'] } }.flatten.compact.sort.uniq end + +RSpec::Core::RakeTask.new('pact:v2:spec') do |task| + task.pattern = 'spec/pact/providers/**/*_spec.rb' + task.rspec_opts = ['-t pact'] +end \ No newline at end of file diff --git a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md index 8fe63a3..e285a5c 100644 --- a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md +++ b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md @@ -94,16 +94,6 @@ * [A request to mark a deployed version as not currently deploye](#a_request_to_mark_a_deployed_version_as_not_currently_deploye_given_a_currently_deployed_version_exists) given a currently deployed version exists -* [A request to publish a pact](#a_request_to_publish_a_pact_given_'Condor'_already_exist_in_the_pact-broker,_but_the_'Pricing_Service'_does_not) given 'Condor' already exist in the pact-broker, but the 'Pricing Service' does not - -* [A request to publish a pact](#a_request_to_publish_a_pact_given_the_'Pricing_Service'_already_exists_in_the_pact-broker) given the 'Pricing Service' already exists in the pact-broker - -* [A request to publish a pact](#a_request_to_publish_a_pact_given_an_error_occurs_while_publishing_a_pact) given an error occurs while publishing a pact - -* [A request to publish a pact with method patch](#a_request_to_publish_a_pact_with_method_patch_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker,_and_Condor_already_has_a_pact_published_for_version_1.3.0) given the 'Pricing Service' and 'Condor' already exist in the pact-broker, and Condor already has a pact published for version 1.3.0 - -* [A request to publish a pact with method put](#a_request_to_publish_a_pact_with_method_put_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker,_and_Condor_already_has_a_pact_published_for_version_1.3.0) given the 'Pricing Service' and 'Condor' already exist in the pact-broker, and Condor already has a pact published for version 1.3.0 - * [A request to publish contracts](#a_request_to_publish_contracts) * [A request to record a deployment](#a_request_to_record_a_deployment_given_version_5556b8149bf8bac76bc30f50a8a2dd4c22c85f30_of_pacticipant_Foo_exists_with_a_test_environment_available_for_deployment) given version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment @@ -1077,8 +1067,7 @@ Pact Broker will respond with: "name": "Pricing Service" }, "interactions": [ - - ] + ] } } ``` @@ -2118,194 +2107,6 @@ Pact Broker will respond with: } } ``` - -Given **'Condor' already exist in the pact-broker, but the 'Pricing Service' does not**, upon receiving **a request to publish a pact** from Pact Broker Client, with -```json -{ - "method": "put", - "path": "/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "consumer": { - "name": "Condor" - }, - "provider": { - "name": "Pricing Service" - }, - "interactions": [ - - ] - } -} -``` -Pact Broker will respond with: -```json -{ - "status": 201, - "headers": { - "Content-Type": "application/hal+json;charset=utf-8" - }, - "body": { - "_links": { - "pb:latest-pact-version": { - "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/latest" - } - } - } -} -``` - -Given **the 'Pricing Service' already exists in the pact-broker**, upon receiving **a request to publish a pact** from Pact Broker Client, with -```json -{ - "method": "put", - "path": "/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "consumer": { - "name": "Condor" - }, - "provider": { - "name": "Pricing Service" - }, - "interactions": [ - - ] - } -} -``` -Pact Broker will respond with: -```json -{ - "status": 201, - "headers": { - "Content-Type": "application/hal+json;charset=utf-8" - }, - "body": { - "_links": { - "pb:latest-pact-version": { - "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/latest" - } - } - } -} -``` - -Given **an error occurs while publishing a pact**, upon receiving **a request to publish a pact** from Pact Broker Client, with -```json -{ - "method": "put", - "path": "/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "consumer": { - "name": "Condor" - }, - "provider": { - "name": "Pricing Service" - }, - "interactions": [ - - ] - } -} -``` -Pact Broker will respond with: -```json -{ - "status": 500, - "headers": { - "Content-Type": "application/hal+json" - }, - "body": { - "error": { - "message": "An error occurred" - } - } -} -``` - -Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker, and Condor already has a pact published for version 1.3.0**, upon receiving **a request to publish a pact with method patch** from Pact Broker Client, with -```json -{ - "method": "patch", - "path": "/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "consumer": { - "name": "Condor" - }, - "provider": { - "name": "Pricing Service" - }, - "interactions": [ - - ] - } -} -``` -Pact Broker will respond with: -```json -{ - "status": 200, - "headers": { - "Content-Type": "application/hal+json;charset=utf-8" - }, - "body": { - "_links": { - "pb:latest-pact-version": { - "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/latest" - } - } - } -} -``` - -Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker, and Condor already has a pact published for version 1.3.0**, upon receiving **a request to publish a pact with method put** from Pact Broker Client, with -```json -{ - "method": "put", - "path": "/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "consumer": { - "name": "Condor" - }, - "provider": { - "name": "Pricing Service" - }, - "interactions": [ - - ] - } -} -``` -Pact Broker will respond with: -```json -{ - "status": 200, - "headers": { - "Content-Type": "application/hal+json;charset=utf-8" - }, - "body": { - "_links": { - "pb:latest-pact-version": { - "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/latest" - } - } - } -} -``` Upon receiving **a request to publish contracts** from Pact Broker Client, with ```json @@ -2411,7 +2212,9 @@ Given **version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exis "headers": { "Content-Type": "application/json", "Accept": "application/hal+json" - } + }, + "body": { + } } ``` Pact Broker will respond with: @@ -2565,8 +2368,7 @@ Pact Broker will respond with: "name": "Pricing Service" }, "interactions": [ - - ] + ] } } ``` @@ -2638,8 +2440,7 @@ Pact Broker will respond with: "name": "Pricing Service" }, "interactions": [ - - ] + ] } } ``` @@ -2651,7 +2452,9 @@ Given **'Condor' exists in the pact-broker with version 1.3.0, tagged with 'prod "path": "/pacticipants/Condor/versions/1.3.0/tags/prod", "headers": { "Content-Type": "application/json" - } + }, + "body": { + } } ``` Pact Broker will respond with: @@ -2678,7 +2481,9 @@ Given **'Condor' does not exist in the pact-broker**, upon receiving **a request "path": "/pacticipants/Condor/versions/1.3.0/tags/prod", "headers": { "Content-Type": "application/json" - } + }, + "body": { + } } ``` Pact Broker will respond with: @@ -2705,7 +2510,9 @@ Given **'Condor' exists in the pact-broker**, upon receiving **a request to tag "path": "/pacticipants/Condor/versions/1.3.0/tags/prod", "headers": { "Content-Type": "application/json" - } + }, + "body": { + } } ``` Pact Broker will respond with: diff --git a/doc/pacts/markdown/Pact Broker Client - PactFlow.md b/doc/pacts/markdown/Pact Broker Client - PactFlow.md index 63990b2..089957b 100644 --- a/doc/pacts/markdown/Pact Broker Client - PactFlow.md +++ b/doc/pacts/markdown/Pact Broker Client - PactFlow.md @@ -260,7 +260,7 @@ PactFlow will respond with: } ], "pb:branch-version": { - } + } } } } diff --git a/doc/pacts/markdown/Pact Broker Client V2 - Pact Broker.md b/doc/pacts/markdown/Pact Broker Client V2 - Pact Broker.md new file mode 100644 index 0000000..7ff6345 --- /dev/null +++ b/doc/pacts/markdown/Pact Broker Client V2 - Pact Broker.md @@ -0,0 +1,2799 @@ +### A pact between Pact Broker Client V2 and Pact Broker + +#### Requests from Pact Broker Client V2 to Pact Broker + +* [A request for a pacticipant version](#a_request_for_a_pacticipant_version_given_version_5556b8149bf8bac76bc30f50a8a2dd4c22c85f30_of_pacticipant_Foo_exists_with_2_environments_that_aren't_test_available_for_deployment) given version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with 2 environments that aren't test available for deployment + +* [A request for a pacticipant version](#a_request_for_a_pacticipant_version_given_version_5556b8149bf8bac76bc30f50a8a2dd4c22c85f30_of_pacticipant_Foo_exists_with_a_test_environment_available_for_deployment) given version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment + +* [A request for a pacticipant version](#a_request_for_a_pacticipant_version_given_version_5556b8149bf8bac76bc30f50a8a2dd4c22c85f30_of_pacticipant_Foo_exists_with_a_test_environment_available_for_release) given version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for release + +* [A request for an environment](#a_request_for_an_environment_given_an_environment_with_name_test_and_UUID_16926ef3-590f-4e3f-838e-719717aa88c9_exists) given an environment with name test and UUID 16926ef3-590f-4e3f-838e-719717aa88c9 exists + +* [A request for the compatibility matrix for a pacticipant that does not exist](#a_request_for_the_compatibility_matrix_for_a_pacticipant_that_does_not_exist) + +* [A request for the compatibility matrix for all versions of Foo and Bar](#a_request_for_the_compatibility_matrix_for_all_versions_of_Foo_and_Bar_given_the_pact_for_Foo_version_1.2.3_and_1.2.4_has_been_verified_by_Bar_version_4.5.6) given the pact for Foo version 1.2.3 and 1.2.4 has been verified by Bar version 4.5.6 + +* [A request for the compatibility matrix for Foo version 1.2.3 and Bar version 4.5.6](#a_request_for_the_compatibility_matrix_for_Foo_version_1.2.3_and_Bar_version_4.5.6_given_the_pact_for_Foo_Thing_version_1.2.3_has_been_verified_by_Bar_version_4.5.6) given the pact for Foo Thing version 1.2.3 has been verified by Bar version 4.5.6 + +* [A request for the compatibility matrix for Foo version 1.2.3 and Bar version 4.5.6](#a_request_for_the_compatibility_matrix_for_Foo_version_1.2.3_and_Bar_version_4.5.6_given_the_pact_for_Foo_version_1.2.3_has_been_verified_by_Bar_version_4.5.6) given the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6 + +* [A request for the compatibility matrix for Foo version 1.2.3 and the latest prod version of Bar](#a_request_for_the_compatibility_matrix_for_Foo_version_1.2.3_and_the_latest_prod_version_of_Bar_given_the_pact_for_Foo_version_1.2.3_has_been_successfully_verified_by_Bar_version_4.5.6_with_tag_prod,_and_1.2.4_unsuccessfully_by_9.9.9) given the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6 with tag prod, and 1.2.4 unsuccessfully by 9.9.9 + +* [A request for the compatibility matrix for Foo version 1.2.3 and the latest prod versions of all other pacticipants](#a_request_for_the_compatibility_matrix_for_Foo_version_1.2.3_and_the_latest_prod_versions_of_all_other_pacticipants_given_the_pact_for_Foo_version_1.2.3_has_been_successfully_verified_by_Bar_version_4.5.6_(tagged_prod)_and_version_5.6.7) given the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6 (tagged prod) and version 5.6.7 + +* [A request for the compatibility matrix for Foo version 1.2.3 and the latest version of Bar](#a_request_for_the_compatibility_matrix_for_Foo_version_1.2.3_and_the_latest_version_of_Bar_given_the_pact_for_Foo_version_1.2.3_has_been_successfully_verified_by_Bar_version_4.5.6,_and_1.2.4_unsuccessfully_by_9.9.9) given the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6, and 1.2.4 unsuccessfully by 9.9.9 + +* [A request for the compatibility matrix where one or more versions does not exist](#a_request_for_the_compatibility_matrix_where_one_or_more_versions_does_not_exist_given_the_pact_for_Foo_version_1.2.3_has_been_verified_by_Bar_version_4.5.6) given the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6 + +* [A request for the compatibility matrix where only the version of Foo is specified](#a_request_for_the_compatibility_matrix_where_only_the_version_of_Foo_is_specified_given_the_pact_for_Foo_version_1.2.3_has_been_verified_by_Bar_version_4.5.6_and_version_5.6.7) given the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6 and version 5.6.7 + +* [A request for the environments](#a_request_for_the_environments_given_an_environment_with_name_test_exists) given an environment with name test exists + +* [A request for the index resource](#a_request_for_the_index_resource) + +* [A request for the index resource](#a_request_for_the_index_resource_given_the_pacticipant_relations_are_present) given the pacticipant relations are present + +* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:environments_relation_exists_in_the_index_resource) given the pb:environments relation exists in the index resource + +* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:latest-tagged-version_relation_exists_in_the_index_resource) given the pb:latest-tagged-version relation exists in the index resource + +* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:latest-version_relation_exists_in_the_index_resource) given the pb:latest-version relation exists in the index resource + +* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:pacticipant-branch_relation_exists_in_the_index_resource) given the pb:pacticipant-branch relation exists in the index resource + +* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:pacticipant-version_and_pb:environments_relations_exist_in_the_index_resource) given the pb:pacticipant-version and pb:environments relations exist in the index resource + +* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:publish-contracts_relations_exists_in_the_index_resource) given the pb:publish-contracts relations exists in the index resource + +* [A request for the index resource with the webhook relation](#a_request_for_the_index_resource_with_the_webhook_relation) + +* [A request for the list of the latest pacts from all consumers for the Pricing Service'](#a_request_for_the_list_of_the_latest_pacts_from_all_consumers_for_the_Pricing_Service'_given_a_latest_pact_between_Condor_and_the_Pricing_Service_exists) given a latest pact between Condor and the Pricing Service exists + +* [A request for the list of the latest prod pacts from all consumers for the Pricing Service'](#a_request_for_the_list_of_the_latest_prod_pacts_from_all_consumers_for_the_Pricing_Service'_given_tagged_as_prod_pact_between_Condor_and_the_Pricing_Service_exists) given tagged as prod pact between Condor and the Pricing Service exists + +* [A request for the successful rows of the compatibility matrix for all versions of Foo and Bar](#a_request_for_the_successful_rows_of_the_compatibility_matrix_for_all_versions_of_Foo_and_Bar_given_the_pact_for_Foo_version_1.2.3_has_been_successfully_verified_by_Bar_version_4.5.6,_and_1.2.4_unsuccessfully_by_9.9.9) given the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6, and 1.2.4 unsuccessfully by 9.9.9 + +* [A request retrieve a pact for a specific version](#a_request_retrieve_a_pact_for_a_specific_version_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker,_and_Condor_already_has_a_pact_published_for_version_1.3.0) given the 'Pricing Service' and 'Condor' already exist in the pact-broker, and Condor already has a pact published for version 1.3.0 + +* [A request to create a global webhook with a JSON body](#a_request_to_create_a_global_webhook_with_a_JSON_body) + +* [A request to create a pacticipant](#a_request_to_create_a_pacticipant) + +* [A request to create a webhook for a consumer and provider](#a_request_to_create_a_webhook_for_a_consumer_and_provider_given_'Condor'_does_not_exist_in_the_pact-broker) given 'Condor' does not exist in the pact-broker + +* [A request to create a webhook with a JSON body and a uuid](#a_request_to_create_a_webhook_with_a_JSON_body_and_a_uuid_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker) given the 'Pricing Service' and 'Condor' already exist in the pact-broker + +* [A request to create a webhook with a JSON body for a consumer](#a_request_to_create_a_webhook_with_a_JSON_body_for_a_consumer_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker) given the 'Pricing Service' and 'Condor' already exist in the pact-broker + +* [A request to create a webhook with a JSON body for a consumer and provider](#a_request_to_create_a_webhook_with_a_JSON_body_for_a_consumer_and_provider_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker) given the 'Pricing Service' and 'Condor' already exist in the pact-broker + +* [A request to create a webhook with a JSON body for a consumer specified by a label](#a_request_to_create_a_webhook_with_a_JSON_body_for_a_consumer_specified_by_a_label) + +* [A request to create a webhook with a JSON body for a consumer that does not exist](#a_request_to_create_a_webhook_with_a_JSON_body_for_a_consumer_that_does_not_exist) + +* [A request to create a webhook with a JSON body for a provider](#a_request_to_create_a_webhook_with_a_JSON_body_for_a_provider_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker) given the 'Pricing Service' and 'Condor' already exist in the pact-broker + +* [A request to create a webhook with a JSON body for a provider specified by a label](#a_request_to_create_a_webhook_with_a_JSON_body_for_a_provider_specified_by_a_label) + +* [A request to create a webhook with a non-JSON body for a consumer and provider](#a_request_to_create_a_webhook_with_a_non-JSON_body_for_a_consumer_and_provider_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker) given the 'Pricing Service' and 'Condor' already exist in the pact-broker + +* [A request to create a webhook with every possible event type](#a_request_to_create_a_webhook_with_every_possible_event_type_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker) given the 'Pricing Service' and 'Condor' already exist in the pact-broker + +* [A request to create an environment](#a_request_to_create_an_environment) + +* [A request to delete a pacticipant branch](#a_request_to_delete_a_pacticipant_branch_given_a_branch_named_main_exists_for_pacticipant_Foo) given a branch named main exists for pacticipant Foo + +* [A request to determine if Bar can be deployed with all Foo tagged prod, ignoring the verification for Foo version 3.4.5](#a_request_to_determine_if_Bar_can_be_deployed_with_all_Foo_tagged_prod,_ignoring_the_verification_for_Foo_version_3.4.5_given_provider_Bar_version_4.5.6_has_a_successful_verification_for_Foo_version_1.2.3_tagged_prod_and_a_failed_verification_for_version_3.4.5_tagged_prod) given provider Bar version 4.5.6 has a successful verification for Foo version 1.2.3 tagged prod and a failed verification for version 3.4.5 tagged prod + +* [A request to get the Pricing Service](#a_request_to_get_the_Pricing_Service_given_the_'Pricing_Service'_already_exists_in_the_pact-broker) given the 'Pricing Service' already exists in the pact-broker + +* [A request to get the Pricing Service](#a_request_to_get_the_Pricing_Service_given_the_'Pricing_Service'_does_not_exist_in_the_pact-broker) given the 'Pricing Service' does not exist in the pact-broker + +* [A request to list pacticipants](#a_request_to_list_pacticipants_given_'Condor'_exists_in_the_pact-broker) given 'Condor' exists in the pact-broker + +* [A request to list the environments](#a_request_to_list_the_environments_given_an_environment_exists) given an environment exists + +* [A request to list the latest pacts](#a_request_to_list_the_latest_pacts_given_a_pact_between_Condor_and_the_Pricing_Service_exists) given a pact between Condor and the Pricing Service exists + +* [A request to list the versions deployed to an environment for a pacticipant name and application instance](#a_request_to_list_the_versions_deployed_to_an_environment_for_a_pacticipant_name_and_application_instance_given_an_version_is_deployed_to_environment_with_UUID_16926ef3-590f-4e3f-838e-719717aa88c9_with_target_customer-1) given an version is deployed to environment with UUID 16926ef3-590f-4e3f-838e-719717aa88c9 with target customer-1 + +* [A request to mark a deployed version as not currently deploye](#a_request_to_mark_a_deployed_version_as_not_currently_deploye_given_a_currently_deployed_version_exists) given a currently deployed version exists + +* [A request to publish contracts](#a_request_to_publish_contracts) + +* [A request to record a deployment](#a_request_to_record_a_deployment_given_version_5556b8149bf8bac76bc30f50a8a2dd4c22c85f30_of_pacticipant_Foo_exists_with_a_test_environment_available_for_deployment) given version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment + +* [A request to record a release](#a_request_to_record_a_release_given_version_5556b8149bf8bac76bc30f50a8a2dd4c22c85f30_of_pacticipant_Foo_exists_with_a_test_environment_available_for_deployment) given version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment + +* [A request to register the repository URL of a pacticipant](#a_request_to_register_the_repository_URL_of_a_pacticipant_given_the_'Pricing_Service'_already_exists_in_the_pact-broker) given the 'Pricing Service' already exists in the pact-broker + +* [A request to register the repository URL of a pacticipant](#a_request_to_register_the_repository_URL_of_a_pacticipant_given_the_'Pricing_Service'_does_not_exist_in_the_pact-broker) given the 'Pricing Service' does not exist in the pact-broker + +* [A request to retrieve a pacticipant](#a_request_to_retrieve_a_pacticipant_given_a_pacticipant_with_name_Foo_exists) given a pacticipant with name Foo exists + +* [A request to retrieve a pacticipant](#a_request_to_retrieve_a_pacticipant) + +* [A request to retrieve the latest 'production' version of Condor](#a_request_to_retrieve_the_latest_'production'_version_of_Condor_given_'Condor'_exists_in_the_pact-broker_with_the_latest_tagged_'production'_version_1.2.3) given 'Condor' exists in the pact-broker with the latest tagged 'production' version 1.2.3 + +* [A request to retrieve the latest pact between Condor and the Pricing Service](#a_request_to_retrieve_the_latest_pact_between_Condor_and_the_Pricing_Service_given_a_pact_between_Condor_and_the_Pricing_Service_exists) given a pact between Condor and the Pricing Service exists + +* [A request to retrieve the latest pact between Condor and the Pricing Service](#a_request_to_retrieve_the_latest_pact_between_Condor_and_the_Pricing_Service_given_no_pact_between_Condor_and_the_Pricing_Service_exists) given no pact between Condor and the Pricing Service exists + +* [A request to retrieve the latest version of Condor](#a_request_to_retrieve_the_latest_version_of_Condor_given_'Condor'_exists_in_the_pact-broker_with_the_latest_version_1.2.3) given 'Condor' exists in the pact-broker with the latest version 1.2.3 + +* [A request to retrieve the pact between the production verison of Condor and the Pricing Service](#a_request_to_retrieve_the_pact_between_the_production_verison_of_Condor_and_the_Pricing_Service_given_a_pact_between_Condor_and_the_Pricing_Service_exists_for_the_production_version_of_Condor) given a pact between Condor and the Pricing Service exists for the production version of Condor + +* [A request to tag the production version of Condor](#a_request_to_tag_the_production_version_of_Condor_given_'Condor'_exists_in_the_pact-broker_with_version_1.3.0,_tagged_with_'prod') given 'Condor' exists in the pact-broker with version 1.3.0, tagged with 'prod' + +* [A request to tag the production version of Condor](#a_request_to_tag_the_production_version_of_Condor_given_'Condor'_does_not_exist_in_the_pact-broker) given 'Condor' does not exist in the pact-broker + +* [A request to tag the production version of Condor](#a_request_to_tag_the_production_version_of_Condor_given_'Condor'_exists_in_the_pact-broker) given 'Condor' exists in the pact-broker + +* [A request to update a pacticipant](#a_request_to_update_a_pacticipant_given_a_pacticipant_with_name_Foo_exists) given a pacticipant with name Foo exists + +* [A request to update a webhook](#a_request_to_update_a_webhook_given_a_webhook_with_the_uuid_696c5f93-1b7f-44bc-8d03-59440fcaa9a0_exists) given a webhook with the uuid 696c5f93-1b7f-44bc-8d03-59440fcaa9a0 exists + +* [An invalid request to create a webhook for a consumer and provider](#an_invalid_request_to_create_a_webhook_for_a_consumer_and_provider_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker) given the 'Pricing Service' and 'Condor' already exist in the pact-broker + +#### Interactions + + +Given **version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with 2 environments that aren't test available for deployment**, upon receiving **a request for a pacticipant version** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:record-deployment": [ + { + "href": "href", + "name": "prod" + }, + { + "href": "href", + "name": "dev" + } + ] + } + } +} +``` + +Given **version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment**, upon receiving **a request for a pacticipant version** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:record-deployment": [ + { + "href": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30/deployed-versions/environment/cb632df3-0a0d-4227-aac3-60114dd36479", + "name": "test" + } + ] + } + } +} +``` + +Given **version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for release**, upon receiving **a request for a pacticipant version** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:record-release": [ + { + "href": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30/released-versions/environment/cb632df3-0a0d-4227-aac3-60114dd36479", + "name": "test" + } + ] + } + } +} +``` + +Given **an environment with name test and UUID 16926ef3-590f-4e3f-838e-719717aa88c9 exists**, upon receiving **a request for an environment** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/environments/16926ef3-590f-4e3f-838e-719717aa88c9", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:currently-deployed-deployed-versions": { + "href": "/environments/16926ef3-590f-4e3f-838e-719717aa88c9/deployed-versions/currently-deployed" + } + } + } +} +``` + +Upon receiving **a request for the compatibility matrix for a pacticipant that does not exist** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][pacticipant]=Wiffle&q[][pacticipant]=Meep&q[][version]=1%2e2%2e3&q[][version]=9%2e9%2e9" +} +``` +Pact Broker will respond with: +```json +{ + "status": 400, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "errors": [ + "an error message" + ] + } +} +``` + +Given **the pact for Foo version 1.2.3 and 1.2.4 has been verified by Bar version 4.5.6**, upon receiving **a request for the compatibility matrix for all versions of Foo and Bar** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][pacticipant]=Foo&q[][pacticipant]=Bar" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + }, + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ] + } +} +``` + +Given **the pact for Foo Thing version 1.2.3 has been verified by Bar version 4.5.6**, upon receiving **a request for the compatibility matrix for Foo version 1.2.3 and Bar version 4.5.6** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][pacticipant]=Foo+Thing&q[][pacticipant]=Bar&q[][version]=1%2e2%2e3&q[][version]=4%2e5%2e6" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ], + "summary": { + "deployable": true, + "reason": "some text", + "unknown": 1 + } + } +} +``` + +Given **the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6**, upon receiving **a request for the compatibility matrix for Foo version 1.2.3 and Bar version 4.5.6** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][pacticipant]=Foo&q[][pacticipant]=Bar&q[][version]=1%2e2%2e3&q[][version]=4%2e5%2e6" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ], + "summary": { + "deployable": true, + "reason": "some text", + "unknown": 1 + } + } +} +``` + +Given **the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6 with tag prod, and 1.2.4 unsuccessfully by 9.9.9**, upon receiving **a request for the compatibility matrix for Foo version 1.2.3 and the latest prod version of Bar** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][latest]=true&q[][pacticipant]=Foo&q[][pacticipant]=Bar&q[][tag]=prod&q[][version]=1%2e2%2e3" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ], + "summary": { + "deployable": true, + "reason": "some text", + "unknown": 1 + } + } +} +``` + +Given **the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6 (tagged prod) and version 5.6.7**, upon receiving **a request for the compatibility matrix for Foo version 1.2.3 and the latest prod versions of all other pacticipants** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/matrix", + "query": "latest=true&latestby=cvp&q[][pacticipant]=Foo&q[][version]=1%2e2%2e3&tag=prod" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "1.2.3" + } + }, + "provider": { + "name": "Bar", + "version": { + "number": "4.5.6" + } + } + } + ] + } +} +``` + +Given **the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6, and 1.2.4 unsuccessfully by 9.9.9**, upon receiving **a request for the compatibility matrix for Foo version 1.2.3 and the latest version of Bar** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][latest]=true&q[][pacticipant]=Foo&q[][pacticipant]=Bar&q[][version]=1%2e2%2e4" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ], + "summary": { + "deployable": true, + "reason": "some text", + "unknown": 1 + } + } +} +``` + +Given **the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6**, upon receiving **a request for the compatibility matrix where one or more versions does not exist** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][pacticipant]=Foo&q[][pacticipant]=Bar&q[][version]=1%2e2%2e3&q[][version]=9%2e9%2e9" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "summary": { + "reason": "an error message" + } + } +} +``` + +Given **the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6 and version 5.6.7**, upon receiving **a request for the compatibility matrix where only the version of Foo is specified** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/matrix", + "query": "latest=true&latestby=cvp&q[][pacticipant]=Foo&q[][version]=1%2e2%2e3" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ], + "summary": { + "deployable": true, + "reason": "some text", + "unknown": 1 + } + } +} +``` + +Given **an environment with name test exists**, upon receiving **a request for the environments** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/environments", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:environments": [ + { + "href": "href", + "name": "test" + } + ] + } + } +} +``` + +Upon receiving **a request for the index resource** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:pacticipant": { + "href": "/pacticipants/{pacticipant}" + }, + "pb:pacticipants": { + "href": "/pacticipants" + }, + "pb:webhooks": { + "href": "/webhooks" + } + } + } +} +``` + +Given **the pacticipant relations are present**, upon receiving **a request for the index resource** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:pacticipant": { + "href": "/pacticipants/{pacticipant}" + }, + "pb:pacticipants": { + "href": "/pacticipants" + } + } + } +} +``` + +Given **the pb:environments relation exists in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:environments": { + "href": "/environments" + } + } + } +} +``` + +Given **the pb:latest-tagged-version relation exists in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/", + "headers": { + "Accept": "application/hal+json, application/json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:latest-tagged-version": { + "href": "http://127.0.0.1:9999/pacticipants/Condor/latest-version/production" + } + } + } +} +``` + +Given **the pb:latest-version relation exists in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/", + "headers": { + "Accept": "application/hal+json, application/json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:latest-version": { + "href": "http://127.0.0.1:9999/pacticipants/Condor/latest-version" + } + } + } +} +``` + +Given **the pb:pacticipant-branch relation exists in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:pacticipant-branch": { + "href": "/pacticipants/{pacticipant}/branches/{branch}" + } + } + } +} +``` + +Given **the pb:pacticipant-version and pb:environments relations exist in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:environments": { + "href": "/environments" + }, + "pb:pacticipant-version": { + "href": "/pacticipants/{pacticipant}/versions/{version}" + } + } + } +} +``` + +Given **the pb:publish-contracts relations exists in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:publish-contracts": { + "href": "/contracts/publish" + } + } + } +} +``` + +Upon receiving **a request for the index resource with the webhook relation** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:webhook": { + "href": "/webhooks/{uuid}", + "templated": true + } + } + } +} +``` + +Given **a latest pact between Condor and the Pricing Service exists**, upon receiving **a request for the list of the latest pacts from all consumers for the Pricing Service'** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacts/provider/Pricing%20Service/latest" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json" + }, + "body": { + "_links": { + "pb:pacts": [ + { + "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", + "name": "Condor", + "title": "Pact between Condor (v1.3.0) and Pricing Service" + } + ], + "provider": { + "href": "http://example.org/pacticipants/Pricing%20Service", + "title": "Pricing Service" + } + } + } +} +``` + +Given **tagged as prod pact between Condor and the Pricing Service exists**, upon receiving **a request for the list of the latest prod pacts from all consumers for the Pricing Service'** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacts/provider/Pricing%20Service/latest/prod" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json" + }, + "body": { + "_links": { + "pb:pacts": [ + { + "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", + "name": "Condor", + "title": "Pact between Condor (v1.3.0) and Pricing Service" + } + ], + "provider": { + "href": "http://example.org/pacticipants/Pricing%20Service", + "title": "Pricing Service" + } + } + } +} +``` + +Given **the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6, and 1.2.4 unsuccessfully by 9.9.9**, upon receiving **a request for the successful rows of the compatibility matrix for all versions of Foo and Bar** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][pacticipant]=Foo&q[][pacticipant]=Bar&success[]=true" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ], + "summary": { + "deployable": true, + "reason": "some text", + "unknown": 1 + } + } +} +``` + +Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker, and Condor already has a pact published for version 1.3.0**, upon receiving **a request retrieve a pact for a specific version** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "consumer": { + "name": "Condor" + }, + "interactions": [ + ], + "provider": { + "name": "Pricing Service" + } + } +} +``` + +Upon receiving **a request to create a global webhook with a JSON body** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/webhooks", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + } +} +``` + +Upon receiving **a request to create a pacticipant** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/pacticipants", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "name": "Foo", + "repositoryUrl": "http://foo" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/pacticipants/Foo" + } + }, + "name": "Foo", + "repositoryUrl": "http://foo" + } +} +``` + +Given **'Condor' does not exist in the pact-broker**, upon receiving **a request to create a webhook for a consumer and provider** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/webhooks/provider/Pricing%20Service/consumer/Condor", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 404, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + } +} +``` + +Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker**, upon receiving **a request to create a webhook with a JSON body and a uuid** from Pact Broker Client V2, with +```json +{ + "method": "PUT", + "path": "/webhooks/696c5f93-1b7f-44bc-8d03-59440fcaa9a0", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "consumer": { + "name": "Condor" + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "provider": { + "name": "Pricing Service" + }, + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + } +} +``` + +Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker**, upon receiving **a request to create a webhook with a JSON body for a consumer** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/webhooks", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "consumer": { + "name": "Condor" + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + } +} +``` + +Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker**, upon receiving **a request to create a webhook with a JSON body for a consumer and provider** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/webhooks/provider/Pricing%20Service/consumer/Condor", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + } +} +``` + +Upon receiving **a request to create a webhook with a JSON body for a consumer specified by a label** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/webhooks", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "consumer": { + "label": "consumer_label" + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + } +} +``` + +Upon receiving **a request to create a webhook with a JSON body for a consumer that does not exist** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/webhooks", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "consumer": { + "name": "Condor" + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 400, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "errors": { + "consumer.name": [ + "Some error" + ] + } + } +} +``` + +Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker**, upon receiving **a request to create a webhook with a JSON body for a provider** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/webhooks", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "provider": { + "name": "Pricing Service" + }, + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + } +} +``` + +Upon receiving **a request to create a webhook with a JSON body for a provider specified by a label** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/webhooks", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "provider": { + "label": "provider_label" + }, + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + } +} +``` + +Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker**, upon receiving **a request to create a webhook with a non-JSON body for a consumer and provider** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/webhooks/provider/Pricing%20Service/consumer/Condor", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": "", + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": "" + } + } +} +``` + +Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker**, upon receiving **a request to create a webhook with every possible event type** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/webhooks/provider/Pricing%20Service/consumer/Condor", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + }, + { + "name": "contract_published" + }, + { + "name": "provider_verification_published" + }, + { + "name": "provider_verification_succeeded" + }, + { + "name": "provider_verification_failed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + }, + { + "name": "contract_published" + }, + { + "name": "provider_verification_published" + }, + { + "name": "provider_verification_succeeded" + }, + { + "name": "provider_verification_failed" + } + ], + "request": { + "body": { + "some": "body" + } + } + } +} +``` + +Upon receiving **a request to create an environment** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/environments", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "contacts": [ + { + "details": { + "emailAddress": "foo@bar.com" + }, + "name": "Foo team" + } + ], + "displayName": "Test", + "name": "test", + "production": false + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "contacts": [ + { + "details": { + "emailAddress": "foo@bar.com" + }, + "name": "Foo team" + } + ], + "displayName": "Test", + "name": "test", + "production": false, + "uuid": "ffe683ef-dcd7-4e4f-877d-f6eb3db8e86e" + } +} +``` + +Given **a branch named main exists for pacticipant Foo**, upon receiving **a request to delete a pacticipant branch** from Pact Broker Client V2, with +```json +{ + "method": "DELETE", + "path": "/pacticipants/Foo/branches/main" +} +``` +Pact Broker will respond with: +```json +{ + "status": 204 +} +``` + +Given **provider Bar version 4.5.6 has a successful verification for Foo version 1.2.3 tagged prod and a failed verification for version 3.4.5 tagged prod**, upon receiving **a request to determine if Bar can be deployed with all Foo tagged prod, ignoring the verification for Foo version 3.4.5** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/matrix", + "query": "ignore[][pacticipant]=Foo&ignore[][version]=3%2e4%2e5&latestby=cvpv&q[][pacticipant]=Bar&q[][pacticipant]=Foo&q[][tag]=prod&q[][version]=4%2e5%2e6" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "1.2.3" + } + }, + "provider": { + "name": "Bar", + "version": { + "number": "4.5.6" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true + } + }, + { + "consumer": { + "name": "Foo", + "version": { + "number": "3.4.5" + } + }, + "ignored": true, + "provider": { + "name": "Bar", + "version": { + "number": "4.5.6" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": false + } + } + ], + "notices": [ + { + "text": "some notice", + "type": "info" + } + ], + "summary": { + "deployable": true, + "ignored": 1 + } + } +} +``` + +Given **the 'Pricing Service' already exists in the pact-broker**, upon receiving **a request to get the Pricing Service** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacticipants/Pricing%20Service" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json" + }, + "body": { + "_embedded": { + "latest-version": { + "_links": { + "self": { + "href": "http://example.org/pacticipants/Pricing%20Service/versions/1.3.0" + } + }, + "number": "1.3.0" + } + }, + "_links": { + "self": { + "href": "http://example.org/pacticipants/Pricing%20Service" + }, + "versions": { + "href": "http://example.org/pacticipants/Pricing%20Service/versions" + } + }, + "name": "Pricing Service", + "repositoryUrl": "git@git.realestate.com.au:business-systems/pricing-service" + } +} +``` + +Given **the 'Pricing Service' does not exist in the pact-broker**, upon receiving **a request to get the Pricing Service** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacticipants/Pricing%20Service" +} +``` +Pact Broker will respond with: +```json +{ + "status": 404 +} +``` + +Given **'Condor' exists in the pact-broker**, upon receiving **a request to list pacticipants** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacticipants" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json" + }, + "body": { + "_links": { + "pacticipants": [ + { + "href": "http://example.org/pacticipants/Condor", + "title": "Condor" + } + ], + "self": { + "href": "http://example.org/pacticipants" + } + }, + "pacticipants": [ + { + "_embedded": { + "latest-version": { + "_links": { + "self": { + "href": "http://example.org/pacticipants/Condor/versions/1.3.0" + } + }, + "number": "1.3.0" + } + }, + "_links": { + "self": { + "href": "http://example.org/pacticipants/Condor" + } + }, + "name": "Condor" + } + ] + } +} +``` + +Given **an environment exists**, upon receiving **a request to list the environments** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/environments", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_embedded": { + "environments": [ + { + "contacts": [ + { + "details": { + "emailAddress": "foo@bar.com" + }, + "name": "Foo team" + } + ], + "displayName": "Test", + "name": "test", + "production": false, + "uuid": "78e85fb2-9df1-48da-817e-c9bea6294e01" + } + ] + } + } +} +``` + +Given **a pact between Condor and the Pricing Service exists**, upon receiving **a request to list the latest pacts** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacts/latest" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json" + }, + "body": { + "_links": { + "self": { + "href": "http://example.org/pacts/latest" + } + }, + "pacts": [ + { + "_embedded": { + "consumer": { + "_embedded": { + "version": { + "number": "1.3.0" + } + }, + "_links": { + "self": { + "href": "http://example.org/pacticipants/Condor" + } + }, + "name": "Condor" + }, + "provider": { + "_links": { + "self": { + "href": "http://example.org/pacticipants/Pricing%20Service" + } + }, + "name": "Pricing Service" + } + }, + "_links": { + "self": [ + { + "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/latest" + }, + { + "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0" + } + ] + } + } + ] + } +} +``` + +Given **an version is deployed to environment with UUID 16926ef3-590f-4e3f-838e-719717aa88c9 with target customer-1**, upon receiving **a request to list the versions deployed to an environment for a pacticipant name and application instance** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/environments/16926ef3-590f-4e3f-838e-719717aa88c9/deployed-versions/currently-deployed", + "query": "pacticipant=Foo", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_embedded": { + "deployedVersions": [ + { + "_links": { + "self": { + "href": "/deployed-versions/ff3adecf-cfc5-4653-a4e3-f1861092f8e0" + } + }, + "applicationInstance": "customer-1" + } + ] + } + } +} +``` + +Given **a currently deployed version exists**, upon receiving **a request to mark a deployed version as not currently deploye** from Pact Broker Client V2, with +```json +{ + "method": "PATCH", + "path": "/deployed-versions/ff3adecf-cfc5-4653-a4e3-f1861092f8e0", + "headers": { + "Content-Type": "application/merge-patch+json" + }, + "body": { + "currentlyDeployed": false + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_embedded": { + "version": { + "number": "2" + } + }, + "currentlyDeployed": false + } +} +``` + +Upon receiving **a request to publish contracts** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/contracts/publish", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "branch": "main", + "buildUrl": "http://build", + "contracts": [ + { + "consumerName": "Foo", + "content": "eyJjb25zdW1lciI6eyJuYW1lIjoiRm9vIn0sInByb3ZpZGVyIjp7Im5hbWUiOiJCYXIifSwiaW50ZXJhY3Rpb25zIjpbeyJkZXNjcmlwdGlvbiI6ImFuIGV4YW1wbGUgcmVxdWVzdCIsInByb3ZpZGVyU3RhdGUiOiJhIHByb3ZpZGVyIHN0YXRlIiwicmVxdWVzdCI6eyJtZXRob2QiOiJHRVQiLCJwYXRoIjoiLyIsImhlYWRlcnMiOnt9fSwicmVzcG9uc2UiOnsic3RhdHVzIjoyMDAsImhlYWRlcnMiOnsiQ29udGVudC1UeXBlIjoiYXBwbGljYXRpb24vaGFsK2pzb24ifX19XSwibWV0YWRhdGEiOnsicGFjdFNwZWNpZmljYXRpb24iOnsidmVyc2lvbiI6IjIuMC4wIn19fQ==", + "contentType": "application/json", + "onConflict": "merge", + "providerName": "Bar", + "specification": "pact" + } + ], + "pacticipantName": "Foo", + "pacticipantVersionNumber": "5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", + "tags": [ + "dev" + ] + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_embedded": { + "pacticipant": { + "name": "Foo" + }, + "version": { + "number": "5556b8149bf8bac76bc30f50a8a2dd4c22c85f30" + } + }, + "_links": { + "pb:contracts": [ + { + "href": "http://some-pact" + } + ], + "pb:pacticipant-version-tags": [ + { + "name": "dev" + } + ] + }, + "logs": [ + { + "level": "info", + "message": "some message" + } + ] + } +} +``` + +Given **version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment**, upon receiving **a request to record a deployment** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30/deployed-versions/environment/cb632df3-0a0d-4227-aac3-60114dd36479", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "applicationInstance": "blue", + "target": "blue" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "target": "blue" + } +} +``` + +Given **version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment**, upon receiving **a request to record a release** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30/released-versions/environment/cb632df3-0a0d-4227-aac3-60114dd36479", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + } +} +``` + +Given **the 'Pricing Service' already exists in the pact-broker**, upon receiving **a request to register the repository URL of a pacticipant** from Pact Broker Client V2, with +```json +{ + "method": "PATCH", + "path": "/pacticipants/Pricing%20Service", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "repository_url": "git@git.realestate.com.au:business-systems/pricing-service" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + } +} +``` + +Given **the 'Pricing Service' does not exist in the pact-broker**, upon receiving **a request to register the repository URL of a pacticipant** from Pact Broker Client V2, with +```json +{ + "method": "PATCH", + "path": "/pacticipants/Pricing%20Service", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "repository_url": "git@git.realestate.com.au:business-systems/pricing-service" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + } +} +``` + +Given **a pacticipant with name Foo exists**, upon receiving **a request to retrieve a pacticipant** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacticipants/Foo", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/pacticipants/Foo" + } + } + } +} +``` + +Upon receiving **a request to retrieve a pacticipant** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacticipants/Foo", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 404 +} +``` + +Given **'Condor' exists in the pact-broker with the latest tagged 'production' version 1.2.3**, upon receiving **a request to retrieve the latest 'production' version of Condor** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacticipants/Condor/latest-version/production", + "headers": { + "Accept": "application/hal+json, application/json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "number": "1.2.3" + } +} +``` + +Given **a pact between Condor and the Pricing Service exists**, upon receiving **a request to retrieve the latest pact between Condor and the Pricing Service** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacts/provider/Pricing%20Service/consumer/Condor/latest" +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json", + "X-Pact-Consumer-Version": "1.3.0" + }, + "body": { + "consumer": { + "name": "Condor" + }, + "interactions": [ + ], + "provider": { + "name": "Pricing Service" + } + } +} +``` + +Given **no pact between Condor and the Pricing Service exists**, upon receiving **a request to retrieve the latest pact between Condor and the Pricing Service** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacts/provider/Pricing%20Service/consumer/Condor/latest" +} +``` +Pact Broker will respond with: +```json +{ + "status": 404 +} +``` + +Given **'Condor' exists in the pact-broker with the latest version 1.2.3**, upon receiving **a request to retrieve the latest version of Condor** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacticipants/Condor/latest-version", + "headers": { + "Accept": "application/hal+json, application/json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "number": "1.2.3" + } +} +``` + +Given **a pact between Condor and the Pricing Service exists for the production version of Condor**, upon receiving **a request to retrieve the pact between the production verison of Condor and the Pricing Service** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/pacts/provider/Pricing%20Service/consumer/Condor/latest/prod", + "headers": { + "Accept": "application/hal+json, application/json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "consumer": { + "name": "Condor" + }, + "interactions": [ + ], + "provider": { + "name": "Pricing Service" + } + } +} +``` + +Given **'Condor' exists in the pact-broker with version 1.3.0, tagged with 'prod'**, upon receiving **a request to tag the production version of Condor** from Pact Broker Client V2, with +```json +{ + "method": "PUT", + "path": "/pacticipants/Condor/versions/1.3.0/tags/prod", + "headers": { + "Content-Type": "application/json" + }, + "body": { + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/pacticipants/Condor/versions/1.3.0/tags/prod" + } + } + } +} +``` + +Given **'Condor' does not exist in the pact-broker**, upon receiving **a request to tag the production version of Condor** from Pact Broker Client V2, with +```json +{ + "method": "PUT", + "path": "/pacticipants/Condor/versions/1.3.0/tags/prod", + "headers": { + "Content-Type": "application/json" + }, + "body": { + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/pacticipants/Condor/versions/1.3.0/tags/prod" + } + } + } +} +``` + +Given **'Condor' exists in the pact-broker**, upon receiving **a request to tag the production version of Condor** from Pact Broker Client V2, with +```json +{ + "method": "PUT", + "path": "/pacticipants/Condor/versions/1.3.0/tags/prod", + "headers": { + "Content-Type": "application/json" + }, + "body": { + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/pacticipants/Condor/versions/1.3.0/tags/prod" + } + } + } +} +``` + +Given **a pacticipant with name Foo exists**, upon receiving **a request to update a pacticipant** from Pact Broker Client V2, with +```json +{ + "method": "PATCH", + "path": "/pacticipants/Foo", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "name": "Foo", + "repositoryUrl": "http://foo" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/pacticipants/Foo" + } + }, + "name": "Foo", + "repositoryUrl": "http://foo" + } +} +``` + +Given **a webhook with the uuid 696c5f93-1b7f-44bc-8d03-59440fcaa9a0 exists**, upon receiving **a request to update a webhook** from Pact Broker Client V2, with +```json +{ + "method": "PUT", + "path": "/webhooks/696c5f93-1b7f-44bc-8d03-59440fcaa9a0", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "consumer": { + "name": "Condor" + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "provider": { + "name": "Pricing Service" + }, + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + } +} +``` + +Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker**, upon receiving **an invalid request to create a webhook for a consumer and provider** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/webhooks/provider/Pricing%20Service/consumer/Condor", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "username": "username" + } + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 400, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "errors": { + "request.url": [ + "Some error" + ] + } + } +} +``` diff --git a/doc/pacts/markdown/Pact Broker Client V2 - PactFlow.md b/doc/pacts/markdown/Pact Broker Client V2 - PactFlow.md new file mode 100644 index 0000000..59ffc3d --- /dev/null +++ b/doc/pacts/markdown/Pact Broker Client V2 - PactFlow.md @@ -0,0 +1,267 @@ +### A pact between Pact Broker Client V2 and PactFlow + +#### Requests from Pact Broker Client V2 to PactFlow + +* [A request for the index resource](#a_request_for_the_index_resource) + +* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:publish-provider-contract_relation_exists_in_the_index_resource) given the pb:publish-provider-contract relation exists in the index resource + +* [A request to create a provider contract](#a_request_to_create_a_provider_contract) + +* [A request to create a provider contract](#a_request_to_create_a_provider_contract_given_there_is_a_pf:ui_href_in_the_response) given there is a pf:ui href in the response + +* [A request to create a webhook for a team](#a_request_to_create_a_webhook_for_a_team_given_a_team_with_UUID_2abbc12a-427d-432a-a521-c870af1739d9_exists) given a team with UUID 2abbc12a-427d-432a-a521-c870af1739d9 exists + +* [A request to publish a provider contract](#a_request_to_publish_a_provider_contract) + +#### Interactions + + +Upon receiving **a request for the index resource** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/", + "headers": { + "Accept": "application/hal+json" + } +} +``` +PactFlow will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:pacticipant": { + "href": "/pacticipants/{pacticipant}" + }, + "pb:pacticipants": { + "href": "/pacticipants" + }, + "pb:webhooks": { + "href": "/webhooks" + } + } + } +} +``` + +Given **the pb:publish-provider-contract relation exists in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client V2, with +```json +{ + "method": "GET", + "path": "/", + "headers": { + "Accept": "application/hal+json" + } +} +``` +PactFlow will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pf:publish-provider-contract": { + "href": "/provider-contracts/provider/{provider}/publish" + } + } + } +} +``` + +Upon receiving **a request to create a provider contract** from Pact Broker Client V2, with +```json +{ + "method": "PUT", + "path": "/contracts/provider/Bar/version/1", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", + "contentType": "application/yaml", + "contractType": "oas", + "verificationResults": { + "content": "c29tZSByZXN1bHRz", + "contentType": "text/plain", + "format": "text", + "success": true, + "verifier": "my custom tool", + "verifierVersion": "1.0" + } + } +} +``` +PactFlow will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + } +} +``` + +Given **there is a pf:ui href in the response**, upon receiving **a request to create a provider contract** from Pact Broker Client V2, with +```json +{ + "method": "PUT", + "path": "/contracts/provider/Bar/version/1", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", + "contentType": "application/yaml", + "contractType": "oas", + "verificationResults": { + "content": "c29tZSByZXN1bHRz", + "contentType": "text/plain", + "format": "text", + "success": true, + "verifier": "my custom tool", + "verifierVersion": "1.0" + } + } +} +``` +PactFlow will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pf:ui": { + "href": "some-url" + } + } + } +} +``` + +Given **a team with UUID 2abbc12a-427d-432a-a521-c870af1739d9 exists**, upon receiving **a request to create a webhook for a team** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/webhooks", + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "url": "https://webhook" + }, + "teamUuid": "2abbc12a-427d-432a-a521-c870af1739d9" + } +} +``` +PactFlow will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "self": { + "href": "/some-url", + "title": "A title" + } + }, + "description": "a webhook", + "teamUuid": "2abbc12a-427d-432a-a521-c870af1739d9" + } +} +``` + +Upon receiving **a request to publish a provider contract** from Pact Broker Client V2, with +```json +{ + "method": "POST", + "path": "/provider-contracts/provider/Bar/publish", + "headers": { + "Accept": "application/hal+json, application/problem+json", + "Content-Type": "application/json" + }, + "body": { + "branch": "main", + "buildUrl": "http://build", + "contract": { + "content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", + "contentType": "application/yaml", + "selfVerificationResults": { + "content": "c29tZSByZXN1bHRz", + "contentType": "text/plain", + "format": "text", + "success": true, + "verifier": "my custom tool", + "verifierVersion": "1.0" + }, + "specification": "oas" + }, + "pacticipantVersionNumber": "1", + "tags": [ + "dev" + ] + } +} +``` +PactFlow will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_embedded": { + "version": { + "number": "1" + } + }, + "_links": { + "pb:branch-version": { + }, + "pb:pacticipant-version-tags": [ + { + } + ] + }, + "notices": [ + { + "text": "some notice", + "type": "info" + } + ] + } +} +``` diff --git a/doc/pacts/markdown/README.md b/doc/pacts/markdown/README.md index 4c45249..8df4e3c 100644 --- a/doc/pacts/markdown/README.md +++ b/doc/pacts/markdown/README.md @@ -1,4 +1,4 @@ -### Pacts for Pact Broker Client +### Pacts for Pact Broker Client V2 * [Pact Broker](Pact%20Broker%20Client%20-%20Pact%20Broker.md) * [PactFlow](Pact%20Broker%20Client%20-%20PactFlow.md) diff --git a/lib/pact_broker/client/versions.rb b/lib/pact_broker/client/versions.rb index 5a14c75..7d03ce7 100644 --- a/lib/pact_broker/client/versions.rb +++ b/lib/pact_broker/client/versions.rb @@ -29,7 +29,7 @@ def latest options end def tag options - response = put(tag_url(options), headers: default_put_headers.merge("Content-Length" => "0")) + response = put(tag_url(options), headers: default_put_headers, body: {}.to_json) handle_response(response) do true end diff --git a/spec/pact/providers/PactBroker/create_environment_spec.rb b/spec/pact/providers/PactBroker/create_environment_spec.rb new file mode 100644 index 0000000..90183d5 --- /dev/null +++ b/spec/pact/providers/PactBroker/create_environment_spec.rb @@ -0,0 +1,94 @@ +require_relative '../../../pact_ruby_v2_spec_helper' +require 'pact_broker/client/environments/create_environment' + +RSpec.describe "create an environment", pact: true do + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + include PactBrokerPactHelperMethods + + let(:params) do + { + name: "test", + display_name: "Test", + production: false, + contact_name: "Foo team", + contact_email_address: "foo@bar.com" + + } + end + let(:pact_broker_client_options) { {} } + let(:request_body) do + { + name: "test", + displayName: "Test", + production: false, + contacts: [{ + name: "Foo team", + details: { + emailAddress: "foo@bar.com" + } + }] + } + end + + let(:options) do + { + verbose: verbose + } + end + let(:verbose) { false } + let(:pact_broker_base_url) { "http://127.0.0.1:9999" } + let(:pact_broker_client_options) { { pact_broker_base_url: pact_broker_base_url } } + + subject { PactBroker::Client::Environments::CreateEnvironment.call(params, options, pact_broker_client_options) } + + def mock_index + new_interaction + .given("the pb:environments relation exists in the index resource") + .upon_receiving("a request for the index resource") + .with_request( + method: "GET", + path: '/', + headers: get_request_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:environments' => { + href: generate_mock_server_url( + regex: ".*(\\/environments)$", + example: "/environments" + ) + } + } + } + ) + end + + def mock_environment_creation_request + new_interaction + .upon_receiving("a request to create an environment") + .with_request( + method: "POST", + path: "/environments", + headers: post_request_headers, + body: request_body + ) + .will_respond_with( + status: 201, + headers: pact_broker_response_headers, + body: request_body.merge("uuid" => match_type_of("ffe683ef-dcd7-4e4f-877d-f6eb3db8e86e")) + ) + end + + it "returns a success result" do + mock_index + mock_environment_creation_request + execute_http_pact do |mock_server| + expect(subject.success).to be true + expect(subject.message).to include "Created test environment in the Pact Broker with UUID ffe683ef-dcd7-4e4f-877d-f6eb3db8e86e" + end + end +end diff --git a/spec/pact/providers/PactBroker/delete_branch_spec.rb b/spec/pact/providers/PactBroker/delete_branch_spec.rb new file mode 100644 index 0000000..ff89d52 --- /dev/null +++ b/spec/pact/providers/PactBroker/delete_branch_spec.rb @@ -0,0 +1,74 @@ +require_relative '../../../pact_ruby_v2_spec_helper' +require "pact_broker/client/branches/delete_branch" + +RSpec.describe "delete a branch", pact: true do + pact_broker # our "Pact Broker" service Pact provider helper + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + include PactBrokerPactHelperMethods + + let(:params) do + { + pacticipant: "Foo", + branch: "main", + error_when_not_found: true + } + end + + let(:options) do + { + verbose: verbose + } + end + + let(:pact_broker_base_url) { "http://127.0.0.1:9999" } + let(:pact_broker_client_options) { { pact_broker_base_url: pact_broker_base_url } } + let(:response_headers) { { "Content-Type" => "application/hal+json"} } + let(:verbose) { false } + + subject { PactBroker::Client::Branches::DeleteBranch.call(params, options, pact_broker_client_options) } + + def mock_index + new_interaction + .given("the pb:pacticipant-branch relation exists in the index resource") + .upon_receiving("a request for the index resource") + .with_request( + method: "GET", + path: '/', + headers: get_request_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:pacticipant-branch' => { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/.*\\/branches\\/.*)$", + example: "/pacticipants/{pacticipant}/branches/{branch}" + ), + } + } + } + ) + end + + def mock_branch_delete_request + new_interaction + .given("a branch named main exists for pacticipant Foo") + .upon_receiving("a request to delete a pacticipant branch") + .with_request( + method: "DELETE", + path: "/pacticipants/Foo/branches/main", + ) + .will_respond_with( + status: 204 + ) + end + it "returns a success result" do + mock_index + mock_branch_delete_request + execute_http_pact do |mock_server| + expect(subject.success).to be true + end + end +end diff --git a/spec/pact/providers/PactBroker/extra_goodies_spec.rb b/spec/pact/providers/PactBroker/extra_goodies_spec.rb new file mode 100644 index 0000000..1a28c67 --- /dev/null +++ b/spec/pact/providers/PactBroker/extra_goodies_spec.rb @@ -0,0 +1,117 @@ +require_relative '../../../pact_ruby_v2_spec_helper' +require 'pact_broker/client/pact_broker_client' + + +module PactBroker::Client + describe PactBrokerClient, :pact => true do + + pact_broker + let(:pact_broker_client) { PactBrokerClient.new(base_url: 'http://localhost:9999') } + + describe "listing pacts" do + context "when pacts exist" do + let(:response_body) { JSON.parse(File.read("./spec/support/pacts_latest_list.json"))} + let(:expected_pacts) do + [{ + :consumer => { + :name => 'Condor', + :version => { + :number => '1.3.0' + } + }, + :provider => { + :name => 'Pricing Service' + } + } + ] + end + let(:interaction) do + new_interaction. + given("a pact between Condor and the Pricing Service exists"). + upon_receiving("a request to list the latest pacts"). + with_request( + method: :get, + path: '/pacts/latest', + headers: {} ). + will_respond_with( headers: {'Content-Type' => match_regex(%r{application/hal\+json.*},'application/hal+json')}, + status: 200, + body: response_body + ) + end + it "returns the response body" do + interaction.execute do |mock_server| + expect(pact_broker_client.pacts.list_latest).to eq(expected_pacts) + end + end + end + end + + describe "listing pacticipants" do + context "when a pacticipant exists" do + let(:response_body) { JSON.parse(File.read("./spec/support/pacticipants_list.json"))} + let(:interaction) do + new_interaction. + given("'Condor' exists in the pact-broker"). + upon_receiving("a request to list pacticipants"). + with_request( + method: :get, + path: '/pacticipants', + headers: {} ). + will_respond_with( headers: {'Content-Type' => match_regex(%r{application/hal\+json.*},'application/hal+json')}, + status: 200, + body: response_body + ) + end + it "returns the response body" do + interaction.execute do |mock_server| + puts "mock_server: #{mock_server.url}" + expect(pact_broker_client.pacticipants.list).to eq response_body + end + end + end + end + + describe "get pacticipant" do + context "when the pacticipant exists" do + let(:response_body) { JSON.parse(File.read("./spec/support/pacticipant_get.json"))} + let(:options) { {pacticipant: 'Pricing Service'}} + let(:interaction) do + new_interaction. + given("the 'Pricing Service' already exists in the pact-broker"). + upon_receiving("a request to get the Pricing Service"). + with_request( + method: :get, + path: '/pacticipants/Pricing%20Service', + headers: {} ). + will_respond_with( headers: {'Content-Type' => match_regex(%r{application/hal\+json.*},'application/hal+json')}, + status: 200, + body: response_body + ) + end + it "returns the response body" do + interaction.execute do |mock_server| + expect(pact_broker_client.pacticipants.get1(options)).to eq response_body + end + end + end + context "when the pacticipant does not exist" do + let(:options) { {pacticipant: 'Pricing Service'}} + let(:interaction) do + new_interaction. + given("the 'Pricing Service' does not exist in the pact-broker"). + upon_receiving("a request to get the Pricing Service"). + with_request( + method: :get, + path: '/pacticipants/Pricing%20Service', + headers: {} ). + will_respond_with( status: 404 ) + end + it "returns nil" do + interaction.execute do |mock_server| + expect(pact_broker_client.pacticipants.get1(options)).to be_nil + end + end + end + end + end +end diff --git a/spec/pact/providers/PactBroker/list_environments_spec.rb b/spec/pact/providers/PactBroker/list_environments_spec.rb new file mode 100644 index 0000000..465219d --- /dev/null +++ b/spec/pact/providers/PactBroker/list_environments_spec.rb @@ -0,0 +1,90 @@ +require_relative '../../../pact_ruby_v2_spec_helper' +require 'pact_broker/client/environments/list_environments' + +RSpec.describe "list environments", pact: true do + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + include PactBrokerPactHelperMethods + + let(:params) { { output: output } } + let(:output) { "text" } + let(:response_body) do + { + _embedded: { + environments: match_each( + { + uuid: "78e85fb2-9df1-48da-817e-c9bea6294e01", + name: "test", + displayName: "Test", + production: false, + contacts: [{ + name: "Foo team", + details: { + emailAddress: "foo@bar.com" + } + }] + } + ) + } + } + end + let(:options) do + { + verbose: verbose + } + end + let(:verbose) { false } + let(:pact_broker_base_url) { "http://127.0.0.1:9999" } + let(:pact_broker_client_options) { { pact_broker_base_url: pact_broker_base_url } } + subject { PactBroker::Client::Environments::ListEnvironments.call(params, options, pact_broker_client_options) } + + def mock_index + new_interaction + .given("the pb:environments relation exists in the index resource") + .upon_receiving("a request for the index resource") + .with_request( + method: "GET", + path: '/', + headers: get_request_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:environments' => { + href: generate_mock_server_url( + regex: ".*(\\/environments)$", + example: "/environments" + ) + } + } + } + ) + end + + def mock_get_environments + new_interaction + .given("an environment exists") + .upon_receiving("a request to list the environments") + .with_request( + method: "GET", + path: "/environments", + headers: get_request_headers + ) + .will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: response_body + ) + end + + it "returns a success result" do + mock_index + mock_get_environments + execute_http_pact do |mock_server| + expect(subject.success).to be true + Approvals.verify(subject.message, :name => "list_environments", format: :txt) + end + end +end diff --git a/spec/pact/providers/PactBroker/pact_broker_client_matrix_ignore_spec.rb b/spec/pact/providers/PactBroker/pact_broker_client_matrix_ignore_spec.rb new file mode 100644 index 0000000..dc4611a --- /dev/null +++ b/spec/pact/providers/PactBroker/pact_broker_client_matrix_ignore_spec.rb @@ -0,0 +1,125 @@ +require_relative '../../../pact_ruby_v2_spec_helper' +require 'pact_broker/client/can_i_deploy' + +module PactBroker::Client + describe Matrix, :pact => true do + + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + + describe "can-i-deploy ignoring a pacticipant version" do + let(:matrix_response_body) { matrix } + let(:matrix) do + { + "summary" => { + "deployable" => true, + "ignored" => 1 + }, + "notices" => match_each("text" => "some notice", "type" => "info"), + "matrix" => [ + { + "consumer" => { + "name" => "Foo", + "version" => { + "number" => "1.2.3" + } + }, + "provider" => { + "name" => "Bar", + "version" => { + "number" => "4.5.6" + } + }, + "verificationResult" => { + "success" => true, + "_links" => { + "self" => { + "href" => match_type_of("http://result") + } + } + } + },{ + "consumer" => { + "name" => "Foo", + "version" => { + "number" => "3.4.5" + } + }, + "provider" => { + "name" => "Bar", + "version" => { + "number" => "4.5.6" + } + }, + "verificationResult" => { + "success" => false, + "_links" => { + "self" => { + "href" => match_type_of("http://result") + } + } + }, + "ignored" => true + } + ] + } + + end + let(:selectors) { [{ pacticipant: "Bar", version: "4.5.6" }, { pacticipant: "Foo", tag: "prod" } ] } + let(:matrix_options) do + { + ignore_selectors: [{ pacticipant: "Foo", version: "3.4.5" }] + } + end + let(:options) { { retry_while_unknown: 0, output: 'table' } } + + before do + new_interaction + .given("provider Bar version 4.5.6 has a successful verification for Foo version 1.2.3 tagged prod and a failed verification for version 3.4.5 tagged prod") + .upon_receiving("a request to determine if Bar can be deployed with all Foo tagged prod, ignoring the verification for Foo version 3.4.5") + .with_request( + method: :get, + path: "/matrix", + # TIP: + # To setup a query parameter with + # - multiple values for the same key + # use an array of hashes otherwise you can pass a hash + query: + [ + {"q[][pacticipant]" => "Bar"}, # q[][pacticipant] => ["Bar"] + {"q[][version]" => "4.5.6"}, + {"q[][pacticipant]" => "Foo"}, # now q[][pacticipant] => ["Bar", "Foo"] + {"q[][tag]" => "prod"}, + {"latestby" => "cvpv"}, + {"ignore[][pacticipant]" => "Foo"}, + {"ignore[][version]" => "3.4.5"} + ] + # { # example of a query hash - duplicate keys will be overwritten + # "q[][pacticipant]" => "Bar", # q[][pacticipant] => ["Bar"] + # "q[][version]" => "4.5.6", + # "q[][pacticipant]" => "Foo", # q[][pacticipant] => ["Foo"] + # "q[][tag]" => "prod", + # "latestby" => "cvpv", + # "ignore[][pacticipant]" => "Foo", + # "ignore[][version]" => "3.4.5", + # } + ) + .will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: matrix_response_body + ) + end + + let(:pact_broker_base_url) { "http://127.0.0.1:9999" } + subject { PactBroker::Client::CanIDeploy.call(selectors, matrix_options, options, { pact_broker_base_url: pact_broker_base_url })} + + it 'returns the CLI output' do + execute_http_pact do |mock_server| + Approvals.verify(subject.message, :name => "can_i_deploy_ignore", format: :txt) + end + end + end + end +end diff --git a/spec/pact/providers/PactBroker/pact_broker_client_matrix_spec.rb b/spec/pact/providers/PactBroker/pact_broker_client_matrix_spec.rb new file mode 100644 index 0000000..27f1aa1 --- /dev/null +++ b/spec/pact/providers/PactBroker/pact_broker_client_matrix_spec.rb @@ -0,0 +1,371 @@ +require_relative '../../../pact_ruby_v2_spec_helper' +require 'pact_broker/client' +require "pact_broker/client/matrix/query" + +module PactBroker::Client + describe Matrix, :pact => true do + + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + + describe "retriving the compatibility matrix" do + let(:matrix_response_body) { match_type_of(matrix) } + let(:matrix) { JSON.parse(File.read('spec/support/matrix.json')) } + let(:selectors) { [{ pacticipant: "Foo", version: "1.2.3" }, { pacticipant: "Bar", version: "4.5.6" }] } + let(:options) { {} } + let(:pact_broker_base_url) { "http://127.0.0.1:9999" } + subject { PactBroker::Client::Matrix::Query.call({ selectors: selectors, options: options }, {}, { pact_broker_base_url: pact_broker_base_url }) } + + context "when results are found" do + let(:interaction) do + new_interaction. + given("the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6"). + upon_receiving("a request for the compatibility matrix for Foo version 1.2.3 and Bar version 4.5.6"). + with_request( + method: :get, + path: "/matrix", + query: [ + {"q[][pacticipant]" => "Foo"}, + {"q[][version]" => "1.2.3"}, + {"q[][pacticipant]" => "Bar"}, + {"q[][version]" => "4.5.6"}, + {"latestby" => "cvpv"} + ] + ). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: matrix_response_body + ) + end + + it 'returns the pact matrix' do + interaction.execute do |mock_server| + matrix = subject + expect(matrix[:matrix].size).to eq 1 + end + end + end + + context "when the pacticipant name has a space in it" do + let(:interaction1) do + new_interaction. + given("the pact for Foo Thing version 1.2.3 has been verified by Bar version 4.5.6"). + upon_receiving("a request for the compatibility matrix for Foo version 1.2.3 and Bar version 4.5.6"). + with_request( + method: :get, + path: "/matrix", + query: [ + {"q[][pacticipant]" => "Foo Thing"}, + {"q[][version]" => "1.2.3"}, + {"q[][pacticipant]" => "Bar"}, + {"q[][version]" => "4.5.6"}, + {"latestby" => "cvpv"} + ] + ). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: matrix_response_body + ) + end + + let(:selectors) { [{ pacticipant: "Foo Thing", version: "1.2.3" }, { pacticipant: "Bar", version: "4.5.6" }] } + + it 'incorrectly escapes the spaces but it still seems to work' do + interaction1.execute do |mock_server| + matrix = subject + expect(matrix[:matrix].size).to eq 1 + end + end + end + + context "with only one version selector" do + let(:interaction) do + new_interaction. + given("the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6 and version 5.6.7"). + upon_receiving("a request for the compatibility matrix where only the version of Foo is specified"). + with_request( + method: :get, + path: "/matrix", + query: [ + {"q[][pacticipant]" => "Foo"}, + {"q[][version]" => "1.2.3"}, + {"latestby" => "cvp"}, + {"latest" => "true"} + ] + ). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: matrix_response_body + ) + end + + let(:selectors) { [{ pacticipant: "Foo", version: "1.2.3" }] } + + it 'returns the row with the lastest verification for version 1.2.3' do + interaction.execute do |mock_server| + matrix = subject + expect(matrix[:matrix].size).to eq 1 + end + end + end + + context "when one or more of the versions does not exist" do + let(:interaction) do + new_interaction. + given("the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6"). + upon_receiving("a request for the compatibility matrix where one or more versions does not exist"). + with_request( + method: :get, + path: "/matrix", + query: [ + {"q[][pacticipant]" => "Foo"}, + {"q[][version]" => "1.2.3"}, + {"q[][pacticipant]" => "Bar"}, + {"q[][version]" => "9.9.9"}, + {"latestby" => "cvpv"} + ] + ). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + summary: { + reason: match_type_of("an error message") + } + } + ) + end + + let(:selectors) { [{ pacticipant: "Foo", version: "1.2.3" }, { pacticipant: "Bar", version: "9.9.9" }] } + + it 'does not raise an error' do + interaction.execute do |mock_server| + subject + end + end + end + + context "when results are not found" do + let(:interaction) do + new_interaction. + upon_receiving("a request for the compatibility matrix for a pacticipant that does not exist"). + with_request( + method: :get, + path: "/matrix", + query: [ + {"q[][pacticipant]" => "Wiffle"}, + {"q[][version]" => "1.2.3"}, + {"q[][pacticipant]" => "Meep"}, + {"q[][version]" => "9.9.9"}, + {"latestby" => "cvpv"} + ] + ). + will_respond_with( + status: 400, + headers: pact_broker_response_headers, + body: { + errors: match_each("an error message") + } + ) + end + + let(:selectors) { [{ pacticipant: "Wiffle", version: "1.2.3" }, { pacticipant: "Meep", version: "9.9.9" }] } + + it 'raises an error' do + interaction.execute do |mock_server| + expect { + subject + }.to raise_error PactBroker::Client::Hal::ErrorResponseReturned, /an error message/ + end + end + end + + context "when no versions are specified" do + let(:interaction) do + new_interaction. + given("the pact for Foo version 1.2.3 and 1.2.4 has been verified by Bar version 4.5.6"). + upon_receiving("a request for the compatibility matrix for all versions of Foo and Bar"). + with_request( + method: :get, + path: "/matrix", + query: [ + {"q[][pacticipant]" => "Foo"}, + {"q[][pacticipant]" => "Bar"}, + {"latestby" => "cvpv"} + ] + ). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + matrix: match_each(matrix_row, 2) + } + ) + end + let(:matrix_row) { JSON.parse(File.read('spec/support/matrix.json'))['matrix'].first } + let(:selectors) { [{ pacticipant: "Foo" }, { pacticipant: "Bar" }] } + + it "returns multiple rows" do + interaction.execute do |mock_server| + matrix = subject + expect(matrix[:matrix].size).to eq 2 + end + end + end + + context "when the success option is true" do + let(:interaction) do + new_interaction. + given("the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6, and 1.2.4 unsuccessfully by 9.9.9"). + upon_receiving("a request for the successful rows of the compatibility matrix for all versions of Foo and Bar"). + with_request( + method: :get, + path: "/matrix", + query: [ + {"q[][pacticipant]" => "Foo"}, + {"q[][pacticipant]" => "Bar"}, + {"latestby" => "cvpv"}, + {"success[]" => "true"} + ] + ). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: matrix_response_body + ) + end + let(:matrix_row) { JSON.parse(File.read('spec/support/matrix.json'))['matrix'].first } + let(:selectors) { [{ pacticipant: "Foo" }, { pacticipant: "Bar" }] } + let(:options) { {success: true} } + + it "returns only the successful row" do + interaction.execute do |mock_server| + matrix = pact_broker_client.matrix.get(selectors, options) + expect(matrix[:matrix].size).to eq 1 + end + end + end + + context "when the latest version for a given tag is specified" do + let(:interaction) do + new_interaction. + given("the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6 with tag prod, and 1.2.4 unsuccessfully by 9.9.9"). + upon_receiving("a request for the compatibility matrix for Foo version 1.2.3 and the latest prod version of Bar"). + with_request( + method: :get, + path: "/matrix", + query: [ + {"q[][pacticipant]" => "Foo"}, + {"q[][version]" => "1.2.3"}, + {"q[][pacticipant]" => "Bar"}, + {"q[][latest]" => "true"}, + {"q[][tag]" => "prod"}, + {"latestby" => "cvpv"} + ] + ). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: matrix_response_body + ) + end + let(:matrix_row) { JSON.parse(File.read('spec/support/matrix.json'))['matrix'].first } + let(:selectors) { [{ pacticipant: "Foo", version: "1.2.3"}, { pacticipant: "Bar", latest: true, tag: 'prod' }] } + let(:options) { {} } + + it "returns the matrix with the latest prod version of Bar" do + interaction.execute do |mock_server| + matrix = pact_broker_client.matrix.get(selectors, options) + expect(matrix[:matrix].size).to eq 1 + end + end + end + + context "when the latest version is specified" do + let(:interaction) do + new_interaction. + given("the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6, and 1.2.4 unsuccessfully by 9.9.9"). + upon_receiving("a request for the compatibility matrix for Foo version 1.2.3 and the latest version of Bar"). + with_request( + method: :get, + path: "/matrix", + query: [ + {"q[][pacticipant]" => "Foo"}, + {"q[][version]" => "1.2.4"}, + {"q[][pacticipant]" => "Bar"}, + {"q[][latest]" => "true"}, + {"latestby" => "cvpv"} + ] + ). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: matrix_response_body + ) + end + let(:matrix_row) { JSON.parse(File.read('spec/support/matrix.json'))['matrix'].first } + let(:selectors) { [{ pacticipant: "Foo", version: "1.2.4"}, { pacticipant: "Bar", latest: true }] } + let(:options) { {} } + + it "returns the matrix with the latest prod version of Bar" do + interaction.execute do |mock_server| + matrix = pact_broker_client.matrix.get(selectors, options) + expect(matrix[:matrix].size).to eq 1 + end + end + end + + context "when checking if we can deploy with the latest tagged versions of the other services" do + let(:interaction) do + new_interaction. + given("the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6 (tagged prod) and version 5.6.7"). + upon_receiving("a request for the compatibility matrix for Foo version 1.2.3 and the latest prod versions of all other pacticipants"). + with_request( + method: :get, + path: "/matrix", + query: [ + {"q[][pacticipant]" => "Foo"}, + {"q[][version]" => "1.2.3"}, + {"latestby" => "cvp"}, + {"latest" => "true"}, + {"tag" => "prod"} + ] + ). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: matrix_response_body + ) + end + + let(:selectors) { [{ pacticipant: "Foo", version: "1.2.3" }] } + + let(:matrix_response_body) { + { + matrix: [{ + consumer: { name: 'Foo', version: { number: '1.2.3' } }, + provider: { name: 'Bar', version: { number: '4.5.6'} }, + }] + } + } + + let(:options) { { to_tag: 'prod' } } + + it "returns the matrix with the latest prod version of Bar" do + interaction.execute do |mock_server| + matrix = pact_broker_client.matrix.get(selectors, options) + expect(matrix[:matrix].size).to eq 1 + end + end + end + + context "with an environment name" do + it "passes the environment name in the options" + end + end + end +end diff --git a/spec/pact/providers/PactBroker/pact_broker_client_publish_spec.rb b/spec/pact/providers/PactBroker/pact_broker_client_publish_spec.rb new file mode 100644 index 0000000..9bdb627 --- /dev/null +++ b/spec/pact/providers/PactBroker/pact_broker_client_publish_spec.rb @@ -0,0 +1,149 @@ +require 'pact_broker/client/pact_broker_client' +require_relative '../../../pact_ruby_v2_spec_helper' + + +module PactBroker::Client + describe PactBrokerClient, :pact => true do + + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + + # This endpoint fails on the pact_broker + # it is the old end point only used with + # PACT_BROKER_FEATURES=publish_pacts_using_old_api=true + describe "publishing a pact", :skip do + + let(:options) { { pact_hash: pact_hash, consumer_version: consumer_version }} + let(:location) { 'http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/latest' } + context "when the provider already exists in the pact-broker" do + + let(:interaction) do + new_interaction. + given("the 'Pricing Service' already exists in the pact-broker"). + upon_receiving("a request to publish a pact"). + with_request( + method: :put, + path: '/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0', + headers: default_request_headers, + body: pact_hash ). + will_respond_with( + headers: pact_broker_response_headers, + status: 201, + body: { + _links: { + :'pb:latest-pact-version' => { + href: location + } + } + } + ) + end + it "returns the URL to find the newly published pact" do + interaction.execute do | mockserver | + expect(pact_broker_client.pacticipants.versions.pacts.publish(options)).to eq location + end + end + end + + context "when the provider, consumer, pact and version already exist in the pact-broker" do + shared_examples "an already-existing pact" do |method| + let(:interaction) do + new_interaction. + given("the 'Pricing Service' and 'Condor' already exist in the pact-broker, and Condor already has a pact published for version 1.3.0"). + upon_receiving("a request to publish a pact with method #{method}"). + with_request( + method: method, + path: '/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0', + headers: default_request_headers, + body: pact_hash ). + will_respond_with( + headers: pact_broker_response_headers, + status: 200, + body: { + _links: { + :'pb:latest-pact-version' => { + href: location + } + } + } + ) + end + + it "returns true" do + interaction.execute do | mockserver | + expect(pact_broker_client.pacticipants.versions.pacts.publish(options)).to be_truthy + end + end + end + + context "when the write method is set to merge" do + let(:client_config) { super().merge(client_options: {write: :merge}) } + + it_behaves_like "an already-existing pact", :patch + end + + context "when the write method is not set" do + it_behaves_like "an already-existing pact", :put + end + end + + context "when the provider does not exist, but the consumer, pact and version already exist in the pact-broker" do + let(:interaction) do + new_interaction. + given("'Condor' already exist in the pact-broker, but the 'Pricing Service' does not"). + upon_receiving("a request to publish a pact"). + with_request( + method: :put, + path: '/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0', + headers: default_request_headers, + body: pact_hash ). + will_respond_with( + headers: pact_broker_response_headers, + status: 201, + body: { + _links: { + :'pb:latest-pact-version' => { + href: location + } + } + } + ) + end + it "returns true" do + interaction.execute do | mockserver | + expect(pact_broker_client.pacticipants.versions.pacts.publish(options)).to be_truthy + end + end + end + + context "when publishing is not successful" do + let(:interaction) do + new_interaction. + given("an error occurs while publishing a pact"). + upon_receiving("a request to publish a pact"). + with_request( + method: :put, + path: '/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0', + headers: default_request_headers, + body: pact_hash ). + will_respond_with( + status: 500, + headers: {'Content-Type' => match_regex(%r{application/.*json.*},'application/hal+json')}, + body: { + error: { + message: match_regex(/.*/,'An error occurred') + } + } + ) + end + it "raises an error" do + interaction.execute do | mockserver | + expect { pact_broker_client.pacticipants.versions.pacts.publish options }.to raise_error /An error occurred/ + end + end + end + + end + end +end diff --git a/spec/pact/providers/PactBroker/pact_broker_client_register_repository_spec.rb b/spec/pact/providers/PactBroker/pact_broker_client_register_repository_spec.rb new file mode 100644 index 0000000..66b31f8 --- /dev/null +++ b/spec/pact/providers/PactBroker/pact_broker_client_register_repository_spec.rb @@ -0,0 +1,58 @@ +require 'pact_broker/client/pact_broker_client' +require_relative '../../../pact_ruby_v2_spec_helper' + +module PactBroker::Client + describe Pacticipants, :pact => true do + + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + + let(:repository_url ) { "git@git.realestate.com.au:business-systems/pricing-service" } + + describe "registering a repository url" do + context "where the pacticipant does not already exist in the pact-broker" do + let(:interaction) do + new_interaction. + given("the 'Pricing Service' does not exist in the pact-broker"). + upon_receiving("a request to register the repository URL of a pacticipant"). + with_request( + method: :patch, + path: '/pacticipants/Pricing%20Service', + headers: old_patch_request_headers, + body: {repository_url: repository_url} ). + will_respond_with( + status: 201, + headers: pact_broker_response_headers + ) + end + it "returns true" do + interaction.execute do | mockserver | + expect(pact_broker_client.pacticipants.update({:pacticipant => 'Pricing Service', :repository_url => repository_url})).to be true + end + end + end + context "where the 'Pricing Service' exists in the pact-broker" do + let(:interaction) do + new_interaction. + given("the 'Pricing Service' already exists in the pact-broker"). + upon_receiving("a request to register the repository URL of a pacticipant"). + with_request( + method: :patch, + path: '/pacticipants/Pricing%20Service', + headers: old_patch_request_headers, + body: { repository_url: repository_url }). + will_respond_with( + status: 200, + headers: pact_broker_response_headers + ) + end + it "returns true" do + interaction.execute do | mockserver | + expect(pact_broker_client.pacticipants.update({:pacticipant => 'Pricing Service', :repository_url => repository_url})).to be true + end + end + end + end + end +end \ No newline at end of file diff --git a/spec/pact/providers/PactBroker/pact_broker_client_retrieve_all_pacts_for_provider_spec.rb b/spec/pact/providers/PactBroker/pact_broker_client_retrieve_all_pacts_for_provider_spec.rb new file mode 100644 index 0000000..2b6c884 --- /dev/null +++ b/spec/pact/providers/PactBroker/pact_broker_client_retrieve_all_pacts_for_provider_spec.rb @@ -0,0 +1,62 @@ +require_relative '../../../pact_ruby_v2_spec_helper' + +require 'pact_broker/client' + +module PactBroker::Client + describe Pacts, :pact => true do + + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + + describe "retriving all pacts for provider" do + let(:response_body) { match_type_of(JSON.parse(File.read("./spec/support/latest_pacts_for_provider.json"))) } + let(:expectedPactsArray) { [{:name => "Condor", :href => "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0"}] } + + context "when retrieving all the latest pacts for provider with prod tag specified" do + let(:interaction) do + new_interaction. + given("tagged as prod pact between Condor and the Pricing Service exists"). + upon_receiving("a request for the list of the latest prod pacts from all consumers for the Pricing Service'"). + with_request( + method: :get, + path: "/pacts/provider/Pricing%20Service/latest/prod", + headers: {}). + will_respond_with( + headers: {'Content-Type' => match_regex(%r{application/.*json.*},'application/hal+json')}, + status: 200, + body: response_body) + end + it 'returns the map of all provider latest prod pacts' do + interaction.execute do | mockserver | + pactsArray = pact_broker_client.pacticipants.versions.pacts.list_latest_for_provider provider: 'Pricing Service', tag: 'prod' + expect(pactsArray.length).to eq(1) + expect(pactsArray).to eq(expectedPactsArray) + end + end + end + context "when retrieving all the latest pacts for provider with no tag specified" do + let(:interaction) do + new_interaction. + given("a latest pact between Condor and the Pricing Service exists"). + upon_receiving("a request for the list of the latest pacts from all consumers for the Pricing Service'"). + with_request( + method: :get, + path: "/pacts/provider/Pricing%20Service/latest", + headers: {}). + will_respond_with( + headers: {'Content-Type' => match_regex(%r{application/.*json.*},'application/hal+json')}, + status: 200, + body: response_body) + end + it 'returns the map of all provider latest pacts' do + interaction.execute do | mockserver | + pactsArray = pact_broker_client.pacticipants.versions.pacts.list_latest_for_provider provider: 'Pricing Service' + expect(pactsArray.length).to eq(1) + expect(pactsArray).to eq(expectedPactsArray) + end + end + end + end + end +end diff --git a/spec/pact/providers/PactBroker/pact_broker_client_retrieve_pact_spec.rb b/spec/pact/providers/PactBroker/pact_broker_client_retrieve_pact_spec.rb new file mode 100644 index 0000000..551b990 --- /dev/null +++ b/spec/pact/providers/PactBroker/pact_broker_client_retrieve_pact_spec.rb @@ -0,0 +1,119 @@ +require_relative '../../../pact_ruby_v2_spec_helper' +require 'pact_broker/client' + +module PactBroker::Client + describe Pacts, :pact => true do + + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + + describe "retrieving a pact" do + describe "retriving a specific version" do + let(:interaction) do + new_interaction. + given("the 'Pricing Service' and 'Condor' already exist in the pact-broker, and Condor already has a pact published for version 1.3.0"). + upon_receiving("a request retrieve a pact for a specific version"). + with_request( + method: :get, + path: '/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0', + headers: {} ). + will_respond_with( + headers: pact_broker_response_headers, + status: 200, + body: pact_hash + ) + end + it "returns the pact json" do + interaction.execute do | mockserver | + response = pact_broker_client.pacticipants.versions.pacts.get consumer: 'Condor', provider: 'Pricing Service', consumer_version: '1.3.0' + expect(response).to eq(pact_json) + end + end + end + + describe "finding the latest version" do + context "when a pact is found" do + + let(:response_headers) do + pact_broker_response_headers.merge( + {'Content-Type' => match_regex(%r{application/.*json.*},'application/hal+json')}, + 'X-Pact-Consumer-Version' => consumer_version + ) + end + let(:interaction) do + new_interaction. + given("a pact between Condor and the Pricing Service exists"). + upon_receiving("a request to retrieve the latest pact between Condor and the Pricing Service"). + with_request( + method: :get, + path: '/pacts/provider/Pricing%20Service/consumer/Condor/latest', + headers: {} + ). + will_respond_with( + status: 200, + headers: response_headers, + body: pact_hash + ) + end + + it "returns the pact json" do + interaction.execute do | mockserver | + response = pact_broker_client.pacticipants.versions.pacts.latest consumer: 'Condor', provider: 'Pricing Service' + expect(response).to eq(pact_json) + end + end + + end + context "when no pact is found" do + let(:interaction) do + new_interaction. + given("no pact between Condor and the Pricing Service exists"). + upon_receiving("a request to retrieve the latest pact between Condor and the Pricing Service"). + with_request( + method: :get, + path: '/pacts/provider/Pricing%20Service/consumer/Condor/latest', + headers: {} + ). + will_respond_with( + status: 404 + ) + end + it "returns nil" do + interaction.execute do | mockserver | + response = pact_broker_client.pacticipants.versions.pacts.latest consumer: 'Condor', provider: 'Pricing Service' + expect(response).to eq(nil) + end + end + end + end + describe "finding the latest production version" do + context "when a pact is found" do + let(:interaction) do + new_interaction. + given("a pact between Condor and the Pricing Service exists for the production version of Condor"). + upon_receiving("a request to retrieve the pact between the production verison of Condor and the Pricing Service"). + with_request( + method: :get, + path: '/pacts/provider/Pricing%20Service/consumer/Condor/latest/prod', + headers: { 'Accept' => 'application/hal+json, application/json'} + ). + will_respond_with( + status: 200, + body: pact_hash, + headers: pact_broker_response_headers + ) + end + + it "returns the pact json" do + interaction.execute do | mockserver | + response = pact_broker_client.pacticipants.versions.pacts.latest consumer: 'Condor', provider: 'Pricing Service', tag: 'prod' + expect(response).to eq(pact_json) + end + end + end + end + end + + end +end diff --git a/spec/pact/providers/PactBroker/pact_broker_client_tags_spec.rb b/spec/pact/providers/PactBroker/pact_broker_client_tags_spec.rb new file mode 100644 index 0000000..d86dc53 --- /dev/null +++ b/spec/pact/providers/PactBroker/pact_broker_client_tags_spec.rb @@ -0,0 +1,110 @@ +require 'spec_helper' +require 'pact_broker/client' +require_relative '../../../pact_ruby_v2_spec_helper' + + +describe PactBroker::Client::Versions, pact: true do + + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + + describe "tagging a version with prod details" do + let(:repository_ref) { "packages/condor-#{version}" } + + let(:tag_options) { {pacticipant: 'Condor', version: version, repository_ref: repository_ref, :tag => 'prod'} } + context "when the component exists" do + let(:interaction) do + new_interaction. + given("'Condor' exists in the pact-broker"). + upon_receiving("a request to tag the production version of Condor"). + with_request( + method: :put, + path: '/pacticipants/Condor/versions/1.3.0/tags/prod', + headers: default_request_headers, body: {}). + will_respond_with( + status: 201, + headers: pact_broker_response_headers, + body: { + _links: { + self: { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/Condor\\/versions\\/1\\.3\\.0\\/tags\\/prod)$", + example: "/pacticipants/Condor/versions/1.3.0/tags/prod" + ) + } + } + } + ) + end + it "returns true" do + interaction.execute do | mockserver | + expect(pact_broker_client.pacticipants.versions.tag tag_options).to be true + end + end + end + context "when the component does not exist" do + let(:interaction) do + new_interaction. + given("'Condor' does not exist in the pact-broker"). + upon_receiving("a request to tag the production version of Condor"). + with_request( + method: :put, + path: '/pacticipants/Condor/versions/1.3.0/tags/prod', + headers: default_request_headers, body: {}). + will_respond_with( + status: 201, + headers: pact_broker_response_headers, + body: { + _links: { + self: { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/Condor\\/versions\\/1\\.3\\.0\\/tags\\/prod)$", + example: "/pacticipants/Condor/versions/1.3.0/tags/prod" + ) + } + } + } + ) + end + it "returns true" do + interaction.execute do | mockserver | + expect(pact_broker_client.pacticipants.versions.tag tag_options).to be true + end + end + end + + context "when the tag already exists" do + let(:interaction) do + new_interaction. + given("'Condor' exists in the pact-broker with version 1.3.0, tagged with 'prod'"). + upon_receiving("a request to tag the production version of Condor"). + with_request( + method: :put, + path: '/pacticipants/Condor/versions/1.3.0/tags/prod', + headers: default_request_headers, body: {}). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + self: { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/Condor\\/versions\\/1\\.3\\.0\\/tags\\/prod)$", + example: "/pacticipants/Condor/versions/1.3.0/tags/prod" + ) + } + } + } + ) + end + + it "returns true" do + interaction.execute do | mockserver | + expect(pact_broker_client.pacticipants.versions.tag tag_options).to be true + end + end + end + end + +end \ No newline at end of file diff --git a/spec/pact/providers/PactBroker/pact_broker_client_versions_spec.rb b/spec/pact/providers/PactBroker/pact_broker_client_versions_spec.rb new file mode 100644 index 0000000..adeb25e --- /dev/null +++ b/spec/pact/providers/PactBroker/pact_broker_client_versions_spec.rb @@ -0,0 +1,133 @@ +require_relative '../../../pact_ruby_v2_spec_helper' + +require 'pact_broker/client' + +describe PactBroker::Client::Versions, pact: true do + + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + + let(:get_headers) { { "Accept" => "application/hal+json, application/json" } } + let(:pact_broker_base_url) { "http://127.0.0.1:9999" } + + describe "retrieving the latest pacticipant version" do + let(:latest_version_path) { "/pacticipants/Condor/latest-version" } + let(:latest_version_url) { pact_broker_base_url + latest_version_path } + + let(:interaction) do + new_interaction + .given("the pb:latest-version relation exists in the index resource") + .upon_receiving("a request for the index resource") + .with_request( + method: :get, + path: '/', + headers: get_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:latest-version' => { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/.*\\/latest-version)$", + example: latest_version_url + ) + } + } + } + ) + + new_interaction + .given("'Condor' exists in the pact-broker with the latest version 1.2.3") + .upon_receiving("a request to retrieve the latest version of Condor") + .with_request( + method: :get, + path: '/pacticipants/Condor/latest-version', + headers: get_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + number: '1.2.3', + _links: { + self: { + href: generate_mock_server_url( + regex: "(.*)$", + example: "/some-url" + ), + } + } + } + ) + end + + it "returns the version hash" do + interaction.execute do | mockserver | + version_hash = pact_broker_client.pacticipants.versions.latest(pacticipant: 'Condor') + expect(version_hash[:number]).to eq '1.2.3' + expect(version_hash[:_links][:self][:href]).to eq 'http://127.0.0.1:9999/some-url' + end + end + end + + describe "retrieving the latest pacticipant version for a tag" do + let(:latest_tagged_version_path) { "/pacticipants/Condor/latest-version/production" } + let(:latest_tagged_version_url) { pact_broker_base_url + latest_tagged_version_path } + + let(:interaction) do + new_interaction + .given("the pb:latest-tagged-version relation exists in the index resource") + .upon_receiving("a request for the index resource") + .with_request( + method: :get, + path: '/', + headers: get_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:latest-tagged-version' => { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/.*\\/latest-version\\/.*)$", + example: latest_tagged_version_url + ) + } + } + } + ) + + new_interaction + .given("'Condor' exists in the pact-broker with the latest tagged 'production' version 1.2.3") + .upon_receiving("a request to retrieve the latest 'production' version of Condor") + .with_request( + method: :get, + path: '/pacticipants/Condor/latest-version/production', + headers: get_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + number: '1.2.3', + _links: { + self: { + href: generate_mock_server_url( + regex: "(.*)$", + example: "/some-url" + ), + } + } + } + ) + end + + it "returns the version hash" do + interaction.execute do | mockserver | + version_hash = pact_broker_client.pacticipants.versions.latest(pacticipant: 'Condor', tag: 'production') + expect(version_hash[:number]).to eq '1.2.3' + expect(version_hash[:_links][:self][:href]).to eq 'http://127.0.0.1:9999/some-url' + end + end + end +end \ No newline at end of file diff --git a/spec/pact/providers/PactBroker/pacticipants_create_spec.rb b/spec/pact/providers/PactBroker/pacticipants_create_spec.rb new file mode 100644 index 0000000..efdfbac --- /dev/null +++ b/spec/pact/providers/PactBroker/pacticipants_create_spec.rb @@ -0,0 +1,157 @@ +require_relative '../../../pact_ruby_v2_spec_helper' + +require 'pact_broker/client/pacticipants/create' + +RSpec.describe "creating or updating a pacticipant", pact: true do + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + include PactBrokerPactHelperMethods + + let(:pact_broker_base_url) { "http://127.0.0.1:9999" } + + before do + index_links = { + 'pb:pacticipants' => { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants)$", + example: "/pacticipants" + ) + }, + 'pb:pacticipant' => { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/\\{pacticipant\\})$", + example: "/pacticipants/{pacticipant}" + ) + } + } + new_interaction + .given("the pacticipant relations are present") + .upon_receiving("a request for the index resource") + .with_request( + method: :get, + path: '/', + headers: get_request_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: index_links + } + ) + end + + let(:params) do + { + name: "Foo", + repository_url: "http://foo" + } + end + + let(:request_body) { { name: "Foo", repositoryUrl: "http://foo" } } + + + let(:response_status) { 201 } + let(:create_success_response) do + { + status: response_status, + headers: pact_broker_response_headers, + body: { + name: "Foo", + repositoryUrl: "http://foo", + _links: { + self: { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/Foo)$", + example: "/pacticipants/Foo" + ), + } + } + } + } + end + + let(:get_success_response) do + { + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + self: { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/Foo)$", + example: "/pacticipants/Foo" + ), + } + } + } + } + end + + let(:pact_broker_client_options) { { pact_broker_base_url: pact_broker_base_url} } + let(:options) { {} } + + subject { PactBroker::Client::Pacticipants2::Create.call(params, options, pact_broker_client_options) } + + context "when the pacticipant does not already exist" do + let(:interaction) do + new_interaction + .upon_receiving("a request to retrieve a pacticipant") + .with_request( + method: :get, + path: '/pacticipants/Foo', + headers: get_request_headers) + .will_respond_with(status: 404) + + new_interaction + .upon_receiving("a request to create a pacticipant") + .with_request( + method: :post, + path: '/pacticipants', + headers: post_request_headers, + body: request_body) + .will_respond_with(**create_success_response) + end + + it "returns a CommandResult with success = true" do + interaction.execute do | mockserver | + expect(subject).to be_a PactBroker::Client::CommandResult + expect(subject.success).to be true + expect(subject.message).to include "Pacticipant \"Foo\" created" + end + end + end + + context "when the pacticipant does already exist" do + let(:interaction) do + new_interaction + .given("a pacticipant with name Foo exists") + .upon_receiving("a request to retrieve a pacticipant") + .with_request( + method: :get, + path: '/pacticipants/Foo', + headers: get_request_headers) + .will_respond_with(**get_success_response) + + new_interaction + .given("a pacticipant with name Foo exists") + .upon_receiving("a request to update a pacticipant") + .with_request( + method: :patch, + path: '/pacticipants/Foo', + headers: post_request_headers, + body: request_body) + .will_respond_with(**create_success_response) + end + + let(:response_status) { 200 } + + it "returns a CommandResult with success = true" do + interaction.execute do | mockserver | + expect(subject).to be_a PactBroker::Client::CommandResult + expect(subject.success).to be true + expect(subject.message).to include "Pacticipant \"Foo\" updated" + end + end + end +end diff --git a/spec/pact/providers/PactBroker/publish_pacts_spec.rb b/spec/pact/providers/PactBroker/publish_pacts_spec.rb new file mode 100644 index 0000000..6b76717 --- /dev/null +++ b/spec/pact/providers/PactBroker/publish_pacts_spec.rb @@ -0,0 +1,122 @@ +require 'pact_broker/client/publish_pacts' +require_relative '../../../pact_ruby_v2_spec_helper' + +RSpec.describe "publishing contracts", pact: true do + before do + allow_any_instance_of(PactBroker::Client::Hal::HttpClient).to receive(:sleep) + allow_any_instance_of(PactBroker::Client::Hal::HttpClient).to receive(:default_max_tries).and_return(1) + end + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + include PactBrokerPactHelperMethods + + let(:pact_broker_base_url) { "http://127.0.0.1:9999" } + let(:pacticipant_name) { "Foo" } + let(:version_number) { "5556b8149bf8bac76bc30f50a8a2dd4c22c85f30" } + let(:output) { "text" } + let(:build_url) { "http://build" } + let(:consumer_version_params) do + { + pacticipant_name: pacticipant_name, + number: version_number, + tags: ["dev"], + branch: "main", + build_url: build_url, + output: output + } + end + let(:pact_file_path_1) { "spec/fixtures/foo-bar.json" } + let(:pact_file_paths) { [pact_file_path_1] } + let(:options) { { merge: true } } + let(:pact_broker_client_options) { {} } + let(:expected_content) { Base64.strict_encode64(JSON.parse(File.read(pact_file_path_1)).to_json) } + let(:request_body) do + { + pacticipantName: pacticipant_name, + pacticipantVersionNumber: version_number, + branch: "main", + tags: ["dev"], + buildUrl: "http://build", + contracts: [ + { + consumerName: pacticipant_name, + providerName: "Bar", + specification: "pact", + contentType: "application/json", + content: expected_content, + onConflict: "merge" + } + ] + } + end + + subject { PactBroker::Client::PublishPacts.call(pact_broker_base_url, pact_file_paths, consumer_version_params, options, pact_broker_client_options) } + + def mock_index + new_interaction + .given("the pb:publish-contracts relations exists in the index resource") + .upon_receiving("a request for the index resource") + .with_request( + method: "GET", + path: '/', + headers: get_request_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:publish-contracts' => { + href: generate_mock_server_url( + regex: ".*(\\/contracts\\/publish)$", + example: "/contracts/publish" + ) + } + } + } + ) + end + + def mock_contract_publication + new_interaction + .upon_receiving("a request to publish contracts") + .with_request( + method: "POST", + path: '/contracts/publish', + headers: post_request_headers, + body: request_body). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _embedded: { + pacticipant: { + name: pacticipant_name + }, + version: { + number: version_number + } + }, + logs: match_each(level: "info", message: "some message"), + _links: { + :'pb:pacticipant-version-tags' => [{ name: "dev"} ], + :'pb:contracts' => [{ href: match_type_of("http://some-pact") }] + } + } + ) + end + + context "with valid params" do + before do + mock_index + mock_contract_publication + end + + it "returns a success result" do + execute_http_pact do | mockserver | + expect(subject.success).to be true + expect(subject.message).to include "some message" + end + end + end +end diff --git a/spec/pact/providers/PactBroker/record_deployment_spec.rb b/spec/pact/providers/PactBroker/record_deployment_spec.rb new file mode 100644 index 0000000..652639c --- /dev/null +++ b/spec/pact/providers/PactBroker/record_deployment_spec.rb @@ -0,0 +1,220 @@ +require_relative '../../../pact_ruby_v2_spec_helper' +require 'pact_broker/client/deployments/record_deployment' + +RSpec.describe "recording a deployment", pact: true do + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + include PactBrokerPactHelperMethods + + let(:pacticipant_name) { "Foo" } + let(:version_number) { "5556b8149bf8bac76bc30f50a8a2dd4c22c85f30" } + let(:environment_name) { "test" } + let(:output) { "text" } + let(:application_instance) { "blue" } + let(:params) do + { + pacticipant_name: pacticipant_name, + version_number: version_number, + environment_name: environment_name, + application_instance: application_instance + } + end + let(:options) do + { + output: output + } + end + + let(:pact_broker_base_url) { "http://127.0.0.1:9999" } + let(:pact_broker_client_options) { { pact_broker_base_url: pact_broker_base_url } } + + subject { PactBroker::Client::Deployments::RecordDeployment.call(params, options, pact_broker_client_options) } + + def mock_index + new_interaction + .given("the pb:pacticipant-version and pb:environments relations exist in the index resource") + .upon_receiving("a request for the index resource") + .with_request( + method: "GET", + path: '/', + headers: get_request_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:pacticipant-version' => { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/.*\\/versions\\/.*)$", + example: "/pacticipants/{pacticipant}/versions/{version}" + ), + }, + :'pb:environments' => { + href: generate_mock_server_url( + regex: ".*(\\/environments)$", + example: "/environments" + ) + } + } + } + ) + end + + def mock_pacticipant_version_with_test_environment_available_for_deployment + new_interaction + .given("version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment") + .upon_receiving("a request for a pacticipant version") + .with_request( + method: "GET", + path: "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", + headers: get_request_headers + ) + .will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + "pb:record-deployment" => [ + { + name: "test", + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/.*\\/versions\\/.*\\/deployed-versions\\/environment\\/.*)$", + example: "/pacticipants/#{pacticipant_name}/versions/#{version_number}/deployed-versions/environment/cb632df3-0a0d-4227-aac3-60114dd36479" + ), + } + ] + } + } + ) + end + + def mock_pacticipant_version_without_test_environment_available_for_deployment + new_interaction + .given("version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with 2 environments that aren't test available for deployment") + .upon_receiving("a request for a pacticipant version") + .with_request( + method: "GET", + path: "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", + headers: get_request_headers + ) + .will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + "pb:record-deployment" => [ + match_type_of( + name: "prod", + href: "href" + ), + match_type_of( + name: "dev", + href: "href" + ), + ] + } + } + ) + end + + def mock_environments + new_interaction + .given("an environment with name test exists") + .upon_receiving("a request for the environments") + .with_request( + method: "GET", + path: "/environments", + headers: get_request_headers + ) + .will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + "pb:environments" => [ + { + name: "test", + href: match_type_of("href") + } + ] + } + } + ) + end + + def mock_record_deployment + new_interaction + .given("version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment") + .upon_receiving("a request to record a deployment") + .with_request( + method: "POST", + path: "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30/deployed-versions/environment/cb632df3-0a0d-4227-aac3-60114dd36479", + headers: post_request_headers, + body: { + applicationInstance: application_instance, + target: application_instance + } + ) + .will_respond_with( + status: 201, + headers: pact_broker_response_headers, + body: { + target: application_instance + } + ) + end + + context "when the deployment is recorded successfully" do + before do + mock_index + mock_pacticipant_version_with_test_environment_available_for_deployment + mock_record_deployment + end + + it "returns a success message" do + execute_http_pact do | mockserver | + expect(subject.success).to be true + expect(subject.message).to include "Recorded deployment of Foo version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 to test environment (application instance blue) in the Pact Broker." + end + end + + context "when the output is json" do + let(:output) { "json" } + + it "returns the JSON payload" do + execute_http_pact do | mockserver | + expect(JSON.parse(subject.message)).to eq "target" => application_instance + end + end + end + end + + context "when the specified environment is not available for recording a deployment" do + before do + mock_index + mock_pacticipant_version_without_test_environment_available_for_deployment + mock_environments + end + + context "when the specified environment does not exist" do + let(:environment_name) { "foo" } + + it "returns an error response" do + execute_http_pact do | mockserver | + expect(subject.success).to be false + expect(subject.message).to include "No environment found" + end + end + end + + context "when the specified environment does exist" do + it "returns an error response" do + execute_http_pact do | mockserver | + expect(subject.success).to be false + expect(subject.message).to include "not an available option" + end + end + end + end +end diff --git a/spec/pact/providers/PactBroker/record_release_spec.rb b/spec/pact/providers/PactBroker/record_release_spec.rb new file mode 100644 index 0000000..718e402 --- /dev/null +++ b/spec/pact/providers/PactBroker/record_release_spec.rb @@ -0,0 +1,149 @@ +require_relative '../../../pact_ruby_v2_spec_helper' +require 'pact_broker/client/deployments/record_deployment' + +RSpec.describe "recording a release", pact: true do + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + include PactBrokerPactHelperMethods + + let(:pacticipant_name) { "Foo" } + let(:version_number) { "5556b8149bf8bac76bc30f50a8a2dd4c22c85f30" } + let(:environment_name) { "test" } + let(:output) { "text" } + let(:target) { "blue" } + let(:params) do + { + pacticipant_name: pacticipant_name, + version_number: version_number, + environment_name: environment_name + } + end + let(:options) do + { + output: output + } + end + + let(:pact_broker_base_url) { "http://127.0.0.1:9999" } + let(:pact_broker_client_options) { { pact_broker_base_url: pact_broker_base_url } } + + subject { PactBroker::Client::Deployments::RecordRelease.call(params, options, pact_broker_client_options) } + + def mock_index + new_interaction + .given("the pb:pacticipant-version and pb:environments relations exist in the index resource") + .upon_receiving("a request for the index resource") + .with_request( + method: "GET", + path: '/', + headers: get_request_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:pacticipant-version' => { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/.*\\/versions\\/.*)$", + example: "/pacticipants/{pacticipant}/versions/{version}" + ), + }, + :'pb:environments' => { + href: generate_mock_server_url( + regex: ".*(\\/environments)$", + example: "/environments" + ) + } + } + } + ) + end + + def mock_pacticipant_version_with_test_environment_available_for_release + new_interaction + .given("version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for release") + .upon_receiving("a request for a pacticipant version") + .with_request( + method: "GET", + path: "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", + headers: get_request_headers + ) + .will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + "pb:record-release" => [ + { + name: "test", + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/.*\\/versions\\/.*\\/released-versions\\/environment\\/.*)$", + example: "/pacticipants/#{pacticipant_name}/versions/#{version_number}/released-versions/environment/cb632df3-0a0d-4227-aac3-60114dd36479" + ), + } + ] + } + } + ) + end + + def mock_environments + new_interaction + .given("an environment with name test exists") + .upon_receiving("a request for the environments") + .with_request( + method: "GET", + path: "/environments", + headers: get_request_headers + ) + .will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + "pb:environments" => [ + { + name: "test", + href: generate_mock_server_url( + regex: ".*(\\/environments\\/.*)$", + example: "/environments/cb632df3-0a0d-4227-aac3-60114dd36479" + ) + } + ] + } + } + ) + end + + def mock_record_release + new_interaction + .given("version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment") + .upon_receiving("a request to record a release") + .with_request( + method: "POST", + path: "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30/released-versions/environment/cb632df3-0a0d-4227-aac3-60114dd36479", + headers: post_request_headers, + body: {} + ) + .will_respond_with( + status: 201, + headers: pact_broker_response_headers + ) + end + + context "when the deployment is recorded successfully" do + before do + mock_index + mock_pacticipant_version_with_test_environment_available_for_release + mock_record_release + end + + it "returns a success message" do + execute_http_pact do | mockserver | + expect(subject.success).to be true + expect(subject.message).to include "Recorded release of Foo version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 to test environment in the Pact Broker." + end + end + end +end diff --git a/spec/pact/providers/PactBroker/record_undeployment_spec.rb b/spec/pact/providers/PactBroker/record_undeployment_spec.rb new file mode 100644 index 0000000..554d666 --- /dev/null +++ b/spec/pact/providers/PactBroker/record_undeployment_spec.rb @@ -0,0 +1,176 @@ +require_relative '../../../pact_ruby_v2_spec_helper' +require 'pact_broker/client/deployments/record_undeployment' + +RSpec.describe "recording an undeployment", pact: true do + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + include PactBrokerPactHelperMethods + + let(:pacticipant_name) { "Foo" } + let(:environment_name) { "test" } + let(:output) { "text" } + let(:application_instance) { "customer-1" } + let(:params) do + { + pacticipant_name: pacticipant_name, + environment_name: environment_name, + application_instance: application_instance + } + end + let(:options) do + { + output: output, + verbose: true + } + end + let(:pact_broker_base_url) { "http://127.0.0.1:9999" } + let(:webmock_base_url) { "http://broker" } + let(:pact_broker_client_options) { { pact_broker_base_url: webmock_base_url } } + + let(:test_environment_placeholder_path) { "/environments/16926ef3-590f-4e3f-838e-719717aa88c9" } + let(:currently_deployed_versions_placeholder_path) { "/environments/16926ef3-590f-4e3f-838e-719717aa88c9/deployed-versions/currently-deployed" } + let(:deployed_version_placeholder_path) { "/deployed-versions/ff3adecf-cfc5-4653-a4e3-f1861092f8e0"} + + subject { PactBroker::Client::Deployments::RecordUndeployment.call(params, options, pact_broker_client_options) } + + let(:index_body_hash) do + { + _links: { + :'pb:environments' => { + href: "#{webmock_base_url}/environments" + } + } + } + end + + let(:environments_hash) do + { + _links: { + :'pb:environments' => [ + { + name: "test", + href: pact_broker_base_url + test_environment_placeholder_path + } + ] + } + } + end + + let!(:index_request) do + stub_request(:get, "http://broker").to_return(status: 200, body: index_body_hash.to_json, headers: { "Content-Type" => "application/hal+json" } ) + end + + let!(:environments_request) do + stub_request(:get, "http://broker/environments").to_return(status: 200, body: environments_hash.to_json, headers: { "Content-Type" => "application/hal+json" } ) + end + + def mock_test_environment + new_interaction + .given("an environment with name test and UUID 16926ef3-590f-4e3f-838e-719717aa88c9 exists") + .upon_receiving("a request for an environment") + .with_request( + method: "GET", + path: test_environment_placeholder_path, + headers: get_request_headers + ) + .will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:currently-deployed-deployed-versions' => { + href: generate_mock_server_url( + regex: ".*(\\/environments\\/.*\\/deployed-versions\\/currently-deployed)$", + example: currently_deployed_versions_placeholder_path + ), + } + } + } + ) + end + + def mock_deployed_versions_search_results + new_interaction + .given("an version is deployed to environment with UUID 16926ef3-590f-4e3f-838e-719717aa88c9 with target customer-1") + .upon_receiving("a request to list the versions deployed to an environment for a pacticipant name and application instance") + .with_request( + method: "GET", + path: currently_deployed_versions_placeholder_path, + query: { pacticipant: pacticipant_name }, + headers: get_request_headers + ) + .will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _embedded: { + deployedVersions: [ + { + applicationInstance: application_instance, + _links: { + self: { + href: generate_mock_server_url( + regex: ".*(\\/deployed-versions\\/.*)$", + example: deployed_version_placeholder_path + ), + } + } + } + ] + } + } + ) + end + + def mock_mark_deployed_version_as_undeployed + new_interaction + .given("a currently deployed version exists") + .upon_receiving("a request to mark a deployed version as not currently deploye") + .with_request( + method: "PATCH", + path: deployed_version_placeholder_path, + body: { currentlyDeployed: false }, + headers: patch_request_headers + ) + .will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: deployed_version_hash + ) + end + + let(:deployed_version_hash) do + { + "currentlyDeployed" => false, + "_embedded" => { + "version" => { + "number" => match_type_of("2") + } + } + } + end + + context "when the deployment is recorded successfully" do + before do + mock_test_environment + mock_deployed_versions_search_results + mock_mark_deployed_version_as_undeployed + end + + it "returns a success message" do + execute_http_pact do | mockserver | + expect(subject.success).to be true + expect(subject.message).to include "Recorded undeployment of Foo version 2 from test environment (application instance customer-1) in the Pact Broker" + end + end + + # context "when the output is json" do + # let(:output) { "json" } + + # it "returns the JSON payload" do + # expect(JSON.parse(subject.message)).to eq [Pact::Reification.from_term(deployed_version_hash)] + # end + # end + end +end diff --git a/spec/pact/providers/PactBroker/webhooks_create_spec.rb b/spec/pact/providers/PactBroker/webhooks_create_spec.rb new file mode 100644 index 0000000..45b01ab --- /dev/null +++ b/spec/pact/providers/PactBroker/webhooks_create_spec.rb @@ -0,0 +1,444 @@ +require_relative '../../../pact_ruby_v2_spec_helper' + +require 'pact_broker/client/webhooks/create' + +RSpec.describe "creating a webhook", pact: true do + + pact_broker + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + include PactBrokerPactHelperMethods + + let(:event_names) { %w{contract_content_changed contract_published provider_verification_published provider_verification_succeeded provider_verification_failed} } + + let(:params) do + { + description: "a webhook", + events: %w{contract_content_changed}, + http_method: "POST", + url: "https://webhook", + headers: { "Foo" => "bar", "Bar" => "foo"}, + username: "username", + password: "password", + body: body, + consumer: "Condor", + provider: "Pricing Service" + }.tap { |it| Pact::Fixture.add_fixture(:create_webhook_params, it) } + end + + let(:body) { { some: "body" }.to_json } + + let(:request_body) do + { + "description" => "a webhook", + "events" => [ + "name" => "contract_content_changed" + ], + "request" => { + "url" => "https://webhook", + "method" => "POST", + "headers" => { + "Foo" => "bar", + "Bar" => "foo" + }, + "body" => { + "some" => "body" + }, + "username" => "username", + "password" => "password" + } + } + end + + let(:response_status) { 201 } + let(:success_response) do + { + status: response_status, + headers: pact_broker_response_headers, + body: { + description: match_type_of("a webhook"), + request: { + body: { + some: "body" + } + }, + events: [ + name: "contract_content_changed" + ], + _links: { + self: { + href: generate_mock_server_url( + regex: "(.*)$", + example: "/some-url" + ), + } + } + } + } + end + + let(:pact_broker_base_url) { "http://127.0.0.1:9999" } + let(:pact_broker_client_options) { { pact_broker_base_url: pact_broker_base_url } } + + subject { PactBroker::Client::Webhooks::Create.call(params, pact_broker_base_url, pact_broker_client_options) } + + context "when a valid webhook with a JSON body is submitted" do + before do + new_interaction + .given("the 'Pricing Service' and 'Condor' already exist in the pact-broker") + .upon_receiving("a request to create a webhook with a JSON body for a consumer and provider") + .with_request( + method: :post, + path: '/webhooks/provider/Pricing%20Service/consumer/Condor', + headers: post_request_headers, + body: request_body) + .will_respond_with(**success_response) + end + + it "returns a CommandResult with success = true" do + execute_http_pact do | mockserver | + expect(subject).to be_a PactBroker::Client::CommandResult + expect(subject.success).to be true + expect(subject.message).to eq "Webhook \"a webhook\" created" + end + end + end + + context "when a valid webhook with every possible event type is sumbitted" do + before do + params.merge!(events: event_names) + request_body.merge!("events" => event_names.map{ |event_name| { "name" => event_name } }) + success_response[:body].merge!(events: event_names.map{ |event_name| { "name" => event_name } }) + + new_interaction + .given("the 'Pricing Service' and 'Condor' already exist in the pact-broker") + .upon_receiving("a request to create a webhook with every possible event type") + .with_request( + method: :post, + path: '/webhooks/provider/Pricing%20Service/consumer/Condor', + headers: post_request_headers, + body: request_body) + .will_respond_with(**success_response) + end + + it "returns a CommandResult with success = true" do + execute_http_pact do | mockserver | + expect(subject.success).to be true + end + end + end + + context "when a valid webhook with an XML body is submitted" do + before do + request_body["request"]["body"] = body + success_response[:body][:request][:body] = body + + new_interaction + .given("the 'Pricing Service' and 'Condor' already exist in the pact-broker") + .upon_receiving("a request to create a webhook with a non-JSON body for a consumer and provider") + .with_request( + method: :post, + path: '/webhooks/provider/Pricing%20Service/consumer/Condor', + headers: post_request_headers, + body: request_body) + .will_respond_with(**success_response) + end + + let(:body) { "" } + + it "returns a CommandResult with success = true" do + execute_http_pact do | mockserver | + expect(subject.success).to be true + end + end + end + + context "when an invalid webhook is submitted" do + before do + params[:url] = nil + request_body["request"].delete("url") + + new_interaction + .given("the 'Pricing Service' and 'Condor' already exist in the pact-broker") + .upon_receiving("an invalid request to create a webhook for a consumer and provider") + .with_request( + method: :post, + path: '/webhooks/provider/Pricing%20Service/consumer/Condor', + headers: post_request_headers, + body: request_body). + will_respond_with( + status: 400, + headers: pact_broker_response_headers, + body: { + errors: { + "request.url" => match_each("Some error") + } + } + ) + end + + it "returns a CommandResult with success = false" do + execute_http_pact do | mockserver | + expect(subject.success).to be false + expect(subject.message).to match /400/ + expect(subject.message).to match /Some error/ + end + end + end + + context "when one of the pacticipants does not exist" do + before do + new_interaction + .given("'Condor' does not exist in the pact-broker") + .upon_receiving("a request to create a webhook for a consumer and provider") + .with_request( + method: :post, + path: '/webhooks/provider/Pricing%20Service/consumer/Condor', + headers: post_request_headers, + body: request_body). + will_respond_with( + status: 404, + headers: pact_broker_response_headers + ) + end + + it "returns a CommandResult with success = false" do + execute_http_pact do | mockserver | + expect(subject.success).to be false + expect(subject.message).to match /404/ + end + end + end + + context "when only a consumer is specified" do + before do + params.delete(:provider) + request_body["consumer"] = { "name" => "Condor" } + mock_pact_broker_index(self, pact_broker_base_url) + + new_interaction + .given("the 'Pricing Service' and 'Condor' already exist in the pact-broker") + .upon_receiving("a request to create a webhook with a JSON body for a consumer") + .with_request( + method: :post, + path: '/webhooks', + headers: post_request_headers, + body: request_body) + .will_respond_with(**success_response) + end + + it "returns a CommandResult with success = true" do + execute_http_pact do | mockserver | + expect(subject.success).to be true + expect(subject.message).to eq "Webhook \"a webhook\" created" + end + end + end + + context "when consumer is specified using a label" do + before do + params.delete(:consumer) + params.delete(:provider) + params.merge!(consumer_label: "consumer_label") + request_body["consumer"] = { "label" => "consumer_label" } + mock_pact_broker_index(self, pact_broker_base_url) + + new_interaction + .upon_receiving("a request to create a webhook with a JSON body for a consumer specified by a label") + .with_request( + method: :post, + path: '/webhooks', + headers: post_request_headers, + body: request_body) + .will_respond_with(**success_response) + end + + it "returns a CommandResult with success = true" do + execute_http_pact do | mockserver | + expect(subject.success).to be true + expect(subject.message).to eq "Webhook \"a webhook\" created" + end + end + end + + context "when only a consumer is specified and it does not exist" do + before do + params.delete(:provider) + request_body["consumer"] = { "name" => "Condor" } + mock_pact_broker_index(self, pact_broker_base_url) + + new_interaction + .upon_receiving("a request to create a webhook with a JSON body for a consumer that does not exist") + .with_request( + method: :post, + path: '/webhooks', + headers: post_request_headers, + body: request_body) + .will_respond_with( + status: 400, + headers: pact_broker_response_headers, + body: { + errors: { + "consumer.name" => match_each("Some error") + } + }) + end + + it "returns a CommandResult with success = true" do + execute_http_pact do | mockserver | + expect(subject.success).to be false + end + end + end + + context "when only a provider is specified" do + before do + params.delete(:consumer) + request_body["provider"] = { "name" => "Pricing Service" } + mock_pact_broker_index(self, pact_broker_base_url) + + new_interaction + .given("the 'Pricing Service' and 'Condor' already exist in the pact-broker") + .upon_receiving("a request to create a webhook with a JSON body for a provider") + .with_request( + method: :post, + path: '/webhooks', + headers: post_request_headers, + body: request_body) + .will_respond_with(**success_response) + end + + it "returns a CommandResult with success = true" do + execute_http_pact do | mockserver | + expect(subject.success).to be true + end + end + end + + context "when provider is specified using a label" do + before do + params.delete(:consumer) + params.delete(:provider) + params.merge!(provider_label: "provider_label") + request_body["provider"] = { "label" => "provider_label" } + mock_pact_broker_index(self, pact_broker_base_url) + + new_interaction + .upon_receiving("a request to create a webhook with a JSON body for a provider specified by a label") + .with_request( + method: :post, + path: '/webhooks', + headers: post_request_headers, + body: request_body) + .will_respond_with(**success_response) + end + + it "returns a CommandResult with success = true" do + execute_http_pact do | mockserver | + expect(subject.success).to be true + expect(subject.message).to eq "Webhook \"a webhook\" created" + end + end + end + + context "when neither consumer nor provider are specified" do + before do + params.delete(:consumer) + params.delete(:provider) + mock_pact_broker_index(self, pact_broker_base_url) + + new_interaction + .upon_receiving("a request to create a global webhook with a JSON body") + .with_request( + method: :post, + path: '/webhooks', + headers: post_request_headers, + body: request_body) + .will_respond_with(**success_response) + end + + it "returns a CommandResult with success = true" do + execute_http_pact do | mockserver | + expect(subject.success).to be true + end + end + end + + context "when a uuid is specified" do + before do + params.merge!(uuid: uuid) + request_body["provider"] = { "name" => "Pricing Service" } + request_body["consumer"] = { "name" => "Condor" } + new_interaction + .upon_receiving("a request for the index resource with the webhook relation") + .with_request( + method: :get, + path: '/', + headers: get_request_headers + ) + .will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:webhook' => { + href: generate_mock_server_url( + regex: ".*(\\/webhooks\\/\\{uuid\\})$", + example: "/webhooks/{uuid}" + ), + templated: true + } + } + } + ) + end + + let(:uuid) { '696c5f93-1b7f-44bc-8d03-59440fcaa9a0' } + + context "when the webhook does not already exist" do + before do + new_interaction + .upon_receiving("a request to create a webhook with a JSON body and a uuid") + .given("the 'Pricing Service' and 'Condor' already exist in the pact-broker") + .with_request( + method: :put, + path: "/webhooks/#{uuid}", + headers: put_request_headers, + body: request_body) + .will_respond_with(**success_response) + end + + it "returns a CommandResult with success = true" do + execute_http_pact do | mockserver | + expect(subject).to be_a PactBroker::Client::CommandResult + expect(subject.success).to be true + expect(subject.message).to eq "Webhook \"a webhook\" created" + end + end + end + + context "when the webhook does exist" do + before do + new_interaction + .upon_receiving("a request to update a webhook") + .given("a webhook with the uuid #{uuid} exists") + .with_request( + method: :put, + path: "/webhooks/#{uuid}", + headers: put_request_headers, + body: request_body) + .will_respond_with(**success_response) + end + + let(:response_status) { 200 } + + it "returns a CommandResult with success = true" do + execute_http_pact do | mockserver | + expect(subject).to be_a PactBroker::Client::CommandResult + expect(subject.success).to be true + expect(subject.message).to eq "Webhook \"a webhook\" updated" + end + end + end + end +end diff --git a/spec/pact/providers/PactFlow/pactflow_publish_provider_contract_spec.rb b/spec/pact/providers/PactFlow/pactflow_publish_provider_contract_spec.rb new file mode 100644 index 0000000..f486e18 --- /dev/null +++ b/spec/pact/providers/PactFlow/pactflow_publish_provider_contract_spec.rb @@ -0,0 +1,145 @@ +require "yaml" +require_relative '../../../pact_ruby_v2_spec_helper' +require "pactflow/client/provider_contracts/publish" + +RSpec.describe "publishing a provider contract to PactFlow", pact: true do + before do + allow_any_instance_of(PactBroker::Client::Hal::HttpClient).to receive(:sleep) + allow_any_instance_of(PactBroker::Client::Hal::HttpClient).to receive(:default_max_tries).and_return(1) + end + + pactflow + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + include PactBrokerPactHelperMethods + + let(:command_params) do + { + provider_name: "Bar", + provider_version_number: "1", + branch_name: "main", + tags: ["dev"], + build_url: "http://build", + contract: { + content: { "some" => "contract" }.to_yaml, + content_type: "application/yaml", + specification: "oas" + }, + verification_results: { + success: true, + content: "some results", + content_type: "text/plain", + format: "text", + verifier: "my custom tool", + verifier_version: "1.0" + } + } + end + + let(:request_body) do + { + "pacticipantVersionNumber" => "1", + "tags" => ["dev"], + "branch" => "main", + "buildUrl" => "http://build", + "contract" => { + "content" => "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", + "contentType" => "application/yaml", + "specification" => "oas", + "selfVerificationResults" => { + "success" => true, + "content" => "c29tZSByZXN1bHRz", + "contentType" => "text/plain", + "format" => "text", + "verifier" => "my custom tool", + "verifierVersion" => "1.0" + } + } + } + end + + let(:response_status) { 200 } + + # Can't tell from the response if the buildUrl was correct, but it's not that important + # Add some assertions to the body to ensure we have called the endpoint correctly, + # not because we use the properties in the CLI output. + # There is unfortunately no good way to determine from the response whether or not + # we have correctly published the self verification results. + let(:success_response) do + { + status: response_status, + headers: pact_broker_response_headers, + body: { + "notices" => match_each("text" => "some notice", "type" => "info"), + "_embedded" => { + "version" => { + # This tells us we have set the version number correctly + "number" => "1" + } + }, + "_links" => { + # The links tell us we have successfully created the tags, but we don't care about the contents + "pb:pacticipant-version-tags" => [{}], + # The link tells us we have successfully created the branch version, but we don't care about the contents + "pb:branch-version" => {}, + } + } + } + end + + let(:options) do + { + verbose: false + } + end + let(:pactflow_mock_service_base_url) { "http://127.0.0.1:9998" } + + let(:pact_broker_client_options) do + { pact_broker_base_url: pactflow_mock_service_base_url } + end + + subject { Pactflow::Client::ProviderContracts::Publish.call(command_params, options, pact_broker_client_options) } + + context "creating a provider contract with valid parameters" do + before do + new_interaction + .given("the pb:publish-provider-contract relation exists in the index resource") + .upon_receiving("a request for the index resource") + .with_request( + method: "GET", + path: "/", + headers: get_request_headers + ).will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pf:publish-provider-contract' => { + href: generate_mock_server_url( + regex: ".*(\\/provider-contracts\\/provider\\/.*\\/publish)$", + example: "/provider-contracts/provider/{provider}/publish" + ), + } + } + } + ) + + new_interaction + .upon_receiving("a request to publish a provider contract") + .with_request( + method: :post, + path: '/provider-contracts/provider/Bar/publish', + headers: post_request_headers.merge("Accept" => "application/hal+json,application/problem+json"), + body: request_body + ).will_respond_with(**success_response) + end + + it "returns a CommandResult with success = true" do + execute_http_pact do |mock_server| + expect(subject).to be_a PactBroker::Client::CommandResult + expect(subject.success).to be true + expect(subject.message).to include "some notice" + end + end + end +end diff --git a/spec/pact/providers/PactFlow/pactflow_publish_provider_contract_the_old_way_spec.rb b/spec/pact/providers/PactFlow/pactflow_publish_provider_contract_the_old_way_spec.rb new file mode 100644 index 0000000..8abe9ed --- /dev/null +++ b/spec/pact/providers/PactFlow/pactflow_publish_provider_contract_the_old_way_spec.rb @@ -0,0 +1,138 @@ +require_relative '../../../pact_ruby_v2_spec_helper' +require "yaml" +require "pactflow/client/provider_contracts/publish_the_old_way" +require "pact_broker/client/versions/create" + + +RSpec.describe "publishing a provider contract to PactFlow the old way", pact: true do + before do + # no point re-testing this + allow(PactBroker::Client::Versions::Create).to receive(:call).and_return(double("result", success: true)) + end + + pactflow + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + include PactBrokerPactHelperMethods + + let(:command_params) do + { + provider_name: "Bar", + provider_version_number: "1", + branch_name: "main", + tags: ["dev"], + contract: { + content: { "some" => "contract" }.to_yaml, + content_type: "application/yaml", + specification: "oas" + }, + verification_results: { + success: true, + content: "some results", + content_type: "text/plain", + format: "text", + verifier: "my custom tool", + verifier_version: "1.0" + } + } + end + + let(:body) { { some: "body" }.to_json } + + let(:request_body) do + { + "content" => "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", + "contractType" => "oas", + "contentType" => "application/yaml", + "verificationResults" => { + "success" => true, + "content" => "c29tZSByZXN1bHRz", + "contentType" => "text/plain", + "format" => "text", + "verifier" => "my custom tool", + "verifierVersion" => "1.0" + } + } + end + + let(:response_status) { 201 } + let(:success_response) do + { + status: response_status, + headers: pact_broker_response_headers + } + end + + let(:options) do + { + verbose: false + } + end + + let(:pactflow_mock_service_base_url) { "http://127.0.0.1:9998" } + + let(:pact_broker_client_options) do + { pact_broker_base_url: pactflow_mock_service_base_url } + end + + + subject { Pactflow::Client::ProviderContracts::PublishTheOldWay.call(command_params, options, pact_broker_client_options) } + + context "creating a provider contract with valid parameters" do + let(:interaction) do + new_interaction + .upon_receiving("a request to create a provider contract") + .with_request( + method: :put, + path: "/contracts/provider/Bar/version/1", + headers: put_request_headers, + body: request_body) + .will_respond_with(**success_response) + end + + it "returns a CommandResult with success = true" do + interaction.execute do |mock_server| + expect(subject).to be_a PactBroker::Client::CommandResult + expect(subject.success).to be true + expect(subject.message).to include "Successfully published provider contract for Bar version 1" + expect(subject.message).not_to include pactflow_mock_service_base_url + end + end + end + + context "creating a provider contract with valid parameters with pf:ui return results" do + let(:success_response_with_pf_ui_url) do + { + status: response_status, + headers: pact_broker_response_headers, + body: { "_links": { + "pf:ui": { + "href": match_type_of("some-url") + } + } } + } + end + let(:interaction) do + new_interaction + .given("there is a pf:ui href in the response") + .upon_receiving("a request to create a provider contract") + .with_request( + method: :put, + path: "/contracts/provider/Bar/version/1", + headers: put_request_headers, + body: request_body + ) + .will_respond_with(**success_response_with_pf_ui_url) + end + + it "returns a CommandResult with success = true and a provider contract ui url" do + interaction.execute do |mock_server| + expect(subject).to be_a PactBroker::Client::CommandResult + expect(subject.success).to be true + expect(subject.message).to include "Successfully published provider contract for Bar version 1" + expect(subject.message).to include "Next steps:" + expect(subject.message).to include "some-url" + end + end + end +end diff --git a/spec/pact/providers/PactFlow/pactflow_webhooks_create_spec.rb b/spec/pact/providers/PactFlow/pactflow_webhooks_create_spec.rb new file mode 100644 index 0000000..192d256 --- /dev/null +++ b/spec/pact/providers/PactFlow/pactflow_webhooks_create_spec.rb @@ -0,0 +1,94 @@ +require_relative '../../../pact_ruby_v2_spec_helper' +require 'pact_broker/client/webhooks/create' + +RSpec.describe "creating a webhook in PactFlow", pact: true do + + pactflow + include_context "pact broker" + include_context "pact broker - pact-ruby-v2" + include PactBrokerPactHelperMethods + + let(:params) do + { + description: "a webhook", + events: %w{contract_content_changed}, + http_method: "POST", + url: "https://webhook", + headers: { "Foo" => "bar", "Bar" => "foo"}, + body: body, + team_uuid: "2abbc12a-427d-432a-a521-c870af1739d9" + } + end + + let(:body) { { some: "body" }.to_json } + + let(:request_body) do + { + "description" => "a webhook", + "events" => [ + "name" => "contract_content_changed" + ], + "request" => { + "url" => "https://webhook", + "method" => "POST", + "headers" => { + "Foo" => "bar", + "Bar" => "foo" + }, + "body" => { + "some" => "body" + }, + }, + "teamUuid" => "2abbc12a-427d-432a-a521-c870af1739d9" + } + end + + let(:response_status) { 201 } + let(:success_response) do + { + status: response_status, + headers: pact_broker_response_headers, + body: { + description: match_type_of("a webhook"), + teamUuid: "2abbc12a-427d-432a-a521-c870af1739d9", + _links: { + self: { + href: generate_mock_server_url( + regex: "(.*)$", + example: "/some-url" + ), + title: match_type_of("A title") + } + } + } + } + end + + let(:pactflow_mock_service_base_url) { "http://127.0.0.1:9998" } + let(:pact_broker_client_options) { {} } + + subject { PactBroker::Client::Webhooks::Create.call(params, pactflow_mock_service_base_url, pact_broker_client_options) } + + context "when a valid webhook with a team specified is submitted" do + before do + mock_pact_broker_index(self, pactflow_mock_service_base_url) + new_interaction + .given("a team with UUID 2abbc12a-427d-432a-a521-c870af1739d9 exists") + .upon_receiving("a request to create a webhook for a team") + .with_request( + method: :post, + path: '/webhooks', + headers: post_request_headers, + body: request_body) + .will_respond_with(**success_response) + end + + it "returns a CommandResult with success = true" do + execute_http_pact do |mock_server| + expect(subject).to be_a PactBroker::Client::CommandResult + expect(subject.success).to be true + expect(subject.message).to eq "Webhook \"a webhook\" created" + end + end + end +end diff --git a/spec/pact_ruby_v2_spec_helper.rb b/spec/pact_ruby_v2_spec_helper.rb new file mode 100644 index 0000000..0e95235 --- /dev/null +++ b/spec/pact_ruby_v2_spec_helper.rb @@ -0,0 +1,78 @@ + +require 'pact/v2' +require 'pact/v2/rspec' +# for pact/v2 with non rail apps +require 'active_support/core_ext/object/deep_dup' +require 'active_support/core_ext/object/blank' +# https://guides.rubyonrails.org/active_support_core_extensions.html#stand-alone-active-support + + +def bar_provider + has_http_pact_between 'Foo', 'Bar', opts: { + pact_dir: "spec/pacts", + log_level: :info, + pact_specification: "2", + # Valid versions are 1, 1.1, 2, 3, 4. Default is V4 + # V prefix is optional, and case insensitive + } + +end + +def pact_broker + has_http_pact_between 'Pact Broker Client V2', 'Pact Broker', opts: { + pact_dir: "spec/pacts", + log_level: :info, + mock_port: 9999, + pact_specification: "2", + } +end + +def pactflow + has_http_pact_between 'Pact Broker Client V2', 'PactFlow', opts: { + pact_dir: "spec/pacts", + log_level: :debug, + mock_port: 9998, + pact_specification: "2", + } +end + + +module PactBrokerPactHelperMethods + + + def mock_pact_broker_index(context, mock_service_url = nil) + new_interaction + .upon_receiving("a request for the index resource") + .with_request( + method: :get, + path: '/', + headers: context.get_request_headers). + will_respond_with( + status: 200, + headers: context.pact_broker_response_headers, + body: { + _links: { + :'pb:webhooks' => { + href: generate_mock_server_url( + regex: ".*(\\/webhooks)$", + example: "/webhooks" + ) + }, + :'pb:pacticipants' => { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants)$", + example: "/pacticipants" + ) + }, + :'pb:pacticipant' => { + href: generate_mock_server_url( + regex: ".*(\\/pacticipants\\/{pacticipant})$", + example: "/pacticipants/{pacticipant}" + ) + } + } + } + ) + end + +end \ No newline at end of file diff --git a/spec/pacts/Pact Broker Client V2-Pact Broker.json b/spec/pacts/Pact Broker Client V2-Pact Broker.json new file mode 100644 index 0000000..996cd96 --- /dev/null +++ b/spec/pacts/Pact Broker Client V2-Pact Broker.json @@ -0,0 +1,2848 @@ +{ + "consumer": { + "name": "Pact Broker Client V2" + }, + "interactions": [ + { + "description": "a request for an environment", + "providerState": "an environment with name test and UUID 16926ef3-590f-4e3f-838e-719717aa88c9 exists", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/environments/16926ef3-590f-4e3f-838e-719717aa88c9" + }, + "response": { + "body": { + "_links": { + "pb:currently-deployed-deployed-versions": { + "href": "/environments/16926ef3-590f-4e3f-838e-719717aa88c9/deployed-versions/currently-deployed" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:currently-deployed-deployed-versions'].href": { + "match": "regex", + "regex": ".*(\\/environments\\/.*\\/deployed-versions\\/currently-deployed)$" + } + }, + "status": 200 + } + }, + { + "description": "a request for the compatibility matrix for Foo version 1.2.3 and Bar version 4.5.6", + "providerState": "the pact for Foo Thing version 1.2.3 has been verified by Bar version 4.5.6", + "request": { + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][pacticipant]=Foo+Thing&q[][pacticipant]=Bar&q[][version]=1%2e2%2e3&q[][version]=4%2e5%2e6" + }, + "response": { + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ], + "summary": { + "deployable": true, + "reason": "some text", + "unknown": 1 + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body": { + "match": "type" + } + }, + "status": 200 + } + }, + { + "description": "a request for the compatibility matrix for Foo version 1.2.3 and Bar version 4.5.6", + "providerState": "the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6", + "request": { + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][pacticipant]=Foo&q[][pacticipant]=Bar&q[][version]=1%2e2%2e3&q[][version]=4%2e5%2e6" + }, + "response": { + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ], + "summary": { + "deployable": true, + "reason": "some text", + "unknown": 1 + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body": { + "match": "type" + } + }, + "status": 200 + } + }, + { + "description": "a request for the compatibility matrix for Foo version 1.2.3 and the latest prod version of Bar", + "providerState": "the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6 with tag prod, and 1.2.4 unsuccessfully by 9.9.9", + "request": { + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][latest]=true&q[][pacticipant]=Foo&q[][pacticipant]=Bar&q[][tag]=prod&q[][version]=1%2e2%2e3" + }, + "response": { + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ], + "summary": { + "deployable": true, + "reason": "some text", + "unknown": 1 + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body": { + "match": "type" + } + }, + "status": 200 + } + }, + { + "description": "a request for the compatibility matrix for Foo version 1.2.3 and the latest prod versions of all other pacticipants", + "providerState": "the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6 (tagged prod) and version 5.6.7", + "request": { + "method": "GET", + "path": "/matrix", + "query": "latest=true&latestby=cvp&q[][pacticipant]=Foo&q[][version]=1%2e2%2e3&tag=prod" + }, + "response": { + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "1.2.3" + } + }, + "provider": { + "name": "Bar", + "version": { + "number": "4.5.6" + } + } + } + ] + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "status": 200 + } + }, + { + "description": "a request for the compatibility matrix for Foo version 1.2.3 and the latest version of Bar", + "providerState": "the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6, and 1.2.4 unsuccessfully by 9.9.9", + "request": { + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][latest]=true&q[][pacticipant]=Foo&q[][pacticipant]=Bar&q[][version]=1%2e2%2e4" + }, + "response": { + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ], + "summary": { + "deployable": true, + "reason": "some text", + "unknown": 1 + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body": { + "match": "type" + } + }, + "status": 200 + } + }, + { + "description": "a request for the compatibility matrix for a pacticipant that does not exist", + "request": { + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][pacticipant]=Wiffle&q[][pacticipant]=Meep&q[][version]=1%2e2%2e3&q[][version]=9%2e9%2e9" + }, + "response": { + "body": { + "errors": [ + "an error message" + ] + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body.errors": { + "match": "type", + "min": 1 + } + }, + "status": 400 + } + }, + { + "description": "a request for the compatibility matrix for all versions of Foo and Bar", + "providerState": "the pact for Foo version 1.2.3 and 1.2.4 has been verified by Bar version 4.5.6", + "request": { + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][pacticipant]=Foo&q[][pacticipant]=Bar" + }, + "response": { + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + }, + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ] + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body.matrix": { + "match": "type", + "min": 2 + } + }, + "status": 200 + } + }, + { + "description": "a request for the compatibility matrix where one or more versions does not exist", + "providerState": "the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6", + "request": { + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][pacticipant]=Foo&q[][pacticipant]=Bar&q[][version]=1%2e2%2e3&q[][version]=9%2e9%2e9" + }, + "response": { + "body": { + "summary": { + "reason": "an error message" + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body.summary.reason": { + "match": "type" + } + }, + "status": 200 + } + }, + { + "description": "a request for the compatibility matrix where only the version of Foo is specified", + "providerState": "the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6 and version 5.6.7", + "request": { + "method": "GET", + "path": "/matrix", + "query": "latest=true&latestby=cvp&q[][pacticipant]=Foo&q[][version]=1%2e2%2e3" + }, + "response": { + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ], + "summary": { + "deployable": true, + "reason": "some text", + "unknown": 1 + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body": { + "match": "type" + } + }, + "status": 200 + } + }, + { + "description": "a request for the index resource", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/" + }, + "response": { + "body": { + "_links": { + "pb:pacticipant": { + "href": "/pacticipants/{pacticipant}" + }, + "pb:pacticipants": { + "href": "/pacticipants" + }, + "pb:webhooks": { + "href": "/webhooks" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:pacticipant'].href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/{pacticipant})$" + }, + "$.body._links['pb:pacticipants'].href": { + "match": "regex", + "regex": ".*(\\/pacticipants)$" + }, + "$.body._links['pb:webhooks'].href": { + "match": "regex", + "regex": ".*(\\/webhooks)$" + } + }, + "status": 200 + } + }, + { + "description": "a request for the index resource", + "providerState": "the pacticipant relations are present", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/" + }, + "response": { + "body": { + "_links": { + "pb:pacticipant": { + "href": "/pacticipants/{pacticipant}" + }, + "pb:pacticipants": { + "href": "/pacticipants" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:pacticipant'].href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/\\{pacticipant\\})$" + }, + "$.body._links['pb:pacticipants'].href": { + "match": "regex", + "regex": ".*(\\/pacticipants)$" + } + }, + "status": 200 + } + }, + { + "description": "a request for the index resource", + "providerState": "the pb:environments relation exists in the index resource", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/" + }, + "response": { + "body": { + "_links": { + "pb:environments": { + "href": "/environments" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:environments'].href": { + "match": "regex", + "regex": ".*(\\/environments)$" + } + }, + "status": 200 + } + }, + { + "description": "a request for the index resource", + "providerState": "the pb:latest-tagged-version relation exists in the index resource", + "request": { + "headers": { + "Accept": "application/hal+json, application/json" + }, + "method": "GET", + "path": "/" + }, + "response": { + "body": { + "_links": { + "pb:latest-tagged-version": { + "href": "http://127.0.0.1:9999/pacticipants/Condor/latest-version/production" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:latest-tagged-version'].href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/.*\\/latest-version\\/.*)$" + } + }, + "status": 200 + } + }, + { + "description": "a request for the index resource", + "providerState": "the pb:latest-version relation exists in the index resource", + "request": { + "headers": { + "Accept": "application/hal+json, application/json" + }, + "method": "GET", + "path": "/" + }, + "response": { + "body": { + "_links": { + "pb:latest-version": { + "href": "http://127.0.0.1:9999/pacticipants/Condor/latest-version" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:latest-version'].href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/.*\\/latest-version)$" + } + }, + "status": 200 + } + }, + { + "description": "a request for the index resource", + "providerState": "the pb:pacticipant-branch relation exists in the index resource", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/" + }, + "response": { + "body": { + "_links": { + "pb:pacticipant-branch": { + "href": "/pacticipants/{pacticipant}/branches/{branch}" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:pacticipant-branch'].href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/.*\\/branches\\/.*)$" + } + }, + "status": 200 + } + }, + { + "description": "a request for the index resource", + "providerState": "the pb:pacticipant-version and pb:environments relations exist in the index resource", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/" + }, + "response": { + "body": { + "_links": { + "pb:environments": { + "href": "/environments" + }, + "pb:pacticipant-version": { + "href": "/pacticipants/{pacticipant}/versions/{version}" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:environments'].href": { + "match": "regex", + "regex": ".*(\\/environments)$" + }, + "$.body._links['pb:pacticipant-version'].href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/.*\\/versions\\/.*)$" + } + }, + "status": 200 + } + }, + { + "description": "a request for a pacticipant version", + "providerState": "version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with 2 environments that aren't test available for deployment", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30" + }, + "response": { + "body": { + "_links": { + "pb:record-deployment": [ + { + "href": "href", + "name": "prod" + }, + { + "href": "href", + "name": "dev" + } + ] + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:record-deployment'][0]": { + "match": "type" + }, + "$.body._links['pb:record-deployment'][1]": { + "match": "type" + } + }, + "status": 200 + } + }, + { + "description": "a request for a pacticipant version", + "providerState": "version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30" + }, + "response": { + "body": { + "_links": { + "pb:record-deployment": [ + { + "href": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30/deployed-versions/environment/cb632df3-0a0d-4227-aac3-60114dd36479", + "name": "test" + } + ] + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:record-deployment'][0].href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/.*\\/versions\\/.*\\/deployed-versions\\/environment\\/.*)$" + } + }, + "status": 200 + } + }, + { + "description": "a request for a pacticipant version", + "providerState": "version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for release", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30" + }, + "response": { + "body": { + "_links": { + "pb:record-release": [ + { + "href": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30/released-versions/environment/cb632df3-0a0d-4227-aac3-60114dd36479", + "name": "test" + } + ] + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:record-release'][0].href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/.*\\/versions\\/.*\\/released-versions\\/environment\\/.*)$" + } + }, + "status": 200 + } + }, + { + "description": "a request for the environments", + "providerState": "an environment with name test exists", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/environments" + }, + "response": { + "body": { + "_links": { + "pb:environments": [ + { + "href": "href", + "name": "test" + } + ] + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:environments'][0].href": { + "match": "type" + } + }, + "status": 200 + } + }, + { + "description": "a request for the index resource", + "providerState": "the pb:publish-contracts relations exists in the index resource", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/" + }, + "response": { + "body": { + "_links": { + "pb:publish-contracts": { + "href": "/contracts/publish" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:publish-contracts'].href": { + "match": "regex", + "regex": ".*(\\/contracts\\/publish)$" + } + }, + "status": 200 + } + }, + { + "description": "a request for the index resource with the webhook relation", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/" + }, + "response": { + "body": { + "_links": { + "pb:webhook": { + "href": "/webhooks/{uuid}", + "templated": true + } + } + }, + "generators": null, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:webhook'].href": { + "match": "regex", + "regex": ".*(\\/webhooks\\/\\{uuid\\})$" + } + }, + "status": 200 + } + }, + { + "description": "a request for the list of the latest pacts from all consumers for the Pricing Service'", + "providerState": "a latest pact between Condor and the Pricing Service exists", + "request": { + "method": "GET", + "path": "/pacts/provider/Pricing%20Service/latest" + }, + "response": { + "body": { + "_links": { + "pb:pacts": [ + { + "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", + "name": "Condor", + "title": "Pact between Condor (v1.3.0) and Pricing Service" + } + ], + "provider": { + "href": "http://example.org/pacticipants/Pricing%20Service", + "title": "Pricing Service" + } + } + }, + "headers": { + "Content-Type": "application/hal+json" + }, + "matchingRules": { + "$.body": { + "match": "type" + }, + "$.headers['Content-Type']": { + "match": "regex", + "regex": "(?-mix:application\\/.*json.*)" + } + }, + "status": 200 + } + }, + { + "description": "a request for the list of the latest prod pacts from all consumers for the Pricing Service'", + "providerState": "tagged as prod pact between Condor and the Pricing Service exists", + "request": { + "method": "GET", + "path": "/pacts/provider/Pricing%20Service/latest/prod" + }, + "response": { + "body": { + "_links": { + "pb:pacts": [ + { + "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", + "name": "Condor", + "title": "Pact between Condor (v1.3.0) and Pricing Service" + } + ], + "provider": { + "href": "http://example.org/pacticipants/Pricing%20Service", + "title": "Pricing Service" + } + } + }, + "headers": { + "Content-Type": "application/hal+json" + }, + "matchingRules": { + "$.body": { + "match": "type" + }, + "$.headers['Content-Type']": { + "match": "regex", + "regex": "(?-mix:application\\/.*json.*)" + } + }, + "status": 200 + } + }, + { + "description": "a request for the successful rows of the compatibility matrix for all versions of Foo and Bar", + "providerState": "the pact for Foo version 1.2.3 has been successfully verified by Bar version 4.5.6, and 1.2.4 unsuccessfully by 9.9.9", + "request": { + "method": "GET", + "path": "/matrix", + "query": "latestby=cvpv&q[][pacticipant]=Foo&q[][pacticipant]=Bar&success[]=true" + }, + "response": { + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "4" + } + }, + "pact": { + "createdAt": "2017-10-10T12:49:04+11:00" + }, + "provider": { + "name": "Bar", + "version": { + "number": "5" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true, + "verifiedAt": "2017-10-10T12:49:04+11:00" + } + } + ], + "summary": { + "deployable": true, + "reason": "some text", + "unknown": 1 + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body": { + "match": "type" + } + }, + "status": 200 + } + }, + { + "description": "a request retrieve a pact for a specific version", + "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker, and Condor already has a pact published for version 1.3.0", + "request": { + "method": "GET", + "path": "/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0" + }, + "response": { + "body": { + "consumer": { + "name": "Condor" + }, + "interactions": [], + "provider": { + "name": "Pricing Service" + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "status": 200 + } + }, + { + "description": "a request to create a global webhook with a JSON body", + "request": { + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/webhooks" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + }, + "$.body.description": { + "match": "type" + } + }, + "status": 201 + } + }, + { + "description": "a request to create a webhook for a consumer and provider", + "providerState": "'Condor' does not exist in the pact-broker", + "request": { + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/webhooks/provider/Pricing%20Service/consumer/Condor" + }, + "response": { + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "status": 404 + } + }, + { + "description": "a request to create a webhook with a JSON body and a uuid", + "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker", + "request": { + "body": { + "consumer": { + "name": "Condor" + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "provider": { + "name": "Pricing Service" + }, + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "PUT", + "path": "/webhooks/696c5f93-1b7f-44bc-8d03-59440fcaa9a0" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + }, + "$.body.description": { + "match": "type" + } + }, + "status": 201 + } + }, + { + "description": "a request to create a webhook with a JSON body for a consumer", + "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker", + "request": { + "body": { + "consumer": { + "name": "Condor" + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/webhooks" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + }, + "$.body.description": { + "match": "type" + } + }, + "status": 201 + } + }, + { + "description": "a request to create a webhook with a JSON body for a consumer and provider", + "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker", + "request": { + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/webhooks/provider/Pricing%20Service/consumer/Condor" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + }, + "$.body.description": { + "match": "type" + } + }, + "status": 201 + } + }, + { + "description": "a request to create a webhook with a JSON body for a consumer specified by a label", + "request": { + "body": { + "consumer": { + "label": "consumer_label" + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/webhooks" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + }, + "$.body.description": { + "match": "type" + } + }, + "status": 201 + } + }, + { + "description": "a request to create a webhook with a JSON body for a consumer that does not exist", + "request": { + "body": { + "consumer": { + "name": "Condor" + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/webhooks" + }, + "response": { + "body": { + "errors": { + "consumer.name": [ + "Some error" + ] + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body.errors['consumer.name']": { + "match": "type", + "min": 1 + } + }, + "status": 400 + } + }, + { + "description": "a request to create a webhook with a JSON body for a provider", + "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker", + "request": { + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "provider": { + "name": "Pricing Service" + }, + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/webhooks" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + }, + "$.body.description": { + "match": "type" + } + }, + "status": 201 + } + }, + { + "description": "a request to create a webhook with a JSON body for a provider specified by a label", + "request": { + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "provider": { + "label": "provider_label" + }, + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/webhooks" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + }, + "$.body.description": { + "match": "type" + } + }, + "status": 201 + } + }, + { + "description": "a request to create a webhook with a non-JSON body for a consumer and provider", + "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker", + "request": { + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": "", + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/webhooks/provider/Pricing%20Service/consumer/Condor" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": "" + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + }, + "$.body.description": { + "match": "type" + } + }, + "status": 201 + } + }, + { + "description": "a request to create a webhook with every possible event type", + "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker", + "request": { + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + }, + { + "name": "contract_published" + }, + { + "name": "provider_verification_published" + }, + { + "name": "provider_verification_succeeded" + }, + { + "name": "provider_verification_failed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/webhooks/provider/Pricing%20Service/consumer/Condor" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + }, + { + "name": "contract_published" + }, + { + "name": "provider_verification_published" + }, + { + "name": "provider_verification_succeeded" + }, + { + "name": "provider_verification_failed" + } + ], + "request": { + "body": { + "some": "body" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + }, + "$.body.description": { + "match": "type" + } + }, + "status": 201 + } + }, + { + "description": "a request to create an environment", + "request": { + "body": { + "contacts": [ + { + "details": { + "emailAddress": "foo@bar.com" + }, + "name": "Foo team" + } + ], + "displayName": "Test", + "name": "test", + "production": false + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/environments" + }, + "response": { + "body": { + "contacts": [ + { + "details": { + "emailAddress": "foo@bar.com" + }, + "name": "Foo team" + } + ], + "displayName": "Test", + "name": "test", + "production": false, + "uuid": "ffe683ef-dcd7-4e4f-877d-f6eb3db8e86e" + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body.uuid": { + "match": "type" + } + }, + "status": 201 + } + }, + { + "description": "a request to delete a pacticipant branch", + "providerState": "a branch named main exists for pacticipant Foo", + "request": { + "method": "DELETE", + "path": "/pacticipants/Foo/branches/main" + }, + "response": { + "status": 204 + } + }, + { + "description": "a request to determine if Bar can be deployed with all Foo tagged prod, ignoring the verification for Foo version 3.4.5", + "providerState": "provider Bar version 4.5.6 has a successful verification for Foo version 1.2.3 tagged prod and a failed verification for version 3.4.5 tagged prod", + "request": { + "method": "GET", + "path": "/matrix", + "query": "ignore[][pacticipant]=Foo&ignore[][version]=3%2e4%2e5&latestby=cvpv&q[][pacticipant]=Bar&q[][pacticipant]=Foo&q[][tag]=prod&q[][version]=4%2e5%2e6" + }, + "response": { + "body": { + "matrix": [ + { + "consumer": { + "name": "Foo", + "version": { + "number": "1.2.3" + } + }, + "provider": { + "name": "Bar", + "version": { + "number": "4.5.6" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": true + } + }, + { + "consumer": { + "name": "Foo", + "version": { + "number": "3.4.5" + } + }, + "ignored": true, + "provider": { + "name": "Bar", + "version": { + "number": "4.5.6" + } + }, + "verificationResult": { + "_links": { + "self": { + "href": "http://result" + } + }, + "success": false + } + } + ], + "notices": [ + { + "text": "some notice", + "type": "info" + } + ], + "summary": { + "deployable": true, + "ignored": 1 + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body.matrix[0].verificationResult._links.self.href": { + "match": "type" + }, + "$.body.matrix[1].verificationResult._links.self.href": { + "match": "type" + }, + "$.body.notices": { + "match": "type", + "min": 1 + } + }, + "status": 200 + } + }, + { + "description": "a request to get the Pricing Service", + "providerState": "the 'Pricing Service' already exists in the pact-broker", + "request": { + "method": "GET", + "path": "/pacticipants/Pricing%20Service" + }, + "response": { + "body": { + "_embedded": { + "latest-version": { + "_links": { + "self": { + "href": "http://example.org/pacticipants/Pricing%20Service/versions/1.3.0" + } + }, + "number": "1.3.0" + } + }, + "_links": { + "self": { + "href": "http://example.org/pacticipants/Pricing%20Service" + }, + "versions": { + "href": "http://example.org/pacticipants/Pricing%20Service/versions" + } + }, + "name": "Pricing Service", + "repositoryUrl": "git@git.realestate.com.au:business-systems/pricing-service" + }, + "headers": { + "Content-Type": "application/hal+json" + }, + "matchingRules": { + "$.headers['Content-Type']": { + "match": "regex", + "regex": "(?-mix:application\\/hal\\+json.*)" + } + }, + "status": 200 + } + }, + { + "description": "a request to get the Pricing Service", + "providerState": "the 'Pricing Service' does not exist in the pact-broker", + "request": { + "method": "GET", + "path": "/pacticipants/Pricing%20Service" + }, + "response": { + "status": 404 + } + }, + { + "description": "a request to list pacticipants", + "providerState": "'Condor' exists in the pact-broker", + "request": { + "method": "GET", + "path": "/pacticipants" + }, + "response": { + "body": { + "_links": { + "pacticipants": [ + { + "href": "http://example.org/pacticipants/Condor", + "title": "Condor" + } + ], + "self": { + "href": "http://example.org/pacticipants" + } + }, + "pacticipants": [ + { + "_embedded": { + "latest-version": { + "_links": { + "self": { + "href": "http://example.org/pacticipants/Condor/versions/1.3.0" + } + }, + "number": "1.3.0" + } + }, + "_links": { + "self": { + "href": "http://example.org/pacticipants/Condor" + } + }, + "name": "Condor" + } + ] + }, + "headers": { + "Content-Type": "application/hal+json" + }, + "matchingRules": { + "$.headers['Content-Type']": { + "match": "regex", + "regex": "(?-mix:application\\/hal\\+json.*)" + } + }, + "status": 200 + } + }, + { + "description": "a request to list the environments", + "providerState": "an environment exists", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/environments" + }, + "response": { + "body": { + "_embedded": { + "environments": [ + { + "contacts": [ + { + "details": { + "emailAddress": "foo@bar.com" + }, + "name": "Foo team" + } + ], + "displayName": "Test", + "name": "test", + "production": false, + "uuid": "78e85fb2-9df1-48da-817e-c9bea6294e01" + } + ] + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._embedded.environments": { + "match": "type", + "min": 1 + } + }, + "status": 200 + } + }, + { + "description": "a request to list the latest pacts", + "providerState": "a pact between Condor and the Pricing Service exists", + "request": { + "method": "GET", + "path": "/pacts/latest" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "http://example.org/pacts/latest" + } + }, + "pacts": [ + { + "_embedded": { + "consumer": { + "_embedded": { + "version": { + "number": "1.3.0" + } + }, + "_links": { + "self": { + "href": "http://example.org/pacticipants/Condor" + } + }, + "name": "Condor" + }, + "provider": { + "_links": { + "self": { + "href": "http://example.org/pacticipants/Pricing%20Service" + } + }, + "name": "Pricing Service" + } + }, + "_links": { + "self": [ + { + "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/latest" + }, + { + "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0" + } + ] + } + } + ] + }, + "headers": { + "Content-Type": "application/hal+json" + }, + "matchingRules": { + "$.headers['Content-Type']": { + "match": "regex", + "regex": "(?-mix:application\\/hal\\+json.*)" + } + }, + "status": 200 + } + }, + { + "description": "a request to list the versions deployed to an environment for a pacticipant name and application instance", + "providerState": "an version is deployed to environment with UUID 16926ef3-590f-4e3f-838e-719717aa88c9 with target customer-1", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/environments/16926ef3-590f-4e3f-838e-719717aa88c9/deployed-versions/currently-deployed", + "query": "pacticipant=Foo" + }, + "response": { + "body": { + "_embedded": { + "deployedVersions": [ + { + "_links": { + "self": { + "href": "/deployed-versions/ff3adecf-cfc5-4653-a4e3-f1861092f8e0" + } + }, + "applicationInstance": "customer-1" + } + ] + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._embedded.deployedVersions[0]._links.self.href": { + "match": "regex", + "regex": ".*(\\/deployed-versions\\/.*)$" + } + }, + "status": 200 + } + }, + { + "description": "a request to mark a deployed version as not currently deploye", + "providerState": "a currently deployed version exists", + "request": { + "body": { + "currentlyDeployed": false + }, + "headers": { + "Content-Type": "application/merge-patch+json" + }, + "method": "PATCH", + "path": "/deployed-versions/ff3adecf-cfc5-4653-a4e3-f1861092f8e0" + }, + "response": { + "body": { + "_embedded": { + "version": { + "number": "2" + } + }, + "currentlyDeployed": false + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._embedded.version.number": { + "match": "type" + } + }, + "status": 200 + } + }, + { + "description": "a request to publish contracts", + "request": { + "body": { + "branch": "main", + "buildUrl": "http://build", + "contracts": [ + { + "consumerName": "Foo", + "content": "eyJjb25zdW1lciI6eyJuYW1lIjoiRm9vIn0sInByb3ZpZGVyIjp7Im5hbWUiOiJCYXIifSwiaW50ZXJhY3Rpb25zIjpbeyJkZXNjcmlwdGlvbiI6ImFuIGV4YW1wbGUgcmVxdWVzdCIsInByb3ZpZGVyU3RhdGUiOiJhIHByb3ZpZGVyIHN0YXRlIiwicmVxdWVzdCI6eyJtZXRob2QiOiJHRVQiLCJwYXRoIjoiLyIsImhlYWRlcnMiOnt9fSwicmVzcG9uc2UiOnsic3RhdHVzIjoyMDAsImhlYWRlcnMiOnsiQ29udGVudC1UeXBlIjoiYXBwbGljYXRpb24vaGFsK2pzb24ifX19XSwibWV0YWRhdGEiOnsicGFjdFNwZWNpZmljYXRpb24iOnsidmVyc2lvbiI6IjIuMC4wIn19fQ==", + "contentType": "application/json", + "onConflict": "merge", + "providerName": "Bar", + "specification": "pact" + } + ], + "pacticipantName": "Foo", + "pacticipantVersionNumber": "5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", + "tags": [ + "dev" + ] + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/contracts/publish" + }, + "response": { + "body": { + "_embedded": { + "pacticipant": { + "name": "Foo" + }, + "version": { + "number": "5556b8149bf8bac76bc30f50a8a2dd4c22c85f30" + } + }, + "_links": { + "pb:contracts": [ + { + "href": "http://some-pact" + } + ], + "pb:pacticipant-version-tags": [ + { + "name": "dev" + } + ] + }, + "logs": [ + { + "level": "info", + "message": "some message" + } + ] + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:contracts'][0].href": { + "match": "type" + }, + "$.body.logs": { + "match": "type", + "min": 1 + } + }, + "status": 200 + } + }, + { + "description": "a request to record a deployment", + "providerState": "version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment", + "request": { + "body": { + "applicationInstance": "blue", + "target": "blue" + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30/deployed-versions/environment/cb632df3-0a0d-4227-aac3-60114dd36479" + }, + "response": { + "body": { + "target": "blue" + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "status": 201 + } + }, + { + "description": "a request to record a release", + "providerState": "version 5556b8149bf8bac76bc30f50a8a2dd4c22c85f30 of pacticipant Foo exists with a test environment available for deployment", + "request": { + "body": {}, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/pacticipants/Foo/versions/5556b8149bf8bac76bc30f50a8a2dd4c22c85f30/released-versions/environment/cb632df3-0a0d-4227-aac3-60114dd36479" + }, + "response": { + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "status": 201 + } + }, + { + "description": "a request to register the repository URL of a pacticipant", + "providerState": "the 'Pricing Service' already exists in the pact-broker", + "request": { + "body": { + "repository_url": "git@git.realestate.com.au:business-systems/pricing-service" + }, + "headers": { + "Content-Type": "application/json" + }, + "method": "PATCH", + "path": "/pacticipants/Pricing%20Service" + }, + "response": { + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "status": 200 + } + }, + { + "description": "a request to register the repository URL of a pacticipant", + "providerState": "the 'Pricing Service' does not exist in the pact-broker", + "request": { + "body": { + "repository_url": "git@git.realestate.com.au:business-systems/pricing-service" + }, + "headers": { + "Content-Type": "application/json" + }, + "method": "PATCH", + "path": "/pacticipants/Pricing%20Service" + }, + "response": { + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "status": 201 + } + }, + { + "description": "a request to retrieve a pacticipant", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/pacticipants/Foo" + }, + "response": { + "status": 404 + } + }, + { + "description": "a request to create a pacticipant", + "request": { + "body": { + "name": "Foo", + "repositoryUrl": "http://foo" + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/pacticipants" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/pacticipants/Foo" + } + }, + "name": "Foo", + "repositoryUrl": "http://foo" + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/Foo)$" + } + }, + "status": 201 + } + }, + { + "description": "a request to retrieve a pacticipant", + "providerState": "a pacticipant with name Foo exists", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/pacticipants/Foo" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/pacticipants/Foo" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/Foo)$" + } + }, + "status": 200 + } + }, + { + "description": "a request to retrieve the latest 'production' version of Condor", + "providerState": "'Condor' exists in the pact-broker with the latest tagged 'production' version 1.2.3", + "request": { + "headers": { + "Accept": "application/hal+json, application/json" + }, + "method": "GET", + "path": "/pacticipants/Condor/latest-version/production" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "number": "1.2.3" + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + } + }, + "status": 200 + } + }, + { + "description": "a request to retrieve the latest pact between Condor and the Pricing Service", + "providerState": "a pact between Condor and the Pricing Service exists", + "request": { + "method": "GET", + "path": "/pacts/provider/Pricing%20Service/consumer/Condor/latest" + }, + "response": { + "body": { + "consumer": { + "name": "Condor" + }, + "interactions": [], + "provider": { + "name": "Pricing Service" + } + }, + "headers": { + "Content-Type": "application/hal+json", + "X-Pact-Consumer-Version": "1.3.0" + }, + "matchingRules": { + "$.headers['Content-Type']": { + "match": "regex", + "regex": "(?-mix:application\\/.*json.*)" + } + }, + "status": 200 + } + }, + { + "description": "a request to retrieve the latest pact between Condor and the Pricing Service", + "providerState": "no pact between Condor and the Pricing Service exists", + "request": { + "method": "GET", + "path": "/pacts/provider/Pricing%20Service/consumer/Condor/latest" + }, + "response": { + "status": 404 + } + }, + { + "description": "a request to retrieve the latest version of Condor", + "providerState": "'Condor' exists in the pact-broker with the latest version 1.2.3", + "request": { + "headers": { + "Accept": "application/hal+json, application/json" + }, + "method": "GET", + "path": "/pacticipants/Condor/latest-version" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "number": "1.2.3" + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + } + }, + "status": 200 + } + }, + { + "description": "a request to retrieve the pact between the production verison of Condor and the Pricing Service", + "providerState": "a pact between Condor and the Pricing Service exists for the production version of Condor", + "request": { + "headers": { + "Accept": "application/hal+json, application/json" + }, + "method": "GET", + "path": "/pacts/provider/Pricing%20Service/consumer/Condor/latest/prod" + }, + "response": { + "body": { + "consumer": { + "name": "Condor" + }, + "interactions": [], + "provider": { + "name": "Pricing Service" + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "status": 200 + } + }, + { + "description": "a request to tag the production version of Condor", + "providerState": "'Condor' does not exist in the pact-broker", + "request": { + "body": {}, + "headers": { + "Content-Type": "application/json" + }, + "method": "PUT", + "path": "/pacticipants/Condor/versions/1.3.0/tags/prod" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/pacticipants/Condor/versions/1.3.0/tags/prod" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/Condor\\/versions\\/1\\.3\\.0\\/tags\\/prod)$" + } + }, + "status": 201 + } + }, + { + "description": "a request to tag the production version of Condor", + "providerState": "'Condor' exists in the pact-broker", + "request": { + "body": {}, + "headers": { + "Content-Type": "application/json" + }, + "method": "PUT", + "path": "/pacticipants/Condor/versions/1.3.0/tags/prod" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/pacticipants/Condor/versions/1.3.0/tags/prod" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/Condor\\/versions\\/1\\.3\\.0\\/tags\\/prod)$" + } + }, + "status": 201 + } + }, + { + "description": "a request to tag the production version of Condor", + "providerState": "'Condor' exists in the pact-broker with version 1.3.0, tagged with 'prod'", + "request": { + "body": {}, + "headers": { + "Content-Type": "application/json" + }, + "method": "PUT", + "path": "/pacticipants/Condor/versions/1.3.0/tags/prod" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/pacticipants/Condor/versions/1.3.0/tags/prod" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/Condor\\/versions\\/1\\.3\\.0\\/tags\\/prod)$" + } + }, + "status": 200 + } + }, + { + "description": "a request to update a pacticipant", + "providerState": "a pacticipant with name Foo exists", + "request": { + "body": { + "name": "Foo", + "repositoryUrl": "http://foo" + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "PATCH", + "path": "/pacticipants/Foo" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/pacticipants/Foo" + } + }, + "name": "Foo", + "repositoryUrl": "http://foo" + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/Foo)$" + } + }, + "status": 200 + } + }, + { + "description": "a request to update a webhook", + "providerState": "a webhook with the uuid 696c5f93-1b7f-44bc-8d03-59440fcaa9a0 exists", + "request": { + "body": { + "consumer": { + "name": "Condor" + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "provider": { + "name": "Pricing Service" + }, + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "url": "https://webhook", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "PUT", + "path": "/webhooks/696c5f93-1b7f-44bc-8d03-59440fcaa9a0" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url" + } + }, + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + } + } + }, + "generators": null, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + }, + "$.body.description": { + "match": "type" + } + }, + "status": 200 + } + }, + { + "description": "an invalid request to create a webhook for a consumer and provider", + "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker", + "request": { + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "password": "password", + "username": "username" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/webhooks/provider/Pricing%20Service/consumer/Condor" + }, + "response": { + "body": { + "errors": { + "request.url": [ + "Some error" + ] + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body.errors['request.url']": { + "match": "type", + "min": 1 + } + }, + "status": 400 + } + } + ], + "metadata": { + "pact-ruby-v2": { + "pact-ffi": "0.4.28" + }, + "pactRust": { + "ffi": "0.4.28", + "mockserver": "1.2.16", + "models": "1.3.5" + }, + "pactSpecification": { + "version": "2.0.0" + } + }, + "provider": { + "name": "Pact Broker" + } +} \ No newline at end of file diff --git a/spec/pacts/Pact Broker Client V2-PactFlow.json b/spec/pacts/Pact Broker Client V2-PactFlow.json new file mode 100644 index 0000000..71e970b --- /dev/null +++ b/spec/pacts/Pact Broker Client V2-PactFlow.json @@ -0,0 +1,294 @@ +{ + "consumer": { + "name": "Pact Broker Client V2" + }, + "interactions": [ + { + "description": "a request for the index resource", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/" + }, + "response": { + "body": { + "_links": { + "pb:pacticipant": { + "href": "/pacticipants/{pacticipant}" + }, + "pb:pacticipants": { + "href": "/pacticipants" + }, + "pb:webhooks": { + "href": "/webhooks" + } + } + }, + "generators": null, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pb:pacticipant'].href": { + "match": "regex", + "regex": ".*(\\/pacticipants\\/{pacticipant})$" + }, + "$.body._links['pb:pacticipants'].href": { + "match": "regex", + "regex": ".*(\\/pacticipants)$" + }, + "$.body._links['pb:webhooks'].href": { + "match": "regex", + "regex": ".*(\\/webhooks)$" + } + }, + "status": 200 + } + }, + { + "description": "a request for the index resource", + "providerState": "the pb:publish-provider-contract relation exists in the index resource", + "request": { + "headers": { + "Accept": "application/hal+json" + }, + "method": "GET", + "path": "/" + }, + "response": { + "body": { + "_links": { + "pf:publish-provider-contract": { + "href": "/provider-contracts/provider/{provider}/publish" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pf:publish-provider-contract'].href": { + "match": "regex", + "regex": ".*(\\/provider-contracts\\/provider\\/.*\\/publish)$" + } + }, + "status": 200 + } + }, + { + "description": "a request to create a provider contract", + "request": { + "body": { + "content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", + "contentType": "application/yaml", + "contractType": "oas", + "verificationResults": { + "content": "c29tZSByZXN1bHRz", + "contentType": "text/plain", + "format": "text", + "success": true, + "verifier": "my custom tool", + "verifierVersion": "1.0" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "PUT", + "path": "/contracts/provider/Bar/version/1" + }, + "response": { + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "status": 201 + } + }, + { + "description": "a request to create a provider contract", + "providerState": "there is a pf:ui href in the response", + "request": { + "body": { + "content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", + "contentType": "application/yaml", + "contractType": "oas", + "verificationResults": { + "content": "c29tZSByZXN1bHRz", + "contentType": "text/plain", + "format": "text", + "success": true, + "verifier": "my custom tool", + "verifierVersion": "1.0" + } + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "PUT", + "path": "/contracts/provider/Bar/version/1" + }, + "response": { + "body": { + "_links": { + "pf:ui": { + "href": "some-url" + } + } + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links['pf:ui'].href": { + "match": "type" + } + }, + "status": 201 + } + }, + { + "description": "a request to create a webhook for a team", + "providerState": "a team with UUID 2abbc12a-427d-432a-a521-c870af1739d9 exists", + "request": { + "body": { + "description": "a webhook", + "events": [ + { + "name": "contract_content_changed" + } + ], + "request": { + "body": { + "some": "body" + }, + "headers": { + "Bar": "foo", + "Foo": "bar" + }, + "method": "POST", + "url": "https://webhook" + }, + "teamUuid": "2abbc12a-427d-432a-a521-c870af1739d9" + }, + "headers": { + "Accept": "application/hal+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/webhooks" + }, + "response": { + "body": { + "_links": { + "self": { + "href": "/some-url", + "title": "A title" + } + }, + "description": "a webhook", + "teamUuid": "2abbc12a-427d-432a-a521-c870af1739d9" + }, + "generators": null, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "(.*)$" + }, + "$.body._links.self.title": { + "match": "type" + }, + "$.body.description": { + "match": "type" + } + }, + "status": 201 + } + }, + { + "description": "a request to publish a provider contract", + "request": { + "body": { + "branch": "main", + "buildUrl": "http://build", + "contract": { + "content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", + "contentType": "application/yaml", + "selfVerificationResults": { + "content": "c29tZSByZXN1bHRz", + "contentType": "text/plain", + "format": "text", + "success": true, + "verifier": "my custom tool", + "verifierVersion": "1.0" + }, + "specification": "oas" + }, + "pacticipantVersionNumber": "1", + "tags": [ + "dev" + ] + }, + "headers": { + "Accept": "application/hal+json, application/problem+json", + "Content-Type": "application/json" + }, + "method": "POST", + "path": "/provider-contracts/provider/Bar/publish" + }, + "response": { + "body": { + "_embedded": { + "version": { + "number": "1" + } + }, + "_links": { + "pb:branch-version": {}, + "pb:pacticipant-version-tags": [ + {} + ] + }, + "notices": [ + { + "text": "some notice", + "type": "info" + } + ] + }, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "matchingRules": { + "$.body.notices": { + "match": "type", + "min": 1 + } + }, + "status": 200 + } + } + ], + "metadata": { + "pact-ruby-v2": { + "pact-ffi": "0.4.28" + }, + "pactRust": { + "ffi": "0.4.28", + "mockserver": "1.2.16", + "models": "1.3.5" + }, + "pactSpecification": { + "version": "2.0.0" + } + }, + "provider": { + "name": "PactFlow" + } +} \ No newline at end of file diff --git a/spec/pacts/pact_broker_client-pact_broker.json b/spec/pacts/pact_broker_client-pact_broker.json index 53b4f95..5f219f8 100644 --- a/spec/pacts/pact_broker_client-pact_broker.json +++ b/spec/pacts/pact_broker_client-pact_broker.json @@ -110,7 +110,7 @@ "matchingRules": { "$.headers.Content-Type": { "match": "regex", - "regex": "application\\/hal\\+json" + "regex": "application\\/hal\\+json.*" } } } @@ -165,7 +165,7 @@ "matchingRules": { "$.headers.Content-Type": { "match": "regex", - "regex": "application\\/hal\\+json" + "regex": "application\\/hal\\+json.*" } } } @@ -209,7 +209,7 @@ "matchingRules": { "$.headers.Content-Type": { "match": "regex", - "regex": "application\\/hal\\+json" + "regex": "application\\/hal\\+json.*" } } } @@ -811,189 +811,6 @@ } } }, - { - "description": "a request to publish a pact", - "providerState": "the 'Pricing Service' already exists in the pact-broker", - "request": { - "method": "put", - "path": "/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "consumer": { - "name": "Condor" - }, - "provider": { - "name": "Pricing Service" - }, - "interactions": [ - - ] - } - }, - "response": { - "status": 201, - "headers": { - "Content-Type": "application/hal+json;charset=utf-8" - }, - "body": { - "_links": { - "pb:latest-pact-version": { - "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/latest" - } - } - } - } - }, - { - "description": "a request to publish a pact with method patch", - "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker, and Condor already has a pact published for version 1.3.0", - "request": { - "method": "patch", - "path": "/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "consumer": { - "name": "Condor" - }, - "provider": { - "name": "Pricing Service" - }, - "interactions": [ - - ] - } - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/hal+json;charset=utf-8" - }, - "body": { - "_links": { - "pb:latest-pact-version": { - "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/latest" - } - } - } - } - }, - { - "description": "a request to publish a pact with method put", - "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker, and Condor already has a pact published for version 1.3.0", - "request": { - "method": "put", - "path": "/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "consumer": { - "name": "Condor" - }, - "provider": { - "name": "Pricing Service" - }, - "interactions": [ - - ] - } - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/hal+json;charset=utf-8" - }, - "body": { - "_links": { - "pb:latest-pact-version": { - "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/latest" - } - } - } - } - }, - { - "description": "a request to publish a pact", - "providerState": "'Condor' already exist in the pact-broker, but the 'Pricing Service' does not", - "request": { - "method": "put", - "path": "/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "consumer": { - "name": "Condor" - }, - "provider": { - "name": "Pricing Service" - }, - "interactions": [ - - ] - } - }, - "response": { - "status": 201, - "headers": { - "Content-Type": "application/hal+json;charset=utf-8" - }, - "body": { - "_links": { - "pb:latest-pact-version": { - "href": "http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/latest" - } - } - } - } - }, - { - "description": "a request to publish a pact", - "providerState": "an error occurs while publishing a pact", - "request": { - "method": "put", - "path": "/pacts/provider/Pricing%20Service/consumer/Condor/version/1.3.0", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "consumer": { - "name": "Condor" - }, - "provider": { - "name": "Pricing Service" - }, - "interactions": [ - - ] - } - }, - "response": { - "status": 500, - "headers": { - "Content-Type": "application/hal+json" - }, - "body": { - "error": { - "message": "An error occurred" - } - }, - "matchingRules": { - "$.headers.Content-Type": { - "match": "regex", - "regex": "application\\/.*json" - }, - "$.body.error.message": { - "match": "regex", - "regex": ".*" - } - } - } - }, { "description": "a request to register the repository URL of a pacticipant", "providerState": "the 'Pricing Service' does not exist in the pact-broker", @@ -1126,8 +943,7 @@ "name": "Pricing Service" }, "interactions": [ - - ] + ] } } }, @@ -1154,13 +970,12 @@ "name": "Pricing Service" }, "interactions": [ - - ] + ] }, "matchingRules": { "$.headers.Content-Type": { "match": "regex", - "regex": "application\\/.*json" + "regex": "application\\/.*json.*" } } } @@ -1203,8 +1018,7 @@ "name": "Pricing Service" }, "interactions": [ - - ] + ] } } }, @@ -1216,6 +1030,8 @@ "path": "/pacticipants/Condor/versions/1.3.0/tags/prod", "headers": { "Content-Type": "application/json" + }, + "body": { } }, "response": { @@ -1246,6 +1062,8 @@ "path": "/pacticipants/Condor/versions/1.3.0/tags/prod", "headers": { "Content-Type": "application/json" + }, + "body": { } }, "response": { @@ -1276,6 +1094,8 @@ "path": "/pacticipants/Condor/versions/1.3.0/tags/prod", "headers": { "Content-Type": "application/json" + }, + "body": { } }, "response": { @@ -1323,7 +1143,7 @@ "matchingRules": { "$.body._links.pb:latest-version.href": { "match": "regex", - "regex": "http:\\/\\/.*{pacticipant}" + "regex": "http:\\/\\/.*{pacticipant}.*" } } } @@ -1888,6 +1708,8 @@ "headers": { "Content-Type": "application/json", "Accept": "application/hal+json" + }, + "body": { } }, "response": { @@ -1960,7 +1782,7 @@ "matchingRules": { "$.body._embedded.deployedVersions[0]._links.self.href": { "match": "regex", - "regex": "^http" + "regex": "^http.*" } } } diff --git a/spec/pacts/pact_broker_client-pactflow.json b/spec/pacts/pact_broker_client-pactflow.json index 8c0d86f..c2774ba 100644 --- a/spec/pacts/pact_broker_client-pactflow.json +++ b/spec/pacts/pact_broker_client-pactflow.json @@ -87,10 +87,10 @@ "_links": { "pb:pacticipant-version-tags": [ { - } + } ], "pb:branch-version": { - } + } } }, "matchingRules": { diff --git a/spec/service_providers/extra_goodies_spec.rb b/spec/service_providers/extra_goodies_spec.rb index 3da2016..f31db12 100644 --- a/spec/service_providers/extra_goodies_spec.rb +++ b/spec/service_providers/extra_goodies_spec.rb @@ -33,7 +33,7 @@ module PactBroker::Client method: :get, path: '/pacts/latest', headers: {} ). - will_respond_with( headers: {'Content-Type' => Pact.term(generate: 'application/hal+json', matcher: %r{application/hal\+json})}, + will_respond_with( headers: {'Content-Type' => Pact.term(generate: 'application/hal+json', matcher: %r{application/hal\+json.*})}, status: 200, body: response_body ) @@ -55,7 +55,7 @@ module PactBroker::Client method: :get, path: '/pacticipants', headers: {} ). - will_respond_with( headers: {'Content-Type' => Pact.term(generate: 'application/hal+json', matcher: %r{application/hal\+json})}, + will_respond_with( headers: {'Content-Type' => Pact.term(generate: 'application/hal+json', matcher: %r{application/hal\+json.*})}, status: 200, body: response_body ) @@ -78,7 +78,7 @@ module PactBroker::Client method: :get, path: '/pacticipants/Pricing%20Service', headers: {} ). - will_respond_with( headers: {'Content-Type' => Pact.term(generate: 'application/hal+json', matcher: %r{application/hal\+json})}, + will_respond_with( headers: {'Content-Type' => Pact.term(generate: 'application/hal+json', matcher: %r{application/hal\+json.*})}, status: 200, body: response_body ) diff --git a/spec/service_providers/pact_broker_client_publish_spec.rb b/spec/service_providers/pact_broker_client_publish_spec.rb index cda1c3b..712ab48 100644 --- a/spec/service_providers/pact_broker_client_publish_spec.rb +++ b/spec/service_providers/pact_broker_client_publish_spec.rb @@ -6,7 +6,7 @@ module PactBroker::Client include_context "pact broker" - describe "publishing a pact" do + describe "publishing a pact", :skip do let(:options) { { pact_hash: pact_hash, consumer_version: consumer_version }} let(:location) { 'http://example.org/pacts/provider/Pricing%20Service/consumer/Condor/latest' } @@ -117,7 +117,7 @@ module PactBroker::Client body: pact_hash ). will_respond_with( status: 500, - headers: {'Content-Type' => Pact.term(generate: 'application/hal+json', matcher: %r{application/.*json})}, + headers: {'Content-Type' => Pact.term(generate: 'application/hal+json', matcher: %r{application/.*json.*})}, body: { error: { message: Pact::Term.new(matcher: /.*/, generate: 'An error occurred') diff --git a/spec/service_providers/pact_broker_client_retrieve_pact_spec.rb b/spec/service_providers/pact_broker_client_retrieve_pact_spec.rb index cf11194..49df51e 100644 --- a/spec/service_providers/pact_broker_client_retrieve_pact_spec.rb +++ b/spec/service_providers/pact_broker_client_retrieve_pact_spec.rb @@ -33,7 +33,7 @@ module PactBroker::Client let(:response_headers) do pact_broker_response_headers.merge( - 'Content-Type' => Pact.term(generate: 'application/hal+json', matcher: %r{application/.*json}), + 'Content-Type' => Pact.term(generate: 'application/hal+json', matcher: %r{application/.*json.*}), 'X-Pact-Consumer-Version' => consumer_version ) end diff --git a/spec/service_providers/pact_broker_client_tags_spec.rb b/spec/service_providers/pact_broker_client_tags_spec.rb index d34ce80..eb04a48 100644 --- a/spec/service_providers/pact_broker_client_tags_spec.rb +++ b/spec/service_providers/pact_broker_client_tags_spec.rb @@ -18,7 +18,7 @@ with( method: :put, path: '/pacticipants/Condor/versions/1.3.0/tags/prod', - headers: default_request_headers). + headers: default_request_headers, body: {}). will_respond_with( status: 201, headers: pact_broker_response_headers, @@ -46,7 +46,7 @@ with( method: :put, path: '/pacticipants/Condor/versions/1.3.0/tags/prod', - headers: default_request_headers). + headers: default_request_headers, body: {}). will_respond_with( status: 201, headers: pact_broker_response_headers, @@ -75,7 +75,7 @@ with( method: :put, path: '/pacticipants/Condor/versions/1.3.0/tags/prod', - headers: default_request_headers). + headers: default_request_headers, body: {}). will_respond_with( status: 200, headers: pact_broker_response_headers, diff --git a/spec/service_providers/pact_broker_client_versions_spec.rb b/spec/service_providers/pact_broker_client_versions_spec.rb index e806ef1..667baea 100644 --- a/spec/service_providers/pact_broker_client_versions_spec.rb +++ b/spec/service_providers/pact_broker_client_versions_spec.rb @@ -25,7 +25,7 @@ body: { _links: { :'pb:latest-version' => { - href: Pact.term(latest_version_url, /http:\/\/.*{pacticipant}/) + href: Pact.term(latest_version_url, /http:\/\/.*{pacticipant}.*/) } } } diff --git a/spec/service_providers/record_release_spec.rb b/spec/service_providers/record_release_spec.rb index 351a705..3a854cd 100644 --- a/spec/service_providers/record_release_spec.rb +++ b/spec/service_providers/record_release_spec.rb @@ -107,7 +107,8 @@ def mock_record_release .with( method: "POST", path: "/HAL-REL-PLACEHOLDER-PB-RECORD-RELEASE-FOO-5556B8149BF8BAC76BC30F50A8A2DD4C22C85F30-TEST", - headers: post_request_headers + headers: post_request_headers, + body: {} ) .will_respond_with( status: 201, diff --git a/spec/service_providers/record_undeployment_spec.rb b/spec/service_providers/record_undeployment_spec.rb index 1a78b8d..ccb197e 100644 --- a/spec/service_providers/record_undeployment_spec.rb +++ b/spec/service_providers/record_undeployment_spec.rb @@ -103,7 +103,7 @@ def mock_deployed_versions_search_results applicationInstance: application_instance, _links: { self: { - href: Pact.term(pact_broker.mock_service_base_url + deployed_version_placeholder_path, /^http/) + href: Pact.term(pact_broker.mock_service_base_url + deployed_version_placeholder_path, /^http.*/) } } } diff --git a/spec/support/shared_context.rb b/spec/support/shared_context.rb index 1423e3b..e7b7e76 100644 --- a/spec/support/shared_context.rb +++ b/spec/support/shared_context.rb @@ -23,3 +23,8 @@ let(:post_request_headers) { { 'Content-Type' => 'application/json', 'Accept' => 'application/hal+json'} } let(:get_request_headers) { { 'Accept' => 'application/hal+json'} } end + +shared_context "pact broker - pact-ruby-v2" do + let(:pact_hash) { PactBroker::Client::PactHash[consumer: {name: 'Condor'}, interactions: [], provider: {name: 'Pricing Service'}] } + let(:broker_base_url) { 'http://localhost:9999' } +end diff --git a/spec/support/ssl_server.rb b/spec/support/ssl_server.rb index 209d684..e532298 100644 --- a/spec/support/ssl_server.rb +++ b/spec/support/ssl_server.rb @@ -32,11 +32,11 @@ def webrick_opts port require "webrick" require "webrick/https" require "rack" - require "rackup/handler/webrick" + require "rack/handler/webrick" opts = webrick_opts(4444) - Rackup::Handler::WEBrick.run(app, **opts) do |server| + Rack::Handler::WEBrick.run(app, **opts) do |server| @server = server end end