Skip to content

Commit 673fcb8

Browse files
committed
feat(pacts for verification): only include WIP pacts that were published after the provider tag was first used
1 parent 073889b commit 673fcb8

File tree

2 files changed

+133
-24
lines changed

2 files changed

+133
-24
lines changed

lib/pact_broker/pacts/repository.rb

+60-18
Original file line numberDiff line numberDiff line change
@@ -125,22 +125,58 @@ def find_latest_pact_versions_for_provider provider_name, tag = nil
125125
end
126126
end
127127

128-
def find_wip_pact_versions_for_provider provider_name, provider_tags = [], options = {}
129-
return [] if provider_tags.empty?
128+
# To find the work in progress pacts for this verification execution:
129+
# For each provider tag that will be applied to this verification result (usually there will just be one, but
130+
# we have to allow for multiple tags),
131+
# find the head pacts (the pacts that are the latest for their tag) that have been successfully
132+
# verified against the provider tag.
133+
# Then, find all the head pacts, and remove the ones that have been successfully verified by ALL
134+
# of the provider tags supplied.
135+
# Then, for all of the head pacts that are remaining (these are the WIP ones) work out which
136+
# provider tags they are pending for.
137+
# Don't include pact publications that were created
138+
def find_wip_pact_versions_for_provider provider_name, provider_tags_names = [], options = {}
139+
return [] if provider_tags_names.empty?
140+
141+
provider = pacticipant_repository.find_by_name(provider_name)
142+
143+
# Hash of provider tag names => list of head pacts
144+
successfully_verified_head_pacts_for_provider_tags = find_successfully_verified_head_pacts_by_provider_tag(provider_name, provider_tags_names, options)
145+
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 |
146+
hash[provider_tag_name] = head_pacts.collect(&:id)
147+
end
130148

131-
# Hash of provider tag names => list of pact_publication_ids
132-
successfully_verified_head_pact_publication_ids_for_each_provider_tag = find_successfully_verified_head_pacts_by_provider_tag(provider_name, provider_tags, options)
149+
# list of pact_publication_ids that are NOT work in progress
150+
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(:&)
133151

134152
pact_publication_ids = find_head_pacts_that_have_not_been_successfully_verified_by_all_provider_tags(
135153
provider_name,
136-
successfully_verified_head_pact_publication_ids_for_each_provider_tag.values.reduce(:&),
154+
head_pact_publication_ids_successully_verified_by_all_provider_tags,
137155
options)
138156

139-
pacts = AllPactPublications.where(id: pact_publication_ids).order_ignore_case(:consumer_name).order_append(:consumer_version_order).collect(&:to_domain)
157+
pacts = AllPactPublications.where(id: pact_publication_ids).order_ignore_case(:consumer_name).order_append(:consumer_version_order)
158+
159+
# The first instance (by date) of each provider tag with that name
160+
# Note: created_at is coming back as a string
161+
provider_tag_collection = PactBroker::Domain::Tag
162+
.select_group(Sequel[:tags][:name], Sequel[:pacticipant_id])
163+
.select_append(Sequel.function(:min, Sequel[:tags][:created_at]).as(:created_at))
164+
.distinct
165+
.join(:versions, { Sequel[:tags][:version_id] => Sequel[:versions][:id] } )
166+
.where(pacticipant_id: provider.id)
167+
.where(name: provider_tags_names)
168+
.all
169+
140170
pacts.collect do | pact|
141-
pending_tags = find_provider_tags_for_which_pact_publication_id_is_pending(pact.id, successfully_verified_head_pact_publication_ids_for_each_provider_tag)
142-
VerifiablePact.new(pact, true, pending_tags, [], pact.consumer_version_tag_names, nil, true)
143-
end
171+
pending_tag_names = find_provider_tags_for_which_pact_publication_id_is_pending(pact, successfully_verified_head_pact_publication_ids_for_each_provider_tag)
172+
pre_existing_tag_names = find_provider_tag_names_that_were_first_used_before_pact_published(pact, provider_tag_collection)
173+
174+
pre_existing_pending_tags = pending_tag_names & pre_existing_tag_names
175+
176+
if pre_existing_pending_tags.any?
177+
VerifiablePact.new(pact.to_domain, true, pre_existing_pending_tags, [], pact.head_tag_names, nil, true)
178+
end
179+
end.compact
144180
end
145181

146182
def find_pact_versions_for_provider provider_name, tag = nil
@@ -333,13 +369,17 @@ def find_all_database_versions_between(consumer_name, options, base_class = Late
333369
query
334370
end
335371

336-
def find_provider_tags_for_which_pact_publication_id_is_pending(pact_publication_id, successfully_verified_head_pact_publication_ids_for_each_provider_tag)
372+
def find_provider_tags_for_which_pact_publication_id_is_pending(pact_publication, successfully_verified_head_pact_publication_ids_for_each_provider_tag)
337373
successfully_verified_head_pact_publication_ids_for_each_provider_tag
338-
.select do | provider_tag, pact_publication_ids |
339-
!pact_publication_ids.include?(pact_publication_id)
374+
.select do | _, pact_publication_ids |
375+
!pact_publication_ids.include?(pact_publication.id)
340376
end.keys
341377
end
342378

379+
def find_provider_tag_names_that_were_first_used_before_pact_published(pact_publication, provider_tag_collection)
380+
provider_tag_collection.select { | tag| DateTime.parse(tag.created_at) < pact_publication.created_at }.collect(&:name)
381+
end
382+
343383
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)
344384
# Exclude the head pacts that have been successfully verified by all the specified provider tags
345385
pact_publication_ids = LatestTaggedPactPublications
@@ -349,18 +389,20 @@ def find_head_pacts_that_have_not_been_successfully_verified_by_all_provider_tag
349389
.select_for_subquery(:id)
350390
end
351391

