pact-support 0.2.1 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e910cf32ae5812de2e20eb59b3e5ca000f4bb993
4
- data.tar.gz: c075489026ea2c5b501c673d085bf0ffe27434a1
3
+ metadata.gz: d30e4cc422b899773c2917361c59ad211792f922
4
+ data.tar.gz: e20e3a998c5ba7f018b615567718193e009bcd26
5
5
  SHA512:
6
- metadata.gz: 06989f174ace83fa1ccccf4ea8bdad96d188a0899706aef0f7bed147eb86682644ed08457f6974a01e2e5abfd425c01f0787d2bbd4f73e9d9258cea1a3c1d7b7
7
- data.tar.gz: 7d3bb57ae30029fd219bf1f54fc1fe7f471940365f4431f5f11b20d18ae67470729a648a41191d1daf799f63b78485ce913d22497a776b8a799fdbecc4823739
6
+ metadata.gz: 3e8e7a5188f514b4c5b47de5e3a5005f863544b1625a8bf932a84ac6a700b8d5926ef87c7bfd35dbe56b7c3579ba7793d9704a64f8eae4592c73b2408696316c
7
+ data.tar.gz: 095ec74b09b3ffa4bce8212d495eb88d93ee69723997d58c2d836be356493fed9bf1b17f0a4e5150f81845cdc67fdf8639598ca5ea9c1e0f9e982bfd5627bfde
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@ Do this to generate your change history
2
2
 
3
3
  git log --pretty=format:' * %h - %s (%an, %ad)'
4
4
 
5
+ ### 0.3.0 (13 Februrary 2015)
6
+
7
+ * 4e29277 - Create a public API for extracting matching rules for pact-mock_service to use. (Beth Skurrie, Fri Feb 13 15:35:14 2015 +1100)
8
+ * 17ffb7e - Improve Pact::Term error message when value does not match regexp. (Beth Skurrie, Thu Feb 12 15:35:28 2015 +1100)
9
+ * ad0b37b - Added logic to convert Term and SomethingLike to v2 matching rules (Beth Skurrie, Thu Feb 12 14:55:34 2015 +1100)
10
+ * cc15c4d - Renamed <index not found> to <item not found>, and <index not to exist> to <item not to exist> (Beth Skurrie, Thu Feb 12 11:47:53 2015 +1100)
11
+ * 1b65c46 - Change "no difference here" to ... in unix diff output (Beth Skurrie, Thu Feb 12 11:43:58 2015 +1100)
12
+ * 3cb5b30 - Fix duplicate "no difference here!" in diff when actual array has more items than the expected (Beth Skurrie, Thu Feb 12 11:30:58 2015 +1100)
13
+ * a9da567 - Changed display of NoDiffAtIndex (Beth Skurrie, Tue Dec 23 15:16:49 2014 +1100)
14
+ * f9619e6 - Log warning message when unsupported rules are detected (Beth Skurrie, Tue Dec 23 14:42:22 2014 +1100)
15
+ * 9875bef - Added support for v2 regular expression matching in provider (Beth Skurrie, Tue Dec 23 14:15:00 2014 +1100)
16
+
5
17
  ### 0.2.1 (21 January 2015)
6
18
 
7
19
  * 4e26c75 - Ignore HTTP method case when determining if routes match. https://github.com/bethesque/pact-support/issues/3 (Beth, Tue Jan 20 20:15:20 2015 +1100)
@@ -2,6 +2,7 @@ require 'pact/consumer_contract/request'
2
2
  require 'pact/consumer_contract/response'
3
3
  require 'pact/symbolize_keys'
4
4
  require 'pact/shared/active_support_support'
5
+ require 'pact/matching_rules'
5
6
 
6
7
  module Pact
7
8
  class Interaction
@@ -18,9 +19,11 @@ module Pact
18
19
  end
19
20
 
20
21
  def self.from_hash hash
