Skip to content

Commit bf8130f

Browse files
committed
feat: allow pact equality to be based only on the content that affects verification results
1 parent e982d49 commit bf8130f

19 files changed

+585
-81
lines changed

lib/pact_broker/configuration.rb

+6-1
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@ class Configuration
2727
]
2828

2929
attr_accessor :log_dir, :database_connection, :auto_migrate_db, :use_hal_browser, :html_pact_renderer
30-
attr_accessor :validate_database_connection_config, :enable_diagnostic_endpoints, :version_parser
30+
attr_accessor :validate_database_connection_config, :enable_diagnostic_endpoints, :version_parser, :sha_generator
3131
attr_accessor :use_case_sensitive_resource_names, :order_versions_by_date
3232
attr_accessor :check_for_potential_duplicate_pacticipant_names
3333
attr_accessor :semver_formats
3434
attr_accessor :enable_public_badge_access, :shields_io_base_url
3535
attr_accessor :webhook_retry_schedule
3636
attr_accessor :disable_ssl_verification
37+
attr_accessor :base_equality_only_on_content_that_affects_verification_results
3738
attr_reader :api_error_reporters
3839
attr_writer :logger
3940

@@ -51,6 +52,8 @@ def logger
5152

5253
def self.default_configuration
5354
require 'pact_broker/versions/parse_semantic_version'
55+
require 'pact_broker/pacts/generate_sha'
56+
5457
config = Configuration.new
5558
config.log_dir = File.expand_path("./log")
5659
config.auto_migrate_db = true
@@ -62,6 +65,8 @@ def self.default_configuration
6265
config.use_case_sensitive_resource_names = true
6366
config.html_pact_renderer = default_html_pact_render
6467
config.version_parser = PactBroker::Versions::ParseSemanticVersion
68+
config.sha_generator = PactBroker::Pacts::GenerateSha
69+
config.base_equality_only_on_content_that_affects_verification_results = false
6570
# Not recommended to set this to true unless there is no way to
6671
# consistently extract an orderable object from the consumer application version number.
6772
config.order_versions_by_date = false

lib/pact_broker/pacts/content.rb

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
require 'pact_broker/pacts/parse'
2+
require 'pact_broker/pacts/sort_content'
3+
4+
module PactBroker
5+
module Pacts
6+
class Content
7+
8+
def initialize pact_hash
9+
@pact_hash = pact_hash
10+
end
11+
12+
def self.from_json json_content
13+
new(Parse.call(json_content))
14+
end
15+
16+
def self.from_hash pact_hash
17+
new(pact_hash)
18+
end
19+
20+
def to_hash
21+
pact_hash
22+
end
23+
24+
def to_json
25+
pact_hash.to_json
26+
end
27+
28+
def sort
29+
Content.from_hash(SortContent.call(pact_hash))
30+
end
31+
32+
# Half thinking this belongs in GenerateSha
33+
def content_that_affects_verification_results
34+
if interactions || messages
35+
cont = {}
36+
cont['interactions'] = interactions if interactions
37+
cont['messages'] = messages if messages
38+
cont['pact_specification_version'] = pact_specification_version if pact_specification_version
39+
cont
40+
else
41+
pact_hash
42+
end
43+
end
44+
45+
def messages
46+
pact_hash.is_a?(Hash) ? pact_hash['messages'] : nil
47+
end
48+
49+
def interactions
50+
pact_hash.is_a?(Hash) ? pact_hash['interactions'] : nil
51+
end
52+
53+
def pact_specification_version
54+
maybe_pact_specification_version_1 = pact_hash['metadata']['pactSpecification']['version'] rescue nil
55+
maybe_pact_specification_version_2 = pact_hash['metadata']['pact-specification']['version'] rescue nil
56+
maybe_pact_specification_version_3 = pact_hash['metadata'] && pact_hash['metadata']['pactSpecificationVersion'] rescue nil
57+
maybe_pact_specification_version_1 || maybe_pact_specification_version_2 || maybe_pact_specification_version_3
58+
end
59+
60+
private
61+
62+
attr_reader :pact_hash
63+
64+
end
65+
end
66+
end

lib/pact_broker/pacts/create_formatted_diff.rb

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ def self.call pact_json_content, previous_pact_json_content
1414
difference = diff(previous_pact_hash, pact_hash)
1515
Pact::Matchers::UnixDiffFormatter.call(difference, colour: false, include_explanation: false)
1616
end
17-
1817
end
1918
end
2019
end

lib/pact_broker/pacts/diff.rb

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
require 'pact_broker/api/pact_broker_urls'
22
require 'pact_broker/date_helper'
33
require 'pact_broker/pacts/create_formatted_diff'
4-
require 'pact_broker/pacts/sort_verifiable_content'
4+
require 'pact_broker/pacts/sort_content'
5+
require 'pact_broker/pacts/parse'
56
require 'pact_broker/repositories'
67
require 'yaml'
78

@@ -110,7 +111,7 @@ def prepare_content json_content
110111
if options[:raw]
111112
json_content
112113
else
113-
PactBroker::Pacts::SortVerifiableContent.call(json_content)
114+
SortContent.call(Parse.call(json_content)).to_json
114115
end
115116
end
116117
end

