pact_broker 2.55.0 → 2.56.0

Sign up to get free protection for your applications and to get access to all the features.
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