pact_broker 2.58.2 → 2.60.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release_gem.yml +27 -7
  3. data/CHANGELOG.md +43 -0
  4. data/README.md +1 -1
  5. data/lib/pact_broker/api/contracts/verifiable_pacts_json_query_schema.rb +10 -3
  6. data/lib/pact_broker/api/decorators/version_decorator.rb +2 -0
  7. data/lib/pact_broker/api/renderers/html_pact_renderer.rb +26 -8
  8. data/lib/pact_broker/api/resources/all_webhooks.rb +9 -0
  9. data/lib/pact_broker/api/resources/badge.rb +4 -0
  10. data/lib/pact_broker/api/resources/can_i_deploy.rb +8 -0
  11. data/lib/pact_broker/api/resources/dashboard.rb +4 -0
  12. data/lib/pact_broker/api/resources/error_handler.rb +2 -4
  13. data/lib/pact_broker/api/resources/group.rb +8 -0
  14. data/lib/pact_broker/api/resources/index.rb +15 -8
  15. data/lib/pact_broker/api/resources/integration.rb +12 -0
  16. data/lib/pact_broker/api/resources/integrations.rb +9 -1
  17. data/lib/pact_broker/api/resources/label.rb +12 -5
  18. data/lib/pact_broker/api/resources/latest_pact.rb +6 -2
  19. data/lib/pact_broker/api/resources/latest_pacts.rb +9 -1
  20. data/lib/pact_broker/api/resources/latest_verifications_for_consumer_version.rb +8 -0
  21. data/lib/pact_broker/api/resources/pact.rb +5 -1
  22. data/lib/pact_broker/api/resources/pacticipant.rb +4 -0
  23. data/lib/pact_broker/api/resources/pacticipants.rb +14 -0
  24. data/lib/pact_broker/api/resources/previous_distinct_pact_version.rb +4 -1
  25. data/lib/pact_broker/api/resources/provider_pacts.rb +5 -1
  26. data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +3 -8
  27. data/lib/pact_broker/badges/service.rb +3 -2
  28. data/lib/pact_broker/configuration.rb +16 -4
  29. data/lib/pact_broker/doc/views/layouts/main.haml +1 -1
  30. data/lib/pact_broker/doc/views/provider-pacts-for-verification.markdown +78 -0
  31. data/lib/pact_broker/domain/pact.rb +9 -0
  32. data/lib/pact_broker/groups/service.rb +1 -1
  33. data/lib/pact_broker/index/service.rb +6 -0
  34. data/lib/pact_broker/matrix/deployment_status_summary.rb +1 -1
  35. data/lib/pact_broker/pacts/content.rb +30 -5
  36. data/lib/pact_broker/pacts/repository.rb +25 -31
  37. data/lib/pact_broker/pacts/selector.rb +8 -0
  38. data/lib/pact_broker/policies.rb +2 -2
  39. data/lib/pact_broker/test/test_data_builder.rb +4 -4
  40. data/lib/pact_broker/ui/controllers/clusters.rb +1 -1
  41. data/lib/pact_broker/ui/controllers/groups.rb +2 -2
  42. data/lib/pact_broker/ui/controllers/index.rb +1 -1
  43. data/lib/pact_broker/ui/controllers/matrix.rb +2 -2
  44. data/lib/pact_broker/ui/views/groups/show.html.erb +3 -3
  45. data/lib/pact_broker/ui/views/index/show-with-tags.haml +10 -10
  46. data/lib/pact_broker/ui/views/index/show.haml +6 -6
  47. data/lib/pact_broker/ui/views/layouts/main.haml +1 -1
  48. data/lib/pact_broker/ui/views/matrix/show.haml +4 -5
  49. data/lib/pact_broker/version.rb +1 -1
  50. data/lib/pact_broker/webhooks/job.rb +8 -2
  51. data/public/javascripts/pact.js +6 -2
  52. data/script/seed.rb +5 -7
  53. data/spec/features/get_provider_pacts_for_verification_spec.rb +0 -18
  54. data/spec/lib/pact_broker/api/contracts/verifiable_pacts_json_query_schema_spec.rb +23 -4
  55. data/spec/lib/pact_broker/api/decorators/version_decorator_spec.rb +4 -0
  56. data/spec/lib/pact_broker/api/renderers/html_pact_renderer_spec.rb +24 -6
  57. data/spec/lib/pact_broker/api/resources/error_handler_spec.rb +18 -1
  58. data/spec/lib/pact_broker/api/resources/provider_pacts_for_verification_spec.rb +8 -38
  59. data/spec/lib/pact_broker/badges/service_spec.rb +6 -6
  60. data/spec/lib/pact_broker/pacts/content_spec.rb +90 -0
  61. data/spec/lib/pact_broker/pacts/repository_find_for_verification_fallback_spec.rb +14 -0
  62. data/spec/lib/pact_broker/pacts/repository_find_for_verification_spec.rb +62 -0
  63. data/spec/lib/pact_broker/webhooks/job_spec.rb +19 -1
  64. data/spec/support/database_cleaner.rb +1 -5
  65. metadata +3 -5
  66. data/lib/pact_broker/api/contracts/verifiable_pacts_query_schema.rb +0 -36
  67. data/spec/lib/pact_broker/api/contracts/verifiable_pacts_query_schema_spec.rb +0 -97
@@ -71,6 +71,27 @@ module PactBroker
71
71
  end
72
72
  end
73
73
 
74
+ context "when the latest version for a particular consumer is requested" do
75
+ let(:consumer_version_selectors) do
76
+ [{
77
+ consumer: "Foo",
78
+ latest: true
79
+ }]
80
+ end
81
+
82
+ it { is_expected.to be_empty }
83
+ end
84
+
85
+ context "when the latest version for all is requested" do
86
+ let(:consumer_version_selectors) do
87
+ [{
88
+ latest: true
89
+ }]
90
+ end
91
+
92
+ it { is_expected.to be_empty }
93
+ end
94
+
74
95
  context "when providerVersionTags is not an array" do
75
96
  let(:provider_version_tags) { true }
76
97
 
@@ -89,7 +110,7 @@ module PactBroker
89
110
  end
90
111
 
91
112
  it "flattens the messages" do
92
- expect(subject[:consumerVersionSelectors].first).to eq "tag is missing at index 0"
113
+ expect(subject[:consumerVersionSelectors].first).to eq "latest must be true, or a tag must be provided (at index 0)"
93
114
  end
94
115
  end
95
116
 
@@ -150,9 +171,7 @@ module PactBroker
150
171
  }]
151
172
  end
152
173
 
153
- it "has an error" do
154
- expect(subject[:consumerVersionSelectors].first).to include "not yet supported"
155
- end
174
+ it { is_expected.to be_empty }
156
175
  end
157
176
  end
158
177
  end
@@ -51,6 +51,10 @@ module PactBroker
51
51
  expect(subject[:_embedded][:tags].first[:name]).to eq "prod"
52
52
  end
53
53
 
54
+ it "includes the timestamps" do
55
+ expect(subject[:createdAt]).to_not be nil
56
+ end
57
+
54
58
  it "includes a list of sorted pacts" do
55
59
  expect(subject[:_links][:'pb:pact-versions']).to be_instance_of(Array)
56
60
  expect(subject[:_links][:'pb:pact-versions'].first[:href]).to include ("1.2.3")
@@ -23,22 +23,26 @@ module PactBroker
23
23
  Timecop.return
24
24
  end
25
25
 
26
- let(:consumer) { double('consumer', name: 'Consumer')}
27
- let(:provider) { double('provider', name: 'Provider')}
26
+ let(:consumer_name) { 'Consumer' }
27
+ let(:provider_name) { 'Provider' }
28
+ let(:consumer_version_number) { '1.2.3' }
29
+ let(:consumer) { double('consumer', name: consumer_name) }
30
+ let(:provider) { double('provider', name: provider_name) }
28
31
  let(:consumer_version) { double('consumer version') }
29
32
  let(:created_at) { DateTime.new(2014, 02, 27) }
30
33
  let(:json_content) { load_fixture('renderer_pact.json') }
31
34
  let(:pact) do
32
35
  double('pact',
33
36
  json_content: json_content,
34
- consumer_version_number: '1.2.3',
37
+ consumer_version_number: consumer_version_number,
35
38
  consumer: consumer,
36
39
  provider: provider,
37
- consumer_version_tag_names: ['prod', 'master'],
40
+ consumer_version_tag_names: consumer_version_tag_names,
38
41
  created_at: created_at,
39
42
  consumer_version: consumer_version
40
43
  )
41
44
  end
45
+ let(:consumer_version_tag_names) { ['prod', 'master'] }
42
46
  let(:pact_url) { '/pact/url' }
43
47
  let(:matrix_url) { '/matrix/url' }
44
48
  let(:options) do
@@ -49,7 +53,7 @@ module PactBroker
49
53
  end
50
54
  let(:logger) { double('logger').as_null_object }
51
55
 
52
- subject { HtmlPactRenderer.call pact, options }
56
+ subject { HtmlPactRenderer.call(pact, options) }
53
57
 
54
58
  describe ".call" do
55
59
  it "renders the pact as HTML" do
@@ -69,7 +73,7 @@ module PactBroker
69
73
  end
70
74
 
71
75
  it "renders the badge image" do
72
- expect(subject).to include "<img src='http://badge'/>"
76
+ expect(subject).to include "<img src=\"http://badge\"/>"
73
77
  end
74
78
 
75
79
  it "renders a text area with the badge markdown" do
@@ -81,6 +85,20 @@ module PactBroker
81
85
  expect(subject).to include matrix_url
82
86
  end
83
87
 
88
+ context "with dodgey data" do
89
+ let(:consumer_name) { '<script>alert("consumer");</script>' }
90
+ let(:provider_name) { '<script>alert("provider");</script>' }
91
+ let(:consumer_version_number) { '<script>alert("version");</script>' }
92
+ let(:consumer_version_tag_names) { ['<script>alert("tag");</script>'] }
93
+
94
+ it "does not contain the literal <script> anywhere except the badge markdown" do
95
+ expect(subject).to_not include consumer_version_number
96
+ expect(subject.scan(consumer_name).count).to eq 1
97
+ expect(subject.scan(provider_name).count).to eq 1
98
+ expect(subject).to include '[![<script>alert("consumer");</script>/<script>alert("provider");</script> Pact Status](http://badge)]'
99
+ end
100
+ end
101
+
84
102
  context "when enable_public_badge_access is false" do
85
103
  before do
86
104
  PactBroker.configuration.enable_public_badge_access = false
@@ -5,7 +5,6 @@ module PactBroker
5
5
  module Resources
6
6
  describe ErrorHandler do
7
7
  describe "call" do
8
-
9
8
  before do
10
9
  allow(ErrorHandler).to receive(:logger).and_return(logger)
11
10
  allow(SecureRandom).to receive(:urlsafe_base64).and_return("bYWfn-+yWPlf")
@@ -45,6 +44,24 @@ module PactBroker
45
44
  subject
46
45
  end
47
46
 
47
+ context "when the error class is in the warning_error_classes list" do
48
+ let(:error) { Sequel::ForeignKeyConstraintViolation.new }
49
+
50
+ it "logs at warn so as not to wake everyone up in the middle of the night" do
51
+ expect(logger).to receive(:warn).with(/bYWfnyWPlf/, error)
52
+ subject
53
+ end
54
+ end
55
+
56
+ context "when the error is not reportable and not a warning level" do
57
+ let(:error) { PactBroker::Error.new('foo') }
58
+
59
+ it "logs at info" do
60
+ expect(logger).to receive(:info).with(/bYWfnyWPlf/, error)
61
+ subject
62
+ end
63
+ end
64
+
48
65
  context "when show_backtrace_in_error_response? is true" do
49
66
  before do
50
67
  allow(PactBroker.configuration).to receive(:show_backtrace_in_error_response?).and_return(true)
@@ -11,7 +11,7 @@ module PactBroker
11
11
  end
12
12
 
13
13
  let(:provider) { double('provider') }
14
- let(:pacts) { double('pacts') }
14
+ let(:pacts) { [] }
15
15
  let(:path) { '/pacts/provider/Bar/for-verification' }
16
16
  let(:decorator) { instance_double('PactBroker::Api::Decorators::VerifiablePactsDecorator') }
17
17
  let(:query) do
@@ -23,35 +23,7 @@ module PactBroker
23
23
  }
24
24
  end
25
25
 
26
- subject { get(path, query) }
27
-
28
- describe "GET" do
29
- it "finds the pacts for verification by the provider" do
30
- # Naughty not mocking out the query parsing...
31
- expect(PactBroker::Pacts::Service).to receive(:find_for_verification).with(
32
- "Bar",
33
- ["master"],
34
- PactBroker::Pacts::Selectors.new([PactBroker::Pacts::Selector.latest_for_tag("dev")]),
35
- {
36
- include_wip_pacts_since: DateTime.parse('2018-01-01'),
37
- include_pending_status: true
38
- }
39
- )
40
- subject
41
- end
42
-
43
- context "when there are validation errors" do
44
- let(:query) do
45
- {
46
- provider_version_tags: true,
47
- }
48
- end
49
-
50
- it "returns the keys with the right case" do
51
- expect(JSON.parse(subject.body)['errors']).to have_key('provider_version_tags')
52
- end
53
- end
54
- end
26
+ subject { post(path, request_body.to_json, request_headers) }
55
27
 
56
28
  describe "POST" do
57
29
  let(:request_body) do
@@ -70,8 +42,6 @@ module PactBroker
70
42
  }
71
43
  end
72
44
 
73
- subject { post(path, request_body.to_json, request_headers) }
74
-
75
45
  it "finds the pacts for verification by the provider" do
76
46
  # Naughty not mocking out the query parsing...
77
47
  expect(PactBroker::Pacts::Service).to receive(:find_for_verification).with(
@@ -97,14 +67,14 @@ module PactBroker
97
67
  expect(JSON.parse(subject.body)['errors']).to have_key('providerVersionTags')
98
68
  end
99
69
  end
100
- end
101
70
 
102
- it "uses the correct options for the decorator" do
103
- expect(decorator).to receive(:to_json) do | options |
104
- expect(options[:user_options][:title]).to eq "Pacts to be verified by provider Bar"
105
- expect(options[:user_options][:include_pending_status]).to eq true
71
+ it "uses the correct options for the decorator" do
72
+ expect(decorator).to receive(:to_json) do | options |
73
+ expect(options[:user_options][:title]).to eq "Pacts to be verified by provider Bar"
74
+ expect(options[:user_options][:include_pending_status]).to eq true
75
+ end
76
+ subject
106
77
  end
107
- subject
108
78
  end
109
79
  end
110
80
  end
@@ -13,7 +13,7 @@ module PactBroker
13
13
  let(:expected_url) { "https://img.shields.io/badge/#{expected_left_text}-#{expected_right_text}-#{expected_color}.svg" }
14
14
  let(:expected_color) { "brightgreen" }
15
15
  let(:expected_right_text) { "verified" }
16
- let(:expected_left_text) { "foo--bar%2fthing__blah%20pact" }
16
+ let(:expected_left_text) { "foo--bar%2Fthing__blah%20pact" }
17
17
  let(:response_status) { 200 }
18
18
  let!(:http_request) do
19
19
  stub_request(:get, expected_url).to_return(:status => response_status, :body => "svg")
@@ -62,7 +62,7 @@ module PactBroker
62
62
  end
63
63
 
64
64
  context "when initials is true" do
65
- let(:expected_left_text) { "fb%2ftb%20pact" }
65
+ let(:expected_left_text) { "fb%2Ftb%20pact" }
66
66
  let(:initials) { true }
67
67
 
68
68
  it "creates a badge with the consumer and provider initials" do
@@ -73,7 +73,7 @@ module PactBroker
73
73
  end
74
74
 
75
75
  context "when initials is true but the consumer and provider names only contain one word" do
76
- let(:expected_left_text) { "foo%2fbar%20pact" }
76
+ let(:expected_left_text) { "foo%2Fbar%20pact" }
77
77
  let(:initials) { true }
78
78
  let(:pact) { double("pact", consumer_name: "Foo", provider_name: "Bar") }
79
79
 
@@ -85,7 +85,7 @@ module PactBroker
85
85
  end
86
86
 
87
87
  context "when initials is true but the consumer and provider names are one camel cased word" do
88
- let(:expected_left_text) { "fa%2fbp%20pact" }
88
+ let(:expected_left_text) { "fa%2Fbp%20pact" }
89
89
  let(:initials) { true }
90
90
  let(:pact) { double("pact", consumer_name: "FooApp", provider_name: "barProvider") }
91
91
 
@@ -97,7 +97,7 @@ module PactBroker
97
97
  end
98
98
 
99
99
  context "when initials is true but the consumer and provider names are one camel cased word" do
100
- let(:expected_left_text) { "fa%2fdat%20pact" }
100
+ let(:expected_left_text) { "fa%2Fdat%20pact" }
101
101
  let(:initials) { true }
102
102
  let(:pact) { double("pact", consumer_name: "FooApp", provider_name: "doAThing") }
103
103
 
@@ -111,7 +111,7 @@ module PactBroker
111
111
  context "when the tags are supplied" do
112
112
  let(:tags) { { consumer_tag: "prod", provider_tag: "master" } }
113
113
 
114
- let(:expected_left_text) { "foo--bar%20(prod)%2fthing__blah%20(master)%20pact" }
114
+ let(:expected_left_text) { "foo--bar%20%28prod%29%2Fthing__blah%20%28master%29%20pact" }
115
115
 
116
116
  it "creates a badge with the consumer and provider names, not initials" do
117
117
  subject
@@ -308,6 +308,14 @@ module PactBroker
308
308
  expect(subject.interactions_missing_test_results.count).to eq 1
309
309
  end
310
310
 
311
+ context "with no interactions" do
312
+ let(:pact_content) { {} }
313
+
314
+ it "does not blow up" do
315
+ expect(subject.interactions_missing_test_results.count).to eq 0
316
+ end
317
+ end
318
+
311
319
  context "with nil test results" do
312
320
  let(:test_results) { nil }
313
321
 
@@ -351,6 +359,88 @@ module PactBroker
351
359
  expect(subject.to_hash).to eq merged_with_empty_tests
352
360
  end
353
361
  end
362
+
363
+ context "with the new format" do
364
+ let(:test_results) do
365
+ [
366
+ {
367
+ "interactionId" => "1",
368
+ "success "=> false
369
+ },{
370
+ "foo" => "bar"
371
+ }
372
+ ]
373
+ end
374
+
375
+ let(:pact_content) do
376
+ {
377
+ "interactions" => [
378
+ {
379
+ "_id" => "1"
380
+ },
381
+ {
382
+ "_id" => "2"
383
+ }
384
+ ]
385
+ }
386
+ end
387
+
388
+ let(:merged) do
389
+ {
390
+ "interactions" => [
391
+ {
392
+ "_id" => "1",
393
+ "tests" => [{
394
+ "interactionId" => "1",
395
+ "success "=> false
396
+ }]
397
+ },{
398
+ "_id" => "2",
399
+ "tests" => []
400
+ }
401
+ ]
402
+ }
403
+ end
404
+
405
+ it "merges the tests into the pact content" do
406
+ expect(subject.to_hash).to eq merged
407
+ end
408
+ end
409
+ end
410
+
411
+ describe "interaction_ids" do
412
+ let(:interaction_1) do
413
+ {
414
+ _id: '1'
415
+ }
416
+ end
417
+
418
+ let(:interaction_2) do
419
+ {
420
+ _id: '2'
421
+ }
422
+ end
423
+
424
+ let(:interaction_3) do
425
+ {
426
+ }
427
+ end
428
+
429
+ let(:content_hash) do
430
+ {
431
+ interactions: [interaction_1, interaction_2, interaction_3]
432
+ }
433
+ end
434
+
435
+ subject { Content.from_json(content_hash.to_json) }
436
+
437
+ its(:interaction_ids) { is_expected.to eq ['1', '2'] }
438
+
439
+ context "when there are no interactions" do
440
+ let(:content_hash) { {} }
441
+
442
+ its(:interaction_ids) { is_expected.to eq [] }
443
+ end
354
444
  end
355
445
  end
356
446
  end
@@ -56,6 +56,20 @@ module PactBroker
56
56
  it "sets the latest flag on the selector" do
57
57
  expect(find_by_consumer_version_number("1").selectors.first.latest).to be true
58
58
  end
59
+
60
+ context "when a consumer is specified" do
61
+ before do
62
+ td.create_pact_with_consumer_version_tag("Foo2", "3", "master", "Bar")
63
+ end
64
+
65
+ let(:selector) { Selector.new(tag: tag, fallback_tag: fallback_tag, latest: true, consumer: "Foo") }
66
+
67
+ it "only returns the pacts for the consumer" do
68
+ expect(subject.size).to eq 1
69
+ expect(subject.first.consumer.name).to eq "Foo"
70
+ expect(subject.first.selectors.first).to eq selector
71
+ end
72
+ end
59
73
  end
60
74
 
61
75
  context "when a pact does not exist for either tag or fallback_tag" do
@@ -40,6 +40,68 @@ module PactBroker
40
40
  end
41
41
  end
42
42
 
43
+ context "when the selector is latest: true" do
44
+ let(:pact_selector_1) { Selector.overall_latest }
45
+ let(:consumer_version_selectors) do
46
+ Selectors.new(pact_selector_1)
47
+ end
48
+
49
+ before do
50
+ td.create_pact_with_hierarchy("Foo1", "1", "Bar")
51
+ .create_pact_with_hierarchy("Foo1", "2", "Bar")
52
+ .create_pact_with_hierarchy("Foo2", "3", "Bar")
53
+ .create_pact_with_hierarchy("Foo2", "4", "Bar2")
54
+ end
55
+
56
+ it "returns the latest pact for each consumer" do
57
+ expect(subject.size).to eq 2
58
+ expect(find_by_consumer_name_and_consumer_version_number("Foo1", "2").selectors).to eq [Selector.overall_latest]
59
+ expect(find_by_consumer_name_and_consumer_version_number("Foo2", "3").selectors).to eq [Selector.overall_latest]
60
+ end
61
+ end
62
+
63
+ context "when the selector is latest: true for a particular consumer" do
64
+ let(:pact_selector_1) { Selector.latest_for_consumer("Foo1") }
65
+
66
+ let(:consumer_version_selectors) do
67
+ Selectors.new(pact_selector_1)
68
+ end
69
+
70
+ before do
71
+ td.create_pact_with_hierarchy("Foo1", "1", "Bar")
72
+ .create_pact_with_hierarchy("Foo1", "2", "Bar")
73
+ .create_pact_with_hierarchy("Foo2", "2", "Bar")
74
+ .create_pact_with_hierarchy("Foo2", "2", "Bar2")
75
+ end
76
+
77
+ it "returns the latest pact for each consumer" do
78
+ expect(subject.size).to eq 1
79
+ expect(find_by_consumer_name_and_consumer_version_number("Foo1", "2").selectors).to eq [pact_selector_1]
80
+ end
81
+ end
82
+
83
+ context "when the selector is latest: true, with a tag, for a particular consumer" do
84
+ let(:pact_selector_1) { Selector.latest_for_tag_and_consumer("prod", "Foo1") }
85
+
86
+ let(:consumer_version_selectors) do
87
+ Selectors.new(pact_selector_1)
88
+ end
89
+
90
+ before do
91
+ td.create_pact_with_hierarchy("Foo1", "1", "Bar")
92
+ .create_consumer_version_tag("prod")
93
+ .create_pact_with_hierarchy("Foo1", "2", "Bar")
94
+ .create_pact_with_hierarchy("Foo2", "2", "Bar")
95
+ .create_consumer_version_tag("prod")
96
+ .create_pact_with_hierarchy("Foo2", "2", "Bar2")
97
+ end
98
+
99
+ it "returns the latest pact for each consumer" do
100
+ expect(subject.size).to eq 1
101
+ expect(find_by_consumer_name_and_consumer_version_number("Foo1", "1").selectors).to eq [pact_selector_1]
102
+ end
103
+ end
104
+
43
105
  context "when the latest consumer tag names are specified" do
44
106
  before do
45
107
  td.create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar")