From 19f1b0be1126b5491197d3102cbe314d5bc7f29f Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Mon, 26 Apr 2021 17:57:45 +1000 Subject: [PATCH] feat: use the pb:publish-contracts relation and endpoint to publish pacts --- .../Pact Broker Client - Pact Broker.md | 100 +++++++++++++++ lib/pact_broker/client/cli/broker.rb | 3 +- lib/pact_broker/client/hal/http_client.rb | 8 +- lib/pact_broker/client/pacts.rb | 1 - lib/pact_broker/client/publish_pacts.rb | 47 ++++--- .../client/publish_pacts_the_old_way.rb | 6 +- .../client/tasks/publication_task.rb | 8 +- script/publish-pact.sh | 18 ++- spec/fixtures/foo-bar.json | 31 +++++ .../client/cli/broker_publish_spec.rb | 63 ++++++---- .../client/publish_pacts_the_old_way_spec.rb | 3 +- .../client/tasks/publication_task_spec.rb | 52 ++++---- .../pacts/pact_broker_client-pact_broker.json | 106 ++++++++++++++++ spec/service_providers/publish_pacts_spec.rb | 115 ++++++++++++++++++ 14 files changed, 482 insertions(+), 79 deletions(-) create mode 100644 spec/fixtures/foo-bar.json create mode 100644 spec/service_providers/publish_pacts_spec.rb diff --git a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md index 403e7861..c63f15d8 100644 --- a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md +++ b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md @@ -40,6 +40,8 @@ * [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:pacticipant-version_relation_exists_in_the_index_resource) given the pb:pacticipant-version relation exists 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 @@ -92,6 +94,8 @@ * [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 * [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 @@ -832,6 +836,33 @@ Pact Broker will respond with: } } ``` + +Given **the pb:publish-contracts relations exists in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client, 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": "http://localhost:1234/HAL-REL-PLACEHOLDER-PB-PUBLISH-CONTRACTS" + } + } + } +} +``` Upon receiving **a request for the index resource with the webhook relation** from Pact Broker Client, with ```json @@ -1928,6 +1959,75 @@ Pact Broker will respond with: } } ``` + +Upon receiving **a request to publish contracts** from Pact Broker Client, with +```json +{ + "method": "POST", + "path": "/HAL-REL-PLACEHOLDER-PB-PUBLISH-CONTRACTS", + "headers": { + "Content-Type": "application/json", + "Accept": "application/hal+json" + }, + "body": { + "pacticipantName": "Foo", + "pacticipantVersionNumber": "5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", + "branch": "main", + "tags": [ + "dev" + ], + "buildUrl": "http://build", + "contracts": [ + { + "consumerName": "Foo", + "providerName": "Bar", + "specification": "pact", + "contentType": "application/json", + "content": "eyJjb25zdW1lciI6eyJuYW1lIjoiRm9vIn0sInByb3ZpZGVyIjp7Im5hbWUiOiJCYXIifSwiaW50ZXJhY3Rpb25zIjpbeyJkZXNjcmlwdGlvbiI6ImFuIGV4YW1wbGUgcmVxdWVzdCIsInByb3ZpZGVyU3RhdGUiOiJhIHByb3ZpZGVyIHN0YXRlIiwicmVxdWVzdCI6eyJtZXRob2QiOiJHRVQiLCJwYXRoIjoiLyIsImhlYWRlcnMiOnt9fSwicmVzcG9uc2UiOnsic3RhdHVzIjoyMDAsImhlYWRlcnMiOnsiQ29udGVudC1UeXBlIjoiYXBwbGljYXRpb24vaGFsK2pzb24ifX19XSwibWV0YWRhdGEiOnsicGFjdFNwZWNpZmljYXRpb24iOnsidmVyc2lvbiI6IjIuMC4wIn19fQ==", + "writeMode": "overwrite" + } + ] + } +} +``` +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", + "buildUrl": "http://build" + } + }, + "logs": [ + { + "level": "info", + "message": "some message" + } + ], + "_links": { + "pb:pacticipant-version-tags": [ + { + "name": "dev" + } + ], + "pb:contracts": [ + { + "href": "http://some-pact" + } + ] + } + } +} +``` 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, with ```json diff --git a/lib/pact_broker/client/cli/broker.rb b/lib/pact_broker/client/cli/broker.rb index f02c326b..9c949434 100644 --- a/lib/pact_broker/client/cli/broker.rb +++ b/lib/pact_broker/client/cli/broker.rb @@ -51,6 +51,7 @@ def can_i_deploy(*ignored_but_necessary) method_option :tag_with_git_branch, aliases: "-g", type: :boolean, default: false, required: false, desc: "Tag consumer version with the name of the current git branch. Default: false" method_option :build_url, desc: "The build URL that created the pact" method_option :merge, type: :boolean, default: false, require: false, desc: "If a pact already exists for this consumer version and provider, merge the contents. Useful when running Pact tests concurrently on different build nodes." + method_option :output, aliases: "-o", desc: "json or text", default: 'text' shared_authentication_options def publish(*pact_files) @@ -284,6 +285,7 @@ def publish_pacts pact_files options.broker_base_url, file_list(pact_files), consumer_version_params, + { merge: options[:merge], output: options.output }.compact, pact_broker_client_options.merge(write_options) ) end @@ -381,7 +383,6 @@ def parse_webhook_options(webhook_url) provider: options.provider, events: events } - end def run_webhook_commands webhook_url diff --git a/lib/pact_broker/client/hal/http_client.rb b/lib/pact_broker/client/hal/http_client.rb index 8d8922b0..ecfa59c1 100644 --- a/lib/pact_broker/client/hal/http_client.rb +++ b/lib/pact_broker/client/hal/http_client.rb @@ -54,7 +54,7 @@ def create_request uri, http_method, body = nil, headers = {} end def perform_request request, uri - response = until_truthy_or_max_times(times: 5, sleep: 5, condition: ->(resp) { resp.code.to_i < 500 }) do + response = until_truthy_or_max_times(condition: ->(resp) { resp.code.to_i < 500 }) do http = Net::HTTP.new(uri.host, uri.port, :ENV) http.set_debug_output(output_stream) if verbose http.use_ssl = (uri.scheme == 'https') @@ -71,7 +71,7 @@ def perform_request request, uri end def until_truthy_or_max_times options = {} - max_tries = options.fetch(:times, 3) + max_tries = options.fetch(:times, default_max_tries) tries = 0 sleep_interval = options.fetch(:sleep, 5) sleep(sleep_interval) if options[:sleep_first] @@ -97,6 +97,10 @@ def until_truthy_or_max_times options = {} end end + def default_max_tries + 5 + end + def sleep seconds Kernel.sleep seconds end diff --git a/lib/pact_broker/client/pacts.rb b/lib/pact_broker/client/pacts.rb index 9aa3236f..2486c67f 100644 --- a/lib/pact_broker/client/pacts.rb +++ b/lib/pact_broker/client/pacts.rb @@ -4,7 +4,6 @@ module PactBroker module Client class Pacts < BaseClient - def publish options consumer_version = options[:consumer_version] pact_hash = options[:pact_hash] diff --git a/lib/pact_broker/client/publish_pacts.rb b/lib/pact_broker/client/publish_pacts.rb index cbd4b5d1..220db76e 100644 --- a/lib/pact_broker/client/publish_pacts.rb +++ b/lib/pact_broker/client/publish_pacts.rb @@ -9,18 +9,19 @@ class PublishPacts using PactBroker::Client::HashRefinements include HalClientMethods - def self.call(pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options={}) - new(pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options).call + def self.call(pact_broker_base_url, pact_file_paths, consumer_version_params, options, pact_broker_client_options={}) + new(pact_broker_base_url, pact_file_paths, consumer_version_params, options, pact_broker_client_options).call end - def initialize pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options={} + def initialize pact_broker_base_url, pact_file_paths, consumer_version_params, options, pact_broker_client_options={} @pact_broker_base_url = pact_broker_base_url @pact_file_paths = pact_file_paths + @consumer_version_params = consumer_version_params @consumer_version_number = consumer_version_params[:number].respond_to?(:strip) ? consumer_version_params[:number].strip : consumer_version_params[:number] - @branch = consumer_version_params[:branch] - @build_url = consumer_version_params[:build_url] + @branch = consumer_version_params[:branch]&.strip + @build_url = consumer_version_params[:build_url]&.strip @tags = consumer_version_params[:tags] ? consumer_version_params[:tags].collect{ |tag| tag.respond_to?(:strip) ? tag.strip : tag } : [] - @version_required = consumer_version_params[:version_required] + @options = options @pact_broker_client_options = pact_broker_client_options end @@ -30,18 +31,18 @@ def call publish_pacts PactBroker::Client::CommandResult.new(success?, message) else - PublishPactsTheOldWay.call(pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options) + PublishPactsTheOldWay.call(pact_broker_base_url, pact_file_paths, consumer_version_params, options, pact_broker_client_options) end end private - attr_reader :pact_broker_base_url, :pact_file_paths, :consumer_version_number, :branch, :tags, :build_url, :pact_broker_client_options, :response_entities + attr_reader :pact_broker_base_url, :pact_file_paths, :consumer_version_params, :consumer_version_number, :branch, :tags, :build_url, :options, :pact_broker_client_options, :response_entities def request_body_for(consumer_name) { pacticipantName: consumer_name, - versionNumber: consumer_version_number, + pacticipantVersionNumber: consumer_version_number, tags: tags, branch: branch, buildUrl: build_url, @@ -60,10 +61,22 @@ def success? end def message + if options[:output] == "json" + response_entities.collect(&:response).collect(&:body).to_a.to_json + else + text_message + end + end + + def text_message response_entities.flat_map do | response_entity | if response_entity.success? - response_entity.logs.collect do | log | - colorized_message(log) + if response_entity.logs + response_entity.logs.collect do | log | + colorized_message(log) + end + else + "Successfully published pacts" end else ::Term::ANSIColor.red(response_entity.response.body.to_s) @@ -90,18 +103,16 @@ def color_for_level(level) end def contracts_for(consumer_name) - pact_files_for(consumer_name).collect do | pact_file | - end - pact_files_for(consumer_name).group_by(&:pact_name).values.collect do | pact_files | $stderr.puts "Merging #{pact_files.collect(&:path).join(", ")}" if pact_files.size > 1 pact_hash = PactHash[merge_contents(pact_files)] { - role: "consumer", + consumerName: pact_hash.consumer_name, providerName: pact_hash.provider_name, specification: "pact", contentType: "application/json", - content: Base64.strict_encode64(pact_hash.to_json) + content: Base64.strict_encode64(pact_hash.to_json), + writeMode: write_mode } end end @@ -122,6 +133,10 @@ def consumer_names pact_files.collect(&:consumer_name).uniq end + def write_mode + options[:merge] ? "merge" : "overwrite" + end + def validate raise PactBroker::Client::Error.new("Please specify the consumer_version_number") unless (consumer_version_number && consumer_version_number.to_s.strip.size > 0) raise PactBroker::Client::Error.new("Please specify the pact_broker_base_url") unless (pact_broker_base_url && pact_broker_base_url.to_s.strip.size > 0) diff --git a/lib/pact_broker/client/publish_pacts_the_old_way.rb b/lib/pact_broker/client/publish_pacts_the_old_way.rb index 306610f5..d3228872 100644 --- a/lib/pact_broker/client/publish_pacts_the_old_way.rb +++ b/lib/pact_broker/client/publish_pacts_the_old_way.rb @@ -14,11 +14,11 @@ class PublishPactsTheOldWay using PactBroker::Client::HashRefinements include HalClientMethods - def self.call(pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options={}) - new(pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options).call + def self.call(pact_broker_base_url, pact_file_paths, consumer_version_params, options, pact_broker_client_options={}) + new(pact_broker_base_url, pact_file_paths, consumer_version_params, options, pact_broker_client_options).call end - def initialize pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options={} + def initialize pact_broker_base_url, pact_file_paths, consumer_version_params, options, pact_broker_client_options={} @pact_broker_base_url = pact_broker_base_url @pact_file_paths = pact_file_paths @consumer_version_number = consumer_version_params[:number].respond_to?(:strip) ? consumer_version_params[:number].strip : consumer_version_params[:number] diff --git a/lib/pact_broker/client/tasks/publication_task.rb b/lib/pact_broker/client/tasks/publication_task.rb index e7d7f2c0..84f36554 100644 --- a/lib/pact_broker/client/tasks/publication_task.rb +++ b/lib/pact_broker/client/tasks/publication_task.rb @@ -58,13 +58,14 @@ def rake_task &block desc "Publish pacts to pact broker" task task_name do block.call(self) - require 'pact_broker/client/publish_pacts_the_old_way' + require 'pact_broker/client/publish_pacts' pact_broker_client_options = { write: write_method, token: pact_broker_token } pact_broker_client_options[:basic_auth] = pact_broker_basic_auth if pact_broker_basic_auth && pact_broker_basic_auth.any? pact_broker_client_options.compact! consumer_version_params = { number: consumer_version, branch: the_branch, build_url: build_url, tags: all_tags, version_required: version_required }.compact - success = PactBroker::Client::PublishPactsTheOldWay.new(pact_broker_base_url, FileList[pattern], consumer_version_params, pact_broker_client_options).call - raise "One or more pacts failed to be published" unless success + result = PactBroker::Client::PublishPacts.new(pact_broker_base_url, FileList[pattern], consumer_version_params, {}, pact_broker_client_options).call + $stdout.puts result.message + raise "One or more pacts failed to be published" unless result.success end end end @@ -86,7 +87,6 @@ def the_branch branch end end - end end end diff --git a/script/publish-pact.sh b/script/publish-pact.sh index 2eda2aab..c4f784ef 100755 --- a/script/publish-pact.sh +++ b/script/publish-pact.sh @@ -1,8 +1,20 @@ -bundle exec bin/pact-broker publish spec/pacts/pact_broker_client-pact_broker.json \ - --consumer-app-version 1.2.7 \ +# bundle exec bin/pact-broker create-or-update-webhook http://localhost:9393 \ +# --uuid d40f38c3-aaa3-47f5-9161-95c07bc16b14 \ +# --request POST \ +# --description "foo webhook" \ +# --contract-published + +bundle exec bin/pact-broker create-or-update-webhook http://localhost:9393 \ + --uuid d40f38c3-aaa3-47f5-9161-95c07bc16555 \ + --provider Bar \ + --request POST \ + --contract-published + +bundle exec bin/pact-broker publish spec/pacts/pact_broker_client-pact_broker.json spec/pacts/foo-bar.json \ + --consumer-app-version 1.2.12 \ --broker-base-url http://localhost:9292 \ --broker-username localhost --broker-password localhost \ --auto-detect-version-properties \ --build-url http://mybuild \ - --branch master + --branch master --tag foo5 diff --git a/spec/fixtures/foo-bar.json b/spec/fixtures/foo-bar.json new file mode 100644 index 00000000..d9679455 --- /dev/null +++ b/spec/fixtures/foo-bar.json @@ -0,0 +1,31 @@ +{ + "consumer": { + "name": "Foo" + }, + "provider": { + "name": "Bar" + }, + "interactions": [ + { + "description": "an example request", + "providerState": "a provider state", + "request": { + "method": "GET", + "path": "/", + "headers": { + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/hal+json" + } + } + } + ], + "metadata": { + "pactSpecification": { + "version": "2.0.0" + } + } +} diff --git a/spec/lib/pact_broker/client/cli/broker_publish_spec.rb b/spec/lib/pact_broker/client/cli/broker_publish_spec.rb index 43617a78..2adcc3e6 100644 --- a/spec/lib/pact_broker/client/cli/broker_publish_spec.rb +++ b/spec/lib/pact_broker/client/cli/broker_publish_spec.rb @@ -1,16 +1,19 @@ require 'pact_broker/client/cli/broker' -require 'pact_broker/client/publish_pacts_the_old_way' +require 'pact_broker/client/publish_pacts' require 'pact_broker/client/git' module PactBroker::Client::CLI describe Broker do describe ".broker" do before do - allow(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).and_return(true) + allow(PactBroker::Client::PublishPacts).to receive(:call).and_return(result) allow(PactBroker::Client::Git).to receive(:branch).and_return("bar") subject.options = OpenStruct.new(minimum_valid_options) + allow($stdout).to receive(:puts) end + let(:success) { true } + let(:result) { instance_double(PactBroker::Client::CommandResult, success: success, message: "message")} let(:file_list) { ["spec/support/cli_test_pacts/foo.json"] } let(:minimum_valid_options) do { @@ -23,12 +26,13 @@ module PactBroker::Client::CLI let(:invoke_broker) { subject.publish(*file_list) } context "with minimum valid options" do - it "invokes the PublishPactsTheOldWay command" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).with( + it "invokes the PublishPacts command" do + expect(PactBroker::Client::PublishPacts).to receive(:call).with( "http://pact-broker", ["spec/support/cli_test_pacts/foo.json"], { number: "1.2.3", tags: [], version_required: false }, - { } + {}, + {} ) invoke_broker end @@ -37,11 +41,12 @@ module PactBroker::Client::CLI context "with a file pattern specified" do let(:file_list) { ["spec/support/cli_test_pacts/*.json"] } - it "invokes the PublishPactsTheOldWay command with the expanded list of files" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).with( + it "invokes the PublishPacts command with the expanded list of files" do + expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, ["spec/support/cli_test_pacts/bar.json", "spec/support/cli_test_pacts/foo.json"], anything, + anything, anything ) invoke_broker @@ -51,11 +56,12 @@ module PactBroker::Client::CLI context "with a pact directory specified" do let(:file_list) { ["spec/support/cli_test_pacts"] } - it "invokes the PublishPactsTheOldWay command with the list of files in the directory" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).with( + it "invokes the PublishPacts command with the list of files in the directory" do + expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, ["spec/support/cli_test_pacts/bar.json", "spec/support/cli_test_pacts/foo.json"], anything, + anything, anything ) invoke_broker @@ -65,11 +71,12 @@ module PactBroker::Client::CLI context "with a windows directory specified" do let(:file_list) { ['spec\\support\cli_test_pacts'] } - it "invokes the PublishPactsTheOldWay command with the list of files in the directory" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).with( + it "invokes the PublishPacts command with the list of files in the directory" do + expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, ["spec/support/cli_test_pacts/bar.json", "spec/support/cli_test_pacts/foo.json"], anything, + anything, anything ) invoke_broker @@ -82,10 +89,11 @@ module PactBroker::Client::CLI end it "passes in the tag" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).with( + expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, anything, hash_including(tags: ['foo']), + anything, anything ) invoke_broker @@ -105,10 +113,11 @@ module PactBroker::Client::CLI end it "adds it to the list of tags when publishing" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).with( + expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, anything, hash_including(tags: ['foo', 'bar']), + anything, anything ) invoke_broker @@ -123,10 +132,11 @@ module PactBroker::Client::CLI end it "passes in the branch option" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).with( + expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, anything, hash_including(branch: "main", version_required: true), + anything, anything ) invoke_broker @@ -147,10 +157,11 @@ module PactBroker::Client::CLI end it "passes in the auto detected branch option with version_required: false" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).with( + expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, anything, hash_including(branch: "bar", version_required: false), + anything, anything ) invoke_broker @@ -172,10 +183,11 @@ module PactBroker::Client::CLI end it "passes in the auto detected branch option with version_required: true" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).with( + expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, anything, hash_including(branch: "bar", version_required: true), + anything, anything ) invoke_broker @@ -189,10 +201,11 @@ module PactBroker::Client::CLI end it "uses the specified branch" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).with( + expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, anything, hash_including(branch: "specified-branch", version_required: true), + anything, anything ) invoke_broker @@ -208,10 +221,11 @@ module PactBroker::Client::CLI end it "passes in the branch option" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).with( + expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, anything, hash_including(build_url: "http://ci"), + anything, anything ) invoke_broker @@ -226,7 +240,8 @@ module PactBroker::Client::CLI end it "passes in the basic auth options" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).with( + expect(PactBroker::Client::PublishPacts).to receive(:call).with( + anything, anything, anything, anything, @@ -258,7 +273,7 @@ module PactBroker::Client::CLI context "when an error is raised publishing" do before do - allow(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).and_raise(PactBroker::Client::Error.new('foo')) + allow(PactBroker::Client::PublishPacts).to receive(:call).and_raise(PactBroker::Client::Error.new('foo')) end it "raises a PactPublicationError" do @@ -267,12 +282,10 @@ module PactBroker::Client::CLI end context "when the publish command is not successful" do - before do - allow(PactBroker::Client::PublishPactsTheOldWay).to receive(:call).and_return(false) - end + let(:success) { false } - it "raises a PactPublicationError" do - expect { invoke_broker }.to raise_error PactPublicationError + it "raises a SystemExit" do + expect { invoke_broker }.to raise_error SystemExit end end end diff --git a/spec/lib/pact_broker/client/publish_pacts_the_old_way_spec.rb b/spec/lib/pact_broker/client/publish_pacts_the_old_way_spec.rb index 9b6ccd9a..b8444a0d 100644 --- a/spec/lib/pact_broker/client/publish_pacts_the_old_way_spec.rb +++ b/spec/lib/pact_broker/client/publish_pacts_the_old_way_spec.rb @@ -63,8 +63,9 @@ module Client let(:index_entry_point) { instance_double("PactBroker::Client::Hal::EntryPoint", :get! => index_resource )} let(:index_resource) { instance_double("PactBroker::Client::Hal::Entity", can?: can_create_version ) } let(:can_create_version) { false } + let(:options) { {} } - subject { PublishPactsTheOldWay.new(pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options) } + subject { PublishPactsTheOldWay.new(pact_broker_base_url, pact_file_paths, consumer_version_params, options, pact_broker_client_options) } describe "call" do it "creates a PactBroker Client" do diff --git a/spec/lib/pact_broker/client/tasks/publication_task_spec.rb b/spec/lib/pact_broker/client/tasks/publication_task_spec.rb index 2a2432df..6908be3f 100644 --- a/spec/lib/pact_broker/client/tasks/publication_task_spec.rb +++ b/spec/lib/pact_broker/client/tasks/publication_task_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' require 'pact_broker/client/tasks/publication_task' -require 'pact_broker/client/publish_pacts_the_old_way' +require 'pact_broker/client/publish_pacts' +require 'pact_broker/client/command_result' module PactBroker::Client describe PublicationTask do @@ -9,13 +10,16 @@ module PactBroker::Client @consumer_version = "1.2.3" end - let(:publish_pacts_the_old_way) { instance_double("PactBroker::ClientSupport::PublishPactsTheOldWay", call: true)} + let(:publish_pacts) { instance_double("PactBroker::ClientSupport::PublishPacts", call: result)} let(:pact_file_list) { ['spec/pact/consumer-provider.json'] } + let(:success) { true } + let(:result) { instance_double(PactBroker::Client::CommandResult, success: success, message: "message")} before do - allow(PactBroker::Client::PublishPactsTheOldWay).to receive(:new).and_return(publish_pacts_the_old_way) + allow(PactBroker::Client::PublishPacts).to receive(:new).and_return(publish_pacts) allow(FileList).to receive(:[]).with(pattern).and_return(pact_file_list) allow(PactBroker::Client::Git).to receive(:branch).and_return('foo') + allow($stdout).to receive(:puts) end let(:pattern) { "spec/pacts/*.json" } @@ -28,16 +32,17 @@ module PactBroker::Client end context "when pacts are succesfully published" do - it "invokes PublishPactsTheOldWay with the default values" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:new).with('http://pact-broker', pact_file_list, { number: '1.2.3', branch: "foo", tags: [], version_required: false}, {}).and_return(publish_pacts_the_old_way) - expect(publish_pacts_the_old_way).to receive(:call).and_return(true) + it "invokes PublishPacts with the default values" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with('http://pact-broker', pact_file_list, { number: '1.2.3', branch: "foo", tags: [], version_required: false}, {}, {}).and_return(publish_pacts) + expect(publish_pacts).to receive(:call).and_return(result) Rake::Task['pact:publish'].execute end end context "when a pact fails to be published" do + let(:success) { false } + it "raises an error" do - expect(publish_pacts_the_old_way).to receive(:call).and_return(false) expect { Rake::Task['pact:publish'].execute }.to raise_error("One or more pacts failed to be published") end end @@ -51,9 +56,9 @@ module PactBroker::Client end end - it "invokes PublishPactsTheOldWay with the write method set" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:new).with('http://pact-broker', pact_file_list, { number: "1.2.3", branch: "foo", tags: [], version_required: false }, {write: :merge}).and_return(publish_pacts_the_old_way) - expect(publish_pacts_the_old_way).to receive(:call).and_return(true) + it "invokes PublishPacts with the write method set" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with('http://pact-broker', pact_file_list, { number: "1.2.3", branch: "foo", tags: [], version_required: false }, {}, {write: :merge}).and_return(publish_pacts) + expect(publish_pacts).to receive(:call).and_return(result) Rake::Task['pact:publish:merge'].execute end end @@ -73,8 +78,8 @@ module PactBroker::Client Rake::Task['pact:publish:git_branch'].execute end - it "invokes PublishPactsTheOldWay with the git branch name as a tag" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:new).with(anything, anything, hash_including(tags: ['bar', 'foo']), anything).and_return(publish_pacts_the_old_way) + it "invokes PublishPacts with the git branch name as a tag" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with(anything, anything, hash_including(tags: ['bar', 'foo']), anything, anything).and_return(publish_pacts) Rake::Task['pact:publish:git_branch'].execute end end @@ -92,8 +97,8 @@ module PactBroker::Client Rake::Task['pact:publish:git_branch_auto_detect_true'].execute end - it "invokes PublishPactsTheOldWay with the branch name" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:new).with(anything, anything, hash_including(branch: "foo"), anything).and_return(publish_pacts_the_old_way) + it "invokes PublishPacts with the branch name" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with(anything, anything, hash_including(branch: "foo"), anything, anything).and_return(publish_pacts) Rake::Task['pact:publish:git_branch_auto_detect_true'].execute end end @@ -112,8 +117,8 @@ module PactBroker::Client Rake::Task['pact:publish:git_branch_auto_detect_true_with_branch'].execute end - it "invokes PublishPactsTheOldWay with the specified branch name" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:new).with(anything, anything, hash_including(branch: "main"), anything).and_return(publish_pacts_the_old_way) + it "invokes PublishPacts with the specified branch name" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with(anything, anything, hash_including(branch: "main"), anything, anything).and_return(publish_pacts) Rake::Task['pact:publish:git_branch_auto_detect_true_with_branch'].execute end end @@ -131,8 +136,8 @@ module PactBroker::Client Rake::Task['pact:publish:git_branch_auto_detect_false'].execute end - it "invokes PublishPactsTheOldWay without the branch name" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:new).with(anything, anything, hash_not_including(branch: "foo"), anything).and_return(publish_pacts_the_old_way) + it "invokes PublishPacts without the branch name" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with(anything, anything, hash_not_including(branch: "foo"), anything, anything).and_return(publish_pacts) Rake::Task['pact:publish:git_branch_auto_detect_false'].execute end end @@ -149,8 +154,8 @@ module PactBroker::Client Rake::Task['pact:publish:git_branch_auto_detect_default'].execute end - it "invokes PublishPactsTheOldWay with the branch name" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:new).with(anything, anything, hash_including(branch: "foo"), anything).and_return(publish_pacts_the_old_way) + it "invokes PublishPacts with the branch name" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with(anything, anything, hash_including(branch: "foo"),anything, anything).and_return(publish_pacts) Rake::Task['pact:publish:git_branch_auto_detect_default'].execute end end @@ -175,14 +180,15 @@ module PactBroker::Client let(:pattern) { @pattern } - it "invokes PublishPactsTheOldWay with the customised values" do - expect(PactBroker::Client::PublishPactsTheOldWay).to receive(:new).with( + it "invokes PublishPacts with the customised values" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with( @pact_broker_base_url, pact_file_list, { number: "1.2.3", tags: [@tag], branch: "foo", version_required: false}, + {}, { basic_auth: @pact_broker_basic_auth, token: @pact_broker_token } ) - expect(publish_pacts_the_old_way).to receive(:call).and_return(true) + expect(publish_pacts).to receive(:call).and_return(result) Rake::Task['pact:publish:custom'].execute end end diff --git a/spec/pacts/pact_broker_client-pact_broker.json b/spec/pacts/pact_broker_client-pact_broker.json index 42011183..056ed4b8 100644 --- a/spec/pacts/pact_broker_client-pact_broker.json +++ b/spec/pacts/pact_broker_client-pact_broker.json @@ -1550,6 +1550,112 @@ } } }, + { + "description": "a request for the index resource", + "providerState": "the pb:publish-contracts relations exists in the index resource", + "request": { + "method": "GET", + "path": "/", + "headers": { + "Accept": "application/hal+json" + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:publish-contracts": { + "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-PB-PUBLISH-CONTRACTS" + } + } + }, + "matchingRules": { + "$.body._links.pb:publish-contracts.href": { + "match": "regex", + "regex": "http:\\/\\/.*" + } + } + } + }, + { + "description": "a request to publish contracts", + "request": { + "method": "POST", + "path": "/HAL-REL-PLACEHOLDER-PB-PUBLISH-CONTRACTS", + "headers": { + "Content-Type": "application/json", + "Accept": "application/hal+json" + }, + "body": { + "pacticipantName": "Foo", + "pacticipantVersionNumber": "5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", + "branch": "main", + "tags": [ + "dev" + ], + "buildUrl": "http://build", + "contracts": [ + { + "consumerName": "Foo", + "providerName": "Bar", + "specification": "pact", + "contentType": "application/json", + "content": "eyJjb25zdW1lciI6eyJuYW1lIjoiRm9vIn0sInByb3ZpZGVyIjp7Im5hbWUiOiJCYXIifSwiaW50ZXJhY3Rpb25zIjpbeyJkZXNjcmlwdGlvbiI6ImFuIGV4YW1wbGUgcmVxdWVzdCIsInByb3ZpZGVyU3RhdGUiOiJhIHByb3ZpZGVyIHN0YXRlIiwicmVxdWVzdCI6eyJtZXRob2QiOiJHRVQiLCJwYXRoIjoiLyIsImhlYWRlcnMiOnt9fSwicmVzcG9uc2UiOnsic3RhdHVzIjoyMDAsImhlYWRlcnMiOnsiQ29udGVudC1UeXBlIjoiYXBwbGljYXRpb24vaGFsK2pzb24ifX19XSwibWV0YWRhdGEiOnsicGFjdFNwZWNpZmljYXRpb24iOnsidmVyc2lvbiI6IjIuMC4wIn19fQ==", + "writeMode": "overwrite" + } + ] + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_embedded": { + "pacticipant": { + "name": "Foo" + }, + "version": { + "number": "5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", + "buildUrl": "http://build" + } + }, + "logs": [ + { + "level": "info", + "message": "some message" + } + ], + "_links": { + "pb:pacticipant-version-tags": [ + { + "name": "dev" + } + ], + "pb:contracts": [ + { + "href": "http://some-pact" + } + ] + } + }, + "matchingRules": { + "$.body.logs": { + "min": 1 + }, + "$.body.logs[*].*": { + "match": "type" + }, + "$.body._links.pb:contracts[0].href": { + "match": "type" + } + } + } + }, { "description": "a request for the index resource", "providerState": "the pb:pacticipant-version and pb:environments relations exist in the index resource", diff --git a/spec/service_providers/publish_pacts_spec.rb b/spec/service_providers/publish_pacts_spec.rb new file mode 100644 index 00000000..f8765013 --- /dev/null +++ b/spec/service_providers/publish_pacts_spec.rb @@ -0,0 +1,115 @@ +require 'pact_broker/client/publish_pacts' +require 'service_providers/pact_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 + include_context "pact broker" + include PactBrokerPactHelperMethods + + 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) { {} } + 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, + writeMode: "overwrite" + } + ] + } + end + + subject { PactBroker::Client::PublishPacts.call(broker_base_url, pact_file_paths, consumer_version_params, options, pact_broker_client_options) } + + def mock_index + pact_broker + .given("the pb:publish-contracts relations exists in the index resource") + .upon_receiving("a request for the index resource") + .with( + method: "GET", + path: '/', + headers: get_request_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:publish-contracts' => { + href: placeholder_url_term("pb:publish-contracts") + } + } + } + ) + end + + def mock_contract_publication + pact_broker + .upon_receiving("a request to publish contracts") + .with( + method: "POST", + path: '/HAL-REL-PLACEHOLDER-PB-PUBLISH-CONTRACTS', + 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, + buildUrl: build_url + } + }, + logs: Pact.each_like(level: "info", message: "some message"), + _links: { + :'pb:pacticipant-version-tags' => [{ name: "dev"} ], + :'pb:contracts' => [{ href: Pact.like("http://some-pact") }] + } + } + ) + end + + context "with valid params" do + before do + mock_index + mock_contract_publication + end + + it "returns a success result" do + expect(subject.success).to be true + expect(subject.message).to include "some message" + end + end +end