Skip to content

Commit

Permalink
feat(pacts for verification): only include WIP pacts that were publis…
Browse files Browse the repository at this point in the history
…hed after the provider tag was first used
  • Loading branch information
bethesque committed Dec 5, 2019
1 parent 073889b commit 673fcb8
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 24 deletions.
78 changes: 60 additions & 18 deletions lib/pact_broker/pacts/repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,22 +125,58 @@ def find_latest_pact_versions_for_provider provider_name, tag = nil
end
end

def find_wip_pact_versions_for_provider provider_name, provider_tags = [], options = {}
return [] if provider_tags.empty?
# To find the work in progress pacts for this verification execution:
# For each provider tag that will be applied to this verification result (usually there will just be one, but
# we have to allow for multiple tags),
# find the head pacts (the pacts that are the latest for their tag) that have been successfully
# verified against the provider tag.
# Then, find all the head pacts, and remove the ones that have been successfully verified by ALL
# of the provider tags supplied.
# Then, for all of the head pacts that are remaining (these are the WIP ones) work out which
# provider tags they are pending for.
# Don't include pact publications that were created
def find_wip_pact_versions_for_provider provider_name, provider_tags_names = [], options = {}
return [] if provider_tags_names.empty?

provider = pacticipant_repository.find_by_name(provider_name)

# Hash of provider tag names => list of head pacts
successfully_verified_head_pacts_for_provider_tags = find_successfully_verified_head_pacts_by_provider_tag(provider_name, provider_tags_names, options)
successfully_verified_head_pact_publication_ids_for_each_provider_tag = successfully_verified_head_pacts_for_provider_tags.each_with_object({}) do | (provider_tag_name, head_pacts), hash |
hash[provider_tag_name] = head_pacts.collect(&:id)
end

# Hash of provider tag names => list of pact_publication_ids
successfully_verified_head_pact_publication_ids_for_each_provider_tag = find_successfully_verified_head_pacts_by_provider_tag(provider_name, provider_tags, options)
# list of pact_publication_ids that are NOT work in progress
head_pact_publication_ids_successully_verified_by_all_provider_tags = successfully_verified_head_pacts_for_provider_tags.values.collect{ |head_pacts| head_pacts.collect(&:id) }.reduce(:&)

pact_publication_ids = find_head_pacts_that_have_not_been_successfully_verified_by_all_provider_tags(
provider_name,
successfully_verified_head_pact_publication_ids_for_each_provider_tag.values.reduce(:&),
head_pact_publication_ids_successully_verified_by_all_provider_tags,
options)

pacts = AllPactPublications.where(id: pact_publication_ids).order_ignore_case(:consumer_name).order_append(:consumer_version_order).collect(&:to_domain)
pacts = AllPactPublications.where(id: pact_publication_ids).order_ignore_case(:consumer_name).order_append(:consumer_version_order)

# The first instance (by date) of each provider tag with that name
# Note: created_at is coming back as a string
provider_tag_collection = PactBroker::Domain::Tag
.select_group(Sequel[:tags][:name], Sequel[:pacticipant_id])
.select_append(Sequel.function(:min, Sequel[:tags][:created_at]).as(:created_at))
.distinct
.join(:versions, { Sequel[:tags][:version_id] => Sequel[:versions][:id] } )
.where(pacticipant_id: provider.id)
.where(name: provider_tags_names)
.all

pacts.collect do | pact|
pending_tags = find_provider_tags_for_which_pact_publication_id_is_pending(pact.id, successfully_verified_head_pact_publication_ids_for_each_provider_tag)
VerifiablePact.new(pact, true, pending_tags, [], pact.consumer_version_tag_names, nil, true)
end
pending_tag_names = find_provider_tags_for_which_pact_publication_id_is_pending(pact, successfully_verified_head_pact_publication_ids_for_each_provider_tag)
pre_existing_tag_names = find_provider_tag_names_that_were_first_used_before_pact_published(pact, provider_tag_collection)

pre_existing_pending_tags = pending_tag_names & pre_existing_tag_names

if pre_existing_pending_tags.any?
VerifiablePact.new(pact.to_domain, true, pre_existing_pending_tags, [], pact.head_tag_names, nil, true)
end
end.compact
end