21
- request = Pact::Request::Expected.from_hash(hash['request'])
22
- response = Pact::Response.from_hash(hash['response'])
23
- new(symbolize_keys(hash).merge({request: request, response: response}))
22
+ request_hash = Pact::MatchingRules.merge(hash['request'], hash['request']['requestMatchingRules'])
23
+ request = Pact::Request::Expected.from_hash(request_hash)
24
+ response_hash = Pact::MatchingRules.merge(hash['response'], hash['response']['responseMatchingRules'])
25
+ response = Pact::Response.from_hash(response_hash)
26
+ new(symbolize_keys(hash).merge(request: request, response: response))
24
27
  end
25
28
 
26
29
  def to_hash
@@ -4,7 +4,7 @@ module Pact
4
4
  class IndexNotFound < Pact::DifferenceIndicator
5
5
 
6
6
  def to_s
7
- "<index not found>"
7
+ "<item not found>"
8
8
  end
9
9
 
10
10
  def empty?
@@ -31,9 +31,9 @@ module Pact
31
31
  when Difference then handle_difference obj, path, descriptions
32
32
  when TypeDifference then handle_mismatched_type obj, path, descriptions
33
33
  when RegexpDifference then handle_mismatched_regexp obj, path, descriptions
34
- when NoDiffIndicator then nil
34
+ when NoDiffAtIndex then nil
35
35
  else
36
- raise "Invalid diff, expected Hash, Array, NoDiffIndicator or Difference, found #{obj.class}"
36
+ raise "Invalid diff, expected Hash, Array, NoDiffAtIndex or Difference, found #{obj.class}"
37
37
  end
38
38
  descriptions
39
39
  end
@@ -11,13 +11,14 @@ require 'pact/matchers/regexp_difference'
11
11
  require 'pact/matchers/type_difference'
12
12
  require 'pact/matchers/expected_type'
13
13
  require 'pact/matchers/actual_type'
14
- require 'pact/matchers/no_diff_indicator'
14
+ require 'pact/matchers/no_diff_at_index'
15
15
 
16
16
  module Pact
17
17
  module Matchers
18
18
 
19
- NO_DIFF_INDICATOR = NoDiffIndicator.new
19
+ NO_DIFF_AT_INDEX = NoDiffAtIndex.new
20
20
  DEFAULT_OPTIONS = {allow_unexpected_keys: true, type: false}.freeze
21
+ NO_DIFF = {}.freeze
21
22
 
22
23
  def diff expected, actual, opts = {}
23
24
  calculate_diff(Pact::Term.unpack_regexps(expected), actual, DEFAULT_OPTIONS.merge(opts))
@@ -44,7 +45,7 @@ module Pact
44
45
 
45
46
  def regexp_diff regexp, actual, options
46
47
  if actual.is_a?(String) && regexp.match(actual)
47
- {}
48
+ NO_DIFF
48
49
  else
49
50
  RegexpDifference.new regexp, actual
50
51
  end
@@ -69,71 +70,78 @@ module Pact
69
70
  diff_found = true
70
71
  difference << item_diff
71
72
  else
72
- difference << NO_DIFF_INDICATOR
73
+ difference << NO_DIFF_AT_INDEX
73
74
  end
74
75
  end
75
- diff_found ? difference : {}
76
+ diff_found ? difference : NO_DIFF
77
+ end
78
+
79
+ def hash_diff expected, actual, options
80
+ if actual.is_a? Hash
81
+ actual_hash_diff expected, actual, options
82
+ else
83
+ Difference.new expected, actual
84
+ end
76
85
  end
77
86
 
78
87
  def actual_hash_diff expected, actual, options
