pact 1.0.39 → 1.1.0.rc1

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 (44) hide show
  1. data/CHANGELOG.md +1 -17
  2. data/Gemfile.lock +2 -2
  3. data/README.md +18 -27
  4. data/bethtest.rb +30 -0
  5. data/documentation/faq.md +6 -36
  6. data/lib/pact/configuration.rb +0 -4
  7. data/lib/pact/consumer/consumer_contract_builder.rb +1 -1
  8. data/lib/pact/consumer_contract/active_support_support.rb +0 -4
  9. data/lib/pact/matchers/diff_decorator.rb +1 -1
  10. data/lib/pact/matchers/index_not_found.rb +12 -3
  11. data/lib/pact/matchers/matchers.rb +2 -1
  12. data/lib/pact/matchers/nested_json_diff_decorator.rb +48 -0
  13. data/lib/pact/matchers/plus_minus_diff_decorator.rb +92 -0
  14. data/lib/pact/matchers/unexpected_index.rb +13 -3
  15. data/lib/pact/matchers/unexpected_key.rb +12 -3
  16. data/lib/pact/provider/configuration.rb +31 -0
  17. data/lib/pact/provider/matchers.rb +4 -4
  18. data/lib/pact/provider/pact_spec_runner.rb +28 -35
  19. data/lib/pact/provider/print_missing_provider_states.rb +2 -2
  20. data/lib/pact/provider/rspec.rb +7 -16
  21. data/lib/pact/provider/world.rb +0 -6
  22. data/lib/pact/shared/key_not_found.rb +16 -3
  23. data/lib/pact/tasks/task_helper.rb +18 -15
  24. data/lib/pact/templates/provider_state.erb +1 -0
  25. data/lib/pact/version.rb +1 -1
  26. data/spec/lib/pact/matchers/matchers_spec.rb +9 -0
  27. data/spec/lib/pact/matchers/nested_json_diff_decorator_spec.rb +48 -0
  28. data/spec/lib/pact/matchers/plus_minus_diff_decorator_spec.rb +186 -0
  29. data/spec/lib/pact/provider/configuration_spec.rb +131 -102
  30. data/spec/lib/pact/verification_task_spec.rb +10 -0
  31. data/spec/support/pact_helper.rb +2 -5
  32. data/spec/support/stubbing.json +1 -1
  33. data/spec/support/test_app_fail.json +2 -29
  34. data/spec/support/test_app_pass.json +4 -4
  35. data/tasks/pact-test.rake +0 -8
  36. metadata +13 -22
  37. data/lib/pact/matchers/difference_indicator.rb +0 -26
  38. data/lib/pact/provider/rspec/formatter.rb +0 -63
  39. data/lib/pact/provider/rspec/silent_json_formatter.rb +0 -18
  40. data/spec/lib/pact/matchers/index_not_found_spec.rb +0 -21
  41. data/spec/lib/pact/matchers/unexpected_index_spec.rb +0 -20
  42. data/spec/lib/pact/matchers/unexpected_key_spec.rb +0 -20
  43. data/spec/lib/pact/shared/key_not_found_spec.rb +0 -20
  44. data/spec/lib/pact/tasks/task_helper_spec.rb +0 -80
@@ -1,11 +1,21 @@
1
- require 'pact/matchers/difference_indicator'
2
-
3
1
  module Pact
4
- class UnexpectedIndex < Pact::DifferenceIndicator
2
+ class UnexpectedIndex
3
+
4
+ def == other
5
+ other.is_a? UnexpectedIndex
6
+ end
5
7
 
6
8
  def to_s
7
9
  '<index not to exist>'
8
10
  end
9
11
 
12
+ def as_json options = {}
13
+ to_s
14
+ end
15
+
16
+ def to_json opts = {}
17
+ as_json.to_json options
18
+ end
19
+
10
20
  end
11
21
  end
@@ -1,11 +1,20 @@
1
- require 'pact/matchers/difference_indicator'
2
-
3
1
  module Pact
4
- class UnexpectedKey < Pact::DifferenceIndicator
2
+ class UnexpectedKey
3
+
4
+ def == other
5
+ other.is_a? UnexpectedKey
6
+ end
5
7
 
