pact_broker 2.66.0 → 2.67.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/Gemfile +1 -0
  4. data/db/migrations/20201006_add_wip_to_verification.rb +5 -0
  5. data/lib/db.rb +3 -1
  6. data/lib/pact_broker/api/decorators/verifiable_pact_decorator.rb +4 -2
  7. data/lib/pact_broker/api/pact_broker_urls.rb +10 -6
  8. data/lib/pact_broker/api/paths.rb +4 -3
  9. data/lib/pact_broker/api/resources/can_i_deploy.rb +14 -0
  10. data/lib/pact_broker/api/resources/group.rb +0 -1
  11. data/lib/pact_broker/api/resources/latest_pact.rb +1 -1
  12. data/lib/pact_broker/api/resources/pact.rb +8 -0
  13. data/lib/pact_broker/api/resources/pact_versions.rb +4 -0
  14. data/lib/pact_broker/api/resources/pact_webhooks.rb +1 -1
  15. data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +1 -2
  16. data/lib/pact_broker/api/resources/tag.rb +0 -1
  17. data/lib/pact_broker/api/resources/tagged_pact_versions.rb +4 -0
  18. data/lib/pact_broker/api/resources/verification.rb +4 -0
  19. data/lib/pact_broker/api/resources/verifications.rb +14 -2
  20. data/lib/pact_broker/groups/service.rb +0 -3
  21. data/lib/pact_broker/locale/en.yml +1 -0
  22. data/lib/pact_broker/matrix/aggregated_row.rb +1 -0
  23. data/lib/pact_broker/matrix/query_results_with_deployment_status_summary.rb +1 -0
  24. data/lib/pact_broker/pacts/all_pact_publications.rb +1 -0
  25. data/lib/pact_broker/pacts/pact_publication.rb +1 -0
  26. data/lib/pact_broker/pacts/repository.rb +1 -0
  27. data/lib/pact_broker/pacts/verifiable_pact_messages.rb +2 -0
  28. data/lib/pact_broker/test/test_data_builder.rb +1 -1
  29. data/lib/pact_broker/verifications/service.rb +2 -1
  30. data/lib/pact_broker/version.rb +1 -1
  31. data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +1 -1
  32. data/spec/features/create_webhook_spec.rb +18 -0
  33. data/spec/features/wip_pacts_spec.rb +258 -33
  34. data/spec/fixtures/approvals/modifiable_resources.approved.json +74 -0
  35. data/spec/lib/pact_broker/api/decorators/relationships_csv_decorator_spec.rb +0 -3
  36. data/spec/lib/pact_broker/api/decorators/verifiable_pact_decorator_spec.rb +7 -2
  37. data/spec/lib/pact_broker/api/pact_broker_urls_spec.rb +6 -5
  38. data/spec/lib/pact_broker/api/resources/can_i_deploy_spec.rb +51 -0
  39. data/spec/lib/pact_broker/api/resources/default_base_resource_approval_spec.rb +59 -0
  40. data/spec/lib/pact_broker/api/resources/latest_pact_spec.rb +2 -2
  41. data/spec/lib/pact_broker/api/resources/verifications_spec.rb +17 -3
  42. data/spec/lib/pact_broker/pacts/latest_tagged_pact_publications_spec.rb +1 -1
  43. data/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_spec.rb +24 -2
  44. data/spec/lib/pact_broker/verifications/service_spec.rb +4 -2
  45. data/spec/lib/pact_broker/webhooks/render_spec.rb +1 -1
  46. data/spec/spec_helper.rb +5 -0
  47. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ded381f9c89732b48d2d27f1f9dc7c26708a54c407c6a777a0f698c221d03b5
4
- data.tar.gz: 9ac73a1fde0c35b5cc5870e8006556bcc74a5c3765d86ba373b9a91040657b14
3
+ metadata.gz: 2874fe6ca6f552e6cba4d848f56998d0473d860ec4aa47530ccf883d9f189cfe
4
+ data.tar.gz: b8e28ffebc86a0f28251489edb9cd92407a4f2758e4fdc519f1d47d7c32cbf2d
5
5
  SHA512:
6
- metadata.gz: 15667e1349e7ff6a2484016659d1eee0a26d95471ea6f0f959135f72b2fcf68714a550421a1ff239efa089514ef0064a07602cd84c22f60fedfc82b30a2337a0
7
- data.tar.gz: e1c836e2cc3b82096ee5d03c9fe215e5010a7b0eef2aad43781a8713e5e8f8b987fbc86c19d5b5c95c819831a6d07f953a3b8b552d6104b3daddc5a1e62680d0
6
+ metadata.gz: f6bae75159d6ed6b50e0c96f71cd0931d52b94e6fb251d21e31153d67ce892dfaecdf64932f0bca27f06418fb61c67b9348129a5f662590c528d401a2a2df27d
7
+ data.tar.gz: 31b4370cd7d1e4c9b4b7825f013a58c327a1b3766f62ce59380bd878160dde2725cee60a2401625772b88378cb258ca8d5ebfa927131cdaf66ac1636352e4cda
@@ -1,3 +1,23 @@
1
+ <a name="v2.67.0"></a>
2
+ ### v2.67.0 (2020-10-16)
3
+
4
+ #### Features
5
+
6
+ * **wip pacts**
7
+ * if a pact was successfully verified because it was included as a WIP pact, keep it as WIP ([16cae55d](/../../commit/16cae55d))
8
+ * add 'wip' column to verification results ([34f98592](/../../commit/34f98592))
9
+
10
+ #### Bug Fixes
11
+
12
+ * typo when rendering created webhook for old webhooks path ([1e6a06a0](/../../commit/1e6a06a0))
13
+ * include can-i-deploy badge in is_badge_path? logic ([31ea5f34](/../../commit/31ea5f34))
14
+
15
+ * **pacts for verification**
16
+ * gracefully log empty request body ([0e48d13a](/../../commit/0e48d13a))
17
+
18
+ * **can-i-deploy**
19
+ * gracefully handle pacticipant not found ([f6903b23](/../../commit/f6903b23))
20
+
1
21
  <a name="v2.66.0"></a>
2
22
  ### v2.66.0 (2020-10-01)
3
23
 
data/Gemfile CHANGED
@@ -25,6 +25,7 @@ group :test do
25
25
  gem 'timecop', '~> 0.9'
26
26
  gem 'faraday', '~>0.15'
27
27
  gem 'docker-api', '~>1.34'
28
+ gem 'approvals', '~>0.0.1', '< 1'
28
29
  end
29
30
 
30
31
  if ENV['INSTALL_MYSQL'] == "true"
@@ -0,0 +1,5 @@
1
+ Sequel.migration do
2
+ change do
3
+ add_column(:verifications, :wip, TrueClass, default: false, null: false)
4
+ end
5
+ end
data/lib/db.rb CHANGED
@@ -27,7 +27,9 @@ module DB
27
27
  def self.connect db_credentials
28
28
  # Keep this conifiguration in sync with lib/pact_broker/app.rb#configure_database_connection
29
29
  Sequel.datetime_class = DateTime
30
- # logger = Logger.new($stdout)
30
+ if ENV['DEBUG'] == 'true'
31
+ logger = Logger.new($stdout)
32
+ end
31
33
  con = Sequel.connect(db_credentials.merge(:logger => logger, :pool_class => Sequel::ThreadedConnectionPool, :encoding => 'utf8'))
32
34
  con.extension(:connection_validator)
33
35
  con.extension(:pagination)
@@ -20,14 +20,16 @@ module PactBroker
20
20
  getter: -> (context) { context[:decorator].notices(context[:options][:user_options]) }
21
21
 
22
22
  def notices(user_options)
23
- pact_url = pact_version_url(represented, user_options[:base_url])
23
+ metadata = represented.wip ? { wip: true } : nil
24
+ pact_url = pact_version_url_with_metadata(represented, metadata, user_options[:base_url])
24
25
  PactBroker::Pacts::BuildVerifiablePactNotices.call(represented, pact_url, user_options)
25
26
  end
26
27
  end
27
28
 
28
29
  link :self do | user_options |
30
+ metadata = represented.wip ? { wip: true } : nil
29
31
  {
30
- href: pact_version_url(represented, user_options[:base_url]),
32
+ href: pact_version_url_with_metadata(represented, metadata, user_options[:base_url]),
31
33
  name: represented.name
32
34
  }
33
35
  end
@@ -58,19 +58,23 @@ module PactBroker
58
58
  "#{pactigration_base_url(base_url, pact)}/pact-version/#{pact.pact_version_sha}"
59
59
  end
60
60
 
61
- def pact_version_url_with_metadata pact, base_url = ''
62
- "#{pactigration_base_url(base_url, pact)}/pact-version/#{pact.pact_version_sha}/metadata/#{build_webhook_metadata(pact)}"
61
+ def pact_version_url_with_metadata pact, metadata, base_url = ''
62
+ if metadata && metadata.any?
63
+ "#{pact_version_url(pact, base_url)}/metadata/#{encode_metadata(metadata)}"
64
+ else
65
+ pact_version_url(pact, base_url)
66
+ end
63
67
  end
64
68
 
65
- def build_webhook_metadata(pact)
66
- encode_webhook_metadata(build_metadata_for_webhook_triggered_by_pact_publication(pact))
69
+ def pact_version_url_with_webhook_metadata pact, base_url = ''
70
+ pact_version_url_with_metadata(pact, build_metadata_for_webhook_triggered_by_pact_publication(pact), base_url)
67
71
  end
68
72
 
69
- def encode_webhook_metadata(metadata)
73
+ def encode_metadata(metadata)
70
74
  Base64.strict_encode64(Rack::Utils.build_nested_query(metadata))
71
75
  end
72
76
 
73
- def decode_webhook_metadata(metadata)
77
+ def decode_pact_metadata(metadata)
74
78
  if metadata
75
79
  begin
76
80
  Rack::Utils.parse_nested_query(Base64.strict_decode64(metadata)).each_with_object({}) do | (k, v), new_hash |
@@ -1,14 +1,15 @@
1
1
  module PactBroker
2
2
  module Api
3
3
  module Paths
4
- PACT_BADGE_PATH = %r{^/pacts/provider/[^/]+/consumer/.*/badge(?:\.[A-Za-z]+)?$}.freeze
5
- MATRIX_BADGE_PATH = %r{^/matrix/provider/[^/]+/latest/[^/]+/consumer/[^/]+/latest/[^/]+/badge(?:\.[A-Za-z]+)?$}.freeze
4
+ PACT_BADGE_PATH = %r{^/pacts/provider/[^/]+/consumer/.*/badge(?:\.[A-Za-z]+)?$}.freeze
5
+ MATRIX_BADGE_PATH = %r{^/matrix/provider/[^/]+/latest/[^/]+/consumer/[^/]+/latest/[^/]+/badge(?:\.[A-Za-z]+)?$}.freeze
6
+ CAN_I_DEPLOY_BADGE_PATH = %r{^/pacticipants/[^/]+/latest-version/[^/]+/can-i-deploy/to/[^/]+/badge(?:\.[A-Za-z]+)?$}.freeze
6
7
 
7
8
  extend self
8
9
 
9
10
  def is_badge_path?(path)
10
11
  # Optimise by checking include? first - regexp slow
11
- path.include?('/badge') && (path =~ PACT_BADGE_PATH || path =~ MATRIX_BADGE_PATH)
12
+ path.include?('/badge') && (path =~ PACT_BADGE_PATH || path =~ MATRIX_BADGE_PATH || path =~ CAN_I_DEPLOY_BADGE_PATH)
12
13
  end
13
14
  end
14
15
  end
@@ -1,11 +1,14 @@
1
1
  require 'pact_broker/api/resources/matrix'
2
2
  require 'pact_broker/matrix/can_i_deploy_query_schema'
3
3
  require 'pact_broker/matrix/parse_can_i_deploy_query'
4
+ require 'pact_broker/messages'
4
5
 
5
6
  module PactBroker
6
7
  module Api
7
8
  module Resources
8
9
  class CanIDeploy < Matrix
10
+ include PactBroker::Messages
11
+
9
12
  def initialize
10
13
  super
11
14
  @query_params = JSON.parse(Rack::Utils.parse_nested_query(request.uri.query).to_json, symbolize_names: true)
@@ -16,6 +19,9 @@ module PactBroker
16
19
  if (errors = query_schema.call(query_params)).any?
17
20
  set_json_validation_error_messages(errors)
18
21
  true
22
+ elsif !pacticipant
23
+ set_json_validation_error_messages(pacticipant: [message('errors.validation.pacticipant_not_found', name: pacticipant_name)])
24
+ true
19
25
  else
20
26
  false
21
27
  end
@@ -32,6 +38,14 @@ module PactBroker
32
38
  def query_schema
33
39
  PactBroker::Api::Contracts::CanIDeployQuerySchema
34
40
  end
41
+
42
+ def pacticipant
43
+ @pacticipant ||= pacticipant_service.find_pacticipant_by_name(pacticipant_name)
44
+ end
45
+
46
+ def pacticipant_name
47
+ selectors.first.pacticipant_name
48
+ end
35
49
  end
36
50
  end
37
51
  end
@@ -5,7 +5,6 @@ module PactBroker
5
5
  module Api
6
6
  module Resources
7
7
  class Group < BaseResource
8
-
9
8
  def content_types_provided
10
9
  [["text/csv", :to_csv]]
11
10
  end
@@ -50,7 +50,7 @@ module PactBroker
50
50
  end
51
51
 
52
52
  def metadata
53
- @metadata ||= encode_webhook_metadata(PactBroker::Pacts::Metadata.build_metadata_for_latest_pact(pact, identifier_from_path))
53
+ @metadata ||= encode_metadata(PactBroker::Pacts::Metadata.build_metadata_for_latest_pact(pact, identifier_from_path))
54
54
  end
55
55
  end
56
56
  end
@@ -92,6 +92,14 @@ module PactBroker
92
92
  :'pacts::pact'
93
93
  end
94
94
 
95
+ def policy_pacticipant
96
+ if resource_exists?
97
+ consumer
98
+ else
99
+ nil
100
+ end
101
+ end
102
+
95
103
  private
96
104
 
97
105
  def pact
@@ -34,6 +34,10 @@ module PactBroker
34
34
  def policy_name
35
35
  :'pacts::pacts'
36
36
  end
37
+
38
+ def policy_pacticipant
39
+ consumer
40
+ end
37
41
  end
38
42
  end
39
43
  end
@@ -52,7 +52,7 @@ module PactBroker
52
52
 
53
53
  def from_json
54
54
  saved_webhook = webhook_service.create next_uuid, webhook, consumer, provider
55
- response.body = Decorators::WebhookDecorator.new(saved_webhook).to_json(decoractor_options)
55
+ response.body = Decorators::WebhookDecorator.new(saved_webhook).to_json(decorator_options)
56
56
  end
57
57
 
58
58
  def to_json
@@ -85,8 +85,7 @@ module PactBroker
85
85
  end
86
86
 
87
87
  def log_request
88
- parameters = request.get? ? nested_query : params
89
- logger.info "Fetching pacts for verification by #{provider_name}", provider_name: provider_name, params: parameters
88
+ logger.info "Fetching pacts for verification by #{provider_name}", provider_name: provider_name, params: query
90
89
  end
91
90
 
92
91
  def nested_query
@@ -48,6 +48,5 @@ module PactBroker
48
48
  end
49
49
  end
50
50
  end
51
-
52
51
  end
53
52
  end
@@ -34,6 +34,10 @@ module PactBroker
34
34
  def policy_name
35
35
  :'pacts::pacts'
36
36
  end
37
+
38
+ def policy_pacticipant
39
+ consumer
40
+ end
37
41
  end
38
42
  end
39
43
  end
@@ -49,6 +49,10 @@ module PactBroker
49
49
  :'verifications::verification'
50
50
  end
51
51
 
52
+ def policy_pacticipant
53
+ provider
54
+ end
55
+
52
56
  private
53
57
 
54
58
  def verification
@@ -48,7 +48,7 @@ module PactBroker
48
48
  end
49
49
 
50
50
  def from_json
51
- verification = verification_service.create(next_verification_number, params(symbolize_names: false), pact, webhook_options)
51
+ verification = verification_service.create(next_verification_number, verification_params, pact, webhook_options)
52
52
  response.body = decorator_for(verification).to_json(decorator_options)
53
53
  true
54
54
  end
@@ -57,6 +57,10 @@ module PactBroker
57
57
  :'verifications::verifications'
58
58
  end
59
59
 
60
+ def policy_pacticipant
61
+ provider
62
+ end
63
+
60
64
  private
61
65
 
62
66
  def pact
@@ -72,7 +76,11 @@ module PactBroker
72
76
  end
73
77
 
74
78
  def metadata
75
- PactBrokerUrls.decode_webhook_metadata(identifier_from_path[:metadata])
79
+ PactBrokerUrls.decode_pact_metadata(identifier_from_path[:metadata])
80
+ end
81
+
82
+ def wip?
83
+ metadata[:wip] == 'true'
76
84
  end
77
85
 
78
86
  def webhook_options
@@ -82,6 +90,10 @@ module PactBroker
82
90
  .with_webhook_context(metadata)
83
91
  }
84
92
  end
93
+
94
+ def verification_params
95
+ params(symbolize_names: false).merge('wip' => wip?)
96
+ end
85
97
  end
86
98
  end
87
99
  end
@@ -2,12 +2,9 @@ require 'pact_broker/repositories'
2
2
  require 'pact_broker/relationships/groupify'
3
3
 
4
4
  module PactBroker
5
-
6
5
  module Groups
7
6
  module Service
8
-
9
7
  extend self
10
-
11
8
  extend PactBroker::Repositories
12
9
  extend PactBroker::Services
13
10
 
@@ -45,6 +45,7 @@ en:
45
45
  pacticipant_name_mismatch: "in pact ('%{name_in_pact}') does not match %{pacticipant} name in path ('%{name}')."
46
46
  connection_encoding_not_utf8: "The Sequel connection encoding (%{encoding}) is strongly recommended to be \"utf8\". If you need to set it to %{encoding} for some particular reason, then disable this check by setting config.validate_database_connection_config = false"
47
47
  invalid_webhook_uuid: The UUID can only contain the characters A-Z, a-z, 0-9, _ and -, and must be 16 or more characters.
48
+ pacticipant_not_found: No pacticipant with name '%{name}' found
48
49
  duplicate_pacticipant: |
49
50
  This is the first time a pact has been published for "%{new_name}".
50
51
  The name "%{new_name}" is very similar to the following existing consumers/providers:
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'pact_broker/verifications/repository'
2
3
 
3
4
  # A collection of matrix rows with the same pact publication id
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'pact_broker/matrix/query_results'
2
3
 
3
4
  module PactBroker
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'pact_broker/domain/tag'
2
3
  require 'pact_broker/domain/pacticipant'
3
4
  require 'pact_broker/domain/version'
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'pact_broker/domain/pact'
2
3
  require 'pact_broker/pacts/pact_version'
3
4
  require 'pact_broker/repositories/helpers'
@@ -559,6 +559,7 @@ module PactBroker
559
559
  verifications_join = {
560
560
  pact_version_id: :pact_version_id,
561
561
  Sequel[:verifications][:success] => true,
562
+ Sequel[:verifications][:wip] => false,
562
563
  Sequel[:verifications][:provider_id] => provider_id
563
564
  }
564
565
  tags_join = {
@@ -1,3 +1,5 @@
1
+ require 'forwardable'
2
+
1
3
  module PactBroker
2
4
  module Pacts
3
5
  class VerifiablePactMessages
@@ -316,7 +316,7 @@ module PactBroker
316
316
  parameters.delete(:comment)
317
317
  tag_names = [parameters.delete(:tag_names), parameters.delete(:tag_name)].flatten.compact
318
318
  provider_version_number = parameters[:provider_version] || '4.5.6'
319
- default_parameters = {success: true, number: 1, test_results: {some: 'results'}}
319
+ default_parameters = { success: true, number: 1, test_results: {some: 'results'}, wip: false }
320
320
  default_parameters[:execution_date] = @now if @now
321
321
  parameters = default_parameters.merge(parameters)
322
322
  parameters.delete(:provider_version)
@@ -21,10 +21,11 @@ module PactBroker
21
21
  end
22
22
 
23
23
  def create next_verification_number, params, pact, webhook_options
24
- logger.info "Creating verification #{next_verification_number} for pact_id=#{pact.id} from params #{params.reject{ |k,_| k == "testResults"}}"
24
+ logger.info "Creating verification #{next_verification_number} for pact_id=#{pact.id}", payload: params.reject{ |k,_| k == "testResults"}
25
25
  verification = PactBroker::Domain::Verification.new
26
26
  provider_version_number = params.fetch('providerApplicationVersion')
27
27
  PactBroker::Api::Decorators::VerificationDecorator.new(verification).from_hash(params)
28
+ verification.wip = params.fetch('wip')
28
29
  verification.number = next_verification_number
29
30
  verification = verification_repository.create(verification, provider_version_number, pact)
30
31
 
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.66.0'
2
+ VERSION = '2.67.0'
3
3
  end
@@ -38,7 +38,7 @@ module PactBroker
38
38
 
39
39
  def to_hash
40
40
  @hash ||= {
41
- PACT_URL => pact ? PactBroker::Api::PactBrokerUrls.pact_version_url_with_metadata(pact, base_url) : "",
41
+ PACT_URL => pact ? PactBroker::Api::PactBrokerUrls.pact_version_url_with_webhook_metadata(pact, base_url) : "",
42
42
  VERIFICATION_RESULT_URL => verification_url,
43
43
  CONSUMER_VERSION_NUMBER => consumer_version_number,
44
44
  PROVIDER_VERSION_NUMBER => verification ? verification.provider_version_number : "",
@@ -123,4 +123,22 @@ describe "Creating a webhook" do
123
123
  expect(PactBroker::Webhooks::Webhook.first.provider).to_not be nil
124
124
  end
125
125
  end
126
+
127
+ context "with the old path" do
128
+ let(:path) { "/pacts/provider/Some%20Provider/consumer/Some%20Consumer/webhooks" }
129
+
130
+ its(:status) { is_expected.to be 201 }
131
+
132
+ it "returns the Location header" do
133
+ expect(subject.headers['Location']).to match(%r{http://example.org/webhooks/.+})
134
+ end
135
+
136
+ it "returns a JSON Content Type" do
137
+ expect(subject.headers['Content-Type']).to eq 'application/hal+json;charset=utf-8'
138
+ end
139
+
140
+ it "returns the newly created webhook" do
141
+ expect(response_body).to include webhook_hash
142
+ end
143
+ end
126
144
  end
@@ -1,26 +1,9 @@
1
1
  RSpec.describe "the lifecycle of a WIP pact" do
2
- before do
3
- td.set_now(DateTime.now - 100)
4
- .create_provider("Bar")
5
- .create_provider_version("1")
6
- .create_provider_version_tag("master")
7
- end
8
2
  let(:pact_content_1) { { interactions: [{ some: 'interaction'}] }.to_json }
9
3
  let(:pact_content_2) { { interactions: [{ some: 'other interaction'}] }.to_json }
4
+ let(:pact_content_3) { { interactions: [{ some: 'other other interaction'}] }.to_json }
10
5
  let(:request_headers) { { "CONTENT_TYPE" => "application/json", "HTTP_ACCEPT" => "application/hal+json"} }
11
6
  let(:provider_version_number) { "2" }
12
- let(:failed_verification_results) do
13
- {
14
- providerApplicationVersion: provider_version_number,
15
- success: false
16
- }.to_json
17
- end
18
- let(:successful_verification_results) do
19
- {
20
- providerApplicationVersion: provider_version_number,
21
- success: true
22
- }.to_json
23
- end
24
7
  let(:pacts_for_verification_request_body) do
25
8
  {
26
9
  consumerVersionSelectors: [ { tag: "master", latest: true } ],
@@ -28,20 +11,48 @@ RSpec.describe "the lifecycle of a WIP pact" do
28
11
  includeWipPactsSince: start_date
29
12
  }.to_json
30
13
  end
14
+
31
15
  let(:start_date) { (Date.today - 1).to_datetime }
32
16
 
17
+ def create_initial_provider_version_on_master
18
+ td.set_now(DateTime.now - 100)
19
+ .create_provider("Bar")
20
+ .create_provider_version("1")
21
+ .create_provider_version_tag("master")
22
+ end
23
+
24
+ def can_i_merge(consumer_version)
25
+ can_i_deploy(consumer_version, "master")
26
+ end
27
+
28
+ def can_i_deploy(consumer_version, provider_tag)
29
+ can_i_deploy_response = get("/can-i-deploy", { pacticipant: "Foo", version: consumer_version, to: provider_tag })
30
+ JSON.parse(can_i_deploy_response.body)["summary"]["deployable"]
31
+ end
32
+
33
33
  def publish_pact_with_master_tag
34
34
  put("/pacts/provider/Bar/consumer/Foo/version/1", pact_content_1, request_headers)
35
35
  put("/pacticipants/Foo/versions/1/tags/master", nil, request_headers)
36
36
  end
37
37
 
38
- def publish_pact_with_feature_tag
39
- put("/pacts/provider/Bar/consumer/Foo/version/2", pact_content_2, request_headers)
40
- put("/pacticipants/Foo/versions/2/tags/feat-x", nil, request_headers)
38
+ def publish_pact_with_feature_tag(version = "2", tag = "feat-x", pact_content = nil)
39
+ put("/pacts/provider/Bar/consumer/Foo/version/#{version}", pact_content || pact_content_2, request_headers)
40
+ put("/pacticipants/Foo/versions/#{version}/tags/#{tag}", nil, request_headers)
41
+ end
42
+
43
+ def publish_new_pact_with_master_tag_after_merging_in_feature_branch
44
+ put("/pacts/provider/Bar/consumer/Foo/version/3", pact_content_2, request_headers)
45
+ put("/pacticipants/Foo/versions/2/tags/master", nil, request_headers)
41
46
  end
42
47
 
43
- def get_pacts_for_verification
44
- post("/pacts/provider/Bar/for-verification", pacts_for_verification_request_body, request_headers)
48
+ def get_pacts_for_verification(request_body = nil)
49
+ post("/pacts/provider/Bar/for-verification", request_body || pacts_for_verification_request_body, request_headers)
50
+ end
51
+
52
+ def pact_urls_from(pacts_for_verification_response)
53
+ JSON.parse(pacts_for_verification_response.body)["_embedded"]["pacts"].collect do | pact |
54
+ pact["_links"]["self"]["href"]
55
+ end
45
56
  end
46
57
 
47
58
  def wip_pact_url_from(pacts_for_verification_response)
@@ -49,16 +60,21 @@ RSpec.describe "the lifecycle of a WIP pact" do
49
60
  end
50
61
 
51
62
  def get_pact(pact_url)
52
- get pact_url, nil, request_headers
63
+ get(pact_url, nil, request_headers)
53
64
  end
54
65
 
55
66
  def verification_results_url_from(pact_response)
56
67
  JSON.parse(pact_response.body)["_links"]["pb:publish-verification-results"]["href"]
57
68
  end
58
69
 
59
- def publish_verification_results_with_tag_master(verification_results_url, results)
60
- post(verification_results_url, results, request_headers)
61
- put("/pacticipants/Bar/versions/#{provider_version_number}/tags/master", nil, request_headers)
70
+ def publish_verification_results_with_tag_master(verification_results_url, success)
71
+ publish_verification_results(provider_version_number, "master", verification_results_url, success)
72
+ end
73
+
74
+ def publish_verification_results(provider_version_number, tag, verification_results_url, success)
75
+ request_body = { success: success, providerApplicationVersion: provider_version_number}.to_json
76
+ post(verification_results_url, request_body, request_headers)
77
+ put("/pacticipants/Bar/versions/#{provider_version_number}/tags/#{tag}", nil, request_headers)
62
78
  end
63
79
 
64
80
  def pending_status_from(pacts_for_verification_response)
@@ -71,10 +87,20 @@ RSpec.describe "the lifecycle of a WIP pact" do
71
87
  end
72
88
  end
73
89
 
90
+ def build_pacts_for_verification_request_body(provider_version_tag, consumer_version_tag = nil)
91
+ {
92
+ consumerVersionSelectors: [ { tag: consumer_version_tag || provider_version_tag, latest: true, fallbackTag: "master" } ],
93
+ providerVersionTags: [provider_version_tag],
94
+ includeWipPactsSince: start_date
95
+ }.to_json
96
+ end
97
+
74
98
  context "when the includeWipPactsSince date is specified" do
75
99
  context "a pact published afer the specified date, with a tag that is not specified explictly in the 'pacts for verification' request" do
76
100
  describe "when it is first published" do
77
101
  it "is included in the list of pacts to verify as a WIP pact" do
102
+ create_initial_provider_version_on_master
103
+
78
104
  publish_pact_with_master_tag
79
105
  publish_pact_with_feature_tag
80
106
 
@@ -85,6 +111,8 @@ RSpec.describe "the lifecycle of a WIP pact" do
85
111
 
86
112
  describe "when it is verified unsuccessfully" do
87
113
  it "is still included as a WIP pact" do
114
+ create_initial_provider_version_on_master
115
+
88
116
  # CONSUMER BUILD
89
117
  # publish pact
90
118
  publish_pact_with_master_tag
@@ -100,7 +128,7 @@ RSpec.describe "the lifecycle of a WIP pact" do
100
128
 
101
129
  # publish failure verification results
102
130
  verification_results_url = verification_results_url_from(pact_response)
103
- publish_verification_results_with_tag_master(verification_results_url, failed_verification_results)
131
+ publish_verification_results_with_tag_master(verification_results_url, false)
104
132
 
105
133
  # ANOTHER PROVIDER BUILD
106
134
  # get pacts for verification
@@ -110,8 +138,10 @@ RSpec.describe "the lifecycle of a WIP pact" do
110
138
  end
111
139
  end
112
140
 
113
- describe "when it is verified successfully" do
114
- it "is no longer included in the list of pacts to verify" do
141
+ describe "when it is verified successfully while included as a WIP pact" do
142
+ it "is still included as a WIP pact" do
143
+ create_initial_provider_version_on_master
144
+
115
145
  # CONSUMER BUILD
116
146
  publish_pact_with_master_tag
117
147
  publish_pact_with_feature_tag
@@ -124,18 +154,213 @@ RSpec.describe "the lifecycle of a WIP pact" do
124
154
 
125
155
  # verify pact... success!
126
156
 
127
- # publish failure verification results
157
+ # publish success verification results
128
158
  verification_results_url = verification_results_url_from(pact_response)
129
- publish_verification_results_with_tag_master(verification_results_url, successful_verification_results)
159
+ publish_verification_results_with_tag_master(verification_results_url, true)
130
160
 
131
161
  # ANOTHER PROVIDER BUILD 2
132
162
  # get pacts for verification
133
163
  # publish successful verification results
134
164
  pacts_for_verification_response = get_pacts_for_verification
135
- # not wip any more
165
+ # still wip
166
+ expect(wip_pacts_from(pacts_for_verification_response).size).to eq 1
167
+ end
168
+ end
169
+
170
+ describe "when it is verified successfully when included explicitly" do
171
+ it "is no longer included as a WIP pact" do
172
+ create_initial_provider_version_on_master
173
+
174
+ # CONSUMER BUILD
175
+ publish_pact_with_master_tag
176
+ publish_pact_with_feature_tag
177
+
178
+ # PROVIDER BUILD
179
+ # fetch pacts to verify
180
+ pacts_for_verification_response = get_pacts_for_verification
181
+ pact_url = wip_pact_url_from(pacts_for_verification_response)
182
+ pact_response = get_pact(pact_url)
183
+
184
+ # verify pact... success!
185
+
186
+ # publish success verification results
187
+ verification_results_url = verification_results_url_from(pact_response)
188
+ publish_verification_results_with_tag_master(verification_results_url, true)
189
+
190
+ # CONSUMER BUILD
191
+ # merge feature branch into master
192
+ publish_new_pact_with_master_tag_after_merging_in_feature_branch
193
+
194
+ # ANOTHER PROVIDER BUILD 3
195
+ # get pacts for verification
196
+ # publish successful verification results
197
+ pacts_for_verification_response = get_pacts_for_verification
198
+ # no longer wip
199
+ expect(wip_pacts_from(pacts_for_verification_response).size).to eq 0
200
+ end
201
+ end
202
+
203
+ describe "a feature branching scenario" do
204
+ it "keeps being WIP until the branch is merged" do
205
+ create_initial_provider_version_on_master
206
+
207
+ # CONSUMER BUILD - master
208
+ publish_pact_with_master_tag
209
+
210
+ # CONSUMER BUILD - feature branch
211
+ publish_pact_with_feature_tag
212
+ expect(can_i_merge("2")).to_not be true
213
+
214
+ # PROVIDER BUILD
215
+ # fetch pacts to verify
216
+ pacts_for_verification_response = get_pacts_for_verification
217
+ pact_url = wip_pact_url_from(pacts_for_verification_response)
218
+ pact_response = get_pact(pact_url)
219
+
220
+ # verify pact... success!
221
+
222
+ # publish success verification results
223
+ verification_results_url = verification_results_url_from(pact_response)
224
+ publish_verification_results("1", "master", verification_results_url, true)
225
+
226
+ # CONSUMER
227
+ expect(can_i_merge("2")).to be true
228
+
229
+ # ANOTHER PROVIDER BUILD before the consumer build runs again
230
+ # fetch pacts to verify
231
+ pacts_for_verification_response = get_pacts_for_verification
232
+ # feat-x pact is still wip
233
+ pact_url = wip_pact_url_from(pacts_for_verification_response)
234
+ pact_response = get_pact(pact_url)
235
+
236
+ # however feat-x pact is no longer pending because it has a successful verification from master!!!
237
+ # Question: do we want this behaviour? Or should pending use the same logic?
238
+ expect(wip_pacts_from(pacts_for_verification_response).first['verificationProperties']['wip']).to be true
239
+ expect(wip_pacts_from(pacts_for_verification_response).first['verificationProperties']['pending']).to be nil
240
+
241
+ # verify pact... success!
242
+
243
+ # publish success verification results
244
+ verification_results_url = verification_results_url_from(pact_response)
245
+ publish_verification_results("2", "master", verification_results_url, true)
246
+
247
+ # CONSUMER BUILD
248
+ # merge feature branch into master
249
+ expect(can_i_merge("2")).to be true
250
+ publish_new_pact_with_master_tag_after_merging_in_feature_branch
251
+ expect(can_i_deploy("3", "master")).to be true
252
+
253
+ # ANOTHER PROVIDER BUILD 3
254
+ # get pacts for verification
255
+ # publish successful verification results
256
+ pacts_for_verification_response = get_pacts_for_verification
257
+ # no longer wip
136
258
  expect(wip_pacts_from(pacts_for_verification_response).size).to eq 0
137
259
  end
138
260
  end
261
+
262
+ describe "a feature branching scenario with matching feature branches" do
263
+ it "stays wip on master even after it has been successfully verified on the provider's feature branch" do
264
+ create_initial_provider_version_on_master
265
+
266
+ # CONSUMER BUILD - master
267
+ publish_pact_with_master_tag
268
+
269
+ # CONSUMER BUILD - feature branch
270
+ publish_pact_with_feature_tag
271
+
272
+ # PROVIDER BUILD - master
273
+ # fetch pacts to verify
274
+ pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("master"))
275
+ pact_url = wip_pact_url_from(pacts_for_verification_response)
276
+ pact_response = get_pact(pact_url)
277
+
278
+ # verify pact... failure (not implemented on master yet)
279
+
280
+ # publish failure verification results
281
+ verification_results_url = verification_results_url_from(pact_response)
282
+ publish_verification_results("1", "master", verification_results_url, false)
283
+
284
+ # PROVIDER BUILD - on a matching feature branch
285
+ # fetch pacts to verify
286
+ pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-x"))
287
+ # pact is not WIP because it has been explicitly included
288
+ expect(wip_pacts_from(pacts_for_verification_response).size).to eq 0
289
+ pact_url = pact_urls_from(pacts_for_verification_response).first
290
+ pact_response = get_pact(pact_url)
291
+
292
+ # verify pact - success on feature branch!
293
+
294
+ # publish successful results from feature branch
295
+ verification_results_url = verification_results_url_from(pact_response)
296
+ publish_verification_results("2", "feat-x", verification_results_url, true)
297
+
298
+ # PROVIDER BUILD - back on master
299
+ pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("master"))
300
+ # still wip for master
301
+ expect(wip_pacts_from(pacts_for_verification_response).size).to eq 1
302
+ end
303
+ end
304
+
305
+ describe "when a brand new provider branch is created" do
306
+ it "does not include any previously created WIP pacts because every single pact is pending for this new branch, and we don't want to verify the world" do
307
+ create_initial_provider_version_on_master
308
+
309
+ # CONSUMER BUILD - master
310
+ publish_pact_with_master_tag
311
+
312
+ # CONSUMER BUILD - feature branch
313
+ publish_pact_with_feature_tag
314
+
315
+ # PROVIDER BUILD - brand new feature branch
316
+ pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-y", "master"))
317
+ expect(wip_pacts_from(pacts_for_verification_response).size).to eq 0
318
+
319
+ # verify master pact successfully
320
+ pact_url = pact_urls_from(pacts_for_verification_response).first
321
+ pact_response = get_pact(pact_url)
322
+
323
+ # publish successful results from feature branch
324
+ verification_results_url = verification_results_url_from(pact_response)
325
+ publish_verification_results("2", "feat-y", verification_results_url, true)
326
+
327
+ # PROVIDER BUILD 2 - feature branch
328
+ pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-y", "master"))
329
+ # still no wip pacts
330
+ expect(wip_pacts_from(pacts_for_verification_response).size).to eq 0
331
+ end
332
+
333
+ it "does include any subsequently created new pacts" do
334
+ create_initial_provider_version_on_master
335
+
336
+ # CONSUMER BUILD - master
337
+ publish_pact_with_master_tag
338
+
339
+ # CONSUMER BUILD - feature branch
340
+ publish_pact_with_feature_tag
341
+
342
+ # PROVIDER BUILD - brand new feature branch
343
+ pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-y", "master"))
344
+ expect(wip_pacts_from(pacts_for_verification_response).size).to eq 0
345
+ # verify master pact successfully (creates a new provider version with tag feat-y)
346
+ pact_url = pact_urls_from(pacts_for_verification_response).first
347
+ pact_response = get_pact(pact_url)
348
+ verification_results_url = verification_results_url_from(pact_response)
349
+ publish_verification_results("2", "feat-y", verification_results_url, true)
350
+ sleep 1 if ::DB.mysql? # time resolution is lower on MySQL, need to make sure the next pacts are created after the above provider version
351
+
352
+ # CONSUMER BUILD - feature branch again
353
+ # republish same pact content with new version
354
+ publish_pact_with_feature_tag("3")
355
+
356
+ # CONSUMER BUILD - another feature branch
357
+ publish_pact_with_feature_tag("4", "feat-z", pact_content_3)
358
+
359
+ # PROVIDER BUILD - brand new feature branch again
360
+ pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-y", "master"))
361
+ expect(wip_pacts_from(pacts_for_verification_response).size).to eq 2
362
+ end
363
+ end
139
364
  end
140
365
  end
141
366
  end