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.
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)}&"