6
8
  def to_s
7
9
  '<key not to exist>'
8
10
  end
9
11
 
12
+ def as_json options = {}
13
+ to_s
14
+ end
15
+
16
+ def to_json opts = {}
17
+ as_json.to_json options
18
+ end
10
19
  end
11
20
  end
@@ -2,6 +2,10 @@ require 'pact/provider/pact_verification'
2
2
  require 'pact/shared/dsl'
3
3
  require 'pact/provider/state/provider_state'
4
4
  require 'pact/provider/state/provider_state_configured_modules'
5
+ require 'pact/matchers/plus_minus_diff_decorator'
6
+ require 'pact/matchers/nested_json_diff_decorator'
7
+
8
+ # TODO break this class up!
5
9
 
6
10
  module Pact
7
11
 
@@ -19,6 +23,12 @@ module Pact
19
23
  module Configuration
20
24
 
21
25
  module ConfigurationExtension
26
+
27
+ DIFF_FORMATTERS = {
28
+ :nested_json => Pact::Matchers::NestedJsonDiffDecorator,
29
+ :plus_and_minus => Pact::Matchers::PlusMinusDiffDecorator
30
+ }
31
+
22
32
  def provider= provider
23
33
  @provider = provider
24
34
  end
@@ -47,6 +57,27 @@ module Pact
47
57
  @config_ru_path = config_ru_path
48
58
  end
49
59
 
60
+ def color_enabled
61
+ # Can't use ||= when the variable might be false, it will execute the expression if it's false
62
+ defined?(@color_enabled) ? @color_enabled : true
63
+ end
64
+
65
+ def color_enabled= color_enabled
66
+ @color_enabled = color_enabled
67
+ end
68
+
69
+ def diff_format= diff_format
70
+ @diff_format = diff_format
71
+ end
72
+
73
+ def diff_format
74
+ @diff_format ||= :nested_json
75
+ end
76
+
77
+ def diff_formatter_class
78
+ DIFF_FORMATTERS.fetch(diff_format)
79
+ end
80
+
50
81
  def include mod
51
82
  Pact::Provider::State::ProviderStateConfiguredModules.instance_eval do
52
83
  include mod
@@ -1,17 +1,17 @@
1
1
  require 'pact/term'
2
- require 'pact/consumer_contract/active_support_support'
3
2
  require 'awesome_print'
4
3
  require 'pact/matchers'
5
4
  require 'awesome_print'
6
5
  require 'rspec'
6
+ require 'pact/matchers/nested_json_diff_decorator'
7
+ require 'pact/matchers/diff_decorator'
7
8
 
8
9
  RSpec::Matchers.define :match_term do |expected|
9
10
  include Pact::Matchers
10
- include Pact::ActiveSupportSupport
11
11
 
12
12
  match do |actual|
13
13
  if (difference = diff(expected, actual)).any?
14
- @message = difference
14
+ @diff_decorator = Pact.configuration.diff_formatter_class.new(difference)
15
15
  false
16
16
  else
17
17
  true
@@ -19,7 +19,7 @@ RSpec::Matchers.define :match_term do |expected|
19
19
  end
20
20
 
21
21
  failure_message_for_should do | actual |
22
- fix_json_formatting @message.to_json
22
+ @diff_decorator.to_s
23
23
  end
24
24
 
25
25
  end
@@ -4,8 +4,7 @@ require 'rspec/core'
4
4
  require 'rspec/core/formatters/documentation_formatter'
5
5
  require 'rspec/core/formatters/json_formatter'
6
6
  require 'pact/provider/pact_helper_locator'
7
- require 'pact/provider/rspec/formatter'
8
- require 'pact/provider/rspec/silent_json_formatter'
7
+ require 'pact/provider/print_missing_provider_states'
9
8
  require_relative 'rspec'
10
9
 
11
10
 
@@ -37,7 +36,6 @@ module Pact
37
36
  run_specs
38
37
  ensure
39
38
  ::RSpec.reset
40
- Pact.clear_world
41
39
  end
42
40
  end
43
41
 
@@ -45,11 +43,11 @@ module Pact
45
43
 
46
44
  def require_pact_helper spec_definition
