pact 1.0.39 → 1.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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