Skip to content

Commit cd37785

Browse files
committed
feat: add ids to interactions when pacts are published
These will be used to match test results published back to the broker with the correct interaction.
1 parent 8cab459 commit cd37785

8 files changed

+209
-5
lines changed

lib/pact_broker/pacts/content.rb

+23
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
require 'pact_broker/pacts/parse'
22
require 'pact_broker/pacts/sort_content'
3+
require 'pact_broker/pacts/generate_interaction_sha'
34

45
module PactBroker
56
module Pacts
67
class Content
8+
include GenerateInteractionSha
79

810
def initialize pact_hash
911
@pact_hash = pact_hash
@@ -29,6 +31,18 @@ def sort
2931
Content.from_hash(SortContent.call(pact_hash))
3032
end
3133

34+
def with_ids
35+
new_pact_hash = pact_hash.dup
36+
if interactions && interactions.is_a?(Array)
37+
new_pact_hash['interactions'] = add_ids(interactions)
38+
end
39+
40+
if messages && messages.is_a?(Array)
41+
new_pact_hash['messages'] = add_ids(messages)
42+
end
43+
Content.from_hash(new_pact_hash)
44+
end
45+
3246
# Half thinking this belongs in GenerateSha
3347
def content_that_affects_verification_results
3448
if interactions || messages
@@ -61,6 +75,15 @@ def pact_specification_version
6175

6276
attr_reader :pact_hash
6377

78+
def add_ids(interactions)
79+
interactions.map do | interaction |
80+
if interaction.is_a?(Hash)
81+
interaction.merge("id" => generate_interaction_sha(interaction))
82+
else
83+
interaction
84+
end
85+
end
86+
end
6487
end
6588
end
6689
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
module GenerateInteractionSha
10+
def self.call interaction_hash, options = {}
11+
ordered_interaction_hash = interaction_hash.keys.sort.each_with_object({}) do | key, new_interaction_hash |
12+
new_interaction_hash[key] = interaction_hash[key] unless key == "id"
13+
end
14+
15+
Digest::SHA1.hexdigest(ordered_interaction_hash.to_json)
16+
end
17+
18+
def generate_interaction_sha interaction_hash, options = {}
19+
GenerateInteractionSha.call(interaction_hash, options)
20+
end
21+
end
22+
end
23+
end

lib/pact_broker/pacts/service.rb

+11-2
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ def update_pact params, existing_pact
121121
logger.info "Updating existing pact publication with params #{params.reject{ |k, v| k == :json_content}}"
122122
logger.debug "Content #{params[:json_content]}"
123123
pact_version_sha = generate_sha(params[:json_content])
124-
updated_pact = pact_repository.update(existing_pact.id, params.merge(pact_version_sha: pact_version_sha))
124+
json_content = add_interaction_ids(params[:json_content])
125+
update_params = { pact_version_sha: pact_version_sha, json_content: json_content }
126+
updated_pact = pact_repository.update(existing_pact.id, update_params)
125127

126128
webhook_service.trigger_webhooks updated_pact, nil, PactBroker::Webhooks::WebhookEvent::CONTRACT_PUBLISHED
127129
# TODO this should use the sha!
@@ -137,12 +139,15 @@ def update_pact params, existing_pact
137139
def create_pact params, version, provider
138140
logger.info "Creating new pact publication with params #{params.reject{ |k, v| k == :json_content}}"
139141
logger.debug "Content #{params[:json_content]}"
142+
pact_version_sha = generate_sha(params[:json_content])
143+
json_content = add_interaction_ids(params[:json_content])
140144
pact = pact_repository.create(
141145
json_content: params[:json_content],
142146
version_id: version.id,
143147
provider_id: provider.id,
144148
consumer_id: version.pacticipant_id,
145-
pact_version_sha: generate_sha(params[:json_content])
149+
pact_version_sha: pact_version_sha,
150+
json_content: json_content
146151
)
147152
trigger_webhooks pact
148153
pact
@@ -152,6 +157,10 @@ def generate_sha(json_content)
152157
PactBroker.configuration.sha_generator.call(json_content)
153158
end
154159