47
45
  if spec_definition[:pact_helper]
48
- Pact.configuration.output_stream.puts "Using #{spec_definition[:pact_helper]}"
46
+ puts "Using #{spec_definition[:pact_helper]}"
49
47
  require spec_definition[:pact_helper]
50
48
  elsif spec_definition[:support_file]
51
- Pact.configuration.output_stream.puts "Using #{spec_definition[:support_file]}"
52
- Pact.configuration.error_stream.puts SUPPORT_FILE_DEPRECATION_MESSAGE
49
+ puts "Using #{spec_definition[:support_file]}"
50
+ $stderr.puts SUPPORT_FILE_DEPRECATION_MESSAGE
53
51
  require spec_definition[:support_file]
54
52
  else
55
53
  require 'pact/provider/client_project_pact_helper'
@@ -72,47 +70,42 @@ module Pact
72
70
  config = ::RSpec.configuration
73
71
 
74
72
  config.color = true
75
- config.pattern = "pattern which doesn't match any files"
76
- config.backtrace_inclusion_patterns = [/pact\/provider\/rspec/]
77
-
78
73
  config.extend Pact::Provider::RSpec::ClassMethods
79
74
  config.include Pact::Provider::RSpec::InstanceMethods
80
75
  config.include Pact::Provider::TestMethods
76
+ config.backtrace_inclusion_patterns = [/pact\/provider\/rspec/]
81
77
 
82
- if options[:silent]
83
- config.output_stream = StringIO.new
84
- config.error_stream = StringIO.new
85
- else
86
- config.error_stream = Pact.configuration.error_stream
87
- config.output_stream = Pact.configuration.output_stream
78
+ config.before :each, :pact => :verify do | example |
79
+ example_description = "#{example.example.example_group.description} #{example.example.description}"
80
+ Pact.configuration.logger.info "Running example '#{example_description}'"
88
81
  end
89
82
 
90
- config.add_formatter Pact::Provider::RSpec::Formatter
91
- config.add_formatter Pact::Provider::RSpec::SilentJsonFormatter
92
-
93
- config.before(:suite) do
94
- # Preload app before suite so the classes loaded in memory are consistent for
95
- # before :each and after :each hooks.
96
- # Otherwise the app and all its dependencies are loaded between the first before :each
97
- # and the first after :each, leading to inconsistent behaviour
98
- # (eg. with database_cleaner transactions)
99
- Pact.configuration.provider.app
83
+ unless options[:silent]
84
+ config.error_stream = $stderr
85
+ config.output_stream = $stdout
100
86
  end
101
- end
102
87
 
103
- def run_specs
104
- exit_code = ::RSpec::Core::CommandLine.new(NoConfigurationOptions.new)
105
- .run(::RSpec.configuration.output_stream, ::RSpec.configuration.error_stream)
106
- @output = JSON.parse(Pact.world.json_formatter_stream.string, symbolize_keys: true)
107
- exit_code
88
+ formatter = ::RSpec::Core::Formatters::DocumentationFormatter.new(config.output)
89
+ @json_formatter = ::RSpec::Core::Formatters::JsonFormatter.new(StringIO.new)
90
+ reporter = ::RSpec::Core::Reporter.new(formatter, @json_formatter)
91
+ config.instance_variable_set(:@reporter, reporter)
108
92
  end
109
93
 
110
- class NoConfigurationOptions
111
- def method_missing(method, *args, &block)
112
- # Do nothing!
94
+ def run_specs
95
+ config = ::RSpec.configuration
96
+ world = ::RSpec::world
97
+ exit_code = config.reporter.report(world.example_count, nil) do |reporter|
98
+ begin
99
+ config.run_hook(:before, :suite)
100
+ world.example_groups.ordered.map {|g| g.run(reporter)}.all? ? 0 : config.failure_exit_code
101
+ ensure
102
+ config.run_hook(:after, :suite)
103
+ end
113
104
  end
105
+ PrintMissingProviderStates.call Pact.world.provider_states.missing_provider_states
106
+ @output = @json_formatter.output_hash
107
+ exit_code
114
108
  end
115
-
116
109
  end
117
110
  end
118
111
  end
