hs-pact-support 1.17.1
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 +7 -0
- data/CHANGELOG.md +620 -0
- data/LICENSE.txt +22 -0
- data/README.md +5 -0
- data/lib/pact/array_like.rb +49 -0
- data/lib/pact/configuration.rb +193 -0
- data/lib/pact/consumer/request.rb +27 -0
- data/lib/pact/consumer_contract/consumer_contract.rb +97 -0
- data/lib/pact/consumer_contract/file_name.rb +22 -0
- data/lib/pact/consumer_contract/headers.rb +51 -0
- data/lib/pact/consumer_contract/http_consumer_contract_parser.rb +37 -0
- data/lib/pact/consumer_contract/interaction.rb +81 -0
- data/lib/pact/consumer_contract/interaction_parser.rb +23 -0
- data/lib/pact/consumer_contract/interaction_v2_parser.rb +57 -0
- data/lib/pact/consumer_contract/interaction_v3_parser.rb +92 -0
- data/lib/pact/consumer_contract/pact_file.rb +157 -0
- data/lib/pact/consumer_contract/provider_state.rb +34 -0
- data/lib/pact/consumer_contract/query.rb +138 -0
- data/lib/pact/consumer_contract/query_hash.rb +89 -0
- data/lib/pact/consumer_contract/query_string.rb +51 -0
- data/lib/pact/consumer_contract/request.rb +83 -0
- data/lib/pact/consumer_contract/response.rb +58 -0
- data/lib/pact/consumer_contract/service_consumer.rb +28 -0
- data/lib/pact/consumer_contract/service_provider.rb +28 -0
- data/lib/pact/consumer_contract/string_with_matching_rules.rb +17 -0
- data/lib/pact/consumer_contract.rb +1 -0
- data/lib/pact/errors.rb +21 -0
- data/lib/pact/helpers.rb +60 -0
- data/lib/pact/http/authorization_header_redactor.rb +32 -0
- data/lib/pact/logging.rb +14 -0
- data/lib/pact/matchers/actual_type.rb +16 -0
- data/lib/pact/matchers/base_difference.rb +39 -0
- data/lib/pact/matchers/differ.rb +153 -0
- data/lib/pact/matchers/difference.rb +13 -0
- data/lib/pact/matchers/difference_indicator.rb +26 -0
- data/lib/pact/matchers/embedded_diff_formatter.rb +60 -0
- data/lib/pact/matchers/expected_type.rb +35 -0
- data/lib/pact/matchers/extract_diff_messages.rb +76 -0
- data/lib/pact/matchers/index_not_found.rb +15 -0
- data/lib/pact/matchers/list_diff_formatter.rb +103 -0
- data/lib/pact/matchers/matchers.rb +285 -0
- data/lib/pact/matchers/multipart_form_diff_formatter.rb +41 -0
- data/lib/pact/matchers/no_diff_at_index.rb +18 -0
- data/lib/pact/matchers/regexp_difference.rb +13 -0
- data/lib/pact/matchers/type_difference.rb +16 -0
- data/lib/pact/matchers/unexpected_index.rb +11 -0
- data/lib/pact/matchers/unexpected_key.rb +11 -0
- data/lib/pact/matchers/unix_diff_formatter.rb +157 -0
- data/lib/pact/matchers.rb +1 -0
- data/lib/pact/matching_rules/extract.rb +91 -0
- data/lib/pact/matching_rules/jsonpath.rb +58 -0
- data/lib/pact/matching_rules/merge.rb +125 -0
- data/lib/pact/matching_rules/v3/extract.rb +94 -0
- data/lib/pact/matching_rules/v3/merge.rb +141 -0
- data/lib/pact/matching_rules.rb +30 -0
- data/lib/pact/reification.rb +56 -0
- data/lib/pact/rspec.rb +51 -0
- data/lib/pact/shared/active_support_support.rb +65 -0
- data/lib/pact/shared/dsl.rb +76 -0
- data/lib/pact/shared/form_differ.rb +32 -0
- data/lib/pact/shared/jruby_support.rb +18 -0
- data/lib/pact/shared/json_differ.rb +10 -0
- data/lib/pact/shared/key_not_found.rb +15 -0
- data/lib/pact/shared/multipart_form_differ.rb +16 -0
- data/lib/pact/shared/null_expectation.rb +31 -0
- data/lib/pact/shared/request.rb +106 -0
- data/lib/pact/shared/text_differ.rb +11 -0
- data/lib/pact/something_like.rb +49 -0
- data/lib/pact/specification_version.rb +18 -0
- data/lib/pact/support/version.rb +5 -0
- data/lib/pact/support.rb +12 -0
- data/lib/pact/symbolize_keys.rb +13 -0
- data/lib/pact/term.rb +85 -0
- data/lib/tasks/pact.rake +29 -0
- metadata +327 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'pact/something_like'
|
2
|
+
require 'pact/array_like'
|
3
|
+
require 'pact/term'
|
4
|
+
|
5
|
+
module Pact
|
6
|
+
module MatchingRules
|
7
|
+
class Extract
|
8
|
+
|
9
|
+
def self.call matchable
|
10
|
+
new(matchable).call
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize matchable
|
14
|
+
@matchable = matchable
|
15
|
+
@rules = Hash.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
recurse matchable, "$", nil
|
20
|
+
rules
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :matchable, :rules
|
26
|
+
|
27
|
+
def recurse object, path, match_type
|
28
|
+
case object
|
29
|
+
when Hash then recurse_hash(object, path, match_type)
|
30
|
+
when Array then recurse_array(object, path, match_type)
|
31
|
+
when Pact::SomethingLike then handle_something_like(object, path, match_type)
|
32
|
+
when Pact::ArrayLike then handle_array_like(object, path, match_type)
|
33
|
+
when Pact::Term then record_regex_rule object, path
|
34
|
+
when Pact::QueryString then recurse(object.query, path, match_type)
|
35
|
+
when Pact::QueryHash then recurse_hash(object.query, path, match_type)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def recurse_hash hash, path, match_type
|
40
|
+
hash.each do | (key, value) |
|
41
|
+
recurse value, "#{path}#{next_path_part(key)}", match_type
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def recurse_array new_array, path, match_type
|
46
|
+
new_array.each_with_index do | value, index |
|
47
|
+
recurse value, "#{path}[#{index}]", match_type
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def handle_something_like something_like, path, match_type
|
52
|
+
record_match_type_rule path, "type"
|
53
|
+
recurse something_like.contents, path, "type"
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_array_like array_like, path, match_type
|
57
|
+
record_rule "#{path}", 'min' => array_like.min
|
58
|
+
record_match_type_rule "#{path}[*].*", 'type'
|
59
|
+
recurse array_like.contents, "#{path}[*]", :array_like
|
60
|
+
end
|
61
|
+
|
62
|
+
def record_rule path, rule
|
63
|
+
rules[path] ||= {}
|
64
|
+
rules[path] = rules[path].merge(rule)
|
65
|
+
end
|
66
|
+
|
67
|
+
def record_regex_rule term, path
|
68
|
+
rules[path] ||= {}
|
69
|
+
rules[path]['match'] = 'regex'
|
70
|
+
rules[path]['regex'] = term.matcher.inspect[1..-2]
|
71
|
+
end
|
72
|
+
|
73
|
+
def record_match_type_rule path, match_type
|
74
|
+
unless match_type == :array_like || match_type.nil?
|
75
|
+
rules[path] ||= {}
|
76
|
+
rules[path]['match'] = match_type
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Beth: there's a potential bug if the key contains a dot and a single quote.
|
81
|
+
# Not sure what to do then.
|
82
|
+
def next_path_part key
|
83
|
+
if key.to_s.include?('.')
|
84
|
+
"['#{key}']"
|
85
|
+
else
|
86
|
+
".#{key}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
# Ripped with appreciation from Joshua Hull's useful JsonPath gem
|
4
|
+
# https://github.com/joshbuddy/jsonpath/blob/792ff9a928998f4252692cd3c1ba378ed931a5aa/lib/jsonpath.rb
|
5
|
+
# Only including the code that Pact needs, to reduce dependencies and potential gem version clashes.
|
6
|
+
|
7
|
+
module Pact
|
8
|
+
module MatchingRules
|
9
|
+
class JsonPath
|
10
|
+
|
11
|
+
attr_reader :path
|
12
|
+
|
13
|
+
def initialize(path)
|
14
|
+
scanner = StringScanner.new(path)
|
15
|
+
@path = []
|
16
|
+
while not scanner.eos?
|
17
|
+
if token = scanner.scan(/\$/)
|
18
|
+
@path << token
|
19
|
+
elsif token = scanner.scan(/@/)
|
20
|
+
@path << token
|
21
|
+
elsif token = scanner.scan(/[:a-zA-Z0-9_-]+/)
|
22
|
+
@path << "['#{token}']"
|
23
|
+
elsif token = scanner.scan(/'(.*?)'/)
|
24
|
+
@path << "[#{token}]"
|
25
|
+
elsif token = scanner.scan(/\[/)
|
26
|
+
count = 1
|
27
|
+
while !count.zero?
|
28
|
+
if t = scanner.scan(/\[/)
|
29
|
+
token << t
|
30
|
+
count += 1
|
31
|
+
elsif t = scanner.scan(/\]/)
|
32
|
+
token << t
|
33
|
+
count -= 1
|
34
|
+
elsif t = scanner.scan(/[^\[\]]*/)
|
35
|
+
token << t
|
36
|
+
end
|
37
|
+
end
|
38
|
+
@path << token
|
39
|
+
elsif token = scanner.scan(/\.\./)
|
40
|
+
@path << token
|
41
|
+
elsif scanner.scan(/\./)
|
42
|
+
nil
|
43
|
+
elsif token = scanner.scan(/\*/)
|
44
|
+
@path << token
|
45
|
+
elsif token = scanner.scan(/[><=] \d+/)
|
46
|
+
@path.last << token
|
47
|
+
elsif token = scanner.scan(/./)
|
48
|
+
@path.last << token
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
path.join
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'pact/array_like'
|
2
|
+
require 'pact/matching_rules/jsonpath'
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
module MatchingRules
|
6
|
+
class Merge
|
7
|
+
|
8
|
+
def self.call expected, matching_rules, root_path = '$'
|
9
|
+
new(expected, matching_rules, root_path).call
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize expected, matching_rules, root_path
|
13
|
+
@expected = expected
|
14
|
+
@matching_rules = standardise_paths(matching_rules)
|
15
|
+
@root_path = JsonPath.new(root_path).to_s
|
16
|
+
@used_rules = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def call
|
20
|
+
return @expected if @matching_rules.nil? || @matching_rules.empty?
|
21
|
+
recurse(@expected, @root_path).tap { log_ignored_rules }
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def standardise_paths matching_rules
|
27
|
+
return matching_rules if matching_rules.nil? || matching_rules.empty?
|
28
|
+
matching_rules.each_with_object({}) do | (path, rule), new_matching_rules |
|
29
|
+
new_matching_rules[JsonPath.new(path).to_s] = rule
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def recurse expected, path
|
34
|
+
recursed = case expected
|
35
|
+
when Hash then recurse_hash(expected, path)
|
36
|
+
when Array then recurse_array(expected, path)
|
37
|
+
else
|
38
|
+
expected
|
39
|
+
end
|
40
|
+
|
41
|
+
wrap(recursed, path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def recurse_hash hash, path
|
45
|
+
recursed = hash.each_with_object({}) do | (k, v), new_hash |
|
46
|
+
new_path = path + "['#{k}']"
|
47
|
+
new_hash[k] = recurse(v, new_path)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def recurse_array array, path
|
52
|
+
parent_match_rule = find_rule(path, 'match')
|
53
|
+
log_used_rule(path, 'match', parent_match_rule) if parent_match_rule
|
54
|
+
|
55
|
+
array_like_children_path = "#{path}[*]*"
|
56
|
+
children_match_rule = find_rule(array_like_children_path, 'match')
|
57
|
+
log_used_rule(array_like_children_path, 'match', children_match_rule) if children_match_rule
|
58
|
+
|
59
|
+
min = find_rule(path, 'min')
|
60
|
+
log_used_rule(path, 'min', min) if min
|
61
|
+
|
62
|
+
if min && (children_match_rule == 'type' || (children_match_rule.nil? && parent_match_rule == 'type'))
|
63
|
+
warn_when_not_one_example_item(array, path)
|
64
|
+
Pact::ArrayLike.new(recurse(array.first, "#{path}[*]"), min: min)
|
65
|
+
else
|
66
|
+
new_array = []
|
67
|
+
array.each_with_index do | item, index |
|
68
|
+
new_path = path + "[#{index}]"
|
69
|
+
new_array << recurse(item, new_path)
|
70
|
+
end
|
71
|
+
new_array
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def warn_when_not_one_example_item array, path
|
76
|
+
unless array.size == 1
|
77
|
+
Pact.configuration.error_stream.puts "WARN: Only the first item will be used to match the items in the array at #{path}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def wrap object, path
|
82
|
+
if find_rule(path, 'match') == 'type' && !find_rule(path, 'min')
|
83
|
+
handle_match_type(object, path)
|
84
|
+
elsif find_rule(path, 'regex')
|
85
|
+
handle_regex(object, path)
|
86
|
+
else
|
87
|
+
object
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def handle_match_type object, path
|
92
|
+
log_used_rule(path, 'match', 'type')
|
93
|
+
Pact::SomethingLike.new(object)
|
94
|
+
end
|
95
|
+
|
96
|
+
def handle_regex object, path
|
97
|
+
regex = find_rule(path, 'regex')
|
98
|
+
log_used_rule(path, 'match', 'regex') # assumed to be present
|
99
|
+
log_used_rule(path, 'regex', regex)
|
100
|
+
Pact::Term.new(generate: object, matcher: Regexp.new(regex))
|
101
|
+
end
|
102
|
+
|
103
|
+
def log_ignored_rules
|
104
|
+
dup_rules = @matching_rules.dup
|
105
|
+
@used_rules.each do | (path, key, value) |
|
106
|
+
dup_rules[path].delete(key) if dup_rules[path][key] == value
|
107
|
+
end
|
108
|
+
|
109
|
+
if dup_rules.any?
|
110
|
+
dup_rules.each do | path, rules |
|
111
|
+
$stderr.puts "WARN: Ignoring unsupported matching rules #{rules} for path #{path}" if rules.any?
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def find_rule(path, key)
|
117
|
+
@matching_rules[path] && @matching_rules[path][key]
|
118
|
+
end
|
119
|
+
|
120
|
+
def log_used_rule path, key, value
|
121
|
+
@used_rules << [path, key, value]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'pact/something_like'
|
2
|
+
require 'pact/array_like'
|
3
|
+
require 'pact/term'
|
4
|
+
|
5
|
+
module Pact
|
6
|
+
module MatchingRules::V3
|
7
|
+
class Extract
|
8
|
+
|
9
|
+
def self.call matchable
|
10
|
+
new(matchable).call
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize matchable
|
14
|
+
@matchable = matchable
|
15
|
+
@rules = Hash.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
recurse matchable, "$", nil
|
20
|
+
rules
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :matchable, :rules
|
26
|
+
|
27
|
+
def recurse object, path, match_type
|
28
|
+
case object
|
29
|
+
when Hash then recurse_hash(object, path, match_type)
|
30
|
+
when Array then recurse_array(object, path, match_type)
|
31
|
+
when Pact::SomethingLike then handle_something_like(object, path, match_type)
|
32
|
+
when Pact::ArrayLike then handle_array_like(object, path, match_type)
|
33
|
+
when Pact::Term then record_regex_rule object, path
|
34
|
+
when Pact::QueryString then recurse(object.query, path, match_type)
|
35
|
+
when Pact::QueryHash then recurse_hash(object.query, path, match_type)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def recurse_hash hash, path, match_type
|
40
|
+
hash.each do | (key, value) |
|
41
|
+
recurse value, "#{path}#{next_path_part(key)}", match_type
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def recurse_array new_array, path, match_type
|
46
|
+
new_array.each_with_index do | value, index |
|
47
|
+
recurse value, "#{path}[#{index}]", match_type
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def handle_something_like something_like, path, match_type
|
52
|
+
record_match_type_rule path, "type"
|
53
|
+
recurse something_like.contents, path, "type"
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_array_like array_like, path, match_type
|
57
|
+
record_rule "#{path}", 'min' => array_like.min
|
58
|
+
record_match_type_rule "#{path}[*].*", 'type'
|
59
|
+
recurse array_like.contents, "#{path}[*]", :array_like
|
60
|
+
end
|
61
|
+
|
62
|
+
def record_rule path, rule
|
63
|
+
rules[path] ||= {}
|
64
|
+
rules[path]['matchers'] ||= []
|
65
|
+
rules[path]['matchers'] << rule
|
66
|
+
end
|
67
|
+
|
68
|
+
def record_regex_rule term, path
|
69
|
+
rules[path] ||= {}
|
70
|
+
rules[path]['matchers'] ||= []
|
71
|
+
rule = { 'match' => 'regex', 'regex' => term.matcher.inspect[1..-2]}
|
72
|
+
rules[path]['matchers'] << rule
|
73
|
+
end
|
74
|
+
|
75
|
+
def record_match_type_rule path, match_type
|
76
|
+
unless match_type == :array_like || match_type.nil?
|
77
|
+
rules[path] ||= {}
|
78
|
+
rules[path]['matchers'] ||= []
|
79
|
+
rules[path]['matchers'] << { 'match' => match_type }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Beth: there's a potential bug if the key contains a dot and a single quote.
|
84
|
+
# Not sure what to do then.
|
85
|
+
def next_path_part key
|
86
|
+
if key.to_s.include?('.')
|
87
|
+
"['#{key}']"
|
88
|
+
else
|
89
|
+
".#{key}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'pact/array_like'
|
2
|
+
require 'pact/matching_rules/jsonpath'
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
module MatchingRules
|
6
|
+
module V3
|
7
|
+
class Merge
|
8
|
+
|
9
|
+
def self.call expected, matching_rules, root_path = '$'
|
10
|
+
new(expected, matching_rules, root_path).call
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize expected, matching_rules, root_path
|
14
|
+
@expected = expected
|
15
|
+
@matching_rules = standardise_paths(matching_rules)
|
16
|
+
@root_path = JsonPath.new(root_path).to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def call
|
20
|
+
return @expected if @matching_rules.nil? || @matching_rules.empty?
|
21
|
+
recurse(@expected, @root_path).tap { log_ignored_rules }
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def standardise_paths matching_rules
|
27
|
+
return matching_rules if matching_rules.nil? || matching_rules.empty?
|
28
|
+
matching_rules.each_with_object({}) do | (path, rules), new_matching_rules |
|
29
|
+
new_matching_rules[JsonPath.new(path).to_s] = Marshal.load(Marshal.dump(rules)) # simplest way to deep clone
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def recurse expected, path
|
34
|
+
recursed = case expected
|
35
|
+
when Hash then recurse_hash(expected, path)
|
36
|
+
when Array then recurse_array(expected, path)
|
37
|
+
else
|
38
|
+
expected
|
39
|
+
end
|
40
|
+
wrap(recursed, path)
|
41
|
+
end
|
42
|
+
|
43
|
+
def recurse_hash hash, path
|
44
|
+
hash.each_with_object({}) do | (k, v), new_hash |
|
45
|
+
new_path = path + "['#{k}']"
|
46
|
+
new_hash[k] = recurse(v, new_path)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def recurse_array array, path
|
51
|
+
# This assumes there is only one rule! TODO make this find the appropriate rule.
|
52
|
+
parent_match_rule = @matching_rules[path]['matchers'].first['match'] rescue nil
|
53
|
+
array_like_children_path = "#{path}[*]*"
|
54
|
+
children_match_rule = @matching_rules[array_like_children_path]['matchers'].first['match'] rescue nil
|
55
|
+
min = @matching_rules[path]['matchers'].first['min'] rescue nil
|
56
|
+
|
57
|
+
if min && children_match_rule == 'type'
|
58
|
+
@matching_rules[path]['matchers'].first.delete('min')
|
59
|
+
@matching_rules[array_like_children_path]['matchers'].first.delete('match')
|
60
|
+
warn_when_not_one_example_item(array, path)
|
61
|
+
Pact::ArrayLike.new(recurse(array.first, "#{path}[*]"), min: min)
|
62
|
+
elsif min && parent_match_rule == 'type'
|
63
|
+
@matching_rules[path]['matchers'].first.delete('min')
|
64
|
+
@matching_rules[path]['matchers'].first.delete('match')
|
65
|
+
warn_when_not_one_example_item(array, path)
|
66
|
+
Pact::ArrayLike.new(recurse(array.first, "#{path}[*]"), min: min)
|
67
|
+
else
|
68
|
+
new_array = []
|
69
|
+
array.each_with_index do | item, index |
|
70
|
+
new_path = path + "[#{index}]"
|
71
|
+
new_array << recurse(item, new_path)
|
72
|
+
end
|
73
|
+
new_array
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def warn_when_not_one_example_item array, path
|
78
|
+
unless array.size == 1
|
79
|
+
Pact.configuration.error_stream.puts "WARN: Only the first item will be used to match the items in the array at #{path}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def wrap object, path
|
84
|
+
rules = @matching_rules[path] && @matching_rules[path]['matchers'] && @matching_rules[path]['matchers'].first
|
85
|
+
array_rules = @matching_rules["#{path}[*]*"] && @matching_rules["#{path}[*]*"]['matchers'] && @matching_rules["#{path}[*]*"]['matchers'].first
|
86
|
+
return object unless rules || array_rules
|
87
|
+
|
88
|
+
if rules['match'] == 'type' && !rules.has_key?('min')
|
89
|
+
handle_match_type(object, path, rules)
|
90
|
+
elsif rules['regex']
|
91
|
+
handle_regex(object, path, rules)
|
92
|
+
else
|
93
|
+
object
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def handle_match_type object, path, rules
|
98
|
+
rules.delete('match')
|
99
|
+
Pact::SomethingLike.new(object)
|
100
|
+
end
|
101
|
+
|
102
|
+
def handle_regex object, path, rules
|
103
|
+
rules.delete('match')
|
104
|
+
regex = rules.delete('regex')
|
105
|
+
Pact::Term.new(generate: object, matcher: Regexp.new(regex))
|
106
|
+
end
|
107
|
+
|
108
|
+
def log_ignored_rules
|
109
|
+
@matching_rules.each do | jsonpath, rules_hash |
|
110
|
+
rules_array = rules_hash["matchers"]
|
111
|
+
if rules_array
|
112
|
+
((rules_array.length - 1)..0).each do | index |
|
113
|
+
rules_array.delete_at(index) if rules_array[index].empty?
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
if @matching_rules.any?
|
119
|
+
@matching_rules.each do | path, rules_hash |
|
120
|
+
rules_hash.each do | key, value |
|
121
|
+
$stderr.puts "WARN: Ignoring unsupported #{key} #{value} for path #{path}" if value_present?(value)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def find_rule(path, key)
|
128
|
+
@matching_rules[path] && @matching_rules[path][key]
|
129
|
+
end
|
130
|
+
|
131
|
+
def log_used_rule path, key, value
|
132
|
+
@used_rules << [path, key, value]
|
133
|
+
end
|
134
|
+
|
135
|
+
def value_present? value
|
136
|
+
value.respond_to?(:any?) ? value.any? : true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'pact/matching_rules/extract'
|
2
|
+
require 'pact/matching_rules/v3/extract'
|
3
|
+
require 'pact/matching_rules/merge'
|
4
|
+
require 'pact/matching_rules/v3/merge'
|
5
|
+
|
6
|
+
module Pact
|
7
|
+
module MatchingRules
|
8
|
+
|
9
|
+
# @api public Used by pact-mock_service
|
10
|
+
def self.extract object_graph, options = {}
|
11
|
+
pact_specification_version = options[:pact_specification_version] || Pact::SpecificationVersion::NIL_VERSION
|
12
|
+
case pact_specification_version.major
|
13
|
+
when nil, 0, 1, 2
|
14
|
+
Extract.(object_graph)
|
15
|
+
else
|
16
|
+
V3::Extract.(object_graph)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.merge object_graph, matching_rules, options = {}
|
21
|
+
pact_specification_version = options[:pact_specification_version] || Pact::SpecificationVersion::NIL_VERSION
|
22
|
+
case pact_specification_version.major
|
23
|
+
when nil, 0, 1, 2
|
24
|
+
Merge.(object_graph, matching_rules)
|
25
|
+
else
|
26
|
+
V3::Merge.(object_graph, matching_rules)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'expgen'
|
2
|
+
require 'pact/term'
|
3
|
+
require 'pact/something_like'
|
4
|
+
require 'pact/array_like'
|
5
|
+
require 'pact/shared/request'
|
6
|
+
require 'pact/consumer_contract/query_hash'
|
7
|
+
require 'pact/consumer_contract/query_string'
|
8
|
+
require 'pact/consumer_contract/string_with_matching_rules'
|
9
|
+
|
10
|
+
module Pact
|
11
|
+
module Reification
|
12
|
+
include ActiveSupportSupport
|
13
|
+
|
14
|
+
def self.from_term(term)
|
15
|
+
case term
|
16
|
+
when Pact::Term, Pact::SomethingLike, Pact::ArrayLike
|
17
|
+
from_term(term.generate)
|
18
|
+
when Regexp
|
19
|
+
from_term(Expgen.gen(term))
|
20
|
+
when Hash
|
21
|
+
term.inject({}) do |mem, (key,t)|
|
22
|
+
mem[key] = from_term(t)
|
23
|
+
mem
|
24
|
+
end
|
25
|
+
when Array
|
26
|
+
term.map{ |t| from_term(t)}
|
27
|
+
when Pact::Request::Base
|
28
|
+
from_term(term.to_hash)
|
29
|
+
when Pact::QueryString
|
30
|
+
from_term(term.query)
|
31
|
+
when Pact::QueryHash
|
32
|
+
if term.original_string
|
33
|
+
term.original_string
|
34
|
+
else
|
35
|
+
from_term(term.query).map { |k, v|
|
36
|
+
if v.nil?
|
37
|
+
k
|
38
|
+
elsif v.is_a?(Array) #For cases where there are multiple instance of the same parameter
|
39
|
+
v.map { |x| "#{k}=#{escape(x)}"}.join('&')
|
40
|
+
else
|
41
|
+
"#{k}=#{escape(v)}"
|
42
|
+
end
|
43
|
+
}.join('&')
|
44
|
+
end
|
45
|
+
when Pact::StringWithMatchingRules
|
46
|
+
String.new(term)
|
47
|
+
else
|
48
|
+
term
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.escape(str)
|
53
|
+
URI.encode_www_form_component(str)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/pact/rspec.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# This is horrible, must work out a better way of doing this
|
2
|
+
module Pact
|
3
|
+
module RSpec
|
4
|
+
|
5
|
+
def self.color_enabled?
|
6
|
+
if ::RSpec.configuration.respond_to?(:color_enabled?)
|
7
|
+
::RSpec.configuration.color_enabled?(::RSpec.configuration.output_stream)
|
8
|
+
else
|
9
|
+
::RSpec.configuration.color_enabled?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.formatter_class
|
14
|
+
if ::RSpec::Core::Formatters.respond_to?(:register)
|
15
|
+
require 'pact/provider/rspec/formatter_rspec_3'
|
16
|
+
Pact::Provider::RSpec::Formatter
|
17
|
+
else
|
18
|
+
require 'pact/provider/rspec/formatter_rspec_2'
|
19
|
+
Pact::Provider::RSpec::Formatter2
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.full_description example
|
24
|
+
example.respond_to?(:full_description) ? example.full_description : example.example.full_description
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.runner_defined?
|
28
|
+
defined?(::RSpec::Core::Runner)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.is_rspec_3
|
32
|
+
defined?(::RSpec) && ::RSpec::Core::Formatters.respond_to?(:register)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.is_rspec_2
|
36
|
+
defined?(::RSpec) && !is_rspec_3
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.with_rspec_3
|
40
|
+
if is_rspec_3
|
41
|
+
yield
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.with_rspec_2
|
46
|
+
if is_rspec_2
|
47
|
+
yield
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|