pact_broker 2.85.1 → 2.86.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release_gem.yml +1 -1
  3. data/.github/workflows/trigger_pact_docs_update.yml +22 -0
  4. data/CHANGELOG.md +10 -0
  5. data/DEVELOPER_DOCUMENTATION.md +0 -2
  6. data/db/migrations/20210914_add_labels_to_webhooks.rb +14 -0
  7. data/db/migrations/20210915_add_verified_by_to_verification.rb +6 -0
  8. data/docker-compose-ci-mysql.yml +1 -0
  9. data/docs/CONFIGURATION.md +255 -66
  10. data/docs/configuration.yml +166 -101
  11. data/lib/db.rb +0 -1
  12. data/lib/pact_broker/api/contracts/webhook_contract.rb +24 -2
  13. data/lib/pact_broker/api/decorators/verification_decorator.rb +4 -0
  14. data/lib/pact_broker/api/decorators/webhook_decorator.rb +27 -4
  15. data/lib/pact_broker/api/pact_broker_urls.rb +4 -0
  16. data/lib/pact_broker/api/resources/all_webhooks.rb +2 -2
  17. data/lib/pact_broker/config/runtime_configuration.rb +4 -0
  18. data/lib/pact_broker/config/runtime_configuration_database_methods.rb +1 -1
  19. data/lib/pact_broker/db/clean.rb +1 -2
  20. data/lib/pact_broker/doc/views/pacticipant/label.markdown +12 -0
  21. data/lib/pact_broker/doc/views/webhooks.markdown +17 -0
  22. data/lib/pact_broker/domain/pacticipant.rb +4 -0
  23. data/lib/pact_broker/domain/verification.rb +16 -4
  24. data/lib/pact_broker/domain/webhook.rb +5 -5
  25. data/lib/pact_broker/domain/webhook_pacticipant.rb +6 -0
  26. data/lib/pact_broker/locale/en.yml +1 -0
  27. data/lib/pact_broker/matrix/head_row.rb +1 -1
  28. data/lib/pact_broker/matrix/quick_row.rb +0 -1
  29. data/lib/pact_broker/matrix/repository.rb +0 -1
  30. data/lib/pact_broker/matrix/row.rb +2 -2
  31. data/lib/pact_broker/pacts/latest_pact_publication_id_for_consumer_version.rb +0 -1
  32. data/lib/pact_broker/pacts/pact_publication.rb +7 -0
  33. data/lib/pact_broker/pacts/pact_publication_clean_selector_dataset_module.rb +19 -0
  34. data/lib/pact_broker/pacts/pact_publication_dataset_module.rb +30 -2
  35. data/lib/pact_broker/pacts/pact_version.rb +24 -1
  36. data/lib/pact_broker/pacts/pacts_for_verification_repository.rb +2 -2
  37. data/lib/pact_broker/pacts/repository.rb +50 -47
  38. data/lib/pact_broker/test/http_test_data_builder.rb +37 -10
  39. data/lib/pact_broker/test/test_data_builder.rb +22 -5
  40. data/lib/pact_broker/verifications/repository.rb +5 -2
  41. data/lib/pact_broker/verifications/service.rb +4 -1
  42. data/lib/pact_broker/version.rb +1 -1
  43. data/lib/pact_broker/webhooks/event_listener.rb +4 -2
  44. data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +1 -0
  45. data/lib/pact_broker/webhooks/repository.rb +10 -4
  46. data/lib/pact_broker/webhooks/webhook.rb +66 -8
  47. data/script/data/verify-pact-for-multiple-selectors.rb +30 -0
  48. data/script/docs/generate-configuration-docs.rb +24 -3
  49. data/script/generate-erd +55 -0
  50. data/spec/features/create_webhook_spec.rb +55 -10
  51. data/spec/features/get_pact_spec.rb +2 -3
  52. data/spec/fixtures/verification.json +4 -0
  53. data/spec/integration/webhooks/pact_publication_spec.rb +51 -0
  54. data/spec/lib/pact_broker/api/contracts/webhook_contract_spec.rb +50 -0
  55. data/spec/lib/pact_broker/api/decorators/verification_decorator_spec.rb +8 -1
  56. data/spec/lib/pact_broker/api/decorators/verification_summary_decorator_spec.rb +3 -1
  57. data/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb +4 -4
  58. data/spec/lib/pact_broker/config/runtime_configuration_documentation_spec.rb +30 -0
  59. data/spec/lib/pact_broker/deployments/environment_service_spec.rb +1 -1
  60. data/spec/lib/pact_broker/matrix/head_row_spec.rb +9 -5
  61. data/spec/lib/pact_broker/pacts/{latest_tagged_pact_publications_spec.rb → pact_publication_clean_selector_dataset_module_spec.rb} +7 -9
  62. data/spec/lib/pact_broker/pacts/pact_version_spec.rb +32 -0
  63. data/spec/lib/pact_broker/pacts/repository_spec.rb +33 -0
  64. data/spec/lib/pact_broker/verifications/service_spec.rb +16 -2
  65. data/spec/lib/pact_broker/webhooks/repository_spec.rb +158 -15
  66. data/spec/lib/pact_broker/webhooks/webhook_spec.rb +8 -5
  67. metadata +16 -11
  68. data/lib/pact_broker/pacts/all_pact_publications.rb +0 -158
  69. data/lib/pact_broker/pacts/latest_pact_publications.rb +0 -48
  70. data/lib/pact_broker/pacts/latest_pact_publications_by_consumer_version.rb +0 -26
  71. data/lib/pact_broker/pacts/latest_tagged_pact_publications.rb +0 -45
  72. data/lib/pact_broker/verifications/latest_verification_for_pact_version.rb +0 -39
  73. data/spec/lib/pact_broker/verifications/latest_verification_for_pact_version_spec.rb +0 -18
@@ -30,12 +30,14 @@ module PactBroker
30
30
  end
31
31
 
32
32
  def find_by_consumer_and_or_provider consumer, provider
33
+
33
34
  where(
34
35
  Sequel.|(
35
36
  { consumer_id: consumer.id, provider_id: provider.id },
36
- { consumer_id: nil, provider_id: provider.id },
37
- { consumer_id: consumer.id, provider_id: nil },
38
- { consumer_id: nil, provider_id: nil}
37
+ { consumer_id: nil, provider_id: provider.id, consumer_label: nil },
38
+ { consumer_id: consumer.id, provider_id: nil, provider_label: nil },
39
+ { consumer_id: nil, provider_id: nil, consumer_label: nil, provider_label: nil },
40
+ *labels_criteria_for_consumer_or_provider(consumer, provider)
39
41
  )
40
42
  )
41
43
  end
@@ -51,6 +53,32 @@ module PactBroker
51
53
  def enabled
52
54
  where(enabled: true)
53
55
  end
56
+
57
+ private
58
+
59
+ def labels_criteria_for_consumer_or_provider(consumer, provider)
60
+ consumer_labels = consumer.labels.map(&:name)
61
+ provider_labels = provider.labels.map(&:name)
62
+
63
+ [].then do |criteria|
64
+ next criteria if consumer_labels.empty?
65
+ criteria + [
66
+ { consumer_label: consumer_labels, provider_label: nil, provider_id: nil },
67
+ { consumer_label: consumer_labels, provider_label: nil, provider_id: provider.id }
68
+ ]
69
+ end.then do |criteria|
70
+ next criteria if provider_labels.empty?
71
+ criteria + [
72
+ { provider_label: provider_labels, consumer_label: nil, consumer_id: nil },
73
+ { provider_label: provider_labels, consumer_label: nil, consumer_id: consumer.id }
74
+ ]
75
+ end.then do |criteria|
76
+ next criteria if consumer_labels.empty? || provider_labels.empty?
77
+ criteria + [
78
+ { consumer_label: consumer_labels, provider_label: provider_labels }
79
+ ]
80
+ end
81
+ end
54
82
  end
55
83
 
56
84
  def update_from_domain webhook