79
- difference = expected.keys.inject({}) do |calculate_diff, key|
80
- if (diff_at_key = calculate_diff(expected[key], actual.fetch(key, Pact::KeyNotFound.new), options)).any?
81
- calculate_diff[key] = diff_at_key
82
- end
83
- calculate_diff
88
+ hash_diff = expected.each_with_object({}) do |(key, value), difference|
89
+ diff_at_key = calculate_diff(value, actual.fetch(key, Pact::KeyNotFound.new), options)
90
+ difference[key] = diff_at_key if diff_at_key.any?
84
91
  end
85
- difference.merge(check_for_unexpected_keys(expected, actual, options))
92
+ hash_diff.merge(check_for_unexpected_keys(expected, actual, options))
86
93
  end
87
94
 
88
95
  def check_for_unexpected_keys expected, actual, options
89
96
  if options[:allow_unexpected_keys]
90
- {}
97
+ NO_DIFF
91
98
  else
92
- (actual.keys - expected.keys).inject({}) do | calculate_diff, key |
93
- calculate_diff[key] = Difference.new(UnexpectedKey.new, actual[key])
94
- calculate_diff
99
+ (actual.keys - expected.keys).each_with_object({}) do | key, running_diff |
100
+ running_diff[key] = Difference.new(UnexpectedKey.new, actual[key])
95
101
  end
96
102
  end
97
103
  end
98
104
 
99
- def hash_diff expected, actual, options
100
- if actual.is_a? Hash
101
- actual_hash_diff expected, actual, options
105
+ def object_diff expected, actual, options
106
+ if options[:type]
107
+ type_difference expected, actual
102
108
  else
109
+ exact_value_diff expected, actual, options
110
+ end
111
+ end
112
+
113
+ def exact_value_diff expected, actual, options
114
+ if expected != actual
103
115
  Difference.new expected, actual
116
+ else
117
+ NO_DIFF
104
118
  end
105
119
  end
106
120
 
107
121
  def type_difference expected, actual
108
122
  if types_match? expected, actual
109
- {}
123
+ NO_DIFF
110
124
  else
111
125
  TypeDifference.new type_diff_expected_display(expected), type_diff_actual_display(actual)
112
126
  end
113
127
  end
114
128
 
115
- def type_diff_actual_display actual
116
- actual.is_a?(KeyNotFound) ? actual : ActualType.new(actual)
117
- end
118
-
119
129
  def type_diff_expected_display expected
120
130
  ExpectedType.new(expected)
121
131
  end
122
132
 
133
+ def type_diff_actual_display actual
134
+ actual.is_a?(KeyNotFound) ? actual : ActualType.new(actual)
135
+ end
136
+
123
137
  def types_match? expected, actual
124
- #There must be a more elegant way to do this
125
- expected.class == actual.class ||
126
- (expected.is_a?(TrueClass) && actual.is_a?(FalseClass)) ||
127
- (expected.is_a?(FalseClass) && actual.is_a?(TrueClass))
138
+ expected.class == actual.class || (is_boolean(expected) && is_boolean(actual))
128
139
  end
129
140
 
130
- def object_diff expected, actual, options
131
- return type_difference(expected, actual) if options[:type]
132
- if expected != actual
133
- Difference.new expected, actual
134
- else
135
- {}
136
- end
141
+ def is_boolean object
142
+ object == true || object == false
137
143
  end
144
+
145
+
138
146
  end
139
147
  end
@@ -1,17 +1,17 @@
1
1
  module Pact
2
2
  module Matchers
3
- class NoDiffIndicator
3
+ class NoDiffAtIndex
4
4
 
5
5
  def to_json options = {}
6
6
  to_s.inspect
7
7
  end
8
8
 
9
9
  def to_s
10
- 'no difference here!'
10
+ '<no difference at this index>'
11
11
  end
12
12
 
13
13
  def == other
14
- other.is_a? NoDiffIndicator
14
+ other.is_a? NoDiffAtIndex
15
15
  end
16
16
  end
17
17
  end
@@ -4,7 +4,7 @@ module Pact
4
4
  class UnexpectedIndex < Pact::DifferenceIndicator
