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 +4 -4
- data/CHANGELOG.md +12 -0
- data/lib/pact/consumer_contract/interaction.rb +6 -3
- data/lib/pact/matchers/index_not_found.rb +1 -1
- data/lib/pact/matchers/list_diff_formatter.rb +2 -2
- data/lib/pact/matchers/matchers.rb +42 -34
- data/lib/pact/matchers/{no_diff_indicator.rb → no_diff_at_index.rb} +3 -3
- data/lib/pact/matchers/unexpected_index.rb +1 -1
- data/lib/pact/matchers/unix_diff_formatter.rb +23 -5
- data/lib/pact/matching_rules.rb +17 -0
- data/lib/pact/matching_rules/extract.rb +64 -0
- data/lib/pact/matching_rules/merge.rb +80 -0
- data/lib/pact/support/version.rb +1 -1
- data/lib/pact/term.rb +1 -1
- data/spec/fixtures/interaction-with-matching-rules.json +27 -0
- data/spec/integration/matching_rules_extract_and_merge_spec.rb +98 -0
- data/spec/lib/pact/consumer_contract/interaction_spec.rb +14 -0
- data/spec/lib/pact/matchers/list_diff_formatter_spec.rb +2 -2
- data/spec/lib/pact/matchers/matchers_spec.rb +9 -8
- data/spec/lib/pact/matchers/no_diff_at_index_spec.rb +15 -0
- data/spec/lib/pact/matchers/unix_diff_formatter_spec.rb +15 -14
- data/spec/lib/pact/matching_rules/extract_spec.rb +105 -0
- data/spec/lib/pact/matching_rules/merge_spec.rb +148 -0
- data/spec/support/spec_support.rb +9 -0
- metadata +51 -39
- data/spec/lib/pact/matchers/no_diff_indicator_spec.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d30e4cc422b899773c2917361c59ad211792f922
|
4
|
+
data.tar.gz: e20e3a998c5ba7f018b615567718193e009bcd26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
22
|
-
|
23
|
-
|
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
|
@@ -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
|
34
|
+
when NoDiffAtIndex then nil
|
35
35
|
else
|
36
|
-
raise "Invalid diff, expected Hash, Array,
|
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/
|
14
|
+
require 'pact/matchers/no_diff_at_index'
|
15
15
|
|
16
16
|
module Pact
|
17
17
|
module Matchers
|
18
18
|
|
19
|
-
|
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 <<
|
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
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
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).
|
93
|
-
|
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
|
100
|
-
if
|
101
|
-
|
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
|
-
|
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
|
131
|
-
|
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
|
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
|
10
|
+
'<no difference at this index>'
|
11
11
|
end
|
12
12
|
|
13
13
|
def == other
|
14
|
-
other.is_a?
|
14
|
+
other.is_a? NoDiffAtIndex
|
15
15
|
end
|
16
16
|
end
|
17
17
|
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
|
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
|
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
|
-
|
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
|