pact 1.0.26 → 1.0.27

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.
@@ -2,6 +2,20 @@ Do this to generate your change history
2
2
 
3
3
  git log --date=relative --pretty=format:' * %h - %s (%an, %ad)'
4
4
 
5
+ ### 1.0.26 (10 December 2013)
6
+
7
+ * 388fc7b - Changing provider set up and tear down to run before :all rather than before :each (Beth, 13 minutes ago)
8
+ * 06b5626 - Updating TODO list in the README. (Beth, 25 hours ago)
9
+ * 823f306 - Update README.md (bethesque, 32 hours ago)
10
+ * 7d96017 - Improving layout of text diff message (Beth Skurrie, 2 days ago)
11
+ * 9c88c3a - Working on a new way to display the diff between an expected and actual request/response (Beth Skurrie, 2 days ago)
12
+ * ff2c448 - Added a Difference class instead of a hash with :expected and :actual (Beth Skurrie, 2 days ago)
13
+ * b34457c - Moved all missing provider state templates into the one message at the end of the test so it's easier to digest and can be copied directly into a file. (Beth Skurrie, 2
14
+ * 1729887 - Moving ProviderStateProxy on to Pact World (Beth Skurrie, 3 days ago)
15
+ * c53cb4d - Starting to add Pact::World (Beth, 4 days ago)
16
+ * f7af9e2 - Recording missing provider states (Beth, 4 days ago)
17
+ * 4caa171 - Starting work on ProviderStateProxy - intent is for it to record missing and unused states to report at the end of the pact:verify (Beth, 4 days ago)
18
+
5
19
  ### 1.0.26 (5 December 2013)
6
20
 
7
21
  * e4be654 - BEST COMMIT TO PACT EVER since the introduction of pact:verify. Got rid of the horrific backtraces. (Beth, 5 hours ago)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pact (1.0.26)
4
+ pact (1.0.27)
5
5
  awesome_print (~> 1.1)
6
6
  find_a_port (~> 1.0.1)
7
7
  json
data/README.md CHANGED
@@ -161,10 +161,8 @@ require 'pact/provider/rspec'
161
161
  require './spec_helper'
162
162
 
163
163
  Pact.service_provider "My Service Provider" do
164
- # If you have a config.ru file, the app will be loaded from it automatically and you can skip the app config.
165
- # Otherwise, set your rack app here as you would when using Rack::Test::Methods
166
164
 
167
- app { MyApp.new }
165
+ app { MyApp.new } # Optional, loads app from config.ru by default
168
166
 
169
167
  honours_pact_with 'My Service Consumer' do
170
168
 
@@ -391,8 +389,12 @@ Short term:
391
389
 
392
390
  Long term:
393
391
  - Provide more flexible matching (eg the keys should match, and the classes of the values should match, but the values of each key do not need to be equal). This is to make the pact verification less brittle.
392
+ - Add support for verifying pact against running server
393
+ - Add XML support
394
+ - Improve display of interaction diffs
394
395
  - Decouple Rspec from Pact and make rspec-pact gem for easy integration
395
396
 
397
+
396
398
  ## Contributing
397
399
 
398
400
  1. Fork it
@@ -1,3 +1,5 @@
1
+ require 'pact/matchers/diff_decorator'
2
+
1
3
  module Pact
2
4
  module Consumer
3
5
  class InteractionMismatch
@@ -12,8 +14,12 @@ module Pact
12
14
  @candiate_diffs = candidate_interactions.collect{ | candidate_interaction| CandidateDiff.new(candidate_interaction, actual_request)}
13
15
  end
14
16
 
15
- def diffs
16
- candiate_diffs.collect(&:diff_summary)
17
+ def to_hash
18
+ candiate_diffs.collect(&:to_hash)
19
+ end
20
+
21
+ def to_s
22
+ candiate_diffs.collect(&:to_s).join("\n")
17
23
  end
18
24
 
19
25
  def short_summary
@@ -36,12 +42,19 @@ module Pact
36
42
  diff.keys
37
43
  end
38
44
 
39
- def diff_summary
45
+ def to_hash
40
46
  summary = {:description => candidate_interaction.description}
41
47
  summary[:provider_state] = candidate_interaction.provider_state if candidate_interaction.provider_state
42
48
  summary.merge(diff)
43
49
  end
44
50
 
51
+ def to_s
52
+ [
53
+ "Diff with interaction: #{candidate_interaction.description_with_provider_state_quoted}",
54
+ Pact::Matchers::DiffDecorator.new(diff).to_s
55
+ ].join("\n")
56
+ end
57
+
45
58
  def diff
46
59
  @diff ||= candidate_interaction.request.difference(actual_request)
47
60
  end
@@ -75,12 +75,6 @@ module Pact
75
75
  InteractionMismatch.new(candidate_interactions, actual_request)
76
76
  end
77
77
 
78
- def diff_summary_for interaction, diff
79
- summary = {:description => interaction.description}
80
- summary[:provider_state] = interaction.provider_state if interaction.provider_state
81
- summary.merge(diff)
82
- end
83
-
84
78
  def request_summary_for interaction
85
79
  summary = {:description => interaction.description}
86
80
  summary[:provider_state] if interaction.provider_state
@@ -91,7 +85,7 @@ module Pact
91
85
  def unrecognised_request_response interaction_mismatch
92
86
  response = {
93
87
  message: "No interaction found for #{interaction_mismatch.actual_request.method_and_path}",
94
- interaction_diffs: interaction_mismatch.diffs
88
+ interaction_diffs: interaction_mismatch.to_hash
95
89
  }
96
90
  [500, {'Content-Type' => 'application/json'}, [response.to_json]]
97
91
  end
@@ -99,7 +93,8 @@ module Pact
99
93
  def log_unrecognised_request_and_interaction_diff interaction_mismatch
100
94
  logger.error "No interaction found on #{name} for #{interaction_mismatch.actual_request.method_and_path}"
101
95
  logger.error 'Interaction diffs for that route:'
102
- logger.ap(interaction_mismatch.diffs, :error)
96
+ logger.ap(interaction_mismatch.to_hash, :error)
97
+ logger.error("Interaction diffs for that route in text format:\n#{interaction_mismatch.to_s}")
103
98
  end
104
99
 
105
100
  def handle_unrecognised_request actual_request, candidate_interactions
@@ -56,6 +56,10 @@ module Pact
56
56
  self == other
57
57
  end
58
58
 
59
+ def description_with_provider_state_quoted
60
+ provider_state ? "\"#{description}\" given \"#{provider_state}\"" : "\"#{description}\""
61
+ end
62
+
59
63
  def to_s
60
64
  JSON.pretty_generate(self)
61
65
  end
@@ -0,0 +1,84 @@
1
+ module Pact
2
+ module Matchers
3
+ class DiffDecorator
4
+
5
+ attr_reader :diff
6
+
7
+ def initialize diff
8
+ @diff = diff
9
+ end
10
+
11
+ def to_hash
12
+ diff
13
+ end
14
+
15
+ def to_s
16
+ diff_descriptions(diff).join("\n")
17
+ end
18
+
19
+ def diff_descriptions obj, path = [], descriptions = []
20
+ case obj
21
+ when Hash then handle_hash obj, path, descriptions
22
+ when Array then handle_array obj, path, descriptions
23
+ when Difference then handle_difference obj, path, descriptions
24
+ when NoDiffIndicator then nil
25
+ else
26
+ raise "Invalid diff, expected Hash, Array, NoDiffIndicator or Difference, found #{obj}"
27
+ end
28
+ descriptions
29
+ end
30
+
31
+ def handle_hash hash, path, descriptions
32
+ hash.each_pair do | key, value |
33
+ diff_descriptions value, path + [key.inspect], descriptions
34
+ end
35
+ end
36
+
37
+ def handle_array array, path, descriptions
38
+ array.each_with_index do | obj, index |
39
+ diff_descriptions obj, path + [index], descriptions
40
+ end
41
+ end
42
+
43
+ def handle_difference difference, path, descriptions
44
+
45
+ case difference.actual
46
+ when Pact::KeyNotFound then handle_key_not_found(difference, path, descriptions)
47
+ when Pact::IndexNotFound then handle_index_not_found(difference, path, descriptions)
48
+ else
49
+ case difference.expected
50
+ when Pact::UnexpectedKey then handle_unexpected_key(difference, path, descriptions)
51
+ when Pact::UnexpectedIndex then handle_unexpected_index(difference, path, descriptions)
52
+ else
53
+ handle_mismatched_value(difference, path, descriptions)
54
+ end
55
+ end
56
+ end
57
+
58
+ def handle_unexpected_index difference, path, descriptions
59
+ descriptions << "\tAt:\n\t\t#{path_to_s(path)}\n\tArray contained unexpected item:\n\t\t#{difference.actual.ai}"
60
+ end
61
+
62
+ def handle_mismatched_value difference, path, descriptions
63
+ descriptions << "\tAt:\n\t\t#{path_to_s(path)}\n\tExpected:\n\t\t#{difference.expected.ai}\n\tActual:\n\t\t#{difference.actual.ai}"
64
+ end
65
+
66
+ def handle_index_not_found difference, path, descriptions
67
+ descriptions << "\tAt:\n\t\t#{path_to_s(path)}\n\tMissing index with value:\n\t\t#{difference.expected.ai}"
68
+ end
69
+
70
+ def handle_key_not_found difference, path, descriptions
71
+ descriptions << "\tAt:\n\t\t#{path_to_s(path)}\n\tMissing key with value:\n\t\t#{difference.expected.ai}"
72
+ end
73
+
74
+ def handle_unexpected_key difference, path, descriptions
75
+ descriptions << "\tAt:\n\t\t#{path_to_s(path)}\n\tHash contained unexpected key with value:\n\t\t#{difference.actual.ai}"
76
+ end
77
+
78
+ def path_to_s path
79
+ "[" + path.join("][") + "]"
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -10,7 +10,50 @@ require 'pact/matchers/index_not_found'
10
10
  module Pact
11
11
  module Matchers
12
12
 
13
- NO_DIFF_INDICATOR = 'no difference here!'
13
+ class Difference
14
+ attr_reader :expected, :actual
15
+ def initialize expected, actual
16
+ @expected = expected
17
+ @actual = actual
18
+ end
19
+
20
+ def any?
21
+ true
22
+ end
23
+
24
+ def to_hash
25
+ {:EXPECTED => expected, :ACTUAL => actual}
26
+ end
27
+
28
+ def to_json options = {}
29
+ to_hash.to_json(options)
30
+ end
31
+
32
+ def to_s
33
+ to_hash.to_s
34
+ end
35
+
36
+ def == other
37
+ other.is_a?(Difference) && other.expected == expected && other.actual == actual
38
+ end
39
+ end
40
+
41
+ class NoDiffIndicator
42
+
43
+ def to_json
44
+ to_s
45
+ end
46
+
47
+ def to_s
48
+ 'no difference here!'
49
+ end
50
+
51
+ def == other
52
+ other.is_a? NoDiffIndicator
53
+ end
54
+ end
55
+
56
+ NO_DIFF_INDICATOR = NoDiffIndicator.new
14
57
  #UnexpectedKey.new = '<key not to be present>'
15
58
  DEFAULT_OPTIONS = {allow_unexpected_keys: true, structure: false}.freeze
16
59
 
@@ -34,7 +77,7 @@ module Pact
34
77
  if actual.is_a?(String) && regexp.match(actual)
35
78
  {}
36
79
  else
37
- {expected: regexp, actual: actual}
80
+ Difference.new regexp, actual
38
81
  end
39
82
  end
40
83
 
@@ -42,7 +85,7 @@ module Pact
42
85
  if actual.is_a? Array
43
86
  actual_array_diff expected, actual, options
44
87
  else
45
- {expected: expected, actual: actual}
88
+ Difference.new expected, actual
46
89
  end
47
90
  end
48
91
 
@@ -78,7 +121,7 @@ module Pact
78
121
  {}
79
122
  else
80
123
  (actual.keys - expected.keys).inject({}) do | diff, key |
81
- diff[key] = {:expected => UnexpectedKey.new, :actual => actual[key]}
124
+ diff[key] = Difference.new(UnexpectedKey.new, actual[key])
82
125
  diff
83
126
  end
84
127
  end
@@ -88,7 +131,7 @@ module Pact
88
131
  if actual.is_a? Hash
89
132
  actual_hash_diff expected, actual, options
90
133
  else
91
- {expected: expected, actual: actual}
134
+ Difference.new expected, actual
92
135
  end
93
136
  end
94
137
 
@@ -96,7 +139,7 @@ module Pact
96
139
  if classes_match? expected, actual
97
140
  {}
98
141
  else
99
- {:expected => structure_diff_expected_display(expected), :actual => structure_diff_actual_display(actual)}
142
+ Difference.new structure_diff_expected_display(expected), structure_diff_actual_display(actual)
100
143
  end
101
144
  end
102
145
 
@@ -118,7 +161,7 @@ module Pact
118
161
  def object_diff expected, actual, options
119
162
  return class_diff(expected, actual) if options[:structure]
120
163
  if expected != actual