5
5
 
6
6
  def to_s
7
- '<index not to exist>'
7
+ '<item not to exist>'
8
8
  end
9
9
 
10
10
  end
@@ -27,7 +27,8 @@ module Pact
27
27
  expected = generate_string(diff, :expected)
28
28
  actual = generate_string(diff, :actual)
29
29
  suffix = @include_explanation ? "\n" + key : ''
30
- @differ.diff_as_string(actual, expected).lstrip + suffix
30
+ string_diff = remove_comma_from_end_of_arrays(@differ.diff_as_string(actual, expected).lstrip)
31
+ string_diff + suffix
31
32
  end
32
33
 
33
34
  private
@@ -39,7 +40,7 @@ module Pact
39
40
  when Difference then copy_diff(thing, target)
40
41
  when TypeDifference then copy_diff(thing, target)
41
42
  when RegexpDifference then copy_diff(thing, target)
42
- when NoDiffIndicator then copy_no_diff(thing, target)
43
+ when NoDiffAtIndex then copy_no_diff(thing, target)
43
44
  else copy_object(thing, target)
44
45
  end
45
46
  end
@@ -48,7 +49,8 @@ module Pact
48
49
  comparable = handle(diff, target)
49
50
  begin
50
51
  # Can't think of an elegant way to check if we can pretty generate other than to try it and maybe fail
51
- fix_blank_lines_in_empty_hashes JSON.pretty_generate(comparable)
52
+ json = fix_blank_lines_in_empty_hashes JSON.pretty_generate(comparable)
53
+ add_comma_to_end_of_arrays json
52
54
  rescue JSON::GeneratorError
53
55
  comparable.to_s
54
56
  end
@@ -69,7 +71,7 @@ module Pact
69
71
  end
70
72
 
71
73
  def copy_no_diff(thing, target)
72
- thing
74
+ NoDifferenceDecorator.new
73
75
  end
74
76
 
75
77
  def copy_diff difference, target
@@ -94,13 +96,29 @@ module Pact
94
96
  " Values where the expected matches the actual are not shown.\n"
95
97
  end
96
98
 
99
+ def add_comma_to_end_of_arrays string
100
+ string.gsub(/(\n\s*\])/, ',\1')
101
+ end
102
+
103
+ def remove_comma_from_end_of_arrays string
104
+ string.gsub(/,(\n\s*\])/, '\1')
105
+ end
106
+
107
+ class NoDifferenceDecorator
108
+
109
+ def to_json options = {}
110
+ "... "
111
+ end
112
+
113
+ end
114
+
97
115
  class RegexpDecorator
98
116
 
99
117
  def initialize regexp
100
118
  @regexp = regexp
101
119
  end
102
120
 
103
- def to_json options={}
121
+ def to_json options = {}
104
122
  @regexp.inspect
105
123
  end
106
124
 
