pact_broker 2.59.2 → 2.60.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: f1933f8f7902c43ed7d9db7c022a66f2569c6301dec2c8ac767e1da7b05f70b5
4
- data.tar.gz: 40d82183c3a94b2bfc2bfa37a9aee12cc27729e1c3a1b3191df980c2ba3ad192
3
+ metadata.gz: dbaaee53f1641458eb110582a96bf0be98d7078ef1fa681ffee934c6e8007347
4
+ data.tar.gz: 84a2f6e6785acc85cf7053b86a54574746cb06de917bd11a0dff23403046aff5
5
5
  SHA512:
6
- metadata.gz: 5078b560d3d0abe897572f35276b3aec1c6752026ba849a9b61517def382ea0d936731a6b2f59801b0101cab7bde636ef3e9e5e7afe9524e13c3def552f48ded
7
- data.tar.gz: 6fc016ea9e10bcbad4f78d60b93a81b5d0aab2ffbfef9d222a094e0348f632680995bce488abe9fefe55cad745afafef323ce3c972db3e78a7e1f6759d222f6e
6
+ metadata.gz: de93223c0a1d52af6563bf31b2ae733c41b6cec65afb4a61be741d78f47cf18484ee2fa292c9660b8db09cb3a1a66c549decb145a0b7a036477a9ac73b70f8c6
7
+ data.tar.gz: 04d3a59551f4fe9f46af8b65cd6c0dc3326ac86a594298eb22d529e33f6a98e39cdf6f9c682bd69bd9bbb85f4f290fe3e2f706d71cfa766b61def28c0dbd7bfc
@@ -1,3 +1,17 @@
1
+ <a name="v2.60.0"></a>
2
+ ### v2.60.0 (2020-09-08)
3
+
4
+ #### Features
5
+
6
+ * **pacts for verification**
7
+ * add deprecation title to beta:provider-pacts-for-verification relation ([47a61f69](/../../commit/47a61f69))
8
+ * do not require environment variable feature toggle to enable feature ([7d0fe1ea](/../../commit/7d0fe1ea))
9
+ * allow consumer to be specified with fallback tags, and overall latest to be specified with or without a consumer ([2d52d173](/../../commit/2d52d173))
10
+
11
+ #### Bug Fixes
12
+
13
+ * correctly handle new test results format when merging test results with pact contents ([b35ab71b](/../../commit/b35ab71b))
14
+
1
15
  <a name="v2.59.2"></a>
2
16
  ### v2.59.2 (2020-08-06)
3
17
 
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![Code Climate](https://codeclimate.com/github/pact-foundation/pact_broker/badges/gpa.svg)](https://codeclimate.com/github/pact-foundation/pact_broker)
7
7
  [![Test Coverage](https://codeclimate.com/github/pact-foundation/pact_broker/badges/coverage.svg)](https://codeclimate.com/github/pact-foundation/pact_broker/coverage)
8
8
 
9
- The Pact Broker is an application for sharing for consumer driven contracts and verification results. It is optimised for use with "pacts" (contracts created by the [Pact][pact-docs] framework), but can be used for any type of contract that can be serialized to JSON.
9
+ The Pact Broker is an application for sharing of consumer driven contracts and verification results. It is optimised for use with "pacts" (contracts created by the [Pact][pact-docs] framework), but can be used for any type of contract that can be serialized to JSON.
10
10
 
11
11
  <br/>
12
12
  <a href="https:/pactflow.io/?utm_source=github&utm_campaign=pact_broker_intro"><img src="docs/images/Pactflow logo - black small.png"></a>
@@ -1,5 +1,6 @@
1
1
  require 'dry-validation'
2
2
  require 'pact_broker/hash_refinements'
3
+ require 'pact_broker/string_refinements'
3
4
  require 'pact_broker/api/contracts/dry_validation_workarounds'
4
5
  require 'pact_broker/api/contracts/dry_validation_predicates'
5
6
 
@@ -9,6 +10,7 @@ module PactBroker
9
10
  class VerifiablePactsJSONQuerySchema
10
11
  extend DryValidationWorkarounds
11
12
  using PactBroker::HashRefinements
13
+ using PactBroker::StringRefinements
12
14
 
13
15
  SCHEMA = Dry::Validation.Schema do
14
16
  configure do
@@ -24,7 +26,7 @@ module PactBroker
24
26
  # end
25
27
  # end
26
28
 
27
- required(:tag).filled(:str?)
29
+ optional(:tag).filled(:str?)
28
30
  optional(:latest).filled(included_in?: [true, false])
29
31
  optional(:fallbackTag).filled(:str?)
30
32
  optional(:consumer).filled(:str?, :not_blank?)
@@ -54,8 +56,9 @@ module PactBroker
54
56
  errors << "fallbackTag can only be set if latest is true (at index #{index})"
55
57
  end
56
58
 
57
- if selector[:consumer] && selector[:latest]
58
- errors << "specifying a consumer with latest == true is not yet supported (at index #{index})"
59
+
60
+ if not_provided?(selector[:tag]) && selector[:latest] != true
61
+ errors << "latest must be true, or a tag must be provided (at index #{index})"
59
62
  end
60
63
  end
61
64
  if errors.any?
@@ -64,6 +67,10 @@ module PactBroker
64
67
  end
65
68
  end
66
69
  end
70
+
71
+ def self.not_provided?(value)
72
+ value.nil? || value.blank?
73
+ end
67
74
  end
68
75
  end
69
76
  end
@@ -121,6 +121,17 @@ module PactBroker
121
121
  title: "Determine if an application can be safely deployed to an environment identified by the given tag",
122
122
  templated: true
123
123
  },
124
+ 'pb:provider-pacts-for-verification' => {
125
+ href: base_url + '/pacts/provider/{provider}/for-verification',
126
+ title: 'Pact versions to be verified for the specified provider',
127
+ templated: true
128
+ },
129
+ 'beta:provider-pacts-for-verification' => {
130
+ name: 'beta',
131
+ href: base_url + '/doc/{rel}?context=index',
132
+ title: 'DEPRECATED - please use pb:provider-pacts-for-verification',
133
+ templated: true
134
+ },
124
135
  'curies' =>
125
136
  [{
126
137
  name: 'pb',
@@ -133,14 +144,6 @@ module PactBroker
133
144
  }]
134
145
  }
135
146
 
136
- if PactBroker.feature_enabled?(:pacts_for_verification)
137
- links_hash['beta:provider-pacts-for-verification'] = {
138
- href: base_url + '/pacts/provider/{provider}/for-verification',
139
- title: 'Pact versions to be verified for the specified provider',
140
- templated: true
141
- }
142
- end
143
-
144
147
  links_hash
145
148
  end
146
149
 
@@ -1,6 +1,5 @@
1
1
  require 'pact_broker/api/resources/provider_pacts'
2
2
  require 'pact_broker/api/decorators/verifiable_pacts_decorator'
3
- require 'pact_broker/api/contracts/verifiable_pacts_query_schema'
4
3
  require 'pact_broker/api/decorators/verifiable_pacts_query_decorator'
5
4
  require 'pact_broker/api/contracts/verifiable_pacts_json_query_schema'
6
5
  require 'pact_broker/hash_refinements'
@@ -12,7 +11,7 @@ module PactBroker
12
11
  using PactBroker::HashRefinements
13
12
 
14
13
  def allowed_methods
15
- ["GET", "POST", "OPTIONS"]
14
+ ["POST", "OPTIONS"]
16
15
  end
17
16
 
18
17
  def content_types_accepted
@@ -36,7 +35,7 @@ module PactBroker
36
35
  private
37
36
 
38
37
  def pacts
39
- pact_service.find_for_verification(
38
+ @pacts ||= pact_service.find_for_verification(
40
39
  provider_name,
41
40
  parsed_query_params.provider_version_tags,
42
41
  parsed_query_params.consumer_version_selectors,
@@ -56,11 +55,7 @@ module PactBroker
56
55
  end
57
56
 
58
57
  def query_schema
59
- if request.get?
60
- PactBroker::Api::Contracts::VerifiablePactsQuerySchema
61
- else
62
- PactBroker::Api::Contracts::VerifiablePactsJSONQuerySchema
63
- end
58
+ PactBroker::Api::Contracts::VerifiablePactsJSONQuerySchema
64
59
  end
65
60
 
66
61
  def parsed_query_params
@@ -0,0 +1,78 @@
1
+ # Provider pacts for verification
2
+
3
+ Path: `/pacts/provider/{provider}/for-verification`
4
+
5
+ Allowed methods: `POST`
6
+
7
+ Content type: `application/hal+json`
8
+
9
+ Returns a deduplicated list of pacts to be verified by the specified provider.
10
+
11
+ ### Body
12
+
13
+ Example: This data structure represents the way a user might specify "I want to verify the latest 'master' pact, all 'prod' pacts, and when I publish the verification results, I'm going to tag the provider version with 'master'"
14
+
15
+ {
16
+ "consumerVersionSelectors": [
17
+ {
18
+ "tag": "master",
19
+ "latest": true
20
+ },{
21
+ "tag": "prod"
22
+ }
23
+ ],
24
+ "providerVersionTags": ["master"],
25
+ "includePendingStatus": true,
26
+ "includeWipPactsSince": "2020-01-01"
27
+ }
28
+
29
+
30
+ `consumerVersionSelectors.tag`: the tag name(s) of the consumer versions to get the pacts for.
31
+
32
+ `consumerVersionSelectors.fallbackTag`: the name of the tag to fallback to if the specified `tag` does not exist. This is useful when the consumer and provider use matching branch names to coordinate the development of new features.
33
+
34
+ `consumerVersionSelectors.latest`: true. If the latest flag is omitted, *all* the pacts with the specified tag will be returned. (This might seem a bit weird, but it's done this way to match the syntax used for the matrix query params. See https://docs.pact.io/selectors)
35
+
36
+ `consumerVersionSelectors.consumer`: allows a selector to only be applied to a certain consumer. This is used when there is an API that has multiple consumers, one of which is a deployed service, and one of which is a mobile consumer. The deployed service only needs the latest production pact verified, where as the mobile consumer may want all the production pacts verified.
37
+
38
+ `providerVersionTags`: the tag name(s) for the provider application version that will be published with the verification results. This is used by the Broker to determine whether or not a particular pact is in pending state or not. This parameter can be specified multiple times.
39
+
40
+ `includePendingStatus`: true|false (default false). When true, a pending boolean will be added to the verificationProperties in the response, and an extra message will appear in the notices array to indicate why this pact is/is not in pending state. This will allow your code to handle the response based on only what is present in the response, and not have to do ifs based on the user's options together with the response. As requested in the "pacts for verification" issue, please print out these messages in the tests if possible. If not possible, perhaps create a separate task which will list the pact URLs and messages for debugging purposes.
41
+
42
+ `includeWipPactsSince`: Date string. The date from which to include the "work in progress" pacts. See https://docs.pact.io/wip for more information on work in progress pacts.
43
+
44
+ ### Response body
45
+
46
+ `pending` flag and the "pending reason" notice will only be included if `includePendingStatus` is set to true.
47
+
48
+
49
+ {
50
+ "_embedded": {
51
+ "pacts": [
52
+ {
53
+ "verificationProperties": {
54
+ "notices": [
55
+ {
56
+ "text": "This pact is being verified because it is the pact for the latest version of Foo tagged with 'dev'",
57
+ "when": "before_verification"
58
+ }
59
+ ],
60
+ "pending": false
61
+ },
62
+ "_links": {
63
+ "self": {
64
+ "href": "http://localhost:9292/pacts/provider/Bar/consumer/Foo/pact-version/0e3369199f4008231946e0245474537443ccda2a",
65
+ "name": "Pact between Foo (v1.0.0) and Bar"
66
+ }
67
+ }
68
+ }
69
+ ]
70
+ },
71
+ "_links": {
72
+ "self": {
73
+ "href": "http://localhost:9292/pacts/provider/Bar/for-verification",
74
+ "title": "Pacts to be verified"
75
+ }
76
+ }
77
+ }
78
+
@@ -1,5 +1,6 @@
1
1
  require 'pact_broker/db'
2
2
  require 'pact_broker/json'
3
+ require 'pact_broker/pacts/content'
3
4
 
4
5
  =begin
5
6
  This class most accurately represents a PactPublication
@@ -76,6 +77,14 @@ module PactBroker
76
77
  JSON.parse(json_content, PACT_PARSING_OPTIONS)
77
78
  end
78
79
 
80
+ def content_object
81
+ @content_object ||= begin
82
+ PactBroker::Pacts::Content.from_json(json_content)
83
+ rescue
84
+ PactBroker::Pacts::Content.new
85
+ end
86
+ end
87
+
79
88
  def pact_publication_id
80
89
  id
81
90
  end
@@ -39,9 +39,15 @@ module PactBroker
39
39
  end
40
40
 
41
41
  def with_test_results(test_results)
42
- tests = test_results && test_results['tests']
43
- if tests.nil? || !tests.is_a?(Array) || tests.empty?
44
- tests = []
42
+ # new format
43
+ if test_results.is_a?(Array)
44
+ tests = test_results
45
+ else
46
+ # old format
47
+ tests = test_results && test_results['tests']
48
+ if tests.nil? || !tests.is_a?(Array) || tests.empty?
49
+ tests = []
50
+ end
45
51
  end
46
52
 
47
53
  new_pact_hash = pact_hash.dup
@@ -67,6 +73,12 @@ module PactBroker
67
73
  Content.from_hash(new_pact_hash)
68
74
  end
69
75
 
76
+ def interaction_ids
77
+ messages_or_interaction_or_empty_array.collect do | interaction |
78
+ interaction['_id']
79
+ end.compact
80
+ end
81
+
70
82
  # Half thinking this belongs in GenerateSha
71
83
  def content_that_affects_verification_results
72
84
  if interactions || messages
@@ -92,6 +104,10 @@ module PactBroker
92
104
  messages || interactions
93
105
  end
94
106
 
107
+ def messages_or_interaction_or_empty_array
108
+ messages_or_interactions || []
109
+ end
110
+
95
111
  def pact_specification_version
96
112
  maybe_pact_specification_version_1 = pact_hash['metadata']['pactSpecification']['version'] rescue nil
97
113
  maybe_pact_specification_version_2 = pact_hash['metadata']['pact-specification']['version'] rescue nil
@@ -130,7 +146,15 @@ module PactBroker
130
146
  end
131
147
 
132
148
  def test_is_for_interaction(interaction, test)
133
- test.is_a?(Hash) && interaction.is_a?(Hash) && test['interactionDescription'] == interaction['description'] && test['interactionProviderState'] == interaction['providerState']
149
+ test.is_a?(Hash) && interaction.is_a?(Hash) && ( interaction_ids_match(interaction, test) || description_and_state_match(interaction, test))
150
+ end
151
+
152
+ def interaction_ids_match(interaction, test)
153
+ interaction['_id'] && interaction['_id'] == test['interactionId']
154
+ end
155
+
156
+ def description_and_state_match(interaction, test)
157
+ test['interactionDescription'] && test['interactionDescription'] == interaction['description'] && test['interactionProviderState'] == interaction['providerState']
134
158
  end
135
159
  end
136
160
  end
@@ -405,42 +405,37 @@ module PactBroker
405
405
  SelectedPact.new(pact_publication.to_domain, Selectors.create_for_overall_latest)
406
406
  end
407
407
  else
408
- []
408
+ selectors_for_overall_latest = consumer_version_selectors.select(&:overall_latest?)
409
+ selectors_for_overall_latest.flat_map do | selector |
410
+ query = scope_for(LatestPactPublications).provider(provider_name)
411
+ query = query.consumer(selector.consumer) if selector.consumer
412
+ query.collect do | latest_pact_publication |
413
+ pact_publication = PactPublication.find(id: latest_pact_publication.id)
414
+ resolved_selector = selector.consumer ? Selector.latest_for_consumer(selector.consumer) : Selector.overall_latest
415
+ SelectedPact.new(pact_publication.to_domain, Selectors.new(resolved_selector))
416
+ end
417
+ end
409
418
  end
410
419
  end
411
420
 
412
421
  def find_pacts_for_which_the_latest_version_for_the_tag_is_required(provider_name, consumer_version_selectors)
413
422
  # The tags for which only the latest version is specified
414
- tag_names = consumer_version_selectors.tag_names_of_selectors_for_latest_pacts
423
+ selectors = consumer_version_selectors.select(&:latest_for_tag?)
415
424
 
416
- # TODO make this an efficient query!
417
- # These are not yet de-duped. Should make the behaviour consistent between this and find_pacts_for_which_all_versions_for_the_tag_are_required ?
418
- if tag_names.any?
419
- scope_for(LatestTaggedPactPublications)
420
- .provider(provider_name)
421
- .where(tag_name: tag_names)
422
- .all
423
- .group_by(&:pact_version_id)
424
- .values
425
- .collect do | pact_publications |
426
- create_selected_pact(pact_publications)
427
- end
428
- else
429
- []
425
+ selectors.flat_map do | selector |
426
+ query = scope_for(LatestTaggedPactPublications).where(tag_name: selector.tag).provider(provider_name)
427
+ query = query.consumer(selector.consumer) if selector.consumer
428
+ query.all.collect do | latest_tagged_pact_publication |
429
+ pact_publication = PactPublication.find(id: latest_tagged_pact_publication.id)
430
+ resolved_pact = selector.consumer ? Selector.latest_for_tag_and_consumer(selector.tag, selector.consumer) : Selector.latest_for_tag(selector.tag)
431
+ SelectedPact.new(
432
+ pact_publication.to_domain,
433
+ Selectors.new(resolved_pact)
434
+ )
435
+ end
430
436
  end
431
437
  end
432
438
 
433
- def create_selected_pact(pact_publications)
434
- selector_tag_names = pact_publications.collect(&:tag_name)
435
- selectors = Selectors.create_for_latest_of_each_tag(selector_tag_names)
436
- last_pact_publication = pact_publications.sort_by(&:consumer_version_order).last
437
- pact_publication = scope_for(PactPublication).find(id: last_pact_publication.id)
438
- SelectedPact.new(
439
- pact_publication.to_domain,
440
- selectors
441
- )
442
- end
443
-
444
439
  def create_fallback_selected_pact(pact_publications, consumer_version_selectors)
445
440
  selector_tag_names = pact_publications.collect(&:tag_name)
446
441
  selectors = Selectors.create_for_latest_fallback_of_each_tag(selector_tag_names)
@@ -454,10 +449,9 @@ module PactBroker
454
449
 
455
450
  def find_pacts_for_which_the_latest_version_for_the_fallback_tag_is_required(provider_name, selectors)
456
451
  selectors.collect do | selector |
457
- scope_for(LatestTaggedPactPublications)
458
- .provider(provider_name)
459
- .where(tag_name: selector.fallback_tag)
460
- .all
452
+ query = scope_for(LatestTaggedPactPublications).provider(provider_name).where(tag_name: selector.fallback_tag)
453
+ query = query.consumer(selector.consumer) if selector.consumer
454
+ query.all
461
455
  .collect do | latest_tagged_pact_publication |
462
456
  pact_publication = unscoped(PactPublication).find(id: latest_tagged_pact_publication.id)
463
457
  SelectedPact.new(
@@ -53,6 +53,14 @@ module PactBroker
53
53
  Selector.new(tag: tag, consumer: consumer)
54
54
  end
55
55
 
56
+ def self.latest_for_tag_and_consumer(tag, consumer)
57
+ Selector.new(latest: true, tag: tag, consumer: consumer)
58
+ end
59
+
60
+ def self.latest_for_consumer(consumer)
61
+ Selector.new(latest: true, consumer: consumer)
62
+ end
63
+
56
64
  def self.from_hash hash
57
65
  Selector.new(hash)
58
66
  end
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.59.2'
2
+ VERSION = '2.60.0'
3
3
  end
@@ -7,9 +7,10 @@ module PactBroker
7
7
  module Webhooks
8
8
  class Job
9
9
 
10
+ INFO_ERROR_PREFIXES = %w{Errno:: Timeout:: Net:: OpenSSL:: EOFError SocketError}
11
+
10
12
  include SuckerPunch::Job
11
13
  include PactBroker::Logging
12
- include PactBroker::Logging
13
14
 
14
15
  def perform data
15
16
  data.fetch(:database_connector).call do
@@ -62,7 +63,12 @@ module PactBroker
62
63
  end
63
64
 
64
65
  def handle_error e
65
- log_error e, "Error executing triggered webhook with ID #{triggered_webhook ? triggered_webhook.id : nil}"
66
+ message = "Error executing triggered webhook with ID #{triggered_webhook ? triggered_webhook.id : nil}"
67
+ if e.class.name.start_with?(*INFO_ERROR_PREFIXES)
68
+ logger.info(message, e)
69
+ else
70
+ logger.warn(message, e)
71
+ end
66
72
  handle_failure
67
73
  end
68
74
 
@@ -24,24 +24,6 @@ describe "Get provider pacts for verification" do
24
24
 
25
25
  let(:path) { "/pacts/provider/Provider/for-verification" }
26
26
 
27
- context "when using GET" do
28
- it "returns a 200 HAL JSON response" do
29
- expect(subject).to be_a_hal_json_success_response
30
- end
31
-
32
- it "returns a list of links to the pacts" do
33
- expect(pacts.size).to eq 1
34
- end
35
-
36
- context "when the provider does not exist" do
37
- let(:path) { "/pacts/provider/ProviderThatDoesNotExist/for-verification" }
38
-
39
- it "returns a 404 response" do
40
- expect(subject).to be_a_404_response
41
- end
42
- end
43
- end
44
-
45
27
  context "when using POST" do
46
28
  let(:request_body) do
47
29
  {
@@ -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
@@ -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
@@ -359,6 +359,88 @@ module PactBroker
359
359
  expect(subject.to_hash).to eq merged_with_empty_tests
360
360
  end
361
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
362
444
  end
363
445
  end
364
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")
@@ -53,7 +53,25 @@ module PactBroker
53
53
 
54
54
  context "when an error occurs for the first time" do
55
55
  before do
56
- allow(PactBroker::Webhooks::Service).to receive(:execute_triggered_webhook_now).and_raise("an error")
56
+ allow(PactBroker::Webhooks::Service).to receive(:execute_triggered_webhook_now).and_raise(error)
57
+ end
58
+
59
+ let(:error) { "an error" }
60
+
61
+ context "when the error is HTTP related (most likely caused by a problem with the webhook or request itself)" do
62
+ let(:error) { Errno::ECONNREFUSED.new }
63
+
64
+ it "logs a message at info" do
65
+ expect(logger).to receive(:info).with(/Error executing/, error)
66
+ subject
67
+ end
68
+ end
69
+
70
+ context "when the error is not HTTP related (most likely caused by a code, platform or environment issue)" do
71
+ it "logs a message at warn" do
72
+ expect(logger).to receive(:warn).with(/Error executing/, instance_of(RuntimeError))
73
+ subject
74
+ end
57
75
  end
58
76
 
59
77
  it "reschedules the job in 10 seconds" do
@@ -8,11 +8,7 @@ RSpec.configure do |config|
8
8
  config.before(:suite) do
9
9
  if defined?(::DB)
10
10
  DatabaseCleaner.strategy = :transaction
11
- if DB.mysql?
12
- DatabaseCleaner.clean_with :deletion
13
- else
14
- DatabaseCleaner.clean_with :truncation
15
- end
11
+ PactBroker::Database.truncate
16
12
  end
17
13
  end
18
14
 
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.59.2
4
+ version: 2.60.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-08-07 00:00:00.000000000 Z
13
+ date: 2020-09-08 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty
@@ -581,7 +581,6 @@ files:
581
581
  - lib/pact_broker/api/contracts/put_pact_params_contract.rb
582
582
  - lib/pact_broker/api/contracts/request_validations.rb
583
583
  - lib/pact_broker/api/contracts/verifiable_pacts_json_query_schema.rb
584
- - lib/pact_broker/api/contracts/verifiable_pacts_query_schema.rb
585
584
  - lib/pact_broker/api/contracts/verification_contract.rb
586
585
  - lib/pact_broker/api/contracts/webhook_contract.rb
587
586
  - lib/pact_broker/api/decorators.rb
@@ -760,6 +759,7 @@ files:
760
759
  - lib/pact_broker/doc/views/pact/tag-prod-version.markdown
761
760
  - lib/pact_broker/doc/views/pact/tag-version.markdown
762
761
  - lib/pact_broker/doc/views/pending-provider-pacts.markdown
762
+ - lib/pact_broker/doc/views/provider-pacts-for-verification.markdown
763
763
  - lib/pact_broker/doc/views/provider.markdown
764
764
  - lib/pact_broker/doc/views/version/latest-verification-results-where-pacticipant-is-consumer.markdown
765
765
  - lib/pact_broker/doc/views/webhooks.markdown
@@ -1120,7 +1120,6 @@ files:
1120
1120
  - spec/lib/pact/doc/markdown/index_renderer_spec.rb
1121
1121
  - spec/lib/pact_broker/api/contracts/put_pact_params_contract_spec.rb
1122
1122
  - spec/lib/pact_broker/api/contracts/verifiable_pacts_json_query_schema_spec.rb
1123
- - spec/lib/pact_broker/api/contracts/verifiable_pacts_query_schema_spec.rb
1124
1123
  - spec/lib/pact_broker/api/contracts/verification_contract_spec.rb
1125
1124
  - spec/lib/pact_broker/api/contracts/webhook_contract_spec.rb
1126
1125
  - spec/lib/pact_broker/api/decorators/dashboard_decorator_spec.rb
@@ -1506,7 +1505,6 @@ test_files:
1506
1505
  - spec/lib/pact/doc/markdown/index_renderer_spec.rb
1507
1506
  - spec/lib/pact_broker/api/contracts/put_pact_params_contract_spec.rb
1508
1507
  - spec/lib/pact_broker/api/contracts/verifiable_pacts_json_query_schema_spec.rb
1509
- - spec/lib/pact_broker/api/contracts/verifiable_pacts_query_schema_spec.rb
1510
1508
  - spec/lib/pact_broker/api/contracts/verification_contract_spec.rb
1511
1509
  - spec/lib/pact_broker/api/contracts/webhook_contract_spec.rb
1512
1510
  - spec/lib/pact_broker/api/decorators/dashboard_decorator_spec.rb
@@ -1,36 +0,0 @@
1
- require 'dry-validation'
2
- require 'pact_broker/api/contracts/dry_validation_workarounds'
3
- require 'pact_broker/api/contracts/dry_validation_predicates'
4
-
5
- module PactBroker
6
- module Api
7
- module Contracts
8
- class VerifiablePactsQuerySchema
9
- extend DryValidationWorkarounds
10
- using PactBroker::HashRefinements
11
-
12
- SCHEMA = Dry::Validation.Schema do
13
- configure do
14
- predicates(DryValidationPredicates)
15
- config.messages_file = File.expand_path("../../../locale/en.yml", __FILE__)
16
- end
17
- optional(:provider_version_tags).maybe(:array?)
18
- optional(:consumer_version_selectors).each do
19
- schema do
20
- required(:tag).filled(:str?)
21
- optional(:latest).filled(included_in?: ["true", "false"])
22
- optional(:fallback_tag).filled(:str?)
23
- optional(:consumer).filled(:str?, :not_blank?)
24
- end
25
- end
26
- optional(:include_pending_status).filled(included_in?: ["true", "false"])
27
- optional(:include_wip_pacts_since).filled(:date?)
28
- end
29
-
30
- def self.call(params)
31
- select_first_message(flatten_indexed_messages(SCHEMA.call(params&.symbolize_keys).messages(full: true)))
32
- end
33
- end
34
- end
35
- end
36
- end
@@ -1,97 +0,0 @@
1
- require 'pact_broker/api/contracts/verifiable_pacts_query_schema'
2
-
3
- module PactBroker
4
- module Api
5
- module Contracts
6
- describe VerifiablePactsQuerySchema do
7
- let(:params) do
8
- {
9
- provider_version_tags: provider_version_tags,
10
- consumer_version_selectors: consumer_version_selectors
11
- }
12
- end
13
-
14
- let(:provider_version_tags) { %w[master] }
15
-
16
- let(:consumer_version_selectors) do
17
- [{
18
- tag: "master",
19
- latest: "true"
20
- }]
21
- end
22
-
23
- subject { VerifiablePactsQuerySchema.(params) }
24
-
25
- context "when the params are valid" do
26
- it "has no errors" do
27
- expect(subject).to eq({})
28
- end
29
- end
30
-
31
- context "when provider_version_tags is not an array" do
32
- let(:provider_version_tags) { "foo" }
33
-
34
- it { is_expected.to have_key(:provider_version_tags) }
35
- end
36
-
37
- context "when the consumer_version_selector is missing a tag" do
38
- let(:consumer_version_selectors) do
39
- [{}]
40
- end
41
-
42
- it "flattens the messages" do
43
- expect(subject[:consumer_version_selectors].first).to eq "tag is missing at index 0"
44
- end
45
- end
46
-
47
- context "when the consumer_version_selectors is missing the latest" do
48
- let(:consumer_version_selectors) do
49
- [{
50
- tag: "master"
51
- }]
52
- end
53
-
54
- it { is_expected.to be_empty }
55
- end
56
-
57
- context "when include_wip_pacts_since key exists" do
58
- let(:include_wip_pacts_since) { nil }
59
- let(:params) do
60
- {
61
- include_wip_pacts_since: include_wip_pacts_since
62
- }
63
- end
64
-
65
- context "when it is nil" do
66
- it { is_expected.to have_key(:include_wip_pacts_since) }
67
- end
68
-
69
- context "when it is not a date" do
70
- let(:include_wip_pacts_since) { "foo" }
71
-
72
- it { is_expected.to have_key(:include_wip_pacts_since) }
73
- end
74
-
75
- context "when it is a valid date" do
76
- let(:include_wip_pacts_since) { "2013-02-13T20:04:45.000+11:00" }
77
-
78
- it { is_expected.to_not have_key(:include_wip_pacts_since) }
79
- end
80
- end
81
-
82
- context "when a blank consumer name is specified" do
83
- let(:consumer_version_selectors) do
84
- [{
85
- tag: "feat-x",
86
- consumer: ""
87
- }]
88
- end
89
-
90
- it "has an error" do
91
- expect(subject[:consumer_version_selectors].first).to include "blank"
92
- end
93
- end
94
- end
95
- end
96
- end
97
- end