pact_broker 2.55.0 → 2.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7a4ca507c57031ed0e5aa7f988f4a0826bd97c2d8904431da26052fb6884558
4
- data.tar.gz: e700fab3146413a638120a9d73655ee5b6ae8393932223654091588e1682798c
3
+ metadata.gz: f4391656ae9cddad910749af5c6bb4c3c94644cf605122e31a40ce8891feb49d
4
+ data.tar.gz: db46a7cdf21674996ed6271dac6c13e2238771004d76693fea6bc11e18fdd146
5
5
  SHA512:
6
- metadata.gz: e4d48c58ceb23d1b20dfdd3e263d52c69e30355c72bc27e00795c0b11a2a56b5c9bf52034b2f0e8d2dd2d09480ac7e6a5ae474d34457de7653e381fd6518e62c
7
- data.tar.gz: e454fa82a641873ae68221190d91c84830aa0def182d6637fca1a34a9a1aecf64a4f165221283d5850deaef1a2d5580610b0ca214b6b2885b6ebe14bdc56250e
6
+ metadata.gz: 77ebcb58e85910f527b5750b4a484d856cb7a84369e4d6dc89097e3cc706c8077e1f2cc83ff8f7c5e1e079eb55b686680cacccb52c79a20a6b42b903d98e8784
7
+ data.tar.gz: c151197191dab0a0b35dd08f7ec0df10a280f28f372a5d72365052a813b67265a2cd1e3d602927641169b5e72fd9e1b6547c0e04a260895693c6a65415fcace0
@@ -1,3 +1,25 @@
1
+ <a name="v2.56.0"></a>
2
+ ### v2.56.0 (2020-06-01)
3
+
4
+
5
+ #### Features
6
+
7
+ * **database**
8
+ * log schema versions and migration info on startup ([b385e535](/../../commit/b385e535))
9
+ * allow options to be passed to Sequel migrate via the MigrationTask ([143613e7](/../../commit/143613e7))
10
+
11
+ * allow Pactflow messages in logs to be hidden by setting PACT_BROKER_HIDE_PACTFLOW_MESSAGES=true ([a7550105](/../../commit/a7550105))
12
+
13
+ * **can-i-deploy**
14
+ * experimental - add a warning message if there are interactions missing verification test results. ([f7ab8cc5](/../../commit/f7ab8cc5))
15
+
16
+
17
+ #### Bug Fixes
18
+
19
+ * use relative URLs when base_url not explictly set to ensure app is not vulnerable to host header attacks ([92c45a0a](/../../commit/92c45a0a))
20
+ * raise PactBroker::Error when either pacticipant is not found in the business layer while attempting to delete an integration ([3c209a46](/../../commit/3c209a46))
21
+
22
+
1
23
  <a name="v2.55.0"></a>
2
24
  ### v2.55.0 (2020-05-22)
3
25
 
@@ -22,6 +22,11 @@ module PactBroker
22
22
  "There are no missing dependencies"
23
23
  when PactBroker::Matrix::Successful
24
24
  "All required verification results are published and successful"
25
+ when PactBroker::Matrix::InteractionsMissingVerifications
26
+ descriptions = reason.interactions.collect do | interaction |
27
+ interaction_description(interaction)
28
+ end.join('; ')
29
+ "WARNING: Although the verification was reported as successful, the results for #{reason.consumer_selector.description} and #{reason.provider_selector.description} may be missing tests for the following interactions: #{descriptions}"
25
30
  else
26
31
  reason
27
32
  end
@@ -44,6 +49,18 @@ module PactBroker
44
49
  ""
45
50
  end
46
51
  end
52
+
53
+ # TODO move this somewhere else
54
+ def interaction_description(interaction)
55
+ if interaction['providerState'] && interaction['providerState'] != ''
56
+ "#{interaction['description']} given #{interaction['providerState']}"
57
+ elsif interaction['providerStates'] && interaction['providerStates'].is_a?(Array) && interaction['providerStates'].any?
58
+ provider_states = interaction['providerStates'].collect{ |ps| ps['name'] }.compact.join(', ')
59
+ "#{interaction['description']} given #{provider_states}"
60
+ else
61
+ interaction['description']
62
+ end
63
+ end
47
64
  end
