pact-support 0.2.1 → 0.3.0

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