160+
def add_interaction_ids(json_content)
161+
Content.from_json(json_content).with_ids.to_json
162+
end
163+
155164
def trigger_webhooks pact
156165
# TODO add tests for this
157166
webhook_service.trigger_webhooks pact, nil, PactBroker::Webhooks::WebhookEvent::CONTRACT_PUBLISHED

spec/features/merge_pact_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
end
2121

2222
it "returns the pact in the body" do
23-
expect(response_body_json).to include JSON.parse(pact_content)
23+
expect(response_body_json).to match_pact JSON.parse(pact_content)
2424
end
2525
end
2626

spec/features/publish_pact_spec.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
end
1717

1818
it "returns the pact in the body" do
19-
expect(response_body_json).to include JSON.parse(pact_content)
19+
expect(response_body_json).to match_pact JSON.parse(pact_content)
2020
end
2121
end
2222

@@ -35,7 +35,7 @@
3535
end
3636

3737
it "returns the pact in the response body" do
38-
expect(response_body_json).to include JSON.parse(pact_content)
38+
expect(response_body_json).to match_pact JSON.parse(pact_content)
3939
end
4040
end
4141

spec/lib/pact_broker/pacts/content_spec.rb

+48
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,54 @@
33
module PactBroker
44
module Pacts
55
describe Content do
6+
describe "with_ids" do
7+
let(:pact_hash) do
8+
{
9+
'ignored' => 'foo',
10+
'interactions' => [interaction],
11+
'metadata' => {
12+
'foo' => 'bar'
13+
}
14+
}
15+
end
16+
let(:interaction) { { "foo" => "bar" } }
17+
18+
before do
19+
allow(GenerateInteractionSha).to receive(:call).and_return("some-id")
20+
end
21+
22+
context "when the interaction is a hash" do
23+
it "adds ids to the interactions" do
24+
expect(Content.from_hash(pact_hash).with_ids.interactions.first["id"]).to eq "some-id"
25+
end
26+
end
27+
28+
context "when the interaction is not a hash" do
29+
let(:interaction) { 1 }
30+
31+
it "does not add an id" do
32+
expect(Content.from_hash(pact_hash).with_ids.interactions.first).to eq interaction
33+
end
34+
end
35+
36+
context "when the pact is a message pact" do
37+
let(:pact_hash) do
38+
{
39+
'ignored' => 'foo',
40+
'messages' => [interaction],
41+
'metadata' => {
42+
'foo' => 'bar'
43+
}
44+
}
45+
end
46+
47+
it "adds ids to the messages" do
48+
expect(Content.from_hash(pact_hash).with_ids.messages.first["id"]).to eq "some-id"
49+
end
50+
end
51+
end
52+
53+
654
describe "content_that_affects_verification_results" do
755

