webmock 1.7.10 → 1.8.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.
- data/.travis.yml +2 -2
- data/CHANGELOG.md +98 -24
- data/Gemfile +2 -3
- data/README.md +45 -4
- data/Rakefile +2 -2
- data/lib/webmock.rb +3 -0
- data/lib/webmock/api.rb +34 -6
- data/lib/webmock/http_lib_adapters/curb_adapter.rb +4 -41
- data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb +1 -1
- data/lib/webmock/http_lib_adapters/excon_adapter.rb +94 -0
- data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +31 -4
- data/lib/webmock/http_lib_adapters/net_http.rb +2 -0
- data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +4 -3
- data/lib/webmock/matchers/hash_including_matcher.rb +25 -0
- data/lib/webmock/rack_response.rb +8 -1
- data/lib/webmock/request_pattern.rb +108 -77
- data/lib/webmock/request_signature.rb +1 -0
- data/lib/webmock/stub_registry.rb +9 -8
- data/lib/webmock/version.rb +1 -1
- data/lib/webmock/webmock.rb +5 -2
- data/minitest/webmock_spec.rb +22 -2
- data/spec/acceptance/curb/curb_spec_helper.rb +12 -2
- data/spec/acceptance/em_http_request/em_http_request_spec.rb +42 -33
- data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +4 -2
- data/spec/acceptance/excon/excon_spec.rb +15 -0
- data/spec/acceptance/excon/excon_spec_helper.rb +37 -0
- data/spec/acceptance/net_http/net_http_spec.rb +7 -0
- data/spec/acceptance/net_http/net_http_spec_helper.rb +3 -1
- data/spec/acceptance/patron/patron_spec.rb +12 -3
- data/spec/acceptance/patron/patron_spec_helper.rb +2 -2
- data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +3 -3
- data/spec/acceptance/shared/callbacks.rb +22 -6
- data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +21 -0
- data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +10 -11
- data/spec/acceptance/shared/precedence_of_stubs.rb +1 -1
- data/spec/acceptance/shared/request_expectations.rb +49 -3
- data/spec/acceptance/shared/returning_declared_responses.rb +9 -21
- data/spec/acceptance/shared/stubbing_requests.rb +80 -4
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +1 -1
- data/spec/acceptance/webmock_shared.rb +11 -8
- data/spec/spec_helper.rb +3 -3
- data/spec/support/my_rack_app.rb +25 -1
- data/spec/support/webmock_server.rb +9 -6
- data/spec/unit/rack_response_spec.rb +18 -0
- data/spec/unit/request_pattern_spec.rb +205 -96
- data/spec/unit/request_signature_spec.rb +36 -34
- data/spec/unit/util/uri_spec.rb +14 -2
- data/test/shared_test.rb +31 -2
- data/webmock.gemspec +9 -7
- metadata +86 -73
@@ -189,7 +189,7 @@ if defined?(EventMachine::HttpClient)
|
|
189
189
|
response_string = []
|
190
190
|
response_string << "HTTP/1.1 #{status[0]} #{status[1]}"
|
191
191
|
|
192
|
-
headers["Content-Length"] = body.
|
192
|
+
headers["Content-Length"] = body.bytesize unless headers["Content-Length"]
|
193
193
|
headers.each do |header, value|
|
194
194
|
value = value.join(", ") if value.is_a?(Array)
|
195
195
|
|
@@ -0,0 +1,94 @@
|
|
1
|
+
begin
|
2
|
+
require 'excon'
|
3
|
+
rescue LoadError
|
4
|
+
# excon not found
|
5
|
+
end
|
6
|
+
|
7
|
+
if defined?(Excon)
|
8
|
+
|
9
|
+
module WebMock
|
10
|
+
module HttpLibAdapters
|
11
|
+
|
12
|
+
class ExconAdapter < HttpLibAdapter
|
13
|
+
adapter_for :excon
|
14
|
+
|
15
|
+
def self.enable!
|
16
|
+
Excon.send(:remove_const, :Connection)
|
17
|
+
Excon.send(:const_set, :Connection, ExconConnection)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.disable!
|
21
|
+
Excon.send(:remove_const, :Connection)
|
22
|
+
Excon.send(:const_set, :Connection, ExconConnection.superclass)
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def self.to_query(hash)
|
27
|
+
string = ""
|
28
|
+
for key, values in hash
|
29
|
+
if values.nil?
|
30
|
+
string << key.to_s << '&'
|
31
|
+
else
|
32
|
+
for value in [*values]
|
33
|
+
string << key.to_s << '=' << CGI.escape(value.to_s) << '&'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
string.chop! # remove trailing '&'
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.build_request(params)
|
41
|
+
params = params.dup
|
42
|
+
method = (params.delete(:method) || :get).to_s.downcase.to_sym
|
43
|
+
params[:query] = to_query(params[:query]) if params[:query].is_a?(Hash)
|
44
|
+
uri = Addressable::URI.new(params).to_s
|
45
|
+
WebMock::RequestSignature.new method, uri, :body => params[:body], :headers => params[:headers]
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.real_response(mock)
|
49
|
+
raise Excon::Errors::Timeout if mock.should_timeout
|
50
|
+
mock.raise_error_if_any
|
51
|
+
Excon::Response.new \
|
52
|
+
:body => mock.body,
|
53
|
+
:status => mock.status[0].to_i,
|
54
|
+
:headers => mock.headers
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.mock_response(real)
|
58
|
+
mock = WebMock::Response.new
|
59
|
+
mock.status = real.status
|
60
|
+
mock.headers = real.headers
|
61
|
+
mock.body = real.body
|
62
|
+
mock
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.perform_callbacks(request, response, options = {})
|
66
|
+
return unless WebMock::CallbackRegistry.any_callbacks?
|
67
|
+
WebMock::CallbackRegistry.invoke_callbacks(options.merge(:lib => :excon), request, response)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
class ExconConnection < ::Excon::Connection
|
73
|
+
|
74
|
+
def request_kernel(params, &block)
|
75
|
+
mock_request = ExconAdapter.build_request params.dup
|
76
|
+
WebMock::RequestRegistry.instance.requested_signatures.put(mock_request)
|
77
|
+
|
78
|
+
if mock_response = WebMock::StubRegistry.instance.response_for_request(mock_request)
|
79
|
+
ExconAdapter.perform_callbacks(mock_request, mock_response, :real_request => false)
|
80
|
+
ExconAdapter.real_response(mock_response)
|
81
|
+
elsif WebMock.net_connect_allowed?(mock_request.uri)
|
82
|
+
real_response = super
|
83
|
+
ExconAdapter.perform_callbacks(mock_request, ExconAdapter.mock_response(real_response), :real_request => true)
|
84
|
+
real_response
|
85
|
+
else
|
86
|
+
raise WebMock::NetConnectNotAllowedError.new(mock_request)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -38,7 +38,7 @@ if defined?(::HTTPClient)
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def do_get_with_webmock(req, proxy, conn, stream = false, &block)
|
41
|
-
request_signature = build_request_signature(req)
|
41
|
+
request_signature = build_request_signature(req, :reuse_existing)
|
42
42
|
|
43
43
|
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
44
44
|
|
@@ -75,6 +75,7 @@ if defined?(::HTTPClient)
|
|
75
75
|
def do_request_async_with_webmock(method, uri, query, body, extheader)
|
76
76
|
req = create_request(method, uri, query, body, extheader)
|
77
77
|
request_signature = build_request_signature(req)
|
78
|
+
webmock_request_signatures << request_signature
|
78
79
|
|
79
80
|
if webmock_responses[request_signature] || WebMock.net_connect_allowed?(request_signature.uri)
|
80
81
|
do_request_async_without_webmock(method, uri, query, body, extheader)
|
@@ -111,7 +112,17 @@ if defined?(::HTTPClient)
|
|
111
112
|
def build_webmock_response(httpclient_response)
|
112
113
|
webmock_response = WebMock::Response.new
|
113
114
|
webmock_response.status = [httpclient_response.status, httpclient_response.reason]
|
114
|
-
|
115
|
+
|
116
|
+
webmock_response.headers = {}.tap do |hash|
|
117
|
+
httpclient_response.header.all.each do |(key, value)|
|
118
|
+
if hash.has_key?(key)
|
119
|
+
hash[key] = Array(hash[key]) + [value]
|
120
|
+
else
|
121
|
+
hash[key] = value
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
115
126
|
if httpclient_response.content.respond_to?(:read)
|
116
127
|
webmock_response.body = httpclient_response.content.read
|
117
128
|
body = HTTP::Message::Body.new
|
@@ -123,7 +134,7 @@ if defined?(::HTTPClient)
|
|
123
134
|
webmock_response
|
124
135
|
end
|
125
136
|
|
126
|
-
def build_request_signature(req)
|
137
|
+
def build_request_signature(req, reuse_existing = false)
|
127
138
|
uri = WebMock::Util::URI.heuristic_parse(req.header.request_uri.to_s)
|
128
139
|
uri.query_values = req.header.request_query if req.header.request_query
|
129
140
|
uri.port = req.header.request_uri.port
|
@@ -149,12 +160,19 @@ if defined?(::HTTPClient)
|
|
149
160
|
uri.userinfo = userinfo
|
150
161
|
end
|
151
162
|
|
152
|
-
WebMock::RequestSignature.new(
|
163
|
+
signature = WebMock::RequestSignature.new(
|
153
164
|
req.header.request_method.downcase.to_sym,
|
154
165
|
uri.to_s,
|
155
166
|
:body => req.content,
|
156
167
|
:headers => headers
|
157
168
|
)
|
169
|
+
|
170
|
+
# reuse a previous identical signature object if we stored one for later use
|
171
|
+
if reuse_existing && previous_signature = previous_signature_for(signature)
|
172
|
+
return previous_signature
|
173
|
+
end
|
174
|
+
|
175
|
+
signature
|
158
176
|
end
|
159
177
|
|
160
178
|
def webmock_responses
|
@@ -163,4 +181,13 @@ if defined?(::HTTPClient)
|
|
163
181
|
end
|
164
182
|
end
|
165
183
|
|
184
|
+
def webmock_request_signatures
|
185
|
+
@webmock_request_signatures ||= []
|
186
|
+
end
|
187
|
+
|
188
|
+
def previous_signature_for(signature)
|
189
|
+
return nil unless index = webmock_request_signatures.index(signature)
|
190
|
+
webmock_request_signatures.delete_at(index)
|
191
|
+
end
|
192
|
+
|
166
193
|
end
|
@@ -61,6 +61,9 @@ if defined?(Typhoeus)
|
|
61
61
|
:body => body,
|
62
62
|
:headers => req.headers
|
63
63
|
)
|
64
|
+
|
65
|
+
req.instance_variable_set(:@__webmock_request_signature, request_signature)
|
66
|
+
|
64
67
|
request_signature
|
65
68
|
end
|
66
69
|
|
@@ -115,9 +118,7 @@ if defined?(Typhoeus)
|
|
115
118
|
end
|
116
119
|
|
117
120
|
AFTER_REQUEST_CALLBACK = Proc.new do |request|
|
118
|
-
request_signature =
|
119
|
-
::WebMock::HttpLibAdapters::TyphoeusAdapter.
|
120
|
-
build_request_signature(request)
|
121
|
+
request_signature = request.instance_variable_get(:@__webmock_request_signature)
|
121
122
|
webmock_response =
|
122
123
|
::WebMock::HttpLibAdapters::TyphoeusAdapter.
|
123
124
|
build_webmock_response(request.response)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module WebMock
|
2
|
+
module Matchers
|
3
|
+
#this is a based on RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher
|
4
|
+
#https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb
|
5
|
+
class HashIncludingMatcher
|
6
|
+
def initialize(expected)
|
7
|
+
@expected = Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(expected).sort]
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(actual)
|
11
|
+
@expected.all? {|k,v| actual.has_key?(k) && v == actual[k]}
|
12
|
+
rescue NoMethodError
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
"hash_including(#{@expected.inspect})"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.from_rspec_matcher(matcher)
|
21
|
+
new(matcher.instance_variable_get(:@expected))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -10,12 +10,19 @@ module WebMock
|
|
10
10
|
status, headers, response = @app.call(env)
|
11
11
|
|
12
12
|
Response.new(
|
13
|
-
:body => response
|
13
|
+
:body => body_from_rack_response(response),
|
14
14
|
:headers => headers,
|
15
15
|
:status => status
|
16
16
|
)
|
17
17
|
end
|
18
18
|
|
19
|
+
def body_from_rack_response(response)
|
20
|
+
body = ""
|
21
|
+
response.each { |line| body << line }
|
22
|
+
response.close if response.respond_to?(:close)
|
23
|
+
return body
|
24
|
+
end
|
25
|
+
|
19
26
|
def build_rack_env(request)
|
20
27
|
uri = request.uri
|
21
28
|
headers = request.headers || {}
|
@@ -40,19 +40,19 @@ module WebMock
|
|
40
40
|
private
|
41
41
|
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
def assign_options(options)
|
44
|
+
@body_pattern = BodyPattern.new(options[:body]) if options.has_key?(:body)
|
45
|
+
@headers_pattern = HeadersPattern.new(options[:headers]) if options.has_key?(:headers)
|
46
|
+
@uri_pattern.add_query_params(options[:query]) if options.has_key?(:query)
|
47
|
+
end
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
def create_uri_pattern(uri)
|
50
|
+
if uri.is_a?(Regexp)
|
51
|
+
URIRegexpPattern.new(uri)
|
52
|
+
else
|
53
|
+
URIStringPattern.new(uri)
|
54
|
+
end
|
54
55
|
end
|
55
|
-
end
|
56
56
|
|
57
57
|
end
|
58
58
|
|
@@ -76,9 +76,31 @@ module WebMock
|
|
76
76
|
def initialize(pattern)
|
77
77
|
@pattern = pattern.is_a?(Addressable::URI) ? pattern : WebMock::Util::URI.normalize_uri(pattern)
|
78
78
|
end
|
79
|
+
|
80
|
+
def add_query_params(query_params)
|
81
|
+
@query_params = if query_params.is_a?(Hash)
|
82
|
+
query_params
|
83
|
+
elsif query_params.is_a?(WebMock::Matchers::HashIncludingMatcher)
|
84
|
+
query_params
|
85
|
+
elsif defined?(RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher) && query_params.is_a?(RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher)
|
86
|
+
WebMock::Matchers::HashIncludingMatcher.from_rspec_matcher(query_params)
|
87
|
+
else
|
88
|
+
Addressable::URI.parse('?' + query_params).query_values
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_s
|
93
|
+
str = @pattern.inspect
|
94
|
+
str += " with query params #{@query_params.inspect}" if @query_params
|
95
|
+
str
|
96
|
+
end
|
79
97
|
end
|
80
98
|
|
81
99
|
class URIRegexpPattern < URIPattern
|
100
|
+
def initialize *args, &block
|
101
|
+
@query_params = nil
|
102
|
+
super
|
103
|
+
end
|
82
104
|
|
83
105
|
def matches?(uri)
|
84
106
|
WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| u.match(@pattern) } &&
|
@@ -90,33 +112,34 @@ module WebMock
|
|
90
112
|
str += " with query params #{@query_params.inspect}" if @query_params
|
91
113
|
str
|
92
114
|
end
|
93
|
-
|
94
|
-
def add_query_params(query_params)
|
95
|
-
@query_params = query_params.is_a?(Hash) ? query_params : Addressable::URI.parse('?' + query_params).query_values
|
96
|
-
end
|
97
|
-
|
98
115
|
end
|
99
116
|
|
100
117
|
class URIStringPattern < URIPattern
|
101
118
|
def matches?(uri)
|
102
119
|
if @pattern.is_a?(Addressable::URI)
|
103
|
-
|
120
|
+
if @query_params
|
121
|
+
uri.omit(:query) === @pattern && (@query_params.nil? || @query_params == uri.query_values)
|
122
|
+
else
|
123
|
+
uri === @pattern
|
124
|
+
end
|
104
125
|
else
|
105
126
|
false
|
106
127
|
end
|
107
128
|
end
|
108
129
|
|
109
130
|
def add_query_params(query_params)
|
110
|
-
|
111
|
-
|
131
|
+
super
|
132
|
+
if @query_params.is_a?(Hash) || @query_params.is_a?(String)
|
133
|
+
@pattern.query_values = (@pattern.query_values || {}).merge(@query_params)
|
134
|
+
@query_params = nil
|
112
135
|
end
|
113
|
-
@pattern.query_values = (@pattern.query_values || {}).merge(query_params)
|
114
136
|
end
|
115
137
|
|
116
138
|
def to_s
|
117
|
-
WebMock::Util::URI.strip_default_port_from_uri_string(@pattern.to_s)
|
139
|
+
str = WebMock::Util::URI.strip_default_port_from_uri_string(@pattern.to_s)
|
140
|
+
str += " with query params #{@query_params.inspect}" if @query_params
|
141
|
+
str
|
118
142
|
end
|
119
|
-
|
120
143
|
end
|
121
144
|
|
122
145
|
|
@@ -136,24 +159,21 @@ module WebMock
|
|
136
159
|
}
|
137
160
|
|
138
161
|
def initialize(pattern)
|
139
|
-
@pattern = pattern
|
140
|
-
|
141
|
-
|
162
|
+
@pattern = if pattern.is_a?(Hash)
|
163
|
+
normalize_hash(pattern)
|
164
|
+
elsif defined?(RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher) && pattern.is_a?(RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher)
|
165
|
+
WebMock::Matchers::HashIncludingMatcher.from_rspec_matcher(pattern)
|
166
|
+
else
|
167
|
+
pattern
|
142
168
|
end
|
143
169
|
end
|
144
170
|
|
145
171
|
def matches?(body, content_type = "")
|
146
172
|
if (@pattern).is_a?(Hash)
|
147
173
|
return true if @pattern.empty?
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
matching_hashes?(WebMock::Util::JSON.parse(body), @pattern)
|
152
|
-
when :xml then
|
153
|
-
matching_hashes?(Crack::XML.parse(body), @pattern)
|
154
|
-
else
|
155
|
-
matching_hashes?(Addressable::URI.parse('?' + body).query_values, @pattern)
|
156
|
-
end
|
174
|
+
matching_hashes?(body_as_hash(body, content_type), @pattern)
|
175
|
+
elsif (@pattern).is_a?(WebMock::Matchers::HashIncludingMatcher)
|
176
|
+
@pattern == body_as_hash(body, content_type)
|
157
177
|
else
|
158
178
|
empty_string?(@pattern) && empty_string?(body) ||
|
159
179
|
@pattern == body ||
|
@@ -166,51 +186,62 @@ module WebMock
|
|
166
186
|
end
|
167
187
|
|
168
188
|
private
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
# The following hashes are examples of matches:
|
176
|
-
#
|
177
|
-
# {a: /\d+/} and {a: '123'}
|
178
|
-
#
|
179
|
-
# {a: '123'} and {a: '123'}
|
180
|
-
#
|
181
|
-
# {a: {b: /\d+/}} and {a: {b: '123'}}
|
182
|
-
#
|
183
|
-
# {a: {b: 'wow'}} and {a: {b: 'wow'}}
|
184
|
-
#
|
185
|
-
# @param [Hash] query_parameters typically the result of parsing
|
186
|
-
# JSON, XML or URL encoded parameters.
|
187
|
-
#
|
188
|
-
# @param [Hash] pattern which contains keys with a string, hash or
|
189
|
-
# regular expression value to use for comparison.
|
190
|
-
#
|
191
|
-
# @return [Boolean] true if the paramaters match the comparison
|
192
|
-
# hash, false if not.
|
193
|
-
def matching_hashes?(query_parameters, pattern)
|
194
|
-
return false unless query_parameters.size == pattern.size
|
195
|
-
query_parameters.each do |key, actual|
|
196
|
-
expected = pattern[key]
|
197
|
-
|
198
|
-
if actual.is_a?(Hash) && expected.is_a?(Hash)
|
199
|
-
return false unless matching_hashes?(actual, expected)
|
189
|
+
def body_as_hash(body, content_type)
|
190
|
+
case BODY_FORMATS[content_type]
|
191
|
+
when :json then
|
192
|
+
WebMock::Util::JSON.parse(body)
|
193
|
+
when :xml then
|
194
|
+
Crack::XML.parse(body)
|
200
195
|
else
|
201
|
-
|
196
|
+
Addressable::URI.parse('?' + body).query_values
|
202
197
|
end
|
203
198
|
end
|
204
|
-
true
|
205
|
-
end
|
206
199
|
|
207
|
-
|
208
|
-
|
209
|
-
|
200
|
+
# Compare two hashes for equality
|
201
|
+
#
|
202
|
+
# For two hashes to match they must have the same length and all
|
203
|
+
# values must match when compared using `#===`.
|
204
|
+
#
|
205
|
+
# The following hashes are examples of matches:
|
206
|
+
#
|
207
|
+
# {a: /\d+/} and {a: '123'}
|
208
|
+
#
|
209
|
+
# {a: '123'} and {a: '123'}
|
210
|
+
#
|
211
|
+
# {a: {b: /\d+/}} and {a: {b: '123'}}
|
212
|
+
#
|
213
|
+
# {a: {b: 'wow'}} and {a: {b: 'wow'}}
|
214
|
+
#
|
215
|
+
# @param [Hash] query_parameters typically the result of parsing
|
216
|
+
# JSON, XML or URL encoded parameters.
|
217
|
+
#
|
218
|
+
# @param [Hash] pattern which contains keys with a string, hash or
|
219
|
+
# regular expression value to use for comparison.
|
220
|
+
#
|
221
|
+
# @return [Boolean] true if the paramaters match the comparison
|
222
|
+
# hash, false if not.
|
223
|
+
def matching_hashes?(query_parameters, pattern)
|
224
|
+
return false unless query_parameters.is_a?(Hash)
|
225
|
+
return false unless query_parameters.keys.sort == pattern.keys.sort
|
226
|
+
query_parameters.each do |key, actual|
|
227
|
+
expected = pattern[key]
|
228
|
+
|
229
|
+
if actual.is_a?(Hash) && expected.is_a?(Hash)
|
230
|
+
return false unless matching_hashes?(actual, expected)
|
231
|
+
else
|
232
|
+
return false unless expected === actual
|
233
|
+
end
|
234
|
+
end
|
235
|
+
true
|
236
|
+
end
|
210
237
|
|
211
|
-
|
212
|
-
|
213
|
-
|
238
|
+
def empty_string?(string)
|
239
|
+
string.nil? || string == ""
|
240
|
+
end
|
241
|
+
|
242
|
+
def normalize_hash(hash)
|
243
|
+
Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(hash).sort]
|
244
|
+
end
|
214
245
|
|
215
246
|
end
|
216
247
|
|
@@ -237,9 +268,9 @@ module WebMock
|
|
237
268
|
|
238
269
|
private
|
239
270
|
|
240
|
-
|
241
|
-
|
242
|
-
|
271
|
+
def empty_headers?(headers)
|
272
|
+
headers.nil? || headers == {}
|
273
|
+
end
|
243
274
|
end
|
244
275
|
|
245
276
|
end
|