@@ -0,0 +1,17 @@
1
+ require 'pact/matching_rules/extract'
2
+ require 'pact/matching_rules/merge'
3
+
4
+ module Pact
5
+ module MatchingRules
6
+
7
+ # @api public Used by pact-mock_service
8
+ def self.extract object_graph
9
+ Extract.(object_graph)
10
+ end
11
+
12
+ def self.merge object_graph, matching_rules
13
+ Merge.(object_graph, matching_rules)
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,64 @@
1
+ module Pact
2
+ module MatchingRules
3
+ class Extract
4
+
5
+ def self.call matchable
6
+ new(matchable).call
7
+ end
8
+
9
+ def initialize matchable
10
+ @matchable = matchable
11
+ @rules = Hash.new
12
+ end
13
+
14
+ def call
15
+ recurse matchable, "$", nil
16
+ rules
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :matchable, :rules
22
+
23
+ def recurse object, path, match_type
24
+
25
+ case object
26
+ when Hash then recurse_hash(object, path, match_type)
27
+ when Array then recurse_array(object, path, match_type)
28
+ when Pact::SomethingLike then recurse_something_like(object, path, match_type)
29
+ when Pact::Term then record_regex_rule object, path
30
+ else
31
+ record_rule object, path, match_type
32
+ end
33
+
34
+ end
35
+
36
+ def recurse_hash hash, path, match_type
37
+ hash.each do | (key, value) |
38
+ recurse value, "#{path}.#{key.to_s}", match_type
39
+ end
40
+ end
41
+
42
+ def recurse_array new_array, path, match_type
43
+ new_array.each_with_index do | value, index |
44
+ recurse value, "#{path}[#{index}]", match_type
45
+ end
46
+ end
47
+
48
+ def recurse_something_like something_like, path, match_type
49
+ recurse something_like.contents, path, "type"
50
+ end
51
+
52
+ def record_regex_rule term, path
53
+ rules[path] ||= {}
54
+ rules[path]['match'] = 'regex'
55
+ rules[path]['regex'] = term.matcher.inspect[1..-2]
56
+ end
57
+
58
+ def record_rule object, path, match_type
59
+ rules[path] ||= {}
60
+ rules[path]['match'] = 'type'
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,80 @@
1
+ module Pact
2
+ module MatchingRules
3
+ class Merge
4
+
5
+ def self.call expected, matching_rules, root_path = '$'
6
+ new(expected, matching_rules, root_path).call
7
+ end
8
+
9
+ def initialize expected, matching_rules, root_path
10
+ @expected = expected
11
+ @matching_rules = matching_rules
12
+ @root_path = root_path
13
+ end
14
+
15
+ def call
16
+ return @expected if @matching_rules.nil? || @matching_rules.empty?
17
+ recurse @expected, @root_path
18
+ end
19
+
20
+ def recurse expected, path
21
+ case expected
22
+ when Hash then recurse_hash(expected, path)
23
+ when Array then recurse_array(expected, path)
24
+ else
25
+ expected
26
+ end
27
+ end
28
+
29
+ def recurse_hash hash, path
30
+ hash.each_with_object({}) do | (k, v), new_hash |
31
+ new_path = path + ".#{k.to_s}"
32
+ new_hash[k] = recurse(wrap(v, new_path), new_path)
33
+ end
34
+ end
35
+
36
+ def recurse_array array, path
37
+ new_array = []
38
+ array.each_with_index do | item, index |
39
+ new_path = path + "[#{index}]"
40
+ new_array << recurse(wrap(item, new_path), new_path)
41
+ end
42
+ new_array
43
+ end
44
+
45
+ def wrap object, path
46
+ rules = @matching_rules[path]
47
+ return object unless rules
48
+
49
+ if rules['match'] == 'type'
50
+ handle_match_type(object, path, rules)
51
+ elsif rules['regex']
52
+ handle_regex(object, path, rules)
53
+ else
54
+ log_ignored_rules(path, rules, {})
55
+ object
56
+ end
57
+ end
58
+
59
+ def handle_match_type object, path, rules
60
+ log_ignored_rules(path, rules, {'match' => 'type'})
61
+ Pact::SomethingLike.new(object)
62
+ end
63
+
64
+ def handle_regex object, path, rules
65
+ log_ignored_rules(path, rules, {'match' => 'regex', 'regex' => rules['regex']})
66
+ Pact::Term.new(generate: object, matcher: Regexp.new(rules['regex']))
67
+ end
68
+
69
+ def log_ignored_rules path, rules, used_rules
70
+ dup_rules = rules.dup
71
+ used_rules.each_pair do | used_key, used_value |
72
+ dup_rules.delete(used_key) if dup_rules[used_key] == used_value
73
+ end
74
+ if dup_rules.any?
75
+ $stderr.puts "WARN: Ignoring unsupported matching rules #{dup_rules} for path #{path}"
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end