From 903a92272c189d09e90b8668db953781e2670fdb Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Mon, 4 Sep 2023 11:18:12 +1000 Subject: [PATCH] feat: publish provider contracts using the new all-in-one endpoint --- .../markdown/Pact Broker Client - Pactflow.md | 89 +++++++++++- doc/pacts/markdown/README.md | 2 +- .../client/cli/provider_contract_commands.rb | 3 +- .../client/provider_contracts/publish.rb | 108 +++++++-------- .../provider_contracts/publish_the_old_way.rb | 104 ++++++++++++++ spec/pacts/pact_broker_client-pactflow.json | 95 ++++++++++++- spec/service_providers/pact_helper.rb | 18 ++- ...pactflow_publish_provider_contract_spec.rb | 93 ++++++------- ...lish_provider_contract_the_old_way_spec.rb | 129 ++++++++++++++++++ 9 files changed, 524 insertions(+), 117 deletions(-) create mode 100644 lib/pactflow/client/provider_contracts/publish_the_old_way.rb create mode 100644 spec/service_providers/pactflow_publish_provider_contract_the_old_way_spec.rb diff --git a/doc/pacts/markdown/Pact Broker Client - Pactflow.md b/doc/pacts/markdown/Pact Broker Client - Pactflow.md index e092e37..fb299b7 100644 --- a/doc/pacts/markdown/Pact Broker Client - Pactflow.md +++ b/doc/pacts/markdown/Pact Broker Client - Pactflow.md @@ -4,12 +4,16 @@ * [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 @@ -45,6 +49,33 @@ PactFlow will respond with: } } ``` + +Given **the pb:publish-provider-contract relation 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" + } +} +``` +PactFlow will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pf:publish-provider-contract": { + "href": "http://localhost:1235/HAL-REL-PLACEHOLDER-PF-PUBLISH-PROVIDER-CONTRACT-{provider}" + } + } + } +} +``` Upon receiving **a request to create a provider contract** from Pact Broker Client, with ```json @@ -56,7 +87,7 @@ Upon receiving **a request to create a provider contract** from Pact Broker Clie "Accept": "application/hal+json" }, "body": { - "content": "LS0tCjpzb21lOiBjb250cmFjdAo=", + "content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", "contractType": "oas", "contentType": "application/yaml", "verificationResults": { @@ -90,7 +121,7 @@ Given **there is a pf:ui href in the response**, upon receiving **a request to c "Accept": "application/hal+json" }, "body": { - "content": "LS0tCjpzb21lOiBjb250cmFjdAo=", + "content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", "contractType": "oas", "contentType": "application/yaml", "verificationResults": { @@ -104,7 +135,7 @@ Given **there is a pf:ui href in the response**, upon receiving **a request to c } } ``` -Pactflow will respond with: +PactFlow will respond with: ```json { "status": 201, @@ -114,7 +145,7 @@ Pactflow will respond with: "body": { "_links": { "pf:ui": { - "href": "http://localhost:1235/contracts/bi-directional/provider/Bar/version/1/provider-contract" + "href": "some-url" } } } @@ -171,3 +202,53 @@ PactFlow will respond with: } } ``` + +Upon receiving **a request to publish a provider contract** from Pact Broker Client, with +```json +{ + "method": "post", + "path": "/HAL-REL-PLACEHOLDER-PF-PUBLISH-PROVIDER-CONTRACT-Bar", + "headers": { + "Content-Type": "application/json", + "Accept": "application/hal+json" + }, + "body": { + "pacticipantVersionNumber": "1", + "tags": [ + "dev" + ], + "branch": "main", + "buildUrl": "http://build", + "contract": { + "content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", + "contentType": "application/yaml", + "specification": "oas", + "verificationResults": { + "success": true, + "content": "c29tZSByZXN1bHRz", + "contentType": "text/plain", + "format": "text", + "verifier": "my custom tool", + "verifierVersion": "1.0" + } + } + } +} +``` +PactFlow will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "notices": [ + { + "text": "some notice", + "type": "info" + } + ] + } +} +``` diff --git a/doc/pacts/markdown/README.md b/doc/pacts/markdown/README.md index 5ba4433..4c45249 100644 --- a/doc/pacts/markdown/README.md +++ b/doc/pacts/markdown/README.md @@ -1,4 +1,4 @@ ### Pacts for Pact Broker Client * [Pact Broker](Pact%20Broker%20Client%20-%20Pact%20Broker.md) -* [PactFlow](Pact%20Broker%20Client%20-%20Pactflow.md) +* [PactFlow](Pact%20Broker%20Client%20-%20PactFlow.md) diff --git a/lib/pactflow/client/cli/provider_contract_commands.rb b/lib/pactflow/client/cli/provider_contract_commands.rb index 66e8256..aa666bb 100644 --- a/lib/pactflow/client/cli/provider_contract_commands.rb +++ b/lib/pactflow/client/cli/provider_contract_commands.rb @@ -26,7 +26,7 @@ def self.included(thor) method_option :verification_results_format, desc: "The format of the verification output eg. junit, text" method_option :verifier, desc: "The tool used to verify the provider contract" method_option :verifier_version, desc: "The version of the tool used to verify the provider contract" - #method_option :build_url, desc: "The build URL that created the pact" + method_option :build_url, desc: "The build URL that created the provider contract" output_option_json_or_text shared_authentication_options @@ -70,6 +70,7 @@ def publish_provider_contract_command_params(provider_contract_path) provider_version_number: options.provider_app_version.strip, branch_name: options.branch && options.branch.strip, tags: (options.tag && options.tag.collect(&:strip)) || [], + build_url: options.build_url, contract: { content: File.read(provider_contract_path), content_type: options.content_type, diff --git a/lib/pactflow/client/provider_contracts/publish.rb b/lib/pactflow/client/provider_contracts/publish.rb index dfb5acf..53a8297 100644 --- a/lib/pactflow/client/provider_contracts/publish.rb +++ b/lib/pactflow/client/provider_contracts/publish.rb @@ -1,13 +1,14 @@ -require "pact_broker/client/base_command" -require "pact_broker/client/versions/create" -require 'pact_broker/client/colorize_notices' require "base64" +require "pact_broker/client/base_command" +require "pact_broker/client/colorize_notices" + module Pactflow module Client module ProviderContracts class Publish < PactBroker::Client::BaseCommand - attr_reader :branch_name, :tags, :provider_name, :provider_version_number, :contract, :verification_results + PUBLISH_RELATION = "pf:publish-provider-contract" + def initialize(params, options, pact_broker_client_options) super @@ -15,62 +16,30 @@ def initialize(params, options, pact_broker_client_options) @provider_version_number = params[:provider_version_number] @branch_name = params[:branch_name] @tags = params[:tags] || [] + @build_url = params[:build_url] @contract = params[:contract] @verification_results = params[:verification_results] end private - def do_call - create_branch_version_and_tags - render_response(create_contract) - end + attr_reader :provider_name, :provider_version_number, :branch_name, :tags, :build_url, :contract, :verification_results - def render_response(res) - notices = [ - { type: 'success', text: "Successfully published provider contract for #{provider_name} version #{provider_version_number} to PactFlow"}, - ] - if res.body && res.body['_links'] && res.body['_links']['pf:ui']['href'] - notices.concat([{ text: "View the uploaded contract at #{res.body['_links']['pf:ui']['href']}" }]) + def do_call + if !force_use_old_api? && index_resource.can?(PUBLISH_RELATION) + publish_provider_contracts + PactBroker::Client::CommandResult.new(success?, message) + else + PublishPactsTheOldWay.call(pact_broker_base_url, pact_file_paths, consumer_version_params, options, pact_broker_client_options) end - notices.concat(next_steps) - PactBroker::Client::CommandResult.new(true, PactBroker::Client::ColorizeNotices.call(notices.collect do |n| - OpenStruct.new(n) - end).join("\n")) - end - - def next_steps - [ - { type: 'prompt', text: 'Next steps:' }, - { type: 'prompt', - text: ' * Check your application is safe to deploy - https://docs.pact.io/can_i_deploy' }, - { text: " pact-broker can-i-deploy --pacticipant #{provider_name} --version #{provider_version_number} --to-environment " }, - { type: 'prompt', - text: ' * Record deployment or release to specified environment (choose one) - https://docs.pact.io/go/record-deployment' }, - { text: " pact-broker record-deployment --pacticipant #{provider_name} --version #{provider_version_number} --environment " }, - { text: " pact-broker record-release --pacticipant #{provider_name} --version #{provider_version_number} --environment " } - ] end - def create_branch_version_and_tags - if branch_name || tags.any? - pacticipant_version_params = { - pacticipant_name: provider_name, - version_number: provider_version_number, - branch_name: branch_name, - tags: tags - } - result = PactBroker::Client::Versions::Create.call(pacticipant_version_params, options, pact_broker_client_options) - if !result.success - raise PactBroker::Client::Error.new(result.message) - end - end + def force_use_old_api? + ENV.fetch("PACT_BROKER_FEATURES", "").include?("publish_provider_contracts_using_old_api") end - def create_contract - contract_path = "#{pact_broker_base_url}/contracts/provider/{provider}/version/{version}" - entrypoint = create_entry_point(contract_path, pact_broker_client_options) - entrypoint.expand(provider: provider_name, version: provider_version_number).put!(contract_params).response + def publish_provider_contracts + @response_entity = index_resource._link(PUBLISH_RELATION).expand(provider: provider_name).post!(contract_params, headers: { "Accept" => "application/hal+json,application/problem+json" }) end def contract_params @@ -82,22 +51,53 @@ def contract_params verifier: verification_results[:verifier], verifierVersion: verification_results[:verifier_version] }.compact - - body_params = { + + contract_params = { content: encode_content(contract[:content]), - contractType: contract[:specification], - contentType: contract[:content_type], + specification: contract[:specification], + contentType: contract[:content_type] }.compact if verification_results_params.any? - body_params[:verificationResults] = verification_results_params + contract_params[:verificationResults] = verification_results_params end - body_params + + { + pacticipantVersionNumber: provider_version_number, + tags: tags, + branch: branch_name, + buildUrl: build_url, + contract: contract_params + } end def encode_content oas Base64.strict_encode64(oas) end + + def message + if options[:output] == "json" + @response_entity.response.raw_body + else + text_message + end + end + + def success? + @response_entity.success? + end + + def text_message + if success? + if @response_entity.notices + PactBroker::Client::ColorizeNotices.call(@response_entity.notices.collect{ |n| OpenStruct.new(n) } ) + else + "Successfully published provider contract for #{provider_name} version #{provider_version_number} to PactFlow" + end + else + ::Term::ANSIColor.red(@response_entity.response.raw_body) + end + end end end end diff --git a/lib/pactflow/client/provider_contracts/publish_the_old_way.rb b/lib/pactflow/client/provider_contracts/publish_the_old_way.rb new file mode 100644 index 0000000..44acaf3 --- /dev/null +++ b/lib/pactflow/client/provider_contracts/publish_the_old_way.rb @@ -0,0 +1,104 @@ +require "pact_broker/client/base_command" +require "pact_broker/client/versions/create" +require 'pact_broker/client/colorize_notices' +require "base64" + +module Pactflow + module Client + module ProviderContracts + class PublishTheOldWay < PactBroker::Client::BaseCommand + attr_reader :branch_name, :tags, :provider_name, :provider_version_number, :contract, :verification_results + + def initialize(params, options, pact_broker_client_options) + super + @provider_name = params[:provider_name] + @provider_version_number = params[:provider_version_number] + @branch_name = params[:branch_name] + @tags = params[:tags] || [] + @contract = params[:contract] + @verification_results = params[:verification_results] + end + + private + + def do_call + create_branch_version_and_tags + render_response(create_contract) + end + + def render_response(res) + notices = [ + { type: 'success', text: "Successfully published provider contract for #{provider_name} version #{provider_version_number} to PactFlow"}, + ] + if res.body && res.body['_links'] && res.body['_links']['pf:ui']['href'] + notices.concat([{ text: "View the uploaded contract at #{res.body['_links']['pf:ui']['href']}" }]) + end + notices.concat(next_steps) + PactBroker::Client::CommandResult.new(true, PactBroker::Client::ColorizeNotices.call(notices.collect do |n| + OpenStruct.new(n) + end).join("\n")) + end + + def next_steps + [ + { type: 'prompt', text: 'Next steps:' }, + { type: 'prompt', + text: ' * Check your application is safe to deploy - https://docs.pact.io/can_i_deploy' }, + { text: " pact-broker can-i-deploy --pacticipant #{provider_name} --version #{provider_version_number} --to-environment " }, + { type: 'prompt', + text: ' * Record deployment or release to specified environment (choose one) - https://docs.pact.io/go/record-deployment' }, + { text: " pact-broker record-deployment --pacticipant #{provider_name} --version #{provider_version_number} --environment " }, + { text: " pact-broker record-release --pacticipant #{provider_name} --version #{provider_version_number} --environment " } + ] + end + + def create_branch_version_and_tags + if branch_name || tags.any? + pacticipant_version_params = { + pacticipant_name: provider_name, + version_number: provider_version_number, + branch_name: branch_name, + tags: tags + } + result = PactBroker::Client::Versions::Create.call(pacticipant_version_params, options, pact_broker_client_options) + if !result.success + raise PactBroker::Client::Error.new(result.message) + end + end + end + + def create_contract + contract_path = "#{pact_broker_base_url}/contracts/provider/{provider}/version/{version}" + entrypoint = create_entry_point(contract_path, pact_broker_client_options) + entrypoint.expand(provider: provider_name, version: provider_version_number).put!(contract_params).response + end + + def contract_params + verification_results_params = { + success: verification_results[:success], + content: verification_results[:content] ? encode_content(verification_results[:content]) : nil, + contentType: verification_results[:content_type], + format: verification_results[:format], + verifier: verification_results[:verifier], + verifierVersion: verification_results[:verifier_version] + }.compact + + body_params = { + content: encode_content(contract[:content]), + contractType: contract[:specification], + contentType: contract[:content_type], + }.compact + + if verification_results_params.any? + body_params[:verificationResults] = verification_results_params + end + body_params + end + + def encode_content oas + Base64.strict_encode64(oas) + end + end + end + end +end diff --git a/spec/pacts/pact_broker_client-pactflow.json b/spec/pacts/pact_broker_client-pactflow.json index 8a7a970..45738d6 100644 --- a/spec/pacts/pact_broker_client-pactflow.json +++ b/spec/pacts/pact_broker_client-pactflow.json @@ -6,6 +6,90 @@ "name": "PactFlow" }, "interactions": [ + { + "description": "a request for the index resource", + "providerState": "the pb:publish-provider-contract relation 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": { + "pf:publish-provider-contract": { + "href": "http://localhost:1235/HAL-REL-PLACEHOLDER-PF-PUBLISH-PROVIDER-CONTRACT-{provider}" + } + } + }, + "matchingRules": { + "$.body._links.pf:publish-provider-contract.href": { + "match": "regex", + "regex": "http:\\/\\/.*{provider}" + } + } + } + }, + { + "description": "a request to publish a provider contract", + "request": { + "method": "post", + "path": "/HAL-REL-PLACEHOLDER-PF-PUBLISH-PROVIDER-CONTRACT-Bar", + "headers": { + "Content-Type": "application/json", + "Accept": "application/hal+json" + }, + "body": { + "pacticipantVersionNumber": "1", + "tags": [ + "dev" + ], + "branch": "main", + "buildUrl": "http://build", + "contract": { + "content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", + "contentType": "application/yaml", + "specification": "oas", + "verificationResults": { + "success": true, + "content": "c29tZSByZXN1bHRz", + "contentType": "text/plain", + "format": "text", + "verifier": "my custom tool", + "verifierVersion": "1.0" + } + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "notices": [ + { + "text": "some notice", + "type": "info" + } + ] + }, + "matchingRules": { + "$.body.notices": { + "min": 1 + }, + "$.body.notices[*].*": { + "match": "type" + } + } + } + }, { "description": "a request to create a provider contract", "request": { @@ -16,7 +100,7 @@ "Accept": "application/hal+json" }, "body": { - "content": "LS0tCjpzb21lOiBjb250cmFjdAo=", + "content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", "contractType": "oas", "contentType": "application/yaml", "verificationResults": { @@ -47,7 +131,7 @@ "Accept": "application/hal+json" }, "body": { - "content": "LS0tCjpzb21lOiBjb250cmFjdAo=", + "content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", "contractType": "oas", "contentType": "application/yaml", "verificationResults": { @@ -68,9 +152,14 @@ "body": { "_links": { "pf:ui": { - "href": "http://localhost:1235/contracts/bi-directional/provider/Bar/version/1/provider-contract" + "href": "some-url" } } + }, + "matchingRules": { + "$.body._links.pf:ui.href": { + "match": "type" + } } } }, diff --git a/spec/service_providers/pact_helper.rb b/spec/service_providers/pact_helper.rb index 8b22f83..eb3d710 100644 --- a/spec/service_providers/pact_helper.rb +++ b/spec/service_providers/pact_helper.rb @@ -25,10 +25,12 @@ module PactBrokerPactHelperMethods + # @param [String] relation eg "pb:pacticipant" + # @param [Array] params eg ["Foo"] def placeholder_path(relation, params = []) path = "/HAL-REL-PLACEHOLDER-#{relation.gsub(':', '-').upcase}" if params.any? - joined_params = params.collect{ |param| "{#{param}}"}.join("-") + joined_params = params.join("-") path = "#{path}-#{joined_params}" end @@ -36,7 +38,19 @@ def placeholder_path(relation, params = []) end def placeholder_url(relation, params = [], mock_service = pact_broker) - "#{mock_service.mock_service_base_url}#{placeholder_path(relation, params)}" + "#{mock_service.mock_service_base_url}#{placeholder_path_for_term(relation, params)}" + end + + # @param [String] relation eg "pb:pacticipants" + # @param [Array] params eg ["pacticipant"] + def placeholder_path_for_term(relation, params = []) + path = "/HAL-REL-PLACEHOLDER-#{relation.gsub(':', '-').upcase}" + if params.any? + joined_params = params.collect{ |param| "{#{param}}"}.join("-") + path = "#{path}-#{joined_params}" + end + + path end def placeholder_url_term(relation, params = [], mock_service = pact_broker) diff --git a/spec/service_providers/pactflow_publish_provider_contract_spec.rb b/spec/service_providers/pactflow_publish_provider_contract_spec.rb index d7ffdcf..c92a339 100644 --- a/spec/service_providers/pactflow_publish_provider_contract_spec.rb +++ b/spec/service_providers/pactflow_publish_provider_contract_spec.rb @@ -1,13 +1,8 @@ +require "yaml" require_relative "pact_helper" require "pactflow/client/provider_contracts/publish" -require "yaml" RSpec.describe "publishing a provider contract to PactFlow", 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 - include_context "pact broker" include PactBrokerPactHelperMethods @@ -17,8 +12,9 @@ provider_version_number: "1", branch_name: "main", tags: ["dev"], + build_url: "http://build", contract: { - content: { some: "contract" }.to_yaml, + content: { "some" => "contract" }.to_yaml, content_type: "application/yaml", specification: "oas" }, @@ -37,9 +33,14 @@ let(:request_body) do { - "content" => "LS0tCjpzb21lOiBjb250cmFjdAo=", - "contractType" => "oas", + "pacticipantVersionNumber" => "1", + "tags" => ["dev"], + "branch" => "main", + "buildUrl" => "http://build", + "contract" => { + "content" => "LS0tCnNvbWU6IGNvbnRyYWN0Cg==", "contentType" => "application/yaml", + "specification" => "oas", "verificationResults" => { "success" => true, "content" => "c29tZSByZXN1bHRz", @@ -48,14 +49,18 @@ "verifier" => "my custom tool", "verifierVersion" => "1.0" } + } } end - let(:response_status) { 201 } + let(:response_status) { 200 } let(:success_response) do { status: response_status, - headers: pact_broker_response_headers + headers: pact_broker_response_headers, + body: { + "notices" => Pact.each_like("text" => "some notice", "type" => "info") + } } end @@ -74,54 +79,38 @@ context "creating a provider contract with valid parameters" do before do pactflow - .upon_receiving("a request to create a provider contract") + .given("the pb:publish-provider-contract relation exists in the index resource") + .upon_receiving("a request for the index resource") .with( - 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 - 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 - - 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": "#{pactflow.mock_service_base_url}/contracts/bi-directional/provider/Bar/version/1/provider-contract" + method: "GET", + path: "/", + headers: get_request_headers + ).will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pf:publish-provider-contract' => { + href: placeholder_url_term("pf:publish-provider-contract", ['provider'], pactflow) + } + } } - } } - } - end - before do + ) + pactflow - .given("there is a pf:ui href in the response") - .upon_receiving("a request to create a provider contract") + .upon_receiving("a request to publish a provider contract") .with( - method: :put, - path: "/contracts/provider/Bar/version/1", - headers: put_request_headers, - body: request_body - ) - .will_respond_with(success_response_with_pf_ui_url) + method: :post, + path: placeholder_path("pf:publish-provider-contract", ["Bar"]), + headers: post_request_headers, + body: request_body + ).will_respond_with(success_response) end - it "returns a CommandResult with success = true and a provider contract ui url" do + it "returns a CommandResult with success = true" do 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 success_response_with_pf_ui_url[:body][:_links][:'pf:ui'][:href] + expect(subject.message).to include "some notice" end end -end \ No newline at end of file +end diff --git a/spec/service_providers/pactflow_publish_provider_contract_the_old_way_spec.rb b/spec/service_providers/pactflow_publish_provider_contract_the_old_way_spec.rb new file mode 100644 index 0000000..0a08c82 --- /dev/null +++ b/spec/service_providers/pactflow_publish_provider_contract_the_old_way_spec.rb @@ -0,0 +1,129 @@ +require_relative "pact_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 + + include_context "pact broker" + 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(: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 + before do + pactflow + .upon_receiving("a request to create a provider contract") + .with( + 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 + 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 + + 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": Pact.like("some-url") + } + } } + } + end + before do + pactflow + .given("there is a pf:ui href in the response") + .upon_receiving("a request to create a provider contract") + .with( + 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 + 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