@@ -3,9 +3,9 @@ module Pact
3
3
  class PrintMissingProviderStates
4
4
 
5
5
  # Hash of consumer names to array of names of missing provider states
6
- def self.call missing_provider_states, output
6
+ def self.call missing_provider_states
7
7
  if missing_provider_states.any?
8
- output.puts orangeify(text(missing_provider_states))
8
+ puts orangeify(text(missing_provider_states))
9
9
  end
10
10
  end
11
11
 
@@ -19,10 +19,12 @@ module Pact
19
19
  include ::RSpec::Core::DSL
20
20
 
21
21
  def honour_pactfile pactfile_uri, options = {}
22
- puts "Filtering specs by: #{options[:criteria]}" if options[:criteria] && options[:criteria].any?
22
+ puts "Filtering specs by: #{options[:criteria]}" if options[:criteria]
23
23
  consumer_contract = Pact::ConsumerContract.from_json(read_pact_from(pactfile_uri, options))
24
- describe "Verifying a pact between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}", :pactfile_uri => pactfile_uri do
25
- honour_consumer_contract consumer_contract, options
24
+ describe "A pact between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}" do
25
+ describe "in #{pactfile_uri}" do
26
+ honour_consumer_contract consumer_contract, options
27
+ end
26
28
  end
27
29
  end
28
30
 
@@ -58,19 +60,12 @@ module Pact
58
60
 
59
61
  def describe_interaction interaction, options
60
62
 
61
- metadata = {
62
- :pact => :verify,
63
- :pact_interaction => interaction,
64
- :pact_interaction_example_description => interaction_description_for_rerun_command(interaction)
65
- }
66
-
67
- describe description_for(interaction), metadata do
63
+ describe description_for(interaction), :pact => :verify do
68
64
 
69
65
  interaction_context = InteractionContext.new
70
66
 
71
67
  before do
72
68
  interaction_context.run_once :before do
73
- Pact.configuration.logger.info "Running example '#{self.example.full_description}'"
74
69
  set_up_provider_state interaction.provider_state, options[:consumer]
75
70
  replay_interaction interaction
76
71
  interaction_context.last_response = last_response
@@ -124,11 +119,7 @@ module Pact
124
119
  end
125
120
 
126
121
  def description_for interaction
127
- interaction.provider_state ? interaction.description : interaction.description.capitalize
128
- end
129
-
130
- def interaction_description_for_rerun_command interaction
131
- description_for(interaction).capitalize + ( interaction.provider_state ? " given #{interaction.provider_state}" : "")
122
+ "#{interaction.description} using #{interaction.request.method.upcase} to #{interaction.request.path}"
132
123
  end
133
124
 
134
125
  def read_pact_from uri, options = {}
@@ -14,12 +14,6 @@ module Pact
14
14
  module Provider
15
15
  class World
16
16
 
17
- attr_reader :json_formatter_stream
18
-
19
- def initialize
20
- @json_formatter_stream = StringIO.new
21
- end
22
-
23
17
  def provider_states
24
18
  @provider_states_proxy ||= Pact::Provider::State::ProviderStateProxy.new
25
19
  end
@@ -1,12 +1,25 @@
1
- require 'pact/matchers/difference_indicator'
2
-
3
1
  module Pact
4
- class KeyNotFound < Pact::DifferenceIndicator
2
+ class KeyNotFound
3
+ def == other
4
+ other.is_a? KeyNotFound
5
+ end
6
+
7
+ def eql? other
8
+ self == other
9
+ end
5
10
 
6
11
  def to_s
7
12
  "<key not found>"
8
13
  end
9
14
 
15
+ def as_json options={}
16
+ to_s
17
+ end
18
+
19
+ def to_json options = {}
20
+ as_json.to_json options
21
+ end
22
+
10
23
  def empty?
11
24
  true
12
25
  end
@@ -1,29 +1,32 @@
1
1
  module Pact
2
2
  module TaskHelper
3
+ def failure_message
4
+ redify(
5
+ "\n* * * * * * * * * * * * * * * * * * *\n" +
6
+ "Provider did not honour pact file.\nSee\n * #{Pact.configuration.log_path}\n * #{Pact.configuration.tmp_dir}\nfor logs and pact files." +
7
+ "\n* * * * * * * * * * * * * * * * * * *\n\n"
8
+ )
9
+ end
3
10
 