lib/pact_broker/pacts/generate_sha.rb

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
require 'digest/sha1'
2+
require 'pact_broker/configuration'
3+
require 'pact_broker/pacts/sort_content'
4+
require 'pact_broker/pacts/parse'
5+
require 'pact_broker/pacts/content'
6+
7+
module PactBroker
8+
module Pacts
9+
class GenerateSha
10+
def self.call json_content, options = {}
11+
content_for_sha = if PactBroker.configuration.base_equality_only_on_content_that_affects_verification_results
12+
extract_verifiable_content_for_sha(json_content)
13+
else
14+
json_content
15+
end
16+
Digest::SHA1.hexdigest(content_for_sha)
17+
end
18+
19+
def self.extract_verifiable_content_for_sha json_content
20+
Content.from_json(json_content).sort.content_that_affects_verification_results.to_json
21+
end
22+
end
23+
end
24+
end

lib/pact_broker/pacts/parse.rb

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
require 'pact_broker/json'
2+
3+
module PactBroker
4+
module Pacts
5+
class Parse
6+
def self.call(json)
7+
JSON.parse(json, PACT_PARSING_OPTIONS)
8+
end
9+
end
10+
end
11+
end

lib/pact_broker/pacts/repository.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
require 'digest/sha1'
21
require 'sequel'
32
require 'ostruct'
43
require 'pact_broker/logging'
4+
require 'pact_broker/pacts/generate_sha'
55
require 'pact_broker/pacts/pact_publication'
66
require 'pact_broker/pacts/all_pact_publications'
77
require 'pact_broker/pacts/latest_pact_publications_by_consumer_version'
88
require 'pact_broker/pacts/latest_pact_publications'
99
require 'pact_broker/pacts/latest_tagged_pact_publications'
1010
require 'pact/shared/json_differ'
1111
require 'pact_broker/domain'
12+
require 'pact_broker/pacts/parse'
1213

1314
module PactBroker
1415
module Pacts
@@ -197,7 +198,7 @@ def different? pact, other_pact
197198
end
198199

199200
def find_or_create_pact_version consumer_id, provider_id, json_content
200-
sha = Digest::SHA1.hexdigest(json_content)
201+
sha = PactBroker.configuration.sha_generator.call(json_content)
201202
PactVersion.find(sha: sha, consumer_id: consumer_id, provider_id: provider_id) || create_pact_version(consumer_id, provider_id, sha, json_content)
202203
end
203204

@@ -206,7 +207,6 @@ def create_pact_version consumer_id, provider_id, sha, json_content
206207
pact_version = PactVersion.new(consumer_id: consumer_id, provider_id: provider_id, sha: sha, content: json_content)
207208
pact_version.save
208209
end
209-
210210
end
211211
end
212212
end

lib/pact_broker/pacts/sort_content.rb

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
require 'pact_broker/json'
2+
3+
module PactBroker
4+
module Pacts
5+
class SortContent
6+
def self.call pact_hash
7+
key = verifiable_content_key_for(pact_hash)
8+
9+
if key
10+
content = pact_hash[key]
11+
sorted_pact_hash = order_object(pact_hash)
12+
sorted_pact_hash[key] = order_verifiable_content(content)
13+
sorted_pact_hash
14+
else
15+
pact_hash
16+
end
17+
end
18+
19+
def self.verifiable_content_key_for pact_hash
20+
if pact_hash['interactions']
21+
'interactions'
22+
elsif pact_hash['messages']
23+
'messages'
24+
else
25+
nil
26+
end
27+
end
28+
29+
30+
def self.order_verifiable_content array
31+
array_with_ordered_hashes = order_object(array)
32+
array_with_ordered_hashes.sort{|a, b| a.to_json <=> b.to_json }
33+
end
34+
35+
def self.order_object thing
36+
case thing
37+
when Hash then order_hash(thing)
38+
when Array then order_child_array(thing)
39+
else thing
40+
end
41+
end
42+
43+
def self.order_child_array array
44+
array.collect{|thing| order_object(thing) }
45+
end
46+
47+
def self.order_hash hash
48+
hash.keys.sort.each_with_object({}) do | key, new_hash |
49+
new_hash[key] = order_object(hash[key])
50+
end
51+
end
52+
end
53+
end
54+
end

lib/pact_broker/pacts/sort_verifiable_content.rb

-41
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
RSpec.describe "base_equality_only_on_content_that_affects_verification_results" do
2+
let(:td) { TestDataBuilder.new }
3+
let(:json_content_1) { load_fixture('foo-bar.json') }
4+
let(:json_content_2) do
5+
pact_hash = load_json_fixture('foo-bar.json')
6+
pact_hash['interactions'] = pact_hash['interactions'].reverse
7+
pact_hash.to_json
8+
end
9+
let(:base_equality_only_on_content_that_affects_verification_results) { true }
10+
11+
before do
12+
PactBroker.configuration.base_equality_only_on_content_that_affects_verification_results = base_equality_only_on_content_that_affects_verification_results
13+
td.create_pact_with_hierarchy("Foo", "1", "Bar", json_content_1)
14+
.create_verification(provider_version: "5")
15+
.create_consumer_version("2")
16+
.create_pact(json_content: json_content_2)
17+
end
18+
19+
context "when a pact is published with a different order of interactions to a previous version, but which is otherwise the same" do
20+
context "when base_equality_only_on_content_that_affects_verification_results is true" do
21+
it "applies the verifications from the previous version" do
22+
expect(PactBroker::Matrix::Row.all).to contain_hash(consumer_version_number: "2", provider_version_number: "5")
23+
end
24+
end
25+
26+
context "when base_equality_only_on_content_that_affects_verification_results is false" do
27+
let(:base_equality_only_on_content_that_affects_verification_results) { false }
28+
29+
it "does not apply the verifications from the previous version" do
30+
expect(PactBroker::Matrix::Row.all).to_not contain_hash(consumer_version_number: "2", provider_version_number: "5")
31+
end
32+
end
33+
end
34+
end

0 commit comments

Comments
 (0)