From 3cab7a008a8985940123bc00b4a809b2119c98d1 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Tue, 9 Mar 2021 09:13:00 +1100 Subject: [PATCH] feat(pacts for verification): display selectors in a formatted list --- .../pacts/verifiable_pact_messages.rb | 46 ++++++---- .../pacts/verifiable_pact_messages_spec.rb | 90 ++++++++++--------- 2 files changed, 79 insertions(+), 57 deletions(-) diff --git a/lib/pact_broker/pacts/verifiable_pact_messages.rb b/lib/pact_broker/pacts/verifiable_pact_messages.rb index e96d7fd74..57836e437 100644 --- a/lib/pact_broker/pacts/verifiable_pact_messages.rb +++ b/lib/pact_broker/pacts/verifiable_pact_messages.rb @@ -1,9 +1,11 @@ require 'forwardable' +require 'pact_broker/messages' module PactBroker module Pacts class VerifiablePactMessages extend Forwardable + include PactBroker::Messages READ_MORE_PENDING = "Read more at https://pact.io/pending" READ_MORE_WIP = "Read more at https://pact.io/wip" @@ -32,7 +34,8 @@ def inclusion_reason "The pact at #{pact_version_url} is being verified because it is a 'work in progress' pact (ie. it is the pact for the latest #{version_text} of #{consumer_name} #{joined_head_consumer_tags_and_branches} and is still in pending state). #{READ_MORE_WIP}" else criteria_or_criterion = selectors.size > 1 ? "criteria" : "criterion" - "The pact at #{pact_version_url} is being verified because it matches the following configured selection #{criteria_or_criterion}: #{selector_descriptions}#{same_content_note}" + version_or_versions = pluralize("the consumer version", selectors.size) + "The pact at #{pact_version_url} is being verified because the pact content belongs to #{version_or_versions} matching the following #{criteria_or_criterion}:\n#{selector_descriptions}" end end @@ -158,15 +161,13 @@ def branches def selector_descriptions selectors.sort.group_by(&:type).values.flat_map do | selectors | selectors_descriptions(selectors) - end.join(", ") + end.collect{ |description| " * #{description}" } + .join("\n") end def selectors_descriptions(selectors) if selectors.first.currently_deployed? - selectors.group_by(&:consumer).flat_map do | consumer_name, selectors | - display_name = consumer_name ? "the version(s) of #{consumer_name}" : "the consumer version(s)" - "pacts for #{display_name} currently deployed to #{join_unquoted(selectors.collect(&:environment))}" - end + currently_deployed_selectors_description(selectors) else selectors.collect do | selector | selector_description(selector) @@ -175,32 +176,41 @@ def selectors_descriptions(selectors) end def selector_description selector - if selector.overall_latest? + description = if selector.overall_latest? consumer_label = selector.consumer ? selector.consumer : 'a consumer' - "latest pact between #{consumer_label} and #{provider_name}" + "latest version of #{consumer_label} that has a pact with #{provider_name}" elsif selector.latest_for_tag? - version_label = selector.consumer ? "version of #{selector.consumer}" : "consumer version" + version_label = selector.consumer ? "version of #{selector.consumer}" : "version" if selector.fallback_tag? - "latest pact for a #{version_label} tagged '#{selector.fallback_tag}' (fallback tag used as no pact was found with tag '#{selector.tag}')" + "latest #{version_label} tagged '#{selector.fallback_tag}' (fallback tag used as no pact was found with tag '#{selector.tag}')" else - "latest pact for a #{version_label} tagged '#{selector.tag}'" + "latest #{version_label} tagged '#{selector.tag}'" end elsif selector.latest_for_branch? - version_label = selector.consumer ? "version of #{selector.consumer}" : "consumer version" + version_label = selector.consumer ? "version of #{selector.consumer}" : "version" if selector.fallback_branch? - "latest pact for a #{version_label} from branch '#{selector.fallback_branch}' (fallback branch used as no pact was found from branch '#{selector.branch}')" + "latest #{version_label} from branch '#{selector.fallback_branch}' (fallback branch used as no pact was found from branch '#{selector.branch}')" else - "latest pact for a #{version_label} from branch '#{selector.branch}'" + "latest #{version_label} from branch '#{selector.branch}'" end elsif selector.all_for_tag_and_consumer? - "pacts for all #{selector.consumer} versions tagged '#{selector.tag}'" + "all #{selector.consumer} versions tagged '#{selector.tag}'" elsif selector.all_for_tag? - "pacts for all consumer versions tagged '#{selector.tag}'" + "all consumer versions tagged '#{selector.tag}'" elsif selector.currently_deployed? - "pacts for consumer version(s) currently deployed to #{selector.environment}" + "version(s) currently deployed to #{selector.environment}" else selector.to_json end + "#{description} (#{selector.consumer_version.number})" + end + + def currently_deployed_selectors_description(selectors) + selectors.group_by(&:consumer).flat_map do | consumer_name, selectors | + display_name = consumer_name ? "version(s) of #{consumer_name}" : "consumer version(s)" + environments_and_versions = selectors.collect{ | selector | "#{selector.environment} (#{selector.consumer_version.number})" } + "#{display_name} currently deployed to #{join_unquoted(environments_and_versions)}" + end end def short_selector_descriptions @@ -227,6 +237,8 @@ def short_selector_description selector "one of #{selector.consumer} #{selector.tag}" elsif selector.tag "one of #{selector.tag}" + elsif selector.currently_deployed? + "deployed to #{selector.environment}" else selector.to_json end diff --git a/spec/lib/pact_broker/pacts/verifiable_pact_messages_spec.rb b/spec/lib/pact_broker/pacts/verifiable_pact_messages_spec.rb index cedf992ac..a170ce4f4 100644 --- a/spec/lib/pact_broker/pacts/verifiable_pact_messages_spec.rb +++ b/spec/lib/pact_broker/pacts/verifiable_pact_messages_spec.rb @@ -25,54 +25,64 @@ module Pacts provider_branch: provider_branch ) end + let(:consumer_version) { double('version', number: "1234" )} subject { VerifiablePactMessages.new(verifiable_pact, pact_version_url) } describe "#inclusion_reason" do + context "when there is one selector" do + let(:selectors) { Selectors.create_for_overall_latest.resolve(consumer_version) } + its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because the pact content belongs to the consumer version matching the following criterion:" } + end + + context "when there is more than one selector" do + let(:selectors) { Selectors.create_for_latest_of_each_branch(%w[main feat-x]).resolve(consumer_version) } + its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because the pact content belongs to the consumer versions matching the following criteria:" } + end + + context "when there are no head consumer tags" do - let(:selectors) { Selectors.create_for_overall_latest } - its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it matches the following configured selection criterion: latest pact between a consumer and Bar" } + let(:selectors) { Selectors.create_for_overall_latest.resolve(consumer_version) } + its(:inclusion_reason) { is_expected.to include "latest version of a consumer that has a pact with Bar (1234)" } end context "when there is 1 head consumer tags" do - let(:selectors) { Selectors.create_for_latest_of_each_tag(%w[dev]) } - its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it matches the following configured selection criterion: latest pact for a consumer version tagged 'dev'" } + let(:selectors) { Selectors.create_for_latest_of_each_tag(%w[dev]).resolve(consumer_version) } + its(:inclusion_reason) { is_expected.to include "latest version tagged 'dev' (1234)" } its(:pact_description) { is_expected.to eq "Pact between Foo and Bar, consumer version 123, latest with tag dev"} end context "when there are branches" do - let(:selectors) { Selectors.create_for_latest_of_each_branch(%w[main feat-x]) } - its(:inclusion_reason) { is_expected.to include "latest pact for a consumer version from branch 'feat-x', latest pact for a consumer version from branch 'main'" } + let(:selectors) { Selectors.create_for_latest_of_each_branch(%w[main feat-x]).resolve(consumer_version) } + its(:inclusion_reason) { is_expected.to include "latest version from branch 'feat-x' (1234)" } + its(:inclusion_reason) { is_expected.to include "latest version from branch 'main' (1234)" } its(:pact_description) { is_expected.to eq "Pact between Foo and Bar, consumer version 123, latest from branch main, latest from branch feat-x"} end context "when there are branches and tags" do - let(:selectors) { Selectors.new([Selector.latest_for_branch("main"), Selector.latest_for_tag("prod")]) } - its(:inclusion_reason) { is_expected.to include "latest pact for a consumer version from branch 'main', latest pact for a consumer version tagged 'prod'" } + let(:selectors) { Selectors.new([Selector.latest_for_branch("main"), Selector.latest_for_tag("prod")]).resolve(consumer_version) } + its(:inclusion_reason) { is_expected.to include "latest version from branch 'main' (1234)" } + its(:inclusion_reason) { is_expected.to include "latest version tagged 'prod' (1234)" } end context "when there are 2 head consumer tags" do - let(:selectors) { Selectors.create_for_latest_of_each_tag(%w[dev prod]) } - its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it matches the following configured selection criteria: latest pact for a consumer version tagged 'dev', latest pact for a consumer version tagged 'prod' (both have the same content)" } - end - - context "when there are 3 head consumer tags" do - let(:selectors) { Selectors.create_for_latest_of_each_tag(%w[dev prod feat-x]) } - its(:inclusion_reason) { is_expected.to include " (all have the same content)" } + let(:selectors) { Selectors.create_for_latest_of_each_tag(%w[dev prod]).resolve(consumer_version) } + its(:inclusion_reason) { is_expected.to include "latest version tagged 'dev' (1234)" } + its(:inclusion_reason) { is_expected.to include "latest version tagged 'prod' (1234)" } end context "when the pact was selected by the fallback tag" do - let(:selectors) { Selectors.new(Selector.latest_for_tag_with_fallback("feat-x", "master")) } - its(:inclusion_reason) { is_expected.to include "latest pact for a consumer version tagged 'master' (fallback tag used as no pact was found with tag 'feat-x')" } + let(:selectors) { Selectors.new(Selector.latest_for_tag_with_fallback("feat-x", "master").resolve_for_fallback(consumer_version)) } + its(:inclusion_reason) { is_expected.to include "latest version tagged 'master' (fallback tag used as no pact was found with tag 'feat-x') (1234)" } end context "when the pact was selected by the fallback tag" do - let(:selectors) { Selectors.new(Selector.latest_for_branch_with_fallback("feat-x", "master")) } - its(:inclusion_reason) { is_expected.to include "latest pact for a consumer version from branch 'master' (fallback branch used as no pact was found from branch 'feat-x')" } + let(:selectors) { Selectors.new(Selector.latest_for_branch_with_fallback("feat-x", "master").resolve_for_fallback(consumer_version)) } + its(:inclusion_reason) { is_expected.to include "latest version from branch 'master' (fallback branch used as no pact was found from branch 'feat-x') (1234)" } end context "when the pact is a WIP pact for the specified provider tags" do - let(:selectors) { Selectors.create_for_latest_of_each_tag(%w[feat-x]) } + let(:selectors) { Selectors.create_for_latest_of_each_tag(%w[feat-x]).resolve(consumer_version) } let(:wip) { true } let(:pending) { true } let(:pending_provider_tags) { %w[dev] } @@ -80,58 +90,58 @@ module Pacts its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it is a 'work in progress' pact (ie. it is the pact for the latest version of Foo tagged with 'feat-x' and is still in pending state)."} context "when the pact is a WIP pact for a consumer branch" do - let(:selectors) { Selectors.create_for_latest_of_each_branch(%w[feat-x feat-y]) } + let(:selectors) { Selectors.create_for_latest_of_each_branch(%w[feat-x feat-y]).resolve(consumer_version) } its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it is a 'work in progress' pact (ie. it is the pact for the latest versions of Foo from branches 'feat-x' and 'feat-y' (both have the same content) and is still in pending state)."} end context "when the pact is a WIP pact for a consumer branch and consumer rags" do - let(:selectors) { Selectors.create_for_latest_of_each_branch(%w[feat-x feat-y]) + Selectors.create_for_latest_of_each_tag(%w[feat-z feat-w]) } + let(:selectors) { Selectors.create_for_latest_of_each_branch(%w[feat-x feat-y]).resolve(consumer_version) + Selectors.create_for_latest_of_each_tag(%w[feat-z feat-w]).resolve(consumer_version) } its(:inclusion_reason) { is_expected.to include "it is the pact for the latest versions of Foo from branches 'feat-x' and 'feat-y' and tagged with 'feat-z' and 'feat-w' (all have the same content)"} end end context "when the pact is one of all versions for a tag" do - let(:selectors) { Selectors.create_for_all_of_each_tag(%w[prod]) } + let(:selectors) { Selectors.create_for_all_of_each_tag(%w[prod]).resolve(consumer_version) } - its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it matches the following configured selection criterion: pacts for all consumer versions tagged 'prod'"} + its(:inclusion_reason) { is_expected.to include "all consumer versions tagged 'prod' (1234)"} end context "when the pact is one of all versions for a tag and consumer" do - let(:selectors) { Selectors.new(Selector.all_for_tag_and_consumer('prod', 'Foo')) } + let(:selectors) { Selectors.new(Selector.all_for_tag_and_consumer('prod', 'Foo')).resolve(consumer_version) } - its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it matches the following configured selection criterion: pacts for all Foo versions tagged 'prod'"} + its(:inclusion_reason) { is_expected.to include "all Foo versions tagged 'prod' (1234)"} end context "when the pact is the latest version for a tag and consumer" do - let(:selectors) { Selectors.new(Selector.latest_for_tag_and_consumer('prod', 'Foo')) } + let(:selectors) { Selectors.new(Selector.latest_for_tag_and_consumer('prod', 'Foo')).resolve(consumer_version) } - its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it matches the following configured selection criterion: latest pact for a version of Foo tagged 'prod'"} + its(:inclusion_reason) { is_expected.to include "latest version of Foo tagged 'prod' (1234)"} end context "when the pact is the latest version for a branch and consumer" do - let(:selectors) { Selectors.new(Selector.latest_for_branch_and_consumer('main', 'Foo')) } + let(:selectors) { Selectors.new(Selector.latest_for_branch_and_consumer('main', 'Foo')).resolve(consumer_version) } - its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it matches the following configured selection criterion: latest pact for a version of Foo from branch 'main'"} + its(:inclusion_reason) { is_expected.to include "latest version of Foo from branch 'main' (1234)"} end context "when the pact is the latest version for a consumer" do - let(:selectors) { Selectors.new(Selector.latest_for_consumer('Foo')) } + let(:selectors) { Selectors.new(Selector.latest_for_consumer('Foo')).resolve(consumer_version) } - its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it matches the following configured selection criterion: latest pact between Foo and Bar"} + its(:inclusion_reason) { is_expected.to include "latest version of Foo that has a pact with Bar (1234)"} end context "when the consumer version is currently deployed to a single environment" do - let(:selectors) { Selectors.new(Selector.for_currently_deployed('test')) } + let(:selectors) { Selectors.new(Selector.for_currently_deployed('test')).resolve(consumer_version) } - its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it matches the following configured selection criterion: pacts for the consumer version(s) currently deployed to test"} + its(:inclusion_reason) { is_expected.to include "consumer version(s) currently deployed to test (1234)"} end context "when the consumer version is currently deployed to a multiple environments" do - let(:selectors) { Selectors.new(Selector.for_currently_deployed('dev'), Selector.for_currently_deployed('test'), Selector.for_currently_deployed('prod')) } + let(:selectors) { Selectors.new(Selector.for_currently_deployed('dev'), Selector.for_currently_deployed('test'), Selector.for_currently_deployed('prod')).resolve(consumer_version) } - its(:inclusion_reason) { is_expected.to include "pacts for the consumer version(s) currently deployed to dev, prod and test (all have the same content)"} + its(:inclusion_reason) { is_expected.to include "consumer version(s) currently deployed to dev (1234), prod (1234) and test (1234)"} end context "when the currently deployed consumer version is for a consumer" do @@ -141,12 +151,12 @@ module Pacts Selector.for_currently_deployed_and_environment_and_consumer('prod', 'Foo'), Selector.for_currently_deployed_and_environment_and_consumer('test', 'Bar'), Selector.for_currently_deployed('test'), - ) + ).resolve(consumer_version) end - its(:inclusion_reason) { is_expected.to include "pacts for the version(s) of Foo currently deployed to prod and test"} - its(:inclusion_reason) { is_expected.to include "pacts for the version(s) of Bar currently deployed to test"} - its(:inclusion_reason) { is_expected.to include "pacts for the consumer version(s) currently deployed to test"} + its(:inclusion_reason) { is_expected.to include "version(s) of Foo currently deployed to prod (1234) and test (1234)"} + its(:inclusion_reason) { is_expected.to include "version(s) of Bar currently deployed to test (1234)"} + its(:inclusion_reason) { is_expected.to include "consumer version(s) currently deployed to test (1234)"} end end