webmock 3.0.0 → 3.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/CI.yml +37 -0
  3. data/CHANGELOG.md +416 -0
  4. data/Gemfile +1 -1
  5. data/README.md +157 -31
  6. data/Rakefile +12 -4
  7. data/lib/webmock/api.rb +12 -0
  8. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
  9. data/lib/webmock/http_lib_adapters/curb_adapter.rb +17 -3
  10. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +7 -4
  11. data/lib/webmock/http_lib_adapters/excon_adapter.rb +5 -2
  12. data/lib/webmock/http_lib_adapters/http_rb/client.rb +4 -1
  13. data/lib/webmock/http_lib_adapters/http_rb/request.rb +7 -1
  14. data/lib/webmock/http_lib_adapters/http_rb/response.rb +24 -3
  15. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +6 -2
  16. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +2 -2
  17. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +28 -9
  18. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +33 -15
  19. data/lib/webmock/http_lib_adapters/net_http.rb +54 -14
  20. data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
  21. data/lib/webmock/http_lib_adapters/patron_adapter.rb +4 -4
  22. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  23. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  24. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  25. data/lib/webmock/matchers/hash_including_matcher.rb +4 -23
  26. data/lib/webmock/rack_response.rb +1 -1
  27. data/lib/webmock/request_body_diff.rb +1 -1
  28. data/lib/webmock/request_execution_verifier.rb +2 -3
  29. data/lib/webmock/request_pattern.rb +108 -46
  30. data/lib/webmock/request_registry.rb +1 -1
  31. data/lib/webmock/request_signature.rb +1 -1
  32. data/lib/webmock/request_signature_snippet.rb +4 -4
  33. data/lib/webmock/response.rb +11 -5
  34. data/lib/webmock/rspec.rb +10 -3
  35. data/lib/webmock/stub_registry.rb +26 -11
  36. data/lib/webmock/stub_request_snippet.rb +10 -6
  37. data/lib/webmock/test_unit.rb +1 -3
  38. data/lib/webmock/util/hash_counter.rb +4 -4
  39. data/lib/webmock/util/headers.rb +17 -2
  40. data/lib/webmock/util/json.rb +1 -2
  41. data/lib/webmock/util/query_mapper.rb +9 -7
  42. data/lib/webmock/util/uri.rb +10 -10
  43. data/lib/webmock/util/values_stringifier.rb +20 -0
  44. data/lib/webmock/version.rb +1 -1
  45. data/lib/webmock/webmock.rb +10 -3
  46. data/lib/webmock.rb +53 -48
  47. data/minitest/webmock_spec.rb +2 -2
  48. data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
  49. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  50. data/spec/acceptance/curb/curb_spec.rb +33 -0
  51. data/spec/acceptance/em_http_request/em_http_request_spec.rb +56 -0
  52. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
  53. data/spec/acceptance/excon/excon_spec.rb +4 -2
  54. data/spec/acceptance/excon/excon_spec_helper.rb +2 -0
  55. data/spec/acceptance/http_rb/http_rb_spec.rb +20 -0
  56. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +5 -2
  57. data/spec/acceptance/httpclient/httpclient_spec.rb +8 -1
  58. data/spec/acceptance/manticore/manticore_spec.rb +51 -0
  59. data/spec/acceptance/net_http/net_http_shared.rb +1 -1
  60. data/spec/acceptance/net_http/net_http_spec.rb +53 -1
  61. data/spec/acceptance/patron/patron_spec.rb +7 -0
  62. data/spec/acceptance/patron/patron_spec_helper.rb +3 -3
  63. data/spec/acceptance/shared/callbacks.rb +3 -2
  64. data/spec/acceptance/shared/request_expectations.rb +14 -0
  65. data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
  66. data/spec/acceptance/shared/stubbing_requests.rb +95 -0
  67. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +1 -1
  68. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +1 -1
  69. data/spec/support/webmock_server.rb +1 -0
  70. data/spec/unit/api_spec.rb +103 -3
  71. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  72. data/spec/unit/request_execution_verifier_spec.rb +12 -12
  73. data/spec/unit/request_pattern_spec.rb +195 -49
  74. data/spec/unit/request_signature_snippet_spec.rb +2 -2
  75. data/spec/unit/response_spec.rb +22 -18
  76. data/spec/unit/stub_request_snippet_spec.rb +30 -10
  77. data/spec/unit/util/query_mapper_spec.rb +13 -0
  78. data/spec/unit/util/uri_spec.rb +74 -2
  79. data/spec/unit/webmock_spec.rb +54 -5
  80. data/test/shared_test.rb +15 -2
  81. data/test/test_webmock.rb +6 -0
  82. data/webmock.gemspec +11 -3
  83. metadata +66 -17
  84. 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 = case pattern