48
65
  end
49
66
  end
@@ -81,11 +81,18 @@ module PactBroker
81
81
  end
82
82
 
83
83
  def prepare_database
84
+ logger.info "Database schema version is #{PactBroker::DB.version(configuration.database_connection)}"
84
85
  if configuration.auto_migrate_db
85
- logger.info "Migrating database"
86
- PactBroker::DB.run_migrations configuration.database_connection, allow_missing_migration_files: configuration.allow_missing_migration_files
86
+ migration_options = { allow_missing_migration_files: configuration.allow_missing_migration_files }
87
+ if PactBroker::DB.is_current?(configuration.database_connection, migration_options)
88
+ logger.info "Skipping database migrations as the latest migration has already been applied"
89
+ else
90
+ logger.info "Migrating database schema"
91
+ PactBroker::DB.run_migrations configuration.database_connection, migration_options
92
+ logger.info "Database schema version is now #{PactBroker::DB.version(configuration.database_connection)}"
93
+ end
87
94
  else
88
- logger.info "Skipping database migrations"
95
+ logger.info "Skipping database schema migrations as database auto migrate is disabled"
89
96
  end
90
97
 
91
98
  if configuration.auto_migrate_db_data
@@ -232,7 +239,9 @@ module PactBroker
232
239
  end
233
240
 
234
241
  def print_startup_message
235
- logger.info "\n\n#{'*' * 80}\n\nWant someone to manage your Pact Broker for you? Check out https://pactflow.io/oss for a hardened, fully supported SaaS version of the Pact Broker with an improved UI + more.\n\n#{'*' * 80}\n"
242
+ if ENV['PACT_BROKER_HIDE_PACTFLOW_MESSAGES'] != 'true'
243
+ logger.info "\n\n#{'*' * 80}\n\nWant someone to manage your Pact Broker for you? Check out https://pactflow.io/oss for a hardened, fully supported SaaS version of the Pact Broker with an improved UI + more.\n\n#{'*' * 80}\n"
244
+ end
236
245
  end
237
246
  end
238
247
  end
@@ -2,12 +2,12 @@ require 'sequel'
2
2
  require 'pact_broker/db/validate_encoding'
3
3
  require 'pact_broker/db/migrate'
4
4
  require 'pact_broker/db/migrate_data'
5
+ require 'pact_broker/db/version'
5
6
 
6
7
  Sequel.datetime_class = DateTime
7
8
 
8
9
  module PactBroker
9
10
  module DB
10
-
11
11
  MIGRATIONS_DIR = File.expand_path("../../../db/migrations", __FILE__)
12
12
 
13
13
  def self.connection= connection
@@ -27,6 +27,14 @@ module PactBroker
27
27
  PactBroker::DB::MigrateData.(database_connection)
28
28
  end
29
29
 
30
+ def self.is_current? database_connection, options = {}
31
+ Sequel::TimestampMigrator.is_current?(database_connection, PactBroker::DB::MIGRATIONS_DIR, options)
32
+ end
33
+
34
+ def self.version database_connection
35
+ PactBroker::DB::Version.call(database_connection)
36
+ end
37
+
30
38
  def self.validate_connection_config
31
39
  PactBroker::DB::ValidateEncoding.(connection)
32
40
  end
@@ -46,7 +46,11 @@ module PactBroker
46
46
  private
47
47
 
48
48
  def base_url
49
- PactBroker.configuration.base_url || request.base_url
49
+ # Using the X-Forwarded headers in the UI can leave the app vulnerable
50
+ # https://www.acunetix.com/blog/articles/automated-detection-of-host-header-attacks/
51
+ # Either use the explicitly configured base url or an empty string,
52
+ # rather than request.base_url, which uses the X-Forwarded headers.
53
+ PactBroker.configuration.base_url || ''
50
54
  end