392+
# Find the head pacts that have been successfully verified by a provider version with the specified tags
393+
# Returns a Hash of provider_tag => LatestTaggedPactPublications with only id and tag_name populated
352394
def find_successfully_verified_head_pacts_by_provider_tag(provider_name, provider_tags, options)
353-
provider_tags.compact.each_with_object({}) do | provider_tag, tag_to_ids_hash |
354-
ids = LatestTaggedPactPublications
395+
provider_tags.compact.each_with_object({}) do | provider_tag, hash |
396+
head_pacts = LatestTaggedPactPublications
355397
.join(:verifications, { pact_version_id: :pact_version_id })
356398
.join(:tags, { Sequel[:verifications][:provider_version_id] => Sequel[:provider_tags][:version_id] }, {table_alias: :provider_tags})
357399
.where(Sequel[:provider_tags][:name] => provider_tag)
358400
.provider(provider_name)
359401
.where(Sequel[:verifications][:success] => true)
360-
.where(Sequel.lit('latest_tagged_pact_publications.created_at > ?', options.fetch(:include_wip_pacts_since)))
361-
.select(Sequel[:latest_tagged_pact_publications][:id].as(:id))
362-
.collect(&:id)
363-
tag_to_ids_hash[provider_tag] = ids
402+
.or(Sequel.lit('latest_tagged_pact_publications.created_at < ?', options.fetch(:include_wip_pacts_since)))
403+
.select(Sequel[:latest_tagged_pact_publications][:id].as(:id), :tag_name)
404+
.all
405+
hash[provider_tag] = head_pacts
364406
end
365407
end
366408
end

spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_spec.rb

+73-6
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,13 @@ module Pacts
3939

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

5864
context "when the latest pact for a tag has failed verification from the specified provider version" do
5965
before do
60-
td.create_pact_with_hierarchy("foo", "1", "bar")
66+
td.create_provider("bar")
67+
.create_provider_version("333")
68+
.create_provider_version_tag("dev")
69+
.add_day
70+
.create_pact_with_hierarchy("foo", "1", "bar")
6171
.create_consumer_version_tag("feat-1")
6272
.create_verification(provider_version: "3", success: false, tag_names: %[dev])
6373
end
@@ -84,7 +94,11 @@ module Pacts
8494

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

98112
context "when the latest pact for a tag has not been verified" do
99113
before do
100-
td.create_pact_with_hierarchy("foo", "1", "bar")
101-
.create_consumer_version_tag("dev")
114+
td.create_provider("bar")
115+
.create_provider_version("333")
116+
.create_provider_version_tag("dev")
117+
.add_day
118+
.create_pact_with_hierarchy("foo", "1", "bar")
119+
.create_consumer_version_tag("feat-1")
102120
end
103121

104122
it "is included" do
@@ -123,7 +141,11 @@ module Pacts
123141

124142
context "when the pact was published before the specified include_wip_pacts_since" do
125143
before do
126-
td.create_pact_with_hierarchy("foo", "1", "bar")
144+
td.create_provider("bar")
145+
.create_provider_version("333")
146+
.create_provider_version_tag("dev")
147+
.add_day
148+
.create_pact_with_hierarchy("foo", "1", "bar")
127149
.create_consumer_version_tag("prod")
128150
end
129151

@@ -133,6 +155,51 @@ module Pacts
133155
expect(subject.size).to be 0
134156
end
135157
end
158+
159+
context "when the first provider tag with a given name was created after the head pact was created" do
160+
before do
161+
td.create_pact_with_hierarchy("foo", "1", "bar")
162+
.create_consumer_version_tag("feat-x")
163+
.add_day
164+
.create_provider_version("5")
165+
.create_provider_version_tag(provider_tags.first)
166+
end
167+
168+
it "doesn't return any pacts" do
169+
expect(subject.size).to be 0
170+
end
171+
end
172+
173+
context "when the provider tag does not exist yet" do
174+
before do
175+
td.create_pact_with_hierarchy("foo", "1", "bar")
176+
.create_consumer_version_tag("feat-x")
177+
end
178+
179+
it "doesn't return any pacts" do
180+
expect(subject.size).to be 0
181+
end
182+
end
183+
184+
context "when a pact was published between the first creation date of two provider tags" do
185+
let(:provider_tags) { %w[dev feat-1] }
186+
187+
before do
188+
td.create_provider("bar")
189+
.create_provider_version("4")
190+
.create_provider_version_tag(provider_tags.first)
191+
.add_day
192+
.create_pact_with_hierarchy("foo", "1", "bar")
193+
.create_consumer_version_tag("feat-x")
194+
.add_day
195+
.create_provider_version("5")
196+
.create_provider_version_tag(provider_tags.last)
197+
end
198+
199+
it "is wip for the first tag but not the second" do
200+
expect(subject.first.pending_provider_tags).to eq [provider_tags.first]
201+
end
202+
end
136203
end
137204
end
138205
end

0 commit comments

Comments
 (0)