pact 1.41.2 → 1.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +228 -0
  3. data/lib/pact/cli/run_pact_verification.rb +23 -7
  4. data/lib/pact/cli/spec_criteria.rb +3 -0
  5. data/lib/pact/cli.rb +5 -0
  6. data/lib/pact/consumer/configuration/dsl.rb +0 -1
  7. data/lib/pact/consumer/consumer_contract_builder.rb +4 -0
  8. data/lib/pact/consumer/interaction_builder.rb +6 -0
  9. data/lib/pact/consumer/spec_hooks.rb +2 -2
  10. data/lib/pact/doc/sort_interactions.rb +2 -3
  11. data/lib/pact/hal/http_client.rb +11 -5
  12. data/lib/pact/hal/link.rb +50 -13
  13. data/lib/pact/hal/non_json_entity.rb +28 -0
  14. data/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb +94 -0
  15. data/lib/pact/pact_broker/notices.rb +34 -0
  16. data/lib/pact/pact_broker/pact_selection_description.rb +24 -0
  17. data/lib/pact/pact_broker.rb +10 -4
  18. data/lib/pact/provider/configuration/dsl.rb +6 -1
  19. data/lib/pact/provider/configuration/message_provider_dsl.rb +59 -0
  20. data/lib/pact/provider/configuration/pact_verification_from_broker.rb +37 -4
  21. data/lib/pact/provider/configuration/service_provider_dsl.rb +1 -1
  22. data/lib/pact/provider/help/content.rb +4 -4
  23. data/lib/pact/provider/help/pact_diff.rb +10 -34
  24. data/lib/pact/provider/help/write.rb +6 -7
  25. data/lib/pact/provider/pact_helper_locator.rb +0 -1
  26. data/lib/pact/provider/pact_source.rb +10 -1
  27. data/lib/pact/provider/pact_spec_runner.rb +24 -16
  28. data/lib/pact/provider/pact_uri.rb +8 -6
  29. data/lib/pact/provider/request.rb +1 -1
  30. data/lib/pact/provider/rspec/formatter_rspec_3.rb +21 -6
  31. data/lib/pact/provider/rspec/json_formatter.rb +100 -0
  32. data/lib/pact/provider/rspec/pact_broker_formatter.rb +1 -1
  33. data/lib/pact/provider/rspec.rb +17 -7
  34. data/lib/pact/provider/test_methods.rb +9 -3
  35. data/lib/pact/provider/verification_results/publish.rb +3 -0
  36. data/lib/pact/provider/verification_results/publish_all.rb +14 -1
  37. data/lib/pact/provider/verification_results/verification_result.rb +1 -4
  38. data/lib/pact/provider/world.rb +1 -1
  39. data/lib/pact/tasks/task_helper.rb +6 -1
  40. data/lib/pact/version.rb +1 -1
  41. data/pact.gemspec +11 -13
  42. metadata +59 -56
  43. data/lib/pact/pact_broker/fetch_pending_pacts.rb +0 -58
@@ -6,13 +6,13 @@ require 'pact/provider/help/prompt_text'
6
6
  module Pact
7
7
  module Provider
8
8
  module RSpec
9
-
10
9
  class Formatter < ::RSpec::Core::Formatters::DocumentationFormatter
11
10
 
12
11
  class NilFormatter < ::RSpec::Core::Formatters::BaseFormatter
13
12
  Pact::RSpec.with_rspec_3 do
14
13
  ::RSpec::Core::Formatters.register self, :start, :example_group_started, :close
15
14
  end
15
+
16
16
  def dump_summary(summary)
17
17
  end
18
18
  end
@@ -93,17 +93,32 @@ module Pact
93
93
 
94
94
  def interaction_rerun_command_for example
95
95
  example_description = example.metadata[:pact_interaction_example_description]
96
- if ENV['PACT_INTERACTION_RERUN_COMMAND']
96
+
97
+ _id = example.metadata[:pact_interaction]._id
98
+ index = example.metadata[:pact_interaction].index
99
+ provider_state = example.metadata[:pact_interaction].provider_state
100
+ description = example.metadata[:pact_interaction].description
101
+ pactfile_uri = example.metadata[:pactfile_uri]
102
+
103
+ if _id && ENV['PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER']
104
+ cmd = String.new(ENV['PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER'])
105
+ cmd.gsub!("<PACT_URI>", example.metadata[:pactfile_uri].to_s)
106
+ cmd.gsub!("<PACT_BROKER_INTERACTION_ID>", "#{_id}")
107
+ colorizer.wrap("#{cmd} ", ::RSpec.configuration.failure_color) + colorizer.wrap("# #{example_description}", ::RSpec.configuration.detail_color)
108
+ elsif ENV['PACT_INTERACTION_RERUN_COMMAND']
97
109
  cmd = String.new(ENV['PACT_INTERACTION_RERUN_COMMAND'])
