webmock 3.0.0 → 3.14.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 +5 -5
- data/.github/workflows/CI.yml +37 -0
- data/CHANGELOG.md +416 -0
- data/Gemfile +1 -1
- data/README.md +157 -31
- data/Rakefile +12 -4
- data/lib/webmock/api.rb +12 -0
- data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
- data/lib/webmock/http_lib_adapters/curb_adapter.rb +17 -3
- data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +7 -4
- data/lib/webmock/http_lib_adapters/excon_adapter.rb +5 -2
- data/lib/webmock/http_lib_adapters/http_rb/client.rb +4 -1
- data/lib/webmock/http_lib_adapters/http_rb/request.rb +7 -1
- data/lib/webmock/http_lib_adapters/http_rb/response.rb +24 -3
- data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +6 -2
- data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +2 -2
- data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +28 -9
- data/lib/webmock/http_lib_adapters/manticore_adapter.rb +33 -15
- data/lib/webmock/http_lib_adapters/net_http.rb +54 -14
- data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
- data/lib/webmock/http_lib_adapters/patron_adapter.rb +4 -4
- data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
- data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
- data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
- data/lib/webmock/matchers/hash_including_matcher.rb +4 -23
- data/lib/webmock/rack_response.rb +1 -1
- data/lib/webmock/request_body_diff.rb +1 -1
- data/lib/webmock/request_execution_verifier.rb +2 -3
- data/lib/webmock/request_pattern.rb +108 -46
- data/lib/webmock/request_registry.rb +1 -1
- data/lib/webmock/request_signature.rb +1 -1
- data/lib/webmock/request_signature_snippet.rb +4 -4
- data/lib/webmock/response.rb +11 -5
- data/lib/webmock/rspec.rb +10 -3
- data/lib/webmock/stub_registry.rb +26 -11
- data/lib/webmock/stub_request_snippet.rb +10 -6
- data/lib/webmock/test_unit.rb +1 -3
- data/lib/webmock/util/hash_counter.rb +4 -4
- data/lib/webmock/util/headers.rb +17 -2
- data/lib/webmock/util/json.rb +1 -2
- data/lib/webmock/util/query_mapper.rb +9 -7
- data/lib/webmock/util/uri.rb +10 -10
- data/lib/webmock/util/values_stringifier.rb +20 -0
- data/lib/webmock/version.rb +1 -1
- data/lib/webmock/webmock.rb +10 -3
- data/lib/webmock.rb +53 -48
- data/minitest/webmock_spec.rb +2 -2
- data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
- data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
- data/spec/acceptance/curb/curb_spec.rb +33 -0
- data/spec/acceptance/em_http_request/em_http_request_spec.rb +56 -0
- data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
- data/spec/acceptance/excon/excon_spec.rb +4 -2
- data/spec/acceptance/excon/excon_spec_helper.rb +2 -0
- data/spec/acceptance/http_rb/http_rb_spec.rb +20 -0
- data/spec/acceptance/http_rb/http_rb_spec_helper.rb +5 -2
- data/spec/acceptance/httpclient/httpclient_spec.rb +8 -1
- data/spec/acceptance/manticore/manticore_spec.rb +51 -0
- data/spec/acceptance/net_http/net_http_shared.rb +1 -1
- data/spec/acceptance/net_http/net_http_spec.rb +53 -1
- data/spec/acceptance/patron/patron_spec.rb +7 -0
- data/spec/acceptance/patron/patron_spec_helper.rb +3 -3
- data/spec/acceptance/shared/callbacks.rb +3 -2
- data/spec/acceptance/shared/request_expectations.rb +14 -0
- data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
- data/spec/acceptance/shared/stubbing_requests.rb +95 -0
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +1 -1
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +1 -1
- data/spec/support/webmock_server.rb +1 -0
- data/spec/unit/api_spec.rb +103 -3
- data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
- data/spec/unit/request_execution_verifier_spec.rb +12 -12
- data/spec/unit/request_pattern_spec.rb +195 -49
- data/spec/unit/request_signature_snippet_spec.rb +2 -2
- data/spec/unit/response_spec.rb +22 -18
- data/spec/unit/stub_request_snippet_spec.rb +30 -10
- data/spec/unit/util/query_mapper_spec.rb +13 -0
- data/spec/unit/util/uri_spec.rb +74 -2
- data/spec/unit/webmock_spec.rb +54 -5
- data/test/shared_test.rb +15 -2
- data/test/test_webmock.rb +6 -0
- data/webmock.gemspec +11 -3
- metadata +66 -17
- data/.travis.yml +0 -20
|
@@ -4,6 +4,10 @@ module WebMock
|
|
|
4
4
|
def rSpecHashIncludingMatcher?(matcher)
|
|
5
5
|
matcher.class.name =~ /R?Spec::Mocks::ArgumentMatchers::HashIncludingMatcher/
|
|
6
6
|
end
|
|
7
|
+
|
|
8
|
+
def rSpecHashExcludingMatcher?(matcher)
|
|
9
|
+
matcher.class.name =~ /R?Spec::Mocks::ArgumentMatchers::HashExcludingMatcher/
|
|
10
|
+
end
|
|
7
11
|
end
|
|
8
12
|
|
|
9
13
|
class RequestPattern
|
|
@@ -20,7 +24,7 @@ module WebMock
|
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
def with(options = {}, &block)
|
|
23
|
-
raise ArgumentError.new('#with method invoked with no arguments. Either options hash or block must be specified.') if options.empty? && !block_given?
|
|
27
|
+
raise ArgumentError.new('#with method invoked with no arguments. Either options hash or block must be specified. Created a block with do..end? Try creating it with curly braces {} instead.') if options.empty? && !block_given?
|
|
24
28
|
assign_options(options)
|
|
25
29
|
@with_block = block
|
|
26
30
|
self
|
|
@@ -37,7 +41,7 @@ module WebMock
|
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
def to_s
|
|
40
|
-
string = "#{@method_pattern.to_s.upcase}"
|
|
44
|
+
string = "#{@method_pattern.to_s.upcase}".dup
|
|
41
45
|
string << " #{@uri_pattern.to_s}"
|
|
42
46
|
string << " with body #{@body_pattern.to_s}" if @body_pattern
|
|
43
47
|
string << " with headers #{@headers_pattern.to_s}" if @headers_pattern
|
|
@@ -76,6 +80,8 @@ module WebMock
|
|
|
76
80
|
URIRegexpPattern.new(uri)
|
|
77
81
|
elsif uri.is_a?(Addressable::Template)
|
|
78
82
|
URIAddressablePattern.new(uri)
|
|
83
|
+
elsif uri.respond_to?(:call)
|
|
84
|
+
URICallablePattern.new(uri)
|
|
79
85
|
else
|
|
80
86
|
URIStringPattern.new(uri)
|
|
81
87
|
end
|
|
@@ -103,11 +109,13 @@ module WebMock
|
|
|
103
109
|
include RSpecMatcherDetector
|
|
104
110
|
|
|
105
111
|
def initialize(pattern)
|
|
106
|
-
@pattern =
|
|
107
|
-
|
|
112
|
+
@pattern = if pattern.is_a?(Addressable::URI) \
|
|
113
|
+
|| pattern.is_a?(Addressable::Template)
|
|
114
|
+
pattern
|
|
115
|
+
elsif pattern.respond_to?(:call)
|
|
108
116
|
pattern
|
|
109
117
|
else
|
|
110
|
-
|
|
118
|
+
WebMock::Util::URI.normalize_uri(pattern)
|
|
111
119
|
end
|
|
112
120
|
@query_params = nil
|
|
113
121
|
end
|
|
@@ -115,65 +123,116 @@ module WebMock
|
|
|
115
123
|
def add_query_params(query_params)
|
|
116
124
|
@query_params = if query_params.is_a?(Hash)
|
|
117
125
|
query_params
|
|
118
|
-
elsif query_params.is_a?(WebMock::Matchers::HashIncludingMatcher)
|
|
126
|
+
elsif query_params.is_a?(WebMock::Matchers::HashIncludingMatcher) \
|
|
127
|
+
|| query_params.is_a?(WebMock::Matchers::HashExcludingMatcher)
|
|
119
128
|
query_params
|
|
120
129
|
elsif rSpecHashIncludingMatcher?(query_params)
|
|
121
130
|
WebMock::Matchers::HashIncludingMatcher.from_rspec_matcher(query_params)
|
|
131
|
+
elsif rSpecHashExcludingMatcher?(query_params)
|
|
132
|
+
WebMock::Matchers::HashExcludingMatcher.from_rspec_matcher(query_params)
|
|
122
133
|
else
|
|
123
134
|
WebMock::Util::QueryMapper.query_to_values(query_params, notation: Config.instance.query_values_notation)
|
|
124
135
|
end
|
|
125
136
|
end
|
|
126
137
|
|
|
138
|
+
def matches?(uri)
|
|
139
|
+
pattern_matches?(uri) && query_params_matches?(uri)
|
|
140
|
+
end
|
|
141
|
+
|
|
127
142
|
def to_s
|
|
128
|
-
str =
|
|
143
|
+
str = pattern_inspect
|
|
129
144
|
str += " with query params #{@query_params.inspect}" if @query_params
|
|
130
145
|
str
|
|
131
146
|
end
|
|
147
|
+
|
|
148
|
+
private
|
|
149
|
+
|
|
150
|
+
def pattern_inspect
|
|
151
|
+
@pattern.inspect
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def query_params_matches?(uri)
|
|
155
|
+
@query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query, notation: Config.instance.query_values_notation)
|
|
156
|
+
end
|
|
132
157
|
end
|
|
133
158
|
|
|
134
|
-
class
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
159
|
+
class URICallablePattern < URIPattern
|
|
160
|
+
private
|
|
161
|
+
|
|
162
|
+
def pattern_matches?(uri)
|
|
163
|
+
@pattern.call(uri)
|
|
138
164
|
end
|
|
165
|
+
end
|
|
139
166
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
167
|
+
class URIRegexpPattern < URIPattern
|
|
168
|
+
private
|
|
169
|
+
|
|
170
|
+
def pattern_matches?(uri)
|
|
171
|
+
WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| u.match(@pattern) }
|
|
144
172
|
end
|
|
145
173
|
end
|
|
146
174
|
|
|
147
175
|
class URIAddressablePattern < URIPattern
|
|
148
|
-
def
|
|
176
|
+
def add_query_params(query_params)
|
|
177
|
+
@@add_query_params_warned ||= false
|
|
178
|
+
if not @@add_query_params_warned
|
|
179
|
+
@@add_query_params_warned = true
|
|
180
|
+
warn "WebMock warning: ignoring query params in RFC 6570 template and checking them with WebMock"
|
|
181
|
+
end
|
|
182
|
+
super(query_params)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
private
|
|
186
|
+
|
|
187
|
+
def pattern_matches?(uri)
|
|
149
188
|
if @query_params.nil?
|
|
150
189
|
# Let Addressable check the whole URI
|
|
151
|
-
|
|
190
|
+
matches_with_variations?(uri)
|
|
152
191
|
else
|
|
153
192
|
# WebMock checks the query, Addressable checks everything else
|
|
154
|
-
|
|
155
|
-
@query_params == WebMock::Util::QueryMapper.query_to_values(uri.query)
|
|
193
|
+
matches_with_variations?(uri.omit(:query))
|
|
156
194
|
end
|
|
157
195
|
end
|
|
158
196
|
|
|
159
|
-
def
|
|
160
|
-
|
|
161
|
-
super(query_params)
|
|
197
|
+
def pattern_inspect
|
|
198
|
+
@pattern.pattern.inspect
|
|
162
199
|
end
|
|
163
200
|
|
|
164
|
-
def
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
201
|
+
def matches_with_variations?(uri)
|
|
202
|
+
template =
|
|
203
|
+
begin
|
|
204
|
+
Addressable::Template.new(WebMock::Util::URI.heuristic_parse(@pattern.pattern))
|
|
205
|
+
rescue Addressable::URI::InvalidURIError
|
|
206
|
+
Addressable::Template.new(@pattern.pattern)
|
|
207
|
+
end
|
|
208
|
+
WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u|
|
|
209
|
+
template_matches_uri?(template, u)
|
|
210
|
+
}
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def template_matches_uri?(template, uri)
|
|
214
|
+
template.match(uri)
|
|
215
|
+
rescue Addressable::URI::InvalidURIError
|
|
216
|
+
false
|
|
168
217
|
end
|
|
169
218
|
end
|
|
170
219
|
|
|
171
220
|
class URIStringPattern < URIPattern
|
|
172
|
-
def
|
|
221
|
+
def add_query_params(query_params)
|
|
222
|
+
super
|
|
223
|
+
if @query_params.is_a?(Hash) || @query_params.is_a?(String)
|
|
224
|
+
query_hash = (WebMock::Util::QueryMapper.query_to_values(@pattern.query, notation: Config.instance.query_values_notation) || {}).merge(@query_params)
|
|
225
|
+
@pattern.query = WebMock::Util::QueryMapper.values_to_query(query_hash, notation: WebMock::Config.instance.query_values_notation)
|
|
226
|
+
@query_params = nil
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
private
|
|
231
|
+
|
|
232
|
+
def pattern_matches?(uri)
|
|
173
233
|
if @pattern.is_a?(Addressable::URI)
|
|
174
234
|
if @query_params
|
|
175
|
-
uri.omit(:query) === @pattern
|
|
176
|
-
(@query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query, notation: Config.instance.query_values_notation))
|
|
235
|
+
uri.omit(:query) === @pattern
|
|
177
236
|
else
|
|
178
237
|
uri === @pattern
|
|
179
238
|
end
|
|
@@ -182,19 +241,8 @@ module WebMock
|
|
|
182
241
|
end
|
|
183
242
|
end
|
|
184
243
|
|
|
185
|
-
def
|
|
186
|
-
|
|
187
|
-
if @query_params.is_a?(Hash) || @query_params.is_a?(String)
|
|
188
|
-
query_hash = (WebMock::Util::QueryMapper.query_to_values(@pattern.query, notation: Config.instance.query_values_notation) || {}).merge(@query_params)
|
|
189
|
-
@pattern.query = WebMock::Util::QueryMapper.values_to_query(query_hash, notation: WebMock::Config.instance.query_values_notation)
|
|
190
|
-
@query_params = nil
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
def to_s
|
|
195
|
-
str = WebMock::Util::URI.strip_default_port_from_uri_string(@pattern.to_s)
|
|
196
|
-
str += " with query params #{@query_params.inspect}" if @query_params
|
|
197
|
-
str
|
|
244
|
+
def pattern_inspect
|
|
245
|
+
WebMock::Util::URI.strip_default_port_from_uri_string(@pattern.to_s)
|
|
198
246
|
end
|
|
199
247
|
end
|
|
200
248
|
|
|
@@ -232,7 +280,7 @@ module WebMock
|
|
|
232
280
|
|
|
233
281
|
if (@pattern).is_a?(Hash)
|
|
234
282
|
return true if @pattern.empty?
|
|
235
|
-
|
|
283
|
+
matching_body_hashes?(body_as_hash(body, content_type), @pattern, content_type)
|
|
236
284
|
elsif (@pattern).is_a?(WebMock::Matchers::HashIncludingMatcher)
|
|
237
285
|
@pattern == body_as_hash(body, content_type)
|
|
238
286
|
else
|
|
@@ -247,8 +295,9 @@ module WebMock
|
|
|
247
295
|
end
|
|
248
296
|
|
|
249
297
|
private
|
|
298
|
+
|
|
250
299
|
def body_as_hash(body, content_type)
|
|
251
|
-
case
|
|
300
|
+
case body_format(content_type)
|
|
252
301
|
when :json then
|
|
253
302
|
WebMock::Util::JSON.parse(body)
|
|
254
303
|
when :xml then
|
|
@@ -258,6 +307,11 @@ module WebMock
|
|
|
258
307
|
end
|
|
259
308
|
end
|
|
260
309
|
|
|
310
|
+
def body_format(content_type)
|
|
311
|
+
normalized_content_type = content_type.sub(/\A(application\/)[a-zA-Z0-9.-]+\+(json|xml)\Z/,'\1\2')
|
|
312
|
+
BODY_FORMATS[normalized_content_type]
|
|
313
|
+
end
|
|
314
|
+
|
|
261
315
|
def assert_non_multipart_body(content_type)
|
|
262
316
|
if content_type =~ %r{^multipart/form-data}
|
|
263
317
|
raise ArgumentError.new("WebMock does not support matching body for multipart/form-data requests yet :(")
|
|
@@ -287,15 +341,16 @@ module WebMock
|
|
|
287
341
|
#
|
|
288
342
|
# @return [Boolean] true if the paramaters match the comparison
|
|
289
343
|
# hash, false if not.
|
|
290
|
-
def
|
|
344
|
+
def matching_body_hashes?(query_parameters, pattern, content_type)
|
|
291
345
|
return false unless query_parameters.is_a?(Hash)
|
|
292
346
|
return false unless query_parameters.keys.sort == pattern.keys.sort
|
|
293
347
|
query_parameters.each do |key, actual|
|
|
294
348
|
expected = pattern[key]
|
|
295
349
|
|
|
296
350
|
if actual.is_a?(Hash) && expected.is_a?(Hash)
|
|
297
|
-
return false unless
|
|
351
|
+
return false unless matching_body_hashes?(actual, expected, content_type)
|
|
298
352
|
else
|
|
353
|
+
expected = WebMock::Util::ValuesStringifier.stringify_values(expected) if url_encoded_body?(content_type)
|
|
299
354
|
return false unless expected === actual
|
|
300
355
|
end
|
|
301
356
|
end
|
|
@@ -310,6 +365,9 @@ module WebMock
|
|
|
310
365
|
Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(hash, deep: true).sort]
|
|
311
366
|
end
|
|
312
367
|
|
|
368
|
+
def url_encoded_body?(content_type)
|
|
369
|
+
content_type =~ %r{^application/x-www-form-urlencoded}
|
|
370
|
+
end
|
|
313
371
|
end
|
|
314
372
|
|
|
315
373
|
class HeadersPattern
|
|
@@ -333,6 +391,10 @@ module WebMock
|
|
|
333
391
|
WebMock::Util::Headers.sorted_headers_string(@pattern)
|
|
334
392
|
end
|
|
335
393
|
|
|
394
|
+
def pp_to_s
|
|
395
|
+
WebMock::Util::Headers.pp_headers_string(@pattern)
|
|
396
|
+
end
|
|
397
|
+
|
|
336
398
|
private
|
|
337
399
|
|
|
338
400
|
def empty_headers?(headers)
|
|
@@ -23,7 +23,7 @@ module WebMock
|
|
|
23
23
|
if requested_signatures.hash.empty?
|
|
24
24
|
"No requests were made."
|
|
25
25
|
else
|
|
26
|
-
text = ""
|
|
26
|
+
text = "".dup
|
|
27
27
|
self.requested_signatures.each do |request_signature, times_executed|
|
|
28
28
|
text << "#{request_signature} was made #{times_executed} time#{times_executed == 1 ? '' : 's' }\n"
|
|
29
29
|
end
|
|
@@ -12,7 +12,7 @@ module WebMock
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def to_s
|
|
15
|
-
string = "#{self.method.to_s.upcase}"
|
|
15
|
+
string = "#{self.method.to_s.upcase}".dup
|
|
16
16
|
string << " #{WebMock::Util::URI.strip_default_port_from_uri_string(self.uri.to_s)}"
|
|
17
17
|
string << " with body '#{body.to_s}'" if body && body.to_s != ''
|
|
18
18
|
if headers && !headers.empty?
|
|
@@ -13,14 +13,14 @@ module WebMock
|
|
|
13
13
|
def stubbing_instructions
|
|
14
14
|
return unless WebMock.show_stubbing_instructions?
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
"You can stub this request with the following snippet:\n\n" +
|
|
17
|
+
WebMock::StubRequestSnippet.new(request_stub).to_s
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def request_stubs
|
|
21
21
|
return if WebMock::StubRegistry.instance.request_stubs.empty?
|
|
22
22
|
|
|
23
|
-
text = "registered request stubs:\n"
|
|
23
|
+
text = "registered request stubs:\n".dup
|
|
24
24
|
WebMock::StubRegistry.instance.request_stubs.each do |stub|
|
|
25
25
|
text << "\n#{WebMock::StubRequestSnippet.new(stub).to_s(false)}"
|
|
26
26
|
add_body_diff(stub, text) if WebMock.show_body_diff?
|
|
@@ -50,7 +50,7 @@ module WebMock
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def pretty_print_to_string(string_to_print)
|
|
53
|
-
StringIO.open("") do |stream|
|
|
53
|
+
StringIO.open("".dup) do |stream|
|
|
54
54
|
PP.pp(string_to_print, stream)
|
|
55
55
|
stream.rewind
|
|
56
56
|
stream.read
|
data/lib/webmock/response.rb
CHANGED
|
@@ -91,10 +91,10 @@ module WebMock
|
|
|
91
91
|
|
|
92
92
|
def ==(other)
|
|
93
93
|
self.body == other.body &&
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
self.headers === other.headers &&
|
|
95
|
+
self.status == other.status &&
|
|
96
|
+
self.exception == other.exception &&
|
|
97
|
+
self.should_timeout == other.should_timeout
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
private
|
|
@@ -111,7 +111,13 @@ module WebMock
|
|
|
111
111
|
valid_types = [Proc, IO, Pathname, String, Array]
|
|
112
112
|
return if @body.nil?
|
|
113
113
|
return if valid_types.any? { |c| @body.is_a?(c) }
|
|
114
|
-
|
|
114
|
+
|
|
115
|
+
if @body.class.is_a?(Hash)
|
|
116
|
+
raise InvalidBody, "must be one of: #{valid_types}, but you've used a #{@body.class}' instead." \
|
|
117
|
+
"\n What shall we encode it to? try calling .to_json .to_xml instead on the hash instead, or otherwise convert it to a string."
|
|
118
|
+
else
|
|
119
|
+
raise InvalidBody, "must be one of: #{valid_types}. '#{@body.class}' given"
|
|
120
|
+
end
|
|
115
121
|
end
|
|
116
122
|
|
|
117
123
|
def read_raw_response(raw_response)
|
data/lib/webmock/rspec.rb
CHANGED
|
@@ -20,14 +20,21 @@ end
|
|
|
20
20
|
|
|
21
21
|
require 'webmock/rspec/matchers'
|
|
22
22
|
|
|
23
|
-
WebMock.enable!
|
|
24
|
-
|
|
25
23
|
RSPEC_CONFIGURER.configure { |config|
|
|
26
24
|
|
|
27
25
|
config.include WebMock::API
|
|
28
26
|
config.include WebMock::Matchers
|
|
29
27
|
|
|
30
|
-
config.
|
|
28
|
+
config.before(:suite) do
|
|
29
|
+
WebMock.enable!
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
config.after(:suite) do
|
|
33
|
+
WebMock.disable!
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
config.around(:each) do |example|
|
|
37
|
+
example.run
|
|
31
38
|
WebMock.reset!
|
|
32
39
|
end
|
|
33
40
|
}
|
|
@@ -10,25 +10,39 @@ module WebMock
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def global_stubs
|
|
13
|
-
@global_stubs ||= []
|
|
13
|
+
@global_stubs ||= Hash.new { |h, k| h[k] = [] }
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def reset!
|
|
17
17
|
self.request_stubs = []
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def register_global_stub(&block)
|
|
20
|
+
def register_global_stub(order = :before_local_stubs, &block)
|
|
21
|
+
unless %i[before_local_stubs after_local_stubs].include?(order)
|
|
22
|
+
raise ArgumentError.new("Wrong order. Use :before_local_stubs or :after_local_stubs")
|
|
23
|
+
end
|
|
24
|
+
|
|
21
25
|
# This hash contains the responses returned by the block,
|
|
22
26
|
# keyed by the exact request (using the object_id).
|
|
23
27
|
# That way, there's no race condition in case #to_return
|
|
24
28
|
# doesn't run immediately after stub.with.
|
|
25
29
|
responses = {}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
response_lock = Mutex.new
|
|
31
|
+
|
|
32
|
+
stub = ::WebMock::RequestStub.new(:any, ->(uri) { true }).with { |request|
|
|
33
|
+
update_response = -> { responses[request.object_id] = yield(request) }
|
|
34
|
+
|
|
35
|
+
# The block can recurse, so only lock if we don't already own it
|
|
36
|
+
if response_lock.owned?
|
|
37
|
+
update_response.call
|
|
38
|
+
else
|
|
39
|
+
response_lock.synchronize(&update_response)
|
|
40
|
+
end
|
|
41
|
+
}.to_return(lambda { |request|
|
|
42
|
+
response_lock.synchronize { responses.delete(request.object_id) }
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
global_stubs[order].push stub
|
|
32
46
|
end
|
|
33
47
|
|
|
34
48
|
def register_request_stub(stub)
|
|
@@ -54,9 +68,10 @@ module WebMock
|
|
|
54
68
|
private
|
|
55
69
|
|
|
56
70
|
def request_stub_for(request_signature)
|
|
57
|
-
(global_stubs + request_stubs
|
|
58
|
-
registered_request_stub
|
|
59
|
-
|
|
71
|
+
(global_stubs[:before_local_stubs] + request_stubs + global_stubs[:after_local_stubs])
|
|
72
|
+
.detect { |registered_request_stub|
|
|
73
|
+
registered_request_stub.request_pattern.matches?(request_signature)
|
|
74
|
+
}
|
|
60
75
|
end
|
|
61
76
|
|
|
62
77
|
def evaluate_response_for_request(response, request_signature)
|
|
@@ -10,23 +10,27 @@ module WebMock
|
|
|
10
10
|
|
|
11
11
|
def to_s(with_response = true)
|
|
12
12
|
request_pattern = @request_stub.request_pattern
|
|
13
|
-
string = "stub_request(:#{request_pattern.method_pattern.to_s},"
|
|
13
|
+
string = "stub_request(:#{request_pattern.method_pattern.to_s},".dup
|
|
14
14
|
string << " \"#{request_pattern.uri_pattern.to_s}\")"
|
|
15
15
|
|
|
16
|
-
with = ""
|
|
16
|
+
with = "".dup
|
|
17
17
|
|
|
18
18
|
if (request_pattern.body_pattern)
|
|
19
|
-
with << "body: #{request_pattern.body_pattern.to_s}"
|
|
19
|
+
with << "\n body: #{request_pattern.body_pattern.to_s}"
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
if (request_pattern.headers_pattern)
|
|
23
|
-
with << "
|
|
23
|
+
with << "," unless with.empty?
|
|
24
24
|
|
|
25
|
-
with << "headers: #{request_pattern.headers_pattern.
|
|
25
|
+
with << "\n headers: #{request_pattern.headers_pattern.pp_to_s}"
|
|
26
26
|
end
|
|
27
27
|
string << ".\n with(#{with})" unless with.empty?
|
|
28
28
|
if with_response
|
|
29
|
-
|
|
29
|
+
if request_pattern.headers_pattern && request_pattern.headers_pattern.matches?({ 'Accept' => "application/json" })
|
|
30
|
+
string << ".\n to_return(status: 200, body: \"{}\", headers: {})"
|
|
31
|
+
else
|
|
32
|
+
string << ".\n to_return(status: 200, body: \"\", headers: {})"
|
|
33
|
+
end
|
|
30
34
|
end
|
|
31
35
|
string
|
|
32
36
|
end
|
data/lib/webmock/test_unit.rb
CHANGED
|
@@ -8,12 +8,10 @@ module Test
|
|
|
8
8
|
class TestCase
|
|
9
9
|
include WebMock::API
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
teardown
|
|
12
12
|
def teardown_with_webmock
|
|
13
|
-
teardown_without_webmock
|
|
14
13
|
WebMock.reset!
|
|
15
14
|
end
|
|
16
|
-
alias_method :teardown, :teardown_with_webmock
|
|
17
15
|
|
|
18
16
|
end
|
|
19
17
|
end
|
|
@@ -2,7 +2,7 @@ require 'thread'
|
|
|
2
2
|
|
|
3
3
|
module WebMock
|
|
4
4
|
module Util
|
|
5
|
-
class
|
|
5
|
+
class HashCounter
|
|
6
6
|
attr_accessor :hash
|
|
7
7
|
def initialize
|
|
8
8
|
self.hash = {}
|
|
@@ -25,13 +25,13 @@ module WebMock
|
|
|
25
25
|
def select(&block)
|
|
26
26
|
return unless block_given?
|
|
27
27
|
@lock.synchronize do
|
|
28
|
-
hash.select
|
|
28
|
+
hash.select(&block)
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def each(&block)
|
|
33
|
-
@order.to_a.
|
|
34
|
-
|
|
33
|
+
@order.to_a.sort_by { |a| a[1] }.each do |a|
|
|
34
|
+
yield(a[0], hash[a[0]])
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
end
|
data/lib/webmock/util/headers.rb
CHANGED
|
@@ -12,7 +12,7 @@ module WebMock
|
|
|
12
12
|
[name.to_s.split(/_|-/).map { |segment| segment.capitalize }.join("-"),
|
|
13
13
|
case value
|
|
14
14
|
when Regexp then value
|
|
15
|
-
when Array then (value.size == 1) ? value.first : value.map {|v| v.to_s}.sort
|
|
15
|
+
when Array then (value.size == 1) ? value.first.to_s : value.map {|v| v.to_s}.sort
|
|
16
16
|
else value.to_s
|
|
17
17
|
end
|
|
18
18
|
]
|
|
@@ -22,7 +22,7 @@ module WebMock
|
|
|
22
22
|
|
|
23
23
|
def self.sorted_headers_string(headers)
|
|
24
24
|
headers = WebMock::Util::Headers.normalize_headers(headers)
|
|
25
|
-
str = '{'
|
|
25
|
+
str = '{'.dup
|
|
26
26
|
str << headers.map do |k,v|
|
|
27
27
|
v = case v
|
|
28
28
|
when Regexp then v.inspect
|
|
@@ -34,6 +34,21 @@ module WebMock
|
|
|
34
34
|
str << '}'
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def self.pp_headers_string(headers)
|
|
38
|
+
headers = WebMock::Util::Headers.normalize_headers(headers)
|
|
39
|
+
seperator = "\n\t "
|
|
40
|
+
str = "{#{seperator} ".dup
|
|
41
|
+
str << headers.map do |k,v|
|
|
42
|
+
v = case v
|
|
43
|
+
when Regexp then v.inspect
|
|
44
|
+
when Array then "["+v.map{|w| "'#{w.to_s}'"}.join(", ")+"]"
|
|
45
|
+
else "'#{v.to_s}'"
|
|
46
|
+
end
|
|
47
|
+
"'#{k}'=>#{v}"
|
|
48
|
+
end.sort.join(",#{seperator} ")
|
|
49
|
+
str << "\n }"
|
|
50
|
+
end
|
|
51
|
+
|
|
37
52
|
def self.decode_userinfo_from_header(header)
|
|
38
53
|
header.sub(/^Basic /, "").unpack("m").first
|
|
39
54
|
end
|
data/lib/webmock/util/json.rb
CHANGED
|
@@ -24,13 +24,12 @@ module WebMock
|
|
|
24
24
|
|
|
25
25
|
# Ensure that ":" and "," are always followed by a space
|
|
26
26
|
def self.convert_json_to_yaml(json) #:nodoc:
|
|
27
|
-
scanner, quoting, marks,
|
|
27
|
+
scanner, quoting, marks, times = StringScanner.new(json), false, [], []
|
|
28
28
|
while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
|
|
29
29
|
case char = scanner[1]
|
|
30
30
|
when '"', "'"
|
|
31
31
|
if !quoting
|
|
32
32
|
quoting = char
|
|
33
|
-
pos = scanner.pos
|
|
34
33
|
elsif quoting == char
|
|
35
34
|
quoting = false
|
|
36
35
|
end
|
|
@@ -81,7 +81,7 @@ module WebMock::Util
|
|
|
81
81
|
value = if value.nil?
|
|
82
82
|
nil
|
|
83
83
|
else
|
|
84
|
-
::Addressable::URI.unencode_component(value.
|
|
84
|
+
::Addressable::URI.unencode_component(value.tr('+', ' '))
|
|
85
85
|
end
|
|
86
86
|
key = Addressable::URI.unencode_component(key)
|
|
87
87
|
key = key.dup.force_encoding(Encoding::ASCII_8BIT) if key.respond_to?(:force_encoding)
|
|
@@ -122,7 +122,7 @@ module WebMock::Util
|
|
|
122
122
|
|
|
123
123
|
def fill_accumulator_for_subscript(accumulator, key, value)
|
|
124
124
|
current_node = accumulator
|
|
125
|
-
subkeys = key.split(/(?=\[\
|
|
125
|
+
subkeys = key.split(/(?=\[[^\[\]]+)/)
|
|
126
126
|
subkeys[0..-2].each do |subkey|
|
|
127
127
|
node = subkey =~ /\[\]\z/ ? [] : {}
|
|
128
128
|
subkey = subkey.gsub(/[\[\]]/, '')
|
|
@@ -161,7 +161,7 @@ module WebMock::Util
|
|
|
161
161
|
else
|
|
162
162
|
if array_value
|
|
163
163
|
current_node[last_key] ||= []
|
|
164
|
-
current_node[last_key] << value
|
|
164
|
+
current_node[last_key] << value unless value.nil?
|
|
165
165
|
else
|
|
166
166
|
current_node[last_key] = value
|
|
167
167
|
end
|
|
@@ -186,7 +186,9 @@ module WebMock::Util
|
|
|
186
186
|
new_query_values = new_query_values.to_hash
|
|
187
187
|
new_query_values = new_query_values.inject([]) do |object, (key, value)|
|
|
188
188
|
key = key.to_s if key.is_a?(::Symbol) || key.nil?
|
|
189
|
-
if value.is_a?(Array)
|
|
189
|
+
if value.is_a?(Array) && value.empty?
|
|
190
|
+
object << [key.to_s + '[]']
|
|
191
|
+
elsif value.is_a?(Array)
|
|
190
192
|
value.each { |v| object << [key.to_s + '[]', v] }
|
|
191
193
|
elsif value.is_a?(Hash)
|
|
192
194
|
value.each { |k, v| object << ["#{key.to_s}[#{k}]", v]}
|
|
@@ -204,7 +206,7 @@ module WebMock::Util
|
|
|
204
206
|
end
|
|
205
207
|
end
|
|
206
208
|
|
|
207
|
-
buffer = ''
|
|
209
|
+
buffer = ''.dup
|
|
208
210
|
new_query_values.each do |parent, value|
|
|
209
211
|
encoded_parent = ::Addressable::URI.encode_component(
|
|
210
212
|
parent.dup, ::Addressable::URI::CharacterClasses::UNRESERVED
|
|
@@ -251,14 +253,14 @@ module WebMock::Util
|
|
|
251
253
|
]
|
|
252
254
|
end
|
|
253
255
|
value.sort!
|
|
254
|
-
buffer = ''
|
|
256
|
+
buffer = ''.dup
|
|
255
257
|
value.each do |key, val|
|
|
256
258
|
new_parent = options[:notation] != :flat_array ? "#{parent}[#{key}]" : parent
|
|
257
259
|
buffer << "#{to_query(new_parent, val, options)}&"
|
|
258
260
|
end
|
|
259
261
|
buffer.chop
|
|
260
262
|
when ::Array
|
|
261
|
-
buffer = ''
|
|
263
|
+
buffer = ''.dup
|
|
262
264
|
value.each_with_index do |val, i|
|
|
263
265
|
new_parent = options[:notation] != :flat_array ? "#{parent}[#{i}]" : parent
|
|
264
266
|
buffer << "#{to_query(new_parent, val, options)}&"
|