121
- {:expected => expected, :actual => actual}
164
+ Difference.new expected, actual
122
165
  else
123
166
  {}
124
167
  end
@@ -1,3 +1,4 @@
1
1
  require 'pact/configuration'
2
2
  require 'pact/provider/configuration'
3
3
  require 'pact/provider/pact_spec_runner'
4
+ require 'pact/provider/world'
@@ -4,6 +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/print_missing_provider_states'
7
8
  require_relative 'rspec'
8
9
 
9
10
 
@@ -101,6 +102,7 @@ module Pact
101
102
  config.run_hook(:after, :suite)
102
103
  end
103
104
  end
105
+ PrintMissingProviderStates.call Pact.world.provider_states.missing_provider_states
104
106
  @output = @json_formatter.output_hash
105
107
  exit_code
106
108
  end
@@ -0,0 +1,30 @@
1
+ module Pact
2
+ module Provider
3
+ class PrintMissingProviderStates
4
+
5
+ # Hash of consumer names to array of names of missing provider states
6
+ def self.call missing_provider_states
7
+ if missing_provider_states.any?
8
+ puts orangeify(text(missing_provider_states))
9
+ end
10
+ end
11
+
12
+ def self.orangeify string
13
+ "\e[33m#{string}\e[m"
14
+ end
15
+
16
+ def self.text missing_provider_states
17
+ create_provider_states_for(missing_provider_states)
18
+ end
19
+
20
+ def self.create_provider_states_for consumers
21
+ ERB.new(template_string).result(binding)
22
+ end
23
+
24
+ def self.template_string
25
+ File.read(File.expand_path( '../../templates/provider_state.erb', __FILE__))
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ module Pact
2
+ module Provider
3
+ class ProviderStateProxy
4
+
5
+ attr_reader :missing_provider_states
6
+
7
+ def initialize
8
+ @missing_provider_states = {}
9
+ end
10
+
11
+ def get name, options = {}
12
+ unless provider_state = ProviderState.get(name, options)
13
+ register_missing_provider_state name, options[:for]
14
+ raise error_message name, options[:for]
15
+ end
16
+ provider_state
17
+ end
18
+
19
+ private
20
+
21
+ def error_message name, consumer
22
+ "Could not find provider state \"#{name}\" for consumer #{consumer}"
23
+ end
24
+
25
+ def register_missing_provider_state name, consumer
26
+ missing_states_for(consumer) << name unless missing_states_for(consumer).include?(name)
27
+ end
28
+
29
+ def missing_states_for consumer
30
+ @missing_provider_states[consumer] ||= []
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -62,12 +62,12 @@ module Pact
62
62
 
63
63
  describe description_for(interaction), :pact => :verify do
64
64
 
65
- before do
65
+ before :all do
66
66
  set_up_provider_state interaction.provider_state, options[:consumer]
67
67
  replay_interaction interaction
68
68
  end
69
69
 
70
- after do
70
+ after :all do
71
71
  tear_down_provider_state interaction.provider_state, options[:consumer]
72
72
  end
73
73
 
@@ -2,7 +2,9 @@ require 'pact/logging'
2
2
  require 'rack/test'
3
3
  require 'pact/consumer_contract/interaction'
4
4
  require 'pact/provider/provider_state'
5
+ require 'pact/provider/provider_state_proxy'
5
6
  require 'pact/provider/request'
7
+ require 'pact/provider/world'
6
8
 
7
9
  module Pact
8
10
  module Provider
@@ -44,24 +46,7 @@ module Pact
44
46
  end
45
47
 
46
48
  def get_provider_state provider_state_name, consumer
47
- unless provider_state = ProviderState.get(provider_state_name, :for => consumer)
48
- extra = consumer ? " for consumer \"#{consumer}\"" : ""
49
- error_msg = <<-eos
50
- Could not find a provider state named \"#{provider_state_name}\"#{extra}.
51
- Have you required the provider states file for this consumer in your pact_helper.rb?
52
- If you have not yet defined a provider state for \"#{provider_state_name}\", here is a template:
53
-
54
- Pact.provider_states_for \"#{consumer}\" do
55
- provider_state \"#{provider_state_name}\" do
56
- set_up do
57
- # Your set up code goes here
58
- end
59
- end
60
- end
61
- eos
62
- raise error_msg
63
- end
64
- provider_state
49
+ Pact.world.provider_states.get(provider_state_name, :for => consumer)
65
50
  end
66
51
  end
67
52
  end