Skip to content

Commit

Permalink
feat(pacts for verification): display selectors in a formatted list
Browse files Browse the repository at this point in the history
  • Loading branch information
bethesque committed Mar 8, 2021
1 parent 2e702e1 commit 3cab7a0
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 57 deletions.
46 changes: 29 additions & 17 deletions lib/pact_broker/pacts/verifiable_pact_messages.rb
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand Down
90 changes: 50 additions & 40 deletions spec/lib/pact_broker/pacts/verifiable_pact_messages_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,113 +25,123 @@ 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] }

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
Expand All @@ -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

Expand Down

0 comments on commit 3cab7a0

Please sign in to comment.