@@ -74,8 +102,8 @@ module PactBroker
74
102
  Domain::Webhook.new(
75
103
  uuid: uuid,
76
104
  description: description,
77
- consumer: consumer,
78
- provider: provider,
105
+ consumer: webhook_consumer,
106
+ provider: webhook_provider,
79
107
  events: events,
80
108
  request: Webhooks::WebhookRequestTemplate.new(request_attributes),
81
109
  enabled: enabled,
@@ -100,7 +128,15 @@ module PactBroker
100
128
  end
101
129
 
102
130
  def is_for? integration
103
- (consumer_id == integration.consumer_id || !consumer_id) && (provider_id == integration.provider_id || !provider_id)
131
+ (
132
+ consumer_id == integration.consumer_id ||
133
+ match_label?(:consumer, integration) ||
134
+ match_all?(:consumer)
135
+ ) && (
136
+ provider_id == integration.provider_id ||
137
+ match_label?(:provider, integration) ||
138
+ match_all?(:provider)
139
+ )
104
140
  end
105
141
 
106
142
  # Keep the triggered webhooks after the webhook has been deleted
@@ -110,7 +146,6 @@ module PactBroker
110
146
  super
111
147
  end
112
148
 
113
-
114
149
  def self.properties_hash_from_domain webhook
115
150
  is_json_request_body = !(String === webhook.request.body || webhook.request.body.nil?) # Can't rely on people to set content type
116
151
  {
@@ -122,9 +157,32 @@ module PactBroker
122
157
  enabled: webhook.enabled.nil? ? true : webhook.enabled,
123
158
  body: (is_json_request_body ? webhook.request.body.to_json : webhook.request.body),
124
159
  is_json_request_body: is_json_request_body,
125
- headers: webhook.request.headers
160
+ headers: webhook.request.headers,
161
+ consumer_label: webhook.consumer&.label,
162
+ provider_label: webhook.provider&.label
126
163
  }
127
164
  end
165
+
166
+ def webhook_consumer
167
+ return if consumer.nil? && consumer_label.nil?
168
+
169
+ Domain::WebhookPacticipant.new(name: consumer&.name, label: consumer_label)
170
+ end
171
+
172
+ def webhook_provider
173
+ return if provider.nil? && provider_label.nil?
174
+
175
+ Domain::WebhookPacticipant.new(name: provider&.name, label: provider_label)
176
+ end
177
+
178
+ def match_all?(name)
179
+ public_send(:"#{name}_id").nil? && public_send(:"#{name}_label").nil?
180
+ end
181
+
182
+ def match_label?(name, integration)
183
+ label = public_send(:"#{name}_label")
184
+ public_send(:"#{name}_id").nil? && integration.public_send(name).label?(label)
185
+ end
128
186
  end
129
187
  end
130
188
  end
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ begin
3
+
4
+ $LOAD_PATH << "#{Dir.pwd}/lib"
5
+ require "pact_broker/test/http_test_data_builder"
6
+ base_url = ENV["PACT_BROKER_BASE_URL"] || "http://localhost:9292"
7
+
8
+ td = PactBroker::Test::HttpTestDataBuilder.new(base_url)
9
+ td.delete_pacticipant("some-consumer")
10
+ .delete_pacticipant("some-provider")
11
+ .create_pacticipant("some-consumer")
12
+ .create_pacticipant("some-provider")
13
+ .publish_pact(consumer: "some-consumer", consumer_version: "1", provider: "some-provider", content_id: "111", branch: "main")
14
+ .publish_pact(consumer: "some-consumer", consumer_version: "2", provider: "some-provider", content_id: "111", branch: "feat/x")
15
+ .get_pacts_for_verification(
16
+ provider_version_tag: "main",
17
+ consumer_version_selectors: [{ branch: "main" }, { branch: "feat/x" }]
18
+ )
19
+ .verify_pact(
20
+ index: 0,
21
+ provider_version_tag: "main",
22
+ provider_version: "1",
23
+ success: true
24
+ )
25
+
26
+ rescue StandardError => e
27
+ puts "#{e.class} #{e.message}"
28
+ puts e.backtrace
29
+ exit 1
30
+ end
@@ -1,7 +1,16 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "yaml"
3
+ INTRO = <<EOM
4
+ <!-- This is a generated file. Please do not edit it directly. -->
5
+
6
+ The Pact Broker supports configuration via environment variables or a YAML file.
4
7
 
8
+ To configure the application using a YAML file, place it in the location `config/pact_broker.yml`,
9
+ relative to the working directory of the application, or set the environment
10
+ variable `PACT_BROKER_CONF` to the full path to the configuration file.
11
+ EOM
12
+
13
+ require "yaml"
5
14
  $stream = StringIO.new
6
15
 
7
16
  def write string
@@ -25,6 +34,7 @@ def in_backticks value
25
34
  end
26
35
 
27
36
  docs_dir = File.expand_path("../../../docs", __FILE__)
37
+ configuration_doc_path = File.join(docs_dir, "CONFIGURATION.md")
28
38
  environment_variable_file = File.join(docs_dir, "configuration.yml")
29
39
  doc = YAML.load(File.read(environment_variable_file))
30
40
 
@@ -42,6 +52,8 @@ write "# Pact Broker Configuration\n\n"
42
52
 
43
53
  write "\n"
44
54
 
55
+ write INTRO
56
+
45
57
  doc["groups"].each do | group |
46
58
  write "<br/>\n\n"
47
59
  write "## #{group["title"]}\n\n<hr/>\n"
@@ -55,9 +67,17 @@ doc["groups"].each do | group |
55
67
  write "### #{name}\n\n"
56
68
  write "#{metadata["description"]}\n\n"
57
69
 
70
+ write "**YAML configuration key name:** #{in_backticks(name)}<br/>"
71
+ write "**Environment variable name:** `PACT_BROKER_#{name.upcase}`<br/>"
72
+ write "**Supported versions:** #{metadata["supported_versions"]}<br/>" if metadata["supported_versions"]
58
73
  write "**Required:** #{metadata["required"] || "false"}<br/>" if metadata["required"]
59
74
  write "**Format:** #{metadata["format"]}<br/>" if metadata["format"]
60
- write "**Default:** #{in_backticks(metadata["default"])}<br/>" if !metadata["default"].nil?
75
+
76
+ write "**Default:** #{in_backticks(metadata["default_value"])}<br/>" if !metadata["default_value"].nil?
77
+ write "**Default:** #{metadata["default_description"]}<br/>" if !metadata["default_description"].nil?
78
+ if metadata["allowed_values_description"]
79
+ write "**Allowed values:** #{metadata["allowed_values_description"]}<br/>"
80
+ end
61
81
  if metadata["allowed_values"]
62
82
  allowed_values = metadata["allowed_values"].collect{ |val| in_backticks(val) }.join(", ")
63
83
  write "**Allowed values:** #{allowed_values}<br/>"
@@ -72,7 +92,7 @@ doc["groups"].each do | group |
72
92
  end
73
93
  end
74
94
 
75
- File.open(File.join(docs_dir, "CONFIGURATION.md"), "w") { |file| file << $stream.string }
95
+ File.open(configuration_doc_path, "w") { |file| file << $stream.string }
76
96
 
77
97
  required_env_vars = []
78
98
 
@@ -84,3 +104,4 @@ end
84
104
 
85
105
  puts "Required:"
86
106
  puts required_env_vars
107
+ puts configuration_doc_path
@@ -0,0 +1,55 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require "sequel"
4
+ Sequel::Model.plugin :subclasses
5
+
6
+ DATABASE_CREDENTIALS = {
7
+ adapter: "postgres",
8
+ database: ENV["DATABASE"] || "postgres",
9
+ username: ENV["DATABASE_USERNAME"] || "postgres",
10
+ password: ENV["DATABASE_PASSWORD"] || "postgres",
11
+ host: "localhost",
12
+ :encoding => "utf8"
13
+ }
14
+ Sequel.connect(DATABASE_CREDENTIALS)
15
+
16
+ $LOAD_PATH.unshift "./lib"
17
+ $LOAD_PATH.unshift "./tasks"
18
+ ENV["RACK_ENV"] = "development"
19
+ require "pact_broker/db/models"
20
+
21
+ def generate_erd_graphviz()
22
+ associations = []
23
+ Sequel::Model.descendents.each do |model|
24
+ next if model.name.nil?
25
+ model.associations.each do |a|
26
+ ar = model.association_reflection(a)
27
+ associations << [model.name, ar[:type], ar.associated_class.name]
28
+ end
29
+ end
30
+ styles = {
31
+ :many_to_one=>:bold,
32
+ :one_to_many=>:solid,
33
+ :many_to_many=>:dashed,
34
+ :one_to_one=>:dotted
35
+ }
36
+
37
+ graph = "digraph G {\n"
38
+ graph += associations.uniq.map{|c, t, ac| " \"#{c}\" -> \"#{ac}\" [style=#{styles[t]}];"}.sort.join("\n")
39
+ graph += "\n}"
40
+
41
+ graph
42
+ end
43
+
44
+ puts "Usage:"
45
+ puts "generate-erd [erd-filename]"
46
+ puts "CAUTION: Please make sure that you have graphviz installed"
47
+ puts
48
+
49
+ GRAPHVIZ_FILENAME = ARGV[0] || "erd"
50
+
51
+ puts "Generating graphviz file ..."
52
+ File.write("#{GRAPHVIZ_FILENAME}.dot", generate_erd_graphviz())
53
+
54
+ puts "Converting graphviz file to a pdf"
55
+ `dot -Tpdf "#{GRAPHVIZ_FILENAME}.dot" > "#{GRAPHVIZ_FILENAME}.pdf"`
@@ -6,10 +6,14 @@ describe "Creating a webhook" do
6
6
  let(:headers) { {"CONTENT_TYPE" => "application/json"} }
7
7
  let(:response_body) { JSON.parse(subject.body, symbolize_names: true)}
8
8
  let(:webhook_json) { webhook_hash.to_json }
9
+ let(:provider) { nil }
10
+ let(:consumer) { nil }
9
11
  let(:webhook_hash) do
10
12
  {
11
13
  description: "trigger build",
12
14
  enabled: false,
15
+ provider: provider,
16
+ consumer: consumer,
13
17
  events: [{
14
18
  name: "contract_content_changed"
15
19
  }],
@@ -23,7 +27,7 @@ describe "Creating a webhook" do
23
27
  a: "body"
24
28
  }
25
29
  }