856
subject { Content.from_hash(pact_hash).content_that_affects_verification_results }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
require 'pact_broker/pacts/generate_interaction_sha'
2+
3+
module PactBroker
4+
module Pacts
5+
describe GenerateInteractionSha do
6+
describe ".call" do
7+
let(:interaction_hash) do
8+
{
9+
"description" => "foo",
10+
"providerStates" => [
11+
"name" => "bar",
12+
"params" => {
13+
"wiffle" => "bar",
14+
"meep" => "eek"
15+
}
16+
]
17+
}
18+
end
19+
20+
let(:interaction_hash_with_different_key_order) do
21+
{
22+
"providerStates" => [
23+
"name" => "bar",
24+
"params" => {
25+
"wiffle" => "bar",
26+
"meep" => "eek"
27+
}
28+
],
29+
"description" => "foo"
30+
}
31+
end
32+
33+
let(:interaction_hash_with_different_params_order) do
34+
{
35+
"description" => "foo",
36+
"providerStates" => [
37+
"name" => "bar",
38+
"params" => {
39+
"meep" => "eek",
40+
"wiffle" => "bar"
41+
}
42+
]
43+
}
44+
end
45+
46+
it "generates a SHA based on the sorted keys" do
47+
expect(GenerateInteractionSha.call(interaction_hash)).to eq "5ec1cc12132d3437a5a2ced5537cdab2d4f44521"
48+
end
49+
50+
it "generates the same SHA if the top level keys are ordered differently" do
51+
expect(GenerateInteractionSha.call(interaction_hash)).to eq GenerateInteractionSha.call(interaction_hash_with_different_key_order)
52+
end
53+
54+
# This could be a whole lot smarter, but I'm not sure it's worth it.
55+
# eg. order of provider state params doesn't matter, but the ordering
56+
# of the provider states themselves may... who knows.
57+
# Let's not try and be too smart about it until we have a use case to flesh it out.
58+
it "generates a different SHA if any of the other keys are ordered differently" do
59+
expect(GenerateInteractionSha.call(interaction_hash)).to_not eq GenerateInteractionSha.call(interaction_hash_with_different_params_order)
60+
end
61+
62+
it "ignores any existing id in the hash" do
63+
interaction_hash["id"] = "foo"
64+
expect(GenerateInteractionSha.call(interaction_hash)).to eq "5ec1cc12132d3437a5a2ced5537cdab2d4f44521"
65+
end
66+
end
67+
end
68+
end
69+
end

spec/lib/pact_broker/pacts/service_spec.rb

+32
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ module Pacts
3131
let(:existing_pact) { nil }
3232
let(:new_pact) { double('new_pact', json_content: json_content) }
3333
let(:json_content) { { the: "contract" }.to_json }
34+
let(:json_content_with_ids) { { the: "contract with ids" }.to_json }
3435
let(:previous_pacts) { [] }
3536
let(:params) do
3637
{
@@ -40,10 +41,30 @@ module Pacts
4041
json_content: json_content
4142
}
4243
end
44+
let(:content) { double('content') }
45+
let(:content_with_interaction_ids) { double('content_with_interaction_ids', to_json: json_content_with_ids) }
46+
47+
before do
48+
allow(Content).to receive(:from_json).and_return(content)
49+
allow(content).to receive(:with_ids).and_return(content_with_interaction_ids)
50+
allow(PactBroker::Pacts::GenerateSha).to receive(:call).and_call_original
51+
end
4352

4453
subject { Service.create_or_update_pact(params) }
4554

55+
4656
context "when no pact exists with the same params" do
57+
it "creates the sha before adding the interaction ids" do
58+
expect(PactBroker::Pacts::GenerateSha).to receive(:call).ordered
59+
expect(content).to receive(:with_ids).ordered
60+
subject
61+
end
62+
63+
it "saves the pact interactions/messages with ids added to them" do
64+
expect(pact_repository).to receive(:create).with hash_including(json_content: json_content_with_ids)
65+
subject
66+
end
67+
4768
it "triggers webhooks for contract publications" do
4869
expect(webhook_service).to receive(:trigger_webhooks).with(new_pact, nil, PactBroker::Webhooks::WebhookEvent::CONTRACT_PUBLISHED)
4970
subject
@@ -53,6 +74,17 @@ module Pacts
5374
context "when a pact exists with the same params" do
5475
let(:existing_pact) { double('existing_pact', id: 4, json_content: { the: "contract" }.to_json) }
5576

77+
it "creates the sha before adding the interaction ids" do
78+
expect(PactBroker::Pacts::GenerateSha).to receive(:call).ordered
79+
expect(content).to receive(:with_ids).ordered
80+
subject
81+
end
82+
83+
it "saves the pact interactions/messages with ids added to them" do
84+
expect(pact_repository).to receive(:update).with(anything, hash_including(json_content: json_content_with_ids))
85+
subject
86+
end
87+
5688
it "triggers webhooks for contract publications" do
5789
expect(webhook_service).to receive(:trigger_webhooks).with(new_pact, nil, PactBroker::Webhooks::WebhookEvent::CONTRACT_PUBLISHED)
5890
subject

0 commit comments

Comments
 (0)