def find_pact_versions_for_provider provider_name, tag = nil
Expand Down Expand Up @@ -333,13 +369,17 @@ def find_all_database_versions_between(consumer_name, options, base_class = Late
query
end

def find_provider_tags_for_which_pact_publication_id_is_pending(pact_publication_id, successfully_verified_head_pact_publication_ids_for_each_provider_tag)
def find_provider_tags_for_which_pact_publication_id_is_pending(pact_publication, successfully_verified_head_pact_publication_ids_for_each_provider_tag)
successfully_verified_head_pact_publication_ids_for_each_provider_tag
.select do | provider_tag, pact_publication_ids |
!pact_publication_ids.include?(pact_publication_id)
.select do | _, pact_publication_ids |
!pact_publication_ids.include?(pact_publication.id)
end.keys
end

def find_provider_tag_names_that_were_first_used_before_pact_published(pact_publication, provider_tag_collection)
provider_tag_collection.select { | tag| DateTime.parse(tag.created_at) < pact_publication.created_at }.collect(&:name)
end

def find_head_pacts_that_have_not_been_successfully_verified_by_all_provider_tags(provider_name, pact_publication_ids_successfully_verified_by_all_provider_tags, options)
# Exclude the head pacts that have been successfully verified by all the specified provider tags
pact_publication_ids = LatestTaggedPactPublications
Expand All @@ -349,18 +389,20 @@ def find_head_pacts_that_have_not_been_successfully_verified_by_all_provider_tag
.select_for_subquery(:id)
end

# Find the head pacts that have been successfully verified by a provider version with the specified tags
# Returns a Hash of provider_tag => LatestTaggedPactPublications with only id and tag_name populated
def find_successfully_verified_head_pacts_by_provider_tag(provider_name, provider_tags, options)
provider_tags.compact.each_with_object({}) do | provider_tag, tag_to_ids_hash |
ids = LatestTaggedPactPublications
provider_tags.compact.each_with_object({}) do | provider_tag, hash |
head_pacts = LatestTaggedPactPublications
.join(:verifications, { pact_version_id: :pact_version_id })
.join(:tags, { Sequel[:verifications][:provider_version_id] => Sequel[:provider_tags][:version_id] }, {table_alias: :provider_tags})
.where(Sequel[:provider_tags][:name] => provider_tag)
.provider(provider_name)
.where(Sequel[:verifications][:success] => true)
.where(Sequel.lit('latest_tagged_pact_publications.created_at > ?', options.fetch(:include_wip_pacts_since)))
.select(Sequel[:latest_tagged_pact_publications][:id].as(:id))
.collect(&:id)
tag_to_ids_hash[provider_tag] = ids
.or(Sequel.lit('latest_tagged_pact_publications.created_at < ?', options.fetch(:include_wip_pacts_since)))
.select(Sequel[:latest_tagged_pact_publications][:id].as(:id), :tag_name)
.all
hash[provider_tag] = head_pacts
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@ module Pacts

context "when the latest pact for a tag has been successfully verified by one of the given provider tags, but not the other" do
before do
td.create_pact_with_hierarchy("foo", "1", "bar")
td.create_provider("bar")
.create_provider_version("44")
.create_provider_version_tag("feat-1")
.add_day
.create_consumer("foo")
.create_consumer_version("1")
.create_pact
.create_consumer_version_tag("prod")
.create_verification(provider_version: "3", tag_names: %w[dev], comment: "not included because already verified")
end
Expand All @@ -57,7 +63,11 @@ module Pacts

context "when the latest pact for a tag has failed verification from the specified provider version" do
before do
td.create_pact_with_hierarchy("foo", "1", "bar")
td.create_provider("bar")
.create_provider_version("333")
.create_provider_version_tag("dev")
.add_day
.create_pact_with_hierarchy("foo", "1", "bar")
.create_consumer_version_tag("feat-1")
.create_verification(provider_version: "3", success: false, tag_names: %[dev])
end
Expand All @@ -84,7 +94,11 @@ module Pacts

context "when the latest pact for a tag has successful and failed verifications" do
before do
td.create_pact_with_hierarchy("foo", "1", "bar")
td.create_provider("bar")
.create_provider_version("333")
.create_provider_version_tag("dev")
.add_day
.create_pact_with_hierarchy("foo", "1", "bar")
.create_consumer_version_tag("dev")
.create_verification(provider_version: "3", success: true, tag_names: %[dev])
.create_verification(provider_version: "5", success: false, number: 2, tag_names: %[dev])
Expand All @@ -97,8 +111,12 @@ module Pacts

context "when the latest pact for a tag has not been verified" do
before do
td.create_pact_with_hierarchy("foo", "1", "bar")
.create_consumer_version_tag("dev")
td.create_provider("bar")
.create_provider_version("333")
.create_provider_version_tag("dev")
.add_day
.create_pact_with_hierarchy("foo", "1", "bar")
.create_consumer_version_tag("feat-1")
end

it "is included" do
Expand All @@ -123,7 +141,11 @@ module Pacts

context "when the pact was published before the specified include_wip_pacts_since" do
before do
td.create_pact_with_hierarchy("foo", "1", "bar")
td.create_provider("bar")
.create_provider_version("333")
.create_provider_version_tag("dev")
.add_day
.create_pact_with_hierarchy("foo", "1", "bar")
.create_consumer_version_tag("prod")
end

Expand All @@ -133,6 +155,51 @@ module Pacts
expect(subject.size).to be 0
end
end

context "when the first provider tag with a given name was created after the head pact was created" do
before do
td.create_pact_with_hierarchy("foo", "1", "bar")
.create_consumer_version_tag("feat-x")
.add_day
.create_provider_version("5")
.create_provider_version_tag(provider_tags.first)
end

it "doesn't return any pacts" do
expect(subject.size).to be 0
end
end

context "when the provider tag does not exist yet" do
before do
td.create_pact_with_hierarchy("foo", "1", "bar")
.create_consumer_version_tag("feat-x")
end

it "doesn't return any pacts" do
expect(subject.size).to be 0
end
end

context "when a pact was published between the first creation date of two provider tags" do
let(:provider_tags) { %w[dev feat-1] }

before do
td.create_provider("bar")
.create_provider_version("4")
.create_provider_version_tag(provider_tags.first)
.add_day
.create_pact_with_hierarchy("foo", "1", "bar")
.create_consumer_version_tag("feat-x")
.add_day
.create_provider_version("5")
.create_provider_version_tag(provider_tags.last)
end

it "is wip for the first tag but not the second" do
expect(subject.first.pending_provider_tags).to eq [provider_tags.first]
end
end
end
end
end
Expand Down

0 comments on commit 673fcb8

Please sign in to comment.