26
- }
30
+ }.compact
27
31
  end
28
32
 
29
33
  subject { post(path, webhook_json, headers) }
@@ -64,33 +68,74 @@ describe "Creating a webhook" do
64
68
 
65
69
  context "for a provider" do
66
70
  let(:path) { "/webhooks" }
71
+ let(:provider) { { name: "Some Provider" } }
67
72
 
68
- before do
69
- webhook_hash[:provider] = { name: "Some Provider" }
70
- end
71
-
72
- its(:status) { is_expected.to be 201 }
73
+ its(:status) { is_expected.to eq 201 }
73
74
 
74
75
  it "creates a webhook without a consumer" do
75
76
  subject
76
77
  expect(PactBroker::Webhooks::Webhook.first.provider).to_not be nil
77
78
  expect(PactBroker::Webhooks::Webhook.first.consumer).to be nil
78
79
  end
80
+
81
+ context "with label" do
82
+ let(:provider) { { label: "my_label" } }
83
+
84
+ its(:status) { is_expected.to eq 201 }
85
+
86
+ it "creates a webhook without explicit consumer and provider with provider label" do
87
+ subject
88
+ expect(PactBroker::Webhooks::Webhook.first.provider).to be nil
89
+ expect(PactBroker::Webhooks::Webhook.first.consumer).to be nil
90
+ expect(PactBroker::Webhooks::Webhook.first.provider_label).to eq "my_label"
91
+ end
92
+ end
93
+
94
+ context "with both label and name" do
95
+ let(:provider) { { name: "Some Provider", label: "my_label" } }
96
+
97
+ its(:status) { is_expected.to eq 400 }
98
+
99
+ it "returns the validation errors" do
100
+ expect(response_body[:errors]).to_not be_empty
101
+ end
102
+ end
79
103
  end
80
104
 
81
105
  context "for a consumer" do
82
106
  let(:path) { "/webhooks" }
83
- before do
84
- webhook_hash[:consumer] = { name: "Some Consumer" }
85
- end
107
+ let(:consumer) { { name: "Some Consumer" } }
86
108
 
87
- its(:status) { is_expected.to be 201 }
109
+ its(:status) { is_expected.to eq 201 }
88
110
 
89
111
  it "creates a webhook without a provider" do
90
112
  subject
91
113
  expect(PactBroker::Webhooks::Webhook.first.consumer).to_not be nil
92
114
  expect(PactBroker::Webhooks::Webhook.first.provider).to be nil
93
115
  end
116
+
117
+ context "with label" do
118
+ let(:consumer) { { label: "my_label" } }
119
+
120
+ its(:status) { is_expected.to eq 201 }
121
+
122
+ it "creates a webhook without explicit consumer and provider with consumer label" do
123
+ subject
124
+ expect(PactBroker::Webhooks::Webhook.first.provider).to be nil
125
+ expect(PactBroker::Webhooks::Webhook.first.consumer).to be nil
126
+ expect(PactBroker::Webhooks::Webhook.first.consumer_label).to eq "my_label"
127
+ end
128
+ end
129
+
130
+ context "with both label and name" do
131
+ let(:consumer) { { name: "Some Consumer", label: "my_label" } }
132
+
133
+ its(:status) { is_expected.to eq 400 }
134
+
135
+ it "returns the validation errors" do
136
+ expect(response_body[:errors]).to_not be_empty
137
+ end
138
+ end
94
139
  end
95
140
 
96
141
  context "with no consumer or provider" do
@@ -6,7 +6,7 @@ describe "retrieving a pact" do
6
6
  let(:path) { "/pacts/provider/a%20provider/consumer/a%20consumer/version/1.2.3A" }
7
7
 
8
8
  before do
9
- TestDataBuilder.new.create_pact_with_hierarchy("A Consumer", "1.2.3a", "A Provider").and_return(:pact)
9
+ td.create_pact_with_hierarchy("A Consumer", "1.2.3a", "A Provider").and_return(:pact)
10
10
  end
11
11
 
12
12
  context "when case sensitivity is turned on" do
@@ -35,8 +35,7 @@ describe "retrieving a pact" do
35
35
  let(:path) { "/pacts/provider/a%20provider/consumer/a%20consumer/latest/PROD" }
36
36
 
37
37
  before do
38
- TestDataBuilder.new
39
- .create_consumer("A Consumer")
38
+ td.create_consumer("A Consumer")
40
39
  .create_consumer_version("1.2.3")
41
40
  .create_consumer_version_tag("prod")
42
41
  .create_provider("A Provider")
@@ -3,5 +3,9 @@
3
3
  "providerApplicationVersion": "4.5.6",
4
4
  "testResults": {
5
5
  "some": "results"
6
+ },
7
+ "verifiedBy": {
8
+ "implementation" : "Ruby",
9
+ "version": "1234"
6
10
  }
7
11
  }
@@ -0,0 +1,51 @@
1
+ RSpec.describe "triggering a webhook for a pact publication" do
2
+ before do
3
+ td.create_global_webhook(event_names: ["contract_published"], body: { "provider_version" => "${pactbroker.providerVersionNumber}"})
4
+ end
5
+
6
+ let(:pact_content) { td.random_json_content("Foo", "Bar") }
7
+
8
+ let!(:request) do
9
+ stub_request(:post, /http/).with(body: expected_webhook_body).to_return(:status => 200)
10
+ end
11
+
12
+ let(:database_connector) { ->(&block) { block.call } }
13
+
14
+ subject { put("/pacts/provider/Bar/consumer/Foo/version/2", pact_content, { "CONTENT_TYPE" => "application/json", "pactbroker.database_connector" => database_connector}) }
15
+
16
+ context "when there is a verification from the main branch of the provider" do
17
+ before do
18
+ td.create_consumer("Foo")
19
+ .create_provider("Bar", main_branch: "main")
20
+ .create_consumer_version("1")
21
+ .create_pact(json_content: pact_content)
22
+ .create_verification(provider_version: "1", branch: "main")
23
+ .create_verification(provider_version: "2", branch: "feat/x", number: 2)
24
+ end
25
+
26
+ let(:expected_webhook_body) { { provider_version: "1"}.to_json }
27
+
28
+ it "uses that in the webhook" do
29
+ subject
30
+ expect(request).to have_been_made
31
+ end
32
+ end
33
+
34
+ context "when there is not a verification from the main branch of the provider" do
35
+ before do
36
+ td.create_consumer("Foo")
37
+ .create_provider("Bar", main_branch: "main")
38
+ .create_consumer_version("1")
39
+ .create_pact(json_content: pact_content)
40
+ .create_verification(provider_version: "1", branch: "feat/y")
41
+ .create_verification(provider_version: "2", branch: "feat/x", number: 2)
42
+ end
43
+
44
+ let(:expected_webhook_body) { { provider_version: "2"}.to_json }
45
+
46
+ it "uses the latest verification in the webhook" do
47
+ subject
48
+ expect(request).to have_been_made
49
+ end
50
+ end
51
+ end