107
- when Addressable::URI, Addressable::Template
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
- WebMock::Util::URI.normalize_uri(pattern)
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 = @pattern.inspect
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 URIRegexpPattern < URIPattern
135
- def matches?(uri)
136
- WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| u.match(@pattern) } &&
137
- (@query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query, notation: Config.instance.query_values_notation))
159
+ class URICallablePattern < URIPattern
160
+ private
161
+
162
+ def pattern_matches?(uri)
163
+ @pattern.call(uri)
138
164
  end
165
+ end
139
166
 
140
- def to_s
141
- str = @pattern.inspect
142
- str += " with query params #{@query_params.inspect}" if @query_params
143
- str
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 matches?(uri)
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
- WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| @pattern.match(u) }
190
+ matches_with_variations?(uri)
152
191
  else
153
192
  # WebMock checks the query, Addressable checks everything else
154
- WebMock::Util::URI.variations_of_uri_as_strings(uri.omit(:query)).any? { |u| @pattern.match(u) } &&
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 add_query_params(query_params)
160
- warn "WebMock warning: ignoring query params in RFC 6570 template and checking them with WebMock"
161
- super(query_params)
197
+ def pattern_inspect
198
+ @pattern.pattern.inspect
162
199
  end
163
200
 
164
- def to_s
165
- str = @pattern.pattern.inspect
166
- str += " with variables #{@pattern.variables.inspect}" if @pattern.variables
167
- str
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 matches?(uri)
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 add_query_params(query_params)
186
- super
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
- matching_hashes?(body_as_hash(body, content_type), @pattern)
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 BODY_FORMATS[content_type]
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 matching_hashes?(query_parameters, pattern)
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 matching_hashes?(actual, expected)
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
- text = "You can stub this request with the following snippet:\n\n"
17
- text << WebMock::StubRequestSnippet.new(request_stub).to_s
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
@@ -91,10 +91,10 @@ module WebMock
91
91
 
92
92
  def ==(other)
93
93
  self.body == other.body &&
94
- self.headers === other.headers &&
95
- self.status == other.status &&
96
- self.exception == other.exception &&
97
- self.should_timeout == other.should_timeout
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
- raise InvalidBody, "must be one of: #{valid_types}. '#{@body.class}' given"
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.after(:each) do
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
- stub = ::WebMock::RequestStub.new(:any, /.*/).with { |request|
28
- responses[request.object_id] = block.call(request)
29
- }.to_return(lambda { |request| responses.delete(request.object_id) })
30
-
31
- global_stubs.push stub
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).detect { |registered_request_stub|
58
- registered_request_stub.request_pattern.matches?(request_signature)
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 << ",\n " unless with.empty?
23
+ with << "," unless with.empty?
24
24
 
25
- with << "headers: #{request_pattern.headers_pattern.to_s}"
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
- string << ".\n to_return(status: 200, body: \"\", headers: {})"
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
@@ -8,12 +8,10 @@ module Test
8
8
  class TestCase
9
9
  include WebMock::API
10
10
 
11
- alias_method :teardown_without_webmock, :teardown
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 Util::HashCounter
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 &block
28
+ hash.select(&block)
29
29
  end
30
30
  end
31
31
 
32
32
  def each(&block)
33
- @order.to_a.sort {|a, b| a[1] <=> b[1]}.each do |a|
34
- block.call(a[0], hash[a[0]])
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
@@ -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
@@ -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, pos, times = StringScanner.new(json), false, [], nil, []
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.gsub(/\+/, ' '))
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(/(?=\[\w)/)
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)}&"