98
- provider_state = example.metadata[:pact_interaction].provider_state
99
- description = example.metadata[:pact_interaction].description
100
- pactfile_uri = example.metadata[:pactfile_uri]
101
110
  cmd.gsub!("<PACT_URI>", pactfile_uri.to_s)
102
111
  cmd.gsub!("<PACT_DESCRIPTION>", description)
103
112
  cmd.gsub!("<PACT_PROVIDER_STATE>", "#{provider_state}")
113
+ cmd.gsub!("<PACT_INTERACTION_INDEX>", "#{index}")
104
114
  colorizer.wrap("#{cmd} ", ::RSpec.configuration.failure_color) + colorizer.wrap("# #{example_description}", ::RSpec.configuration.detail_color)
105
115
  else
106
- colorizer.wrap("* #{example_description}", ::RSpec.configuration.failure_color)
116
+ message = if _id
117
+ "* #{example_description} (to re-run just this interaction, set environment variable PACT_BROKER_INTERACTION_ID=\"#{_id}\")"
118
+ else
119
+ "* #{example_description} (to re-run just this interaction, set environment variables PACT_DESCRIPTION=\"#{description}\" PACT_PROVIDER_STATE=\"#{provider_state}\")"
120
+ end
121
+ colorizer.wrap(message, ::RSpec.configuration.failure_color)
107
122
  end
108
123
  end
109
124
 
@@ -0,0 +1,100 @@
1
+ require 'rspec/core/formatters/json_formatter'
2
+
3
+ module Pact
4
+ module Provider
5
+ module RSpec
6
+ class JsonFormatter < ::RSpec::Core::Formatters::JsonFormatter
7
+ ::RSpec::Core::Formatters.register self, :message, :dump_summary, :dump_profile, :stop, :seed, :close
8
+
9
+ def dump_summary(summary)
10
+ super(create_custom_summary(summary))
11
+ output_hash[:summary][:pacts] = pacts(summary)
12
+ end
13
+
14
+ def format_example(example)
15
+ {
16
+ :id => example.id,
17
+ :interaction_index => example.metadata[:pact_interaction].index,
18
+ :description => example.description,
19
+ :full_description => example.full_description,
20
+ :status => calculate_status(example),
21
+ :file_path => example.metadata[:file_path],
22
+ :line_number => example.metadata[:line_number],
23
+ :run_time => example.execution_result.run_time,
24
+ :mismatches => extract_differences(example),
25
+ :pact_url => example.metadata[:pact_uri].uri
26
+ }
27
+ end
28
+
29
+ def stop(notification)
30
+ output_hash[:examples] = notification.examples.map do |example|
31
+ format_example(example).tap do |hash|
32
+ e = example.exception
33
+ if e
34
+ hash[:exception] = {
35
+ class: e.class.name,
36
+ message: e.message,
37
+ }
38
+ # No point providing a backtrace for a mismatch, too much noise
39
+ if !e.is_a?(::RSpec::Expectations::ExpectationNotMetError)
40
+ hash[:exception][:backtrace]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def calculate_status(example)
48
+ if example.execution_result.status == :failed && example.metadata[:pact_ignore_failures]
49
+ 'pending'
50
+ else
51
+ example.execution_result.status.to_s
52
+ end
53
+ end
54
+
55
+ # There will most likely be only one pact associated with this RSpec execution, because
56
+ # the most likely user of this formatter is the Go implementation that parses the JSON
57
+ # and builds Go tests from them.
58
+ # If the JSON formatter is used by someone else and they have multiple pacts, all the notices
59
+ # for the pacts will be mushed together in one collection, so it will be hard to know which notice
60
+ # belongs to which pact.
61
+ def pacts(summary)
62
+ unique_pact_metadatas(summary).collect do | example_metadata |
63
+ pact_uri = example_metadata[:pact_uri]
64
+ notices = (pact_uri.metadata[:notices] && pact_uri.metadata[:notices].before_verification_notices) || []
65
+ {
66
+ notices: notices,
67
+ url: pact_uri.uri,
68
+ consumer_name: example_metadata[:pact_consumer_contract].consumer.name,
69
+ provider_name: example_metadata[:pact_consumer_contract].provider.name,
70
+ short_description: pact_uri.metadata[:short_description]
71
+ }
72
+ end
73
+ end
74
+
75
+ def unique_pact_metadatas(summary)
76
+ summary.examples.collect(&:metadata).group_by{ | metadata | metadata[:pact_uri].uri }.values.collect(&:first)
77
+ end
78
+
79
+ def create_custom_summary(summary)
80
+ ::RSpec::Core::Notifications::SummaryNotification.new(
81
+ summary.duration,
82
+ summary.examples,
83
+ summary.examples.select{ | example | example.execution_result.status == :failed && !example.metadata[:pact_ignore_failures] },
84
+ summary.examples.select{ | example | example.execution_result.status == :failed && example.metadata[:pact_ignore_failures] },
85
+ summary.load_time,
86
+ summary.errors_outside_of_examples_count
87
+ )
88
+ end
89
+
90
+ def extract_differences(example)
91
+ if example.metadata[:pact_diff]
92
+ Pact::Matchers::ExtractDiffMessages.call(example.metadata[:pact_diff]).to_a
93
+ else
94
+ []
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -66,7 +66,7 @@ module Pact
66
66
  if example.metadata[:pact_diff]
67
67
  hash[:differences] = Pact::Matchers::ExtractDiffMessages.call(example.metadata[:pact_diff])
68
68
  .to_a
69
- .collect{ | description | {description: description} }
69
+ .collect{ | description | { description: description } }
70
70
  end
71
71
  end
72
72
  end
@@ -17,16 +17,24 @@ module Pact
17
17
  end
18
18
 
19
19
  module ClassMethods
20
+ EMPTY_ARRAY = [].freeze
20
21
 
21
22
  include ::RSpec::Core::DSL
22
23
 
23
- def honour_pactfile pact_uri, pact_json, options
24
- pact_description = options[:ignore_failures] ? "Pending pact" : "pact"
25
- Pact.configuration.output_stream.puts "INFO: Reading #{pact_description} at #{pact_uri}"
26
- Pact.configuration.output_stream.puts "INFO: Filtering interactions by: #{options[:criteria]}" if options[:criteria] && options[:criteria].any?
24
+ def honour_pactfile pact_source, pact_json, options
25
+ pact_uri = pact_source.uri
26
+ Pact.configuration.output_stream.puts "INFO: Reading pact at #{pact_uri}"
27
+ if pact_uri.metadata[:notices]
28
+ pact_uri.metadata[:notices].before_verification_notices_text.each do | text |
29
+ Pact.configuration.output_stream.puts("DEBUG: #{text}")
30
+ end
31
+ end
32
+
33
+ Pact.configuration.output_stream.puts "DEBUG: Filtering interactions by: #{options[:criteria]}" if options[:criteria] && options[:criteria].any?
27
34
  consumer_contract = Pact::ConsumerContract.from_json(pact_json)
28
- ::RSpec.describe "Verifying a #{pact_description} between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}", pactfile_uri: pact_uri do
29
- honour_consumer_contract consumer_contract, options.merge(pact_json: pact_json, pact_uri: pact_uri)
35
+ suffix = pact_uri.metadata[:pending] ? " [PENDING]": ""
36
+ ::RSpec.describe "Verifying a pact between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}#{suffix}", pactfile_uri: pact_uri do
37
+ honour_consumer_contract consumer_contract, options.merge(pact_json: pact_json, pact_uri: pact_uri, pact_source: pact_source, consumer_contract: consumer_contract)
30
38
  end
31
39
  end
32
40
 
@@ -74,7 +82,9 @@ module Pact
74
82
  pact_interaction: interaction,
75
83
  pact_interaction_example_description: interaction_description_for_rerun_command(interaction),
76
84
  pact_uri: options[:pact_uri],
77
- pact_ignore_failures: options[:ignore_failures]
85
+ pact_source: options[:pact_source],
86
+ pact_ignore_failures: options[:pact_uri].metadata[:pending] || options[:ignore_failures],
87
+ pact_consumer_contract: options[:consumer_contract]
78
88
  }
79
89
 
80
90
  describe description_for(interaction), metadata do
@@ -21,7 +21,11 @@ module Pact
21
21
 
22
22
  logger.info "Sending #{request.method.upcase} request to path: \"#{request.path}\" with headers: #{request.headers}, see debug logs for body"
23
23
  logger.debug "body :#{request.body}"
24
- response = self.send(request.method.downcase, *args)
24
+ response = if self.respond_to?(:custom_request)
25
+ self.custom_request(request.method.upcase, *args)
26
+ else
27
+ self.send(request.method.downcase, *args)
28
+ end
25
29
  logger.info "Received response with status: #{response.status}, headers: #{response.headers}, see debug logs for body"
26
30
  logger.debug "body: #{response.body}"
27
31
  end
@@ -29,7 +33,9 @@ module Pact
29
33
  def parse_body_from_response rack_response
30
34
  case rack_response.headers['Content-Type']
31
35
  when /json/
32
- JSON.load(rack_response.body)
36
+ # For https://github.com/pact-foundation/pact-net/issues/237
37
+ # Only required for the pact-ruby-standalone ¯\_(ツ)_/¯
38
+ JSON.load("[#{rack_response.body}]").first
33
39
  else
34
40
  rack_response.body
35
41
  end
@@ -46,7 +52,7 @@ module Pact
46
52
  def tear_down_provider_states provider_states, consumer, options = {}
47
53
  # If there are no provider state, execute with an nil state to ensure global and base states are executed
48
54
  Pact.configuration.provider_state_tear_down.call(nil, consumer, options) if provider_states.nil? || provider_states.empty?
49
- provider_states.reverse.each do | provider_state |
55
+ provider_states.reverse_each do | provider_state |
50
56
  Pact.configuration.provider_state_tear_down.call(provider_state.name, consumer, options.merge(params: provider_state.params))
51
57
  end
52
58
  end
@@ -33,6 +33,9 @@ module Pact
33
33
  if can_publish_verification_results?
34
34
  tag_versions_if_configured
35
35
  publish_verification_results
36
+ true
37
+ else
38
+ false
36
39
  end
37
40
  end
38
41
 
@@ -17,7 +17,12 @@ module Pact
17
17
 
18
18
  def call
19
19
  verification_results.collect do | (pact_source, verification_result) |
20
- Publish.call(pact_source, verification_result)
20
+ published = false
21
+ begin
22
+ published = Publish.call(pact_source, verification_result)
23
+ ensure
24
+ print_after_verification_notices(pact_source, verification_result, published)
25
+ end
21
26
  end
22
27
  end
23
28
 
@@ -29,6 +34,14 @@ module Pact
29
34
  end
30
35
  end
31
36
 
37
+ def print_after_verification_notices(pact_source, verification_result, published)
38
+ if pact_source.uri.metadata[:notices]
39
+ pact_source.uri.metadata[:notices].after_verification_notices_text(verification_result.success, published).each do | text |
40
+ Pact.configuration.output_stream.puts "DEBUG: #{text}"
41
+ end
42
+ end
43
+ end
44
+
32
45
  attr_reader :pact_sources, :test_results_hash
33
46
  end
34
47
  end
@@ -4,6 +4,7 @@ module Pact
4
4
  module Provider
5
5
  module VerificationResults
6
6
  class VerificationResult
7
+ attr_reader :success, :provider_application_version, :test_results_hash
7
8
 
8
9
  def initialize publishable, success, provider_application_version, test_results_hash
9
10
  @publishable = publishable
@@ -31,10 +32,6 @@ module Pact
31
32
  def to_s
32
33
  "[success: #{success}, providerApplicationVersion: #{provider_application_version}]"
33
34
  end
34
-
35
- private
36
-
37
- attr_reader :success, :provider_application_version, :test_results_hash
38
35
  end
39
36
  end
40
37
  end
@@ -43,7 +43,7 @@ module Pact
43
43
  end
44
44
 
45
45
  def pact_uris_from_pact_uri_sources
46
- pact_uri_sources.collect{| pact_uri_source| pact_uri_source.call }.flatten
46
+ pact_uri_sources.collect(&:call).flatten
47
47
  end
48
48
  end
49
49
  end
@@ -7,6 +7,7 @@ module Pact
7
7
  module TaskHelper
8
8
 
9
9
  PACT_INTERACTION_RERUN_COMMAND = "bundle exec rake pact:verify:at[<PACT_URI>] PACT_DESCRIPTION=\"<PACT_DESCRIPTION>\" PACT_PROVIDER_STATE=\"<PACT_PROVIDER_STATE>\""
10
+ PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER = "bundle exec rake pact:verify:at[<PACT_URI>] PACT_BROKER_INTERACTION_ID=\"<PACT_BROKER_INTERACTION_ID>\""
10
11
 
11
12
  extend self
12
13
 
@@ -34,6 +35,8 @@ module Pact
34
35
  command_parts << "--backtrace" if ENV['BACKTRACE'] == 'true'
35
36
  command_parts << "--description #{Shellwords.escape(ENV['PACT_DESCRIPTION'])}" if ENV['PACT_DESCRIPTION']
36
37
  command_parts << "--provider-state #{Shellwords.escape(ENV['PACT_PROVIDER_STATE'])}" if ENV['PACT_PROVIDER_STATE']
38
+ command_parts << "--pact-broker-interaction-id #{Shellwords.escape(ENV['PACT_BROKER_INTERACTION_ID'])}" if ENV['PACT_BROKER_INTERACTION_ID']
39
+ command_parts << "--interaction-index #{Shellwords.escape(ENV['PACT_INTERACTION_INDEX'])}" if ENV['PACT_INTERACTION_INDEX']
37
40
  command_parts.flatten.join(" ")
38
41
  end
39
42
 
@@ -41,7 +44,9 @@ module Pact
41
44
  Pact.configuration.output_stream.puts command
42
45
  temporarily_set_env_var 'PACT_EXECUTING_LANGUAGE', 'ruby' do
43
46
  temporarily_set_env_var 'PACT_INTERACTION_RERUN_COMMAND', PACT_INTERACTION_RERUN_COMMAND do
44
- exit_status = system(command) ? 0 : 1
47
+ temporarily_set_env_var 'PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER', PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER do
48
+ exit_status = system(command) ? 0 : 1
49
+ end
45
50
  end
46
51
  end
47
52
  end
data/lib/pact/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # Remember to bump pact-provider-proxy when this changes major version
2
2
  module Pact
3
- VERSION = "1.41.2"
3
+ VERSION = "1.54.0"
4
4
  end
data/pact.gemspec CHANGED
@@ -1,5 +1,4 @@
1
- # -*- encoding: utf-8 -*-
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path("lib", __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'pact/version'
5
4
 
@@ -15,36 +14,35 @@ Gem::Specification.new do |gem|
15
14
  gem.required_ruby_version = '>= 2.0'
16
15
 
17
16
  gem.files = `git ls-files bin lib pact.gemspec CHANGELOG.md LICENSE.txt`.split($/)
18
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
19
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
19
  gem.require_paths = ["lib"]
21
20
  gem.license = 'MIT'
22
21
 
23
22
  gem.metadata = {
23
+ 'changelog_uri' => 'https://github.com/pact-foundation/pact-ruby/blob/master/CHANGELOG.md',
24
24
  'source_code_uri' => 'https://github.com/pact-foundation/pact-ruby',
25
25
  'bug_tracker_uri' => 'https://github.com/pact-foundation/pact-ruby/issues',
26
26
  'documentation_uri' => 'https://github.com/pact-foundation/pact-ruby/blob/master/README.md'
27
27
  }
28
28
 
29
- gem.add_runtime_dependency 'randexp', '~> 0.1.7'
30
- gem.add_runtime_dependency 'rspec', '>=2.14'
29
+ gem.add_runtime_dependency 'rspec', '~> 3.0'
31
30
  gem.add_runtime_dependency 'rack-test', '>= 0.6.3', '< 2.0.0'
32
- gem.add_runtime_dependency 'thor'
33
- gem.add_runtime_dependency 'json','> 1.8.5'
34
- gem.add_runtime_dependency 'webrick'
31
+ gem.add_runtime_dependency 'thor', '>= 0.20', '< 2.0'
32
+ gem.add_runtime_dependency 'webrick', '~> 1.3'
35
33
  gem.add_runtime_dependency 'term-ansicolor', '~> 1.0'
36
34
 
37
35
  gem.add_runtime_dependency 'pact-support', '~> 1.9'
38
- gem.add_runtime_dependency 'pact-mock_service', '~> 3.0'
36
+ gem.add_runtime_dependency 'pact-mock_service', '~> 3.0', '>= 3.3.1'
39
37
 
40
- gem.add_development_dependency 'rake', '~> 10.0.3'
38
+ gem.add_development_dependency 'rake', '~> 13.0'
41
39
  gem.add_development_dependency 'webmock', '~> 3.0'
42
- #gem.add_development_dependency 'pry-byebug'
43
40
  gem.add_development_dependency 'fakefs', '0.5' # 0.6.0 blows up
44
41
  gem.add_development_dependency 'hashie', '~> 2.0'
45
- gem.add_development_dependency 'activesupport'
42
+ gem.add_development_dependency 'activesupport', '~> 5.2'
46
43
  gem.add_development_dependency 'faraday', '~> 0.13'
47
- gem.add_development_dependency 'appraisal', '~> 2.2'
48
44
  gem.add_development_dependency 'conventional-changelog', '~> 1.3'
49
45
  gem.add_development_dependency 'bump', '~> 0.5'
46
+ gem.add_development_dependency 'pact-message', '~> 0.6'
47
+ gem.add_development_dependency 'rspec-its', '~> 1.3'
50
48
  end