4
- extend self
11
+ def redify string
12
+ "\e[31m#{string}\e[m"
13
+ end
5
14
 
6
15
  def handle_verification_failure
7
16
  exit_status = yield
8
- abort if exit_status != 0
17
+ if exit_status != 0
18
+ $stderr.puts failure_message
19
+ fail
20
+ end
9
21
  end
10
22
 
11
23
  def spec_criteria defaults = {description: nil, provider_state: nil}
12
24
  criteria = {}
13
-
14
- description = ENV.fetch("PACT_DESCRIPTION", defaults[:description])
15
- criteria[:description] = Regexp.new(description) if description
16
-
17
- provider_state = ENV.fetch("PACT_PROVIDER_STATE", defaults[:provider_state])
18
- if provider_state
19
- if provider_state.length == 0
20
- criteria[:provider_state] = nil #Allow PACT_PROVIDER_STATE="" to mean no provider state
21
- else
22
- criteria[:provider_state] = Regexp.new(provider_state)
23
- end
25
+ [:description, :provider_state].each do | key |
26
+ value = ENV.fetch("PACT_#{key.to_s.upcase}", defaults[key])
27
+ criteria[key] = Regexp.new(value) unless value.nil?
24
28
  end
25
-
26
- criteria
29
+ criteria.any? ? criteria : nil
27
30
  end
28
31
  end
29
32
  end
@@ -1,3 +1,4 @@
1
+
1
2
  Could not find one or more provider states.
2
3
  Have you required the provider states file for this consumer in your pact_helper.rb?
3
4
  If you have not yet defined these states, here is a template:
@@ -1,3 +1,3 @@
1
1
  module Pact
2
- VERSION = "1.0.39"
2
+ VERSION = "1.1.0.rc1"
3
3
  end
@@ -158,6 +158,15 @@ module Pact::Matchers
158
158
  end
159
159
  end
160
160
 
161
+ context "when the different index is in the middle of an array" do
162
+ subject { [1,2,3] }
163
+ let(:actual) { [1,7,3]}
164
+ let(:difference) { [Pact::Matchers::NO_DIFF_INDICATOR, Difference.new(2, 7), Pact::Matchers::NO_DIFF_INDICATOR] }
165
+ it 'returns the diff' do
166
+ expect(diff(subject, actual)).to eq(difference)
167
+ end
168
+ end
169
+
161
170
  context "when actual array is longer than the expected" do
162
171
  subject { [1] }
163
172
  let(:actual) { [1,2]}
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'pact/matchers/nested_json_diff_decorator'
3
+
4
+ module Pact
5
+ module Matchers
6
+ describe NestedJsonDiffDecorator do
7
+
8
+ let(:diff) do
9
+ {
10
+ :something => Difference.new({name: 'Fred'}, KeyNotFound.new)
11
+ }
12
+ end
13
+
14
+ subject { NestedJsonDiffDecorator.new(diff) }
15
+
16
+ describe "#to_s" do
17
+
18
+ context "when color_enabled is true" do
19
+ it "returns nicely formatted json" do
20
+ expect(subject.to_s.split("\n").size).to eq 8
21
+ end
22
+
23
+ it "returns a string displaying the diff in colour" do
24
+ expect(subject.to_s).to include NestedJsonDiffDecorator::EXPECTED_COLOURED
25
+ expect(subject.to_s).to include NestedJsonDiffDecorator::ACTUAL_COLOURED
26
+ end
27
+ end
28
+
29
+ context "when color_enabled is false" do
30
+ before do
31
+ Pact.configuration.stub(:color_enabled).and_return(false)
32
+ end
33
+
34
+ it "returns nicely formatted json" do
35
+ expect(subject.to_s.split("\n").size).to eq 8
36
+ end
37
+
38
+ it "returns a string displaying the diff without colour" do
39
+ expect(subject.to_s).to_not include NestedJsonDiffDecorator::EXPECTED_COLOURED
40
+ expect(subject.to_s).to_not include NestedJsonDiffDecorator::ACTUAL_COLOURED
41
+ end
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+ end