51
55
  end
52
56
  end
@@ -2,6 +2,7 @@ require 'json'
2
2
  require 'sequel'
3
3
  require 'pact_broker/repositories/helpers'
4
4
  require 'pact_broker/tags/tag_with_latest_flag'
5
+ require 'pact_broker/pacts/content'
5
6
 
6
7
 
7
8
  module PactBroker
@@ -83,6 +84,18 @@ module PactBroker
83
84
  def latest_pact_publication
84
85
  pact_version.latest_pact_publication
85
86
  end
87
+
88
+ def interactions_missing_test_results
89
+ @interactions_missing_test_results ||= pact_content_with_test_results.interactions_missing_test_results
90
+ end
91
+
92
+ def all_interactions_missing_test_results?
93
+ pact_content_with_test_results.interactions.count == pact_content_with_test_results.interactions_missing_test_results.count
94
+ end
95
+
96
+ def pact_content_with_test_results
97
+ @pact_content_with_test_results = PactBroker::Pacts::Content.from_json(pact_version.content).with_test_results(test_results)
98
+ end
86
99
  end
87
100
 
88
101
  Verification.plugin :timestamps
@@ -31,8 +31,8 @@ module PactBroker
31
31
  end
32
32
 
33
33
  def self.delete(consumer_name, provider_name)
34
- consumer = pacticipant_service.find_pacticipant_by_name(consumer_name)
35
- provider = pacticipant_service.find_pacticipant_by_name(provider_name)
34
+ consumer = pacticipant_service.find_pacticipant_by_name!(consumer_name)
35
+ provider = pacticipant_service.find_pacticipant_by_name!(provider_name)
36
36
  # this takes care of the triggered webhooks and webhook executions
37
37
  pact_service.delete_all_pact_publications_between(consumer_name, and: provider_name)
38
38
  verification_service.delete_all_verifications_between(consumer_name, and: provider_name)
@@ -34,7 +34,9 @@ module PactBroker
34
34
  message = "#{e.class} #{e.message}\n#{e.backtrace.join("\n")}"
35
35
  message = "#{description} - #{message}" if description
36
36
  logger.error message
37
- logger.info "\n\n#{'*' * 80}\n\nPrefer it was someone else's job to deal with this error? Check out https://pactflow.io/oss for a hardened, fully supported SaaS version of the Pact Broker with an improved UI + more.\n\n#{'*' * 80}\n"
37
+ if ENV['PACT_BROKER_HIDE_PACTFLOW_MESSAGES'] != 'true'
38
+ logger.info "\n\n#{'*' * 80}\n\nPrefer it was someone else's job to deal with this error? Check out https://pactflow.io/oss for a hardened, fully supported SaaS version of the Pact Broker with an improved UI + more.\n\n#{'*' * 80}\n"
39
+ end
38
40
  end
39
41
  end
40
42
 
@@ -31,7 +31,7 @@ module PactBroker
31
31
  end
32
32
 
33
33
  def reasons
34
- error_messages.any? ? error_messages : success_messages
34
+ error_messages.any? ? error_messages + warning_messages : success_messages + warning_messages
35
35
  end
36
36
 
37
37
  private
@@ -51,6 +51,10 @@ module PactBroker
51
51
  end
52
52
  end
53
53
 
54
+ def warning_messages
55
+ warnings_for_missing_interactions
56
+ end
57
+
54
58
  def specified_selectors_that_do_not_exist
55
59
  resolved_selectors.select(&:specified_version_that_does_not_exist?)
56
60
  end
@@ -182,6 +186,24 @@ module PactBroker
182
186
  [dummy_consumer_selector, dummy_provider_selector]
183
187
  end.flatten
184
188
  end
189
+
190
+ # experimental
191
+ def warnings_for_missing_interactions
192
+ rows.select(&:success).collect do | row |
193
+ begin
194
+ if row.verification.interactions_missing_test_results.any? && !row.verification.all_interactions_missing_test_results?
195
+ InteractionsMissingVerifications.new(selector_for(row.consumer_name), selector_for(row.provider_name), row.verification.interactions_missing_test_results)
196
+ end
197
+ rescue StandardError => e
198
+ log_error(e, "Error determining if there were missing interaction verifications")
199
+ nil
200
+ end
201
+ end.compact.tap { |it| report_missing_interaction_verifications(it) if it.any? }
202
+ end
203
+
204
+ def report_missing_interaction_verifications(messages)
205
+ logger.warn("Interactions missing verifications", messages)
206
+ end
185
207
  end
186
208
  end
187
209
  end
@@ -73,5 +73,14 @@ module PactBroker
73
73
  # provider verifications.
74
74
  class NoDependenciesMissing < Reason
75
75
  end
76
+
77
+ class InteractionsMissingVerifications < ErrorReasonWithTwoSelectors
78
+ attr_reader :interactions
79
+
80
+ def initialize(consumer_selector, provider_selector, interactions)
81
+ super(consumer_selector, provider_selector)
82
+ @interactions = interactions
83
+ end
84
+ end
76
85
  end
77
86
  end
@@ -31,14 +31,25 @@ module PactBroker
31
31
  Content.from_hash(SortContent.call(pact_hash))
32
32
  end
33
33
 
34
+ def interactions_missing_test_results
35
+ interactions.reject do | interaction |
36
+ interaction['tests']&.any?
37
+ end
38
+ end
39
+
34
40
  def with_test_results(test_results)
41
+ tests = test_results && test_results['tests']
42
+ if tests.nil? || !tests.is_a?(Array) || tests.empty?
43
+ tests = []
44
+ end
45
+
35
46
  new_pact_hash = pact_hash.dup
36
47
  if interactions && interactions.is_a?(Array)
37
- new_pact_hash['interactions'] = merge_verification_results(interactions, test_results)
48
+ new_pact_hash['interactions'] = merge_verification_results(interactions, tests)
38
49
  end
39
50
 
40
51
  if messages && messages.is_a?(Array)
41
- new_pact_hash['messages'] = merge_verification_results(messages, test_results)
52
+ new_pact_hash['messages'] = merge_verification_results(messages, tests)
42
53
  end
43
54
  Content.from_hash(new_pact_hash)
44
55
  end
@@ -107,6 +118,19 @@ module PactBroker
107
118
  end
108
119
  end
109
120
  end
121
+
122
+ def merge_verification_results(interactions, tests)
123
+ interactions.collect(&:dup).collect do | interaction |
124
+ interaction['tests'] = tests.select do | test |
125
+ test_is_for_interaction(interaction, test)
126
+ end
127
+ interaction
128
+ end
129
+ end
130
+
131
+ def test_is_for_interaction(interaction, test)
132
+ test.is_a?(Hash) && interaction.is_a?(Hash) && test['interactionDescription'] == interaction['description'] && test['interactionProviderState'] == interaction['providerState']
133
+ end
110
134
  end
111
135
  end
112
136
  end
@@ -107,8 +107,8 @@ module PactBroker
107
107
  end
108
108
 
109
109
  def delete_all_pact_publications_between consumer_name, options
110
- consumer = pacticipant_repository.find_by_name(consumer_name)
111
- provider = pacticipant_repository.find_by_name(options.fetch(:and))
110
+ consumer = pacticipant_repository.find_by_name!(consumer_name)
111
+ provider = pacticipant_repository.find_by_name!(options.fetch(:and))
112
112
  query = PactPublication.where(consumer: consumer, provider: provider)
113
113
  query = query.tag(options[:tag]) if options[:tag]
114
114
 
@@ -16,8 +16,10 @@ module PactBroker
16
16
  class MigrationTask < ::Rake::TaskLib
17
17
 
18
18
  attr_accessor :database_connection
19
+ attr_accessor :options
19
20
 
20
21
  def initialize &block
22
+ @options = {}
21
23
  rake_task &block
22
24
  end
23
25
 
@@ -27,12 +29,29 @@ module PactBroker
27
29
  desc "Run sequel migrations for pact broker database"
28
30
  task :migrate, [:target] do | t, args |
29
31
  require 'pact_broker/db/migrate'
32
+ require 'pact_broker/db/version'
33
+
30
34
  instance_eval(&block)
31
- options = {}
35
+
32
36
  if args[:target]
33
37
  options[:target] = args[:target].to_i
34
38
  end
39
+
40
+ if (logger = database_connection.loggers.first)
41
+ current_version = PactBroker::DB::Version.call(database_connection)
42
+ if options[:target]
43
+ logger.info "Migrating from schema version #{current_version} to #{options[:target]}"
44
+ else
45
+ logger.info "Migrating from schema version #{current_version} to latest"
46
+ end
47
+ end
48
+
35
49
  PactBroker::DB::Migrate.call(database_connection, options)
50
+
51
+ if logger
52
+ current_version = PactBroker::DB::Version.call(database_connection)
53
+ logger.info "Current schema version is now #{current_version}"
54
+ end
36
55
  end
37
56
  end
38
57
  end
@@ -12,7 +12,7 @@ module PactBroker
12
12
  set :dump_errors, false # The padrino logger logs these for us. If this is enabled we get duplicate logging.
13
13
 
14
14
  def base_url
15
- PactBroker.configuration.base_url || request.base_url
15
+ PactBroker.configuration.base_url || ''
16
16
  end
17
17
  end
18
18
  end
@@ -7,7 +7,7 @@
7
7
  %a{href: "#{base_url}?tags=true"}
8
8
  Show latest tags
9
9
  - else
10
- %a{href: "#{base_url}"}
10
+ %a{href: "#{base_url}/"}
11
11
  Hide latest tags
12
12
 
13
13
  %a{href: "#{base_url}/hal-browser/browser.html"}
@@ -136,8 +136,8 @@ module PactBroker
136
136
  end
137
137
 
138
138
  def delete_all_verifications_between(consumer_name, options)
139
- consumer = pacticipant_repository.find_by_name(consumer_name)
140
- provider = pacticipant_repository.find_by_name(options.fetch(:and))
139
+ consumer = pacticipant_repository.find_by_name!(consumer_name)
140
+ provider = pacticipant_repository.find_by_name!(options.fetch(:and))
141
141
  PactBroker::Domain::Verification.where(provider: provider, consumer: consumer).delete
142
142
  end
143
143
 
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.55.0'
2
+ VERSION = '2.56.0'
3
3
  end
@@ -2,8 +2,10 @@
2
2
  "success": true,
3
3
  "providerApplicationVersion": "1.0.0",
4
4
  "testResults": {
5
- "examples": [
5
+ "tests": [
6
6
  {
7
+ "interactionDescription": "a request for something",
8
+ "interactionProviderState": null,
7
9
  "description": "has status code 200",
8
10
  "file_path": "/redacted/.gem/ruby/2.4.1/gems/pact-1.17.0/lib/pact/provider/rspec.rb",
9
11
  "full_description": "Verifying a pact between me and they Greeting with GET / returns a response which has status code 200",
@@ -17,6 +17,17 @@
17
17
  "status": 200,
18
18
  "body" : "something"
19
19
  }
20
+ },{
21
+ "description" : "a request for something else",
22
+ "providerState": null,
23
+ "request": {
24
+ "method": "get",
25
+ "path" : "/something"
26
+ },
27
+ "response": {
28
+ "status": 200,
29
+ "body" : "something"
30
+ }
20
31
  }
21
32
  ]
22
33
  }
@@ -1,4 +1,4 @@
1
- describe "Deleting pact versions" do
1
+ describe "Deleting an integration" do
2
2
 
3
3
  let(:path) { "/integrations/provider/Bar/consumer/Foo" }
4
4
 
@@ -19,7 +19,7 @@ describe "Deleting pact versions" do
19
19
  end
20
20
  end
21
21
 
22
- context "when the pact does not exist" do
22
+ context "when the integration does not exist" do
23
23
  it "returns a 404 Not Found" do
24
24
  expect(subject.status).to be 404
25
25
  end
@@ -32,5 +32,21 @@ describe "UI index" do
32
32
  expect(subject.body.scan('<tr').to_a.count).to eq 3
33
33
  end
34
34
  end
35
+
36
+ context "with the base_url not set" do
37
+ it "returns relative links" do
38
+ expect(subject.body).to include "href='/stylesheets"
39
+ end
40
+ end
41
+
42
+ context "with the base_url set" do
43
+ before do
44
+ allow(PactBroker.configuration).to receive(:base_url).and_return('http://example.org/pact-broker')
45
+ end
46
+
47
+ it "returns absolute links" do
48
+ expect(subject.body).to include "href='http://example.org/pact-broker/stylesheets"
49
+ end
50
+ end
35
51
  end
36
52
  end
@@ -31,7 +31,7 @@ describe "UI matrix" do
31
31
  subject { get("/matrix?q%5B%5Dpacticipant=Foo&q%5B%5Dtag=ctag&q%5B%5Dlatest=true&q%5B%5Dpacticipant=Bar&q%5B%5Dtag=ptag&q%5B%5Dlatest=true&latestby=cvpv&limit=100") }
32
32
 
33
33
  it "returns a page with a badge" do
34
- expect(subject.body).to include "http://example.org/matrix/provider/Bar/latest/ptag/consumer/Foo/latest/ctag/badge.svg"
34
+ expect(subject.body).to include "/matrix/provider/Bar/latest/ptag/consumer/Foo/latest/ctag/badge.svg"
35
35
  end
36
36
  end
37
37
  end
@@ -10,7 +10,7 @@ module PactBroker
10
10
 
11
11
  describe "the number of Reason classes" do
12
12
  it "is 9 - add another spec here if a new Reason is added" do
13
- expect(REASON_CLASSES.size).to eq 9
13
+ expect(REASON_CLASSES.size).to eq 10
14
14
  end
15
15
  end
16
16
 
@@ -52,6 +52,23 @@ module PactBroker
52
52
 
53
53
  its(:to_s) { is_expected.to eq "All required verification results are published and successful" }
54
54
  end
55
+
56
+ context "when the reason is PactBroker::Matrix::InteractionsMissingVerifications" do
57
+ let(:reason) { PactBroker::Matrix::InteractionsMissingVerifications.new(consumer_selector, provider_selector, interactions) }
58
+ let(:interactions) do
59
+ [
60
+ {
61
+ "description" => "desc1",
62
+ "providerState" => "p2"
63
+ },{
64
+ "description" => "desc1",
65
+ "providerStates" => [ { "name" => "desc3"}, { "name" => "desc4"} ]
66
+ }
67
+ ]
68
+ end
69
+
70
+ its(:to_s) { is_expected.to eq "WARNING: Although the verification was reported as successful, the results for version 2 of Foo and version 6 of Bar may be missing tests for the following interactions: desc1 given p2; desc1 given desc3, desc4" }
71
+ end
55
72
  end
56
73
  end
57
74
  end
@@ -26,6 +26,22 @@ module PactBroker
26
26
  subject
27
27
  expect(last_response.body).to include "<html>"
28
28
  end
29
+
30
+ context "with the base_url not set" do
31
+ it "returns relative links" do
32
+ expect(subject.body).to include "href='/css"
33
+ end
34
+ end
35
+
36
+ context "with the base_url set" do
37
+ before do
38
+ allow(PactBroker.configuration).to receive(:base_url).and_return('http://example.org/pact-broker')
39
+ end
40
+
41
+ it "returns absolute links" do
42
+ expect(subject.body).to include "href='http://example.org/pact-broker/css"
43
+ end
44
+ end
29
45
  end
30
46
 
31
47
  context "when the resource does not exist" do
@@ -248,6 +248,12 @@ module PactBroker
248
248
  Service.delete("Foo", "Foo")
249
249
  end
250
250
  end
251
+
252
+ context "when the pacticipants are not found for some bizarre reason (I can't see how this can happen, but it has)" do
253
+ it "raises an error" do
254
+ expect { Service.delete("Foo", "Bar") }.to raise_error(PactBroker::Error, /found/)
255
+ end
256
+ end
251
257
  end
252
258
 
253
259
  describe "delete_all" do
@@ -29,7 +29,8 @@ module PactBroker
29
29
  provider_name: bar.name,
30
30
  provider_id: bar.id,
31
31
  success: row_1_success,
32
- pacticipant_names: [foo.name, bar.name]
32
+ pacticipant_names: [foo.name, bar.name],
33
+ verification: verification_1
33
34
  )
34
35
  end
35
36
 
@@ -46,7 +47,8 @@ module PactBroker
46
47
  provider_name: baz.name,
47
48
  provider_id: baz.id,
48
49
  success: true,
49
- pacticipant_names: [foo.name, baz.name]
50
+ pacticipant_names: [foo.name, baz.name],
51
+ verification: verification_2
50
52
  )
51
53
  end
52
54
 
@@ -65,6 +67,8 @@ module PactBroker
65
67
  let(:foo_version) { double('foo version', number: "ddec8101dabf4edf9125a69f9a161f0f294af43c", id: 10)}
66
68
  let(:bar_version) { double('bar version', number: "14131c5da3abf323ccf410b1b619edac76231243", id: 11)}
67
69
  let(:baz_version) { double('baz version', number: "4ee06460f10e8207ad904fa9fa6c4842e462ab59", id: 12)}
70
+ let(:verification_1) { double('verification 1', interactions_missing_test_results: []) }
71
+ let(:verification_2) { double('verification 2', interactions_missing_test_results: []) }
68
72
 
69
73
  let(:resolved_selectors) do
70
74
  [
@@ -416,6 +416,49 @@ module PactBroker
416
416
  expect(subject.deployment_status_summary).to be_deployable
417
417
  end
418
418
  end
419
+
420
+ describe "when verification results are published missing tests for some interactions" do
421
+ let(:pact_content) do
422
+ {
423
+ "interactions" => [
424
+ {
425
+ "description" => "desc1"
426
+ },{
427
+ "description" => "desc2"
428
+ }
429
+ ]
430
+ }
431
+ end
432
+
433
+ let(:verification_tests) do
434
+ [
435
+ {
436
+ "interactionDescription" => "desc1"
437
+ }
438
+ ]
439
+ end
440
+
441
+ before do
442
+ td.create_consumer("Foo")
443
+ .create_provider("Bar")
444
+ .create_consumer_version
445
+ .create_pact(json_content: pact_content.to_json)
446
+ .create_verification(provider_version: "1", test_results: { tests: verification_tests })
447
+ end
448
+
449
+ let(:selectors) do
450
+ [
451
+ UnresolvedSelector.new(pacticipant_name: "Foo", latest: true),
452
+ UnresolvedSelector.new(pacticipant_name: "Bar", latest: true)
453
+ ]
454
+ end
455
+
456
+ let(:options) { { latestby: "cvpv"} }
457
+
458
+ it "should include a warning" do
459
+ expect(subject.deployment_status_summary.reasons.last).to be_a(PactBroker::Matrix::InteractionsMissingVerifications)
460
+ end
461
+ end
419
462
  end
420
463
  end
421
464
  end
@@ -227,6 +227,131 @@ module PactBroker
227
227
  its(:pact_specification_version) { is_expected.to eq nil }
228
228
  end
229
229
  end
230
+
231
+ describe "with_test_results" do
232
+ let(:test_results) do
233
+ {
234
+ "tests" => [
235
+ {
236
+ "interactionProviderState" => "ps1",
237
+ "interactionDescription" => "desc1"
238
+ },{
239
+ "interactionProviderState" => "ps1",
240
+ "interactionDescription" => "desc1"
241
+ }
242
+ ]
243
+ }
244
+ end
245
+
246
+ let(:pact_content) do
247
+ {
248
+ "interactions" => [
249
+ {
250
+ "providerState" => "ps1",
251
+ "description" => "desc1",
252
+ },
253
+ {
254
+ "providerState" => "ps2",
255
+ "description" => "desc2",
256
+ }
257
+ ]
258
+ }
259
+ end
260
+
261
+ subject { Content.from_hash(pact_content).with_test_results(test_results) }
262
+
263
+ let(:merged) do
264
+ {
265
+ "interactions" => [
266
+ {
267
+ "providerState" => "ps1",
268
+ "description" => "desc1",
269
+ "tests" => [
270
+ {
271
+ "interactionProviderState" => "ps1",
272
+ "interactionDescription" => "desc1",
273
+ },{
274
+ "interactionProviderState" => "ps1",
275
+ "interactionDescription" => "desc1",
276
+ }
277
+ ]
278
+ },{
279
+ "providerState" => "ps2",
280
+ "description" => "desc2",
281
+ "tests" => []
282
+ }
283
+ ]
284
+ }
285
+ end
286
+
287
+ let(:merged_with_empty_tests) do
288
+ {
289
+ "interactions" => [
290
+ {
291
+ "providerState" => "ps1",
292
+ "description" => "desc1",
293
+ "tests" => []
294
+ },{
295
+ "providerState" => "ps2",
296
+ "description" => "desc2",
297
+ "tests" => []
298
+ }
299
+ ]
300
+ }
301
+ end
302
+
303
+ it "merges the contents with the results" do
304
+ expect(subject.to_hash).to eq merged
305
+ end
306
+
307
+ it "returns interactions without test results" do
308
+ expect(subject.interactions_missing_test_results.count).to eq 1
309
+ end
310
+
311
+ context "with nil test results" do
312
+ let(:test_results) { nil }
313
+
314
+ it "does not blow up" do
315
+ expect(subject.to_hash).to eq merged_with_empty_tests
316
+ end
317
+
318
+ it "returns interactions without test results" do
319
+ expect(subject.interactions_missing_test_results.count).to eq 2
320
+ end
321
+ end
322
+
323
+ context "with nil tests" do
324
+ let(:test_results) { {} }
325
+
326
+ it "does not blow up" do
327
+ expect(subject.to_hash).to eq merged_with_empty_tests
328
+ end
329
+ end
330
+
331
+ context "with empty tests" do
332
+ let(:test_results) { { "tests" => [] } }
333
+
334
+ it "does not blow up" do
335
+ expect(subject.to_hash).to eq merged_with_empty_tests
336
+ end
337
+ end
338
+
339
+ context "with tests not an array" do
340
+ let(:test_results) { { "tests" => {} } }
341
+
342
+ it "does not blow up" do
343
+ expect(subject.to_hash).to eq merged_with_empty_tests
344
+ end
345
+ end
346
+
347
+ context "with tests an array of not hashes" do
348
+ let(:test_results) { { "tests" => [1] } }
349
+
350
+ it "does not blow up" do
351
+ expect(subject.to_hash).to eq merged_with_empty_tests
352
+ end
353
+ end
354
+ end
230
355
  end
231
356
  end
232
357
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pact_broker
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.55.0
4
+ version: 2.56.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bethany Skurrie
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-05-22 00:00:00.000000000 Z
13
+ date: 2020-06-01 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty