webmock 3.5.0 → 3.25.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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +476 -2
  3. data/README.md +166 -42
  4. data/lib/webmock/api.rb +2 -0
  5. data/lib/webmock/assertion_failure.rb +2 -0
  6. data/lib/webmock/callback_registry.rb +2 -0
  7. data/lib/webmock/config.rb +2 -0
  8. data/lib/webmock/cucumber.rb +2 -0
  9. data/lib/webmock/deprecation.rb +2 -0
  10. data/lib/webmock/errors.rb +2 -0
  11. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +228 -0
  12. data/lib/webmock/http_lib_adapters/curb_adapter.rb +14 -3
  13. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +22 -9
  14. data/lib/webmock/http_lib_adapters/excon_adapter.rb +5 -0
  15. data/lib/webmock/http_lib_adapters/http_lib_adapter.rb +2 -0
  16. data/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb +2 -0
  17. data/lib/webmock/http_lib_adapters/http_rb/client.rb +4 -1
  18. data/lib/webmock/http_lib_adapters/http_rb/request.rb +17 -5
  19. data/lib/webmock/http_lib_adapters/http_rb/response.rb +49 -5
  20. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +11 -3
  21. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +8 -2
  22. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +7 -5
  23. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +25 -7
  24. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +35 -15
  25. data/lib/webmock/http_lib_adapters/net_http.rb +59 -112
  26. data/lib/webmock/http_lib_adapters/net_http_response.rb +3 -1
  27. data/lib/webmock/http_lib_adapters/patron_adapter.rb +4 -2
  28. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +18 -2
  29. data/lib/webmock/matchers/any_arg_matcher.rb +2 -0
  30. data/lib/webmock/matchers/hash_argument_matcher.rb +2 -0
  31. data/lib/webmock/matchers/hash_excluding_matcher.rb +2 -0
  32. data/lib/webmock/matchers/hash_including_matcher.rb +2 -0
  33. data/lib/webmock/minitest.rb +2 -0
  34. data/lib/webmock/rack_response.rb +5 -1
  35. data/lib/webmock/request_body_diff.rb +3 -1
  36. data/lib/webmock/request_execution_verifier.rb +2 -0
  37. data/lib/webmock/request_pattern.rb +118 -61
  38. data/lib/webmock/request_registry.rb +2 -0
  39. data/lib/webmock/request_signature.rb +4 -2
  40. data/lib/webmock/request_signature_snippet.rb +2 -0
  41. data/lib/webmock/request_stub.rb +34 -0
  42. data/lib/webmock/response.rb +22 -14
  43. data/lib/webmock/responses_sequence.rb +2 -0
  44. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +2 -0
  45. data/lib/webmock/rspec/matchers/webmock_matcher.rb +2 -0
  46. data/lib/webmock/rspec/matchers.rb +2 -0
  47. data/lib/webmock/rspec.rb +4 -1
  48. data/lib/webmock/stub_registry.rb +28 -11
  49. data/lib/webmock/stub_request_snippet.rb +2 -0
  50. data/lib/webmock/test_unit.rb +3 -3
  51. data/lib/webmock/util/hash_counter.rb +12 -6
  52. data/lib/webmock/util/hash_keys_stringifier.rb +2 -0
  53. data/lib/webmock/util/hash_validator.rb +2 -0
  54. data/lib/webmock/util/headers.rb +23 -10
  55. data/lib/webmock/util/parsers/json.rb +72 -0
  56. data/lib/webmock/util/parsers/parse_error.rb +7 -0
  57. data/lib/webmock/util/parsers/xml.rb +16 -0
  58. data/lib/webmock/util/query_mapper.rb +6 -2
  59. data/lib/webmock/util/uri.rb +11 -9
  60. data/lib/webmock/util/values_stringifier.rb +2 -0
  61. data/lib/webmock/util/version_checker.rb +7 -5
  62. data/lib/webmock/version.rb +3 -1
  63. data/lib/webmock/webmock.rb +22 -3
  64. data/lib/webmock.rb +5 -2
  65. metadata +99 -175
  66. data/.gemtest +0 -0
  67. data/.gitignore +0 -34
  68. data/.rspec-tm +0 -2
  69. data/.travis.yml +0 -21
  70. data/Gemfile +0 -9
  71. data/Rakefile +0 -30
  72. data/lib/webmock/util/json.rb +0 -67
  73. data/minitest/test_helper.rb +0 -34
  74. data/minitest/test_webmock.rb +0 -9
  75. data/minitest/webmock_spec.rb +0 -60
  76. data/spec/acceptance/curb/curb_spec.rb +0 -481
  77. data/spec/acceptance/curb/curb_spec_helper.rb +0 -147
  78. data/spec/acceptance/em_http_request/em_http_request_spec.rb +0 -406
  79. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +0 -77
  80. data/spec/acceptance/excon/excon_spec.rb +0 -77
  81. data/spec/acceptance/excon/excon_spec_helper.rb +0 -50
  82. data/spec/acceptance/http_rb/http_rb_spec.rb +0 -82
  83. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +0 -54
  84. data/spec/acceptance/httpclient/httpclient_spec.rb +0 -217
  85. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +0 -57
  86. data/spec/acceptance/manticore/manticore_spec.rb +0 -56
  87. data/spec/acceptance/manticore/manticore_spec_helper.rb +0 -35
  88. data/spec/acceptance/net_http/net_http_shared.rb +0 -153
  89. data/spec/acceptance/net_http/net_http_spec.rb +0 -331
  90. data/spec/acceptance/net_http/net_http_spec_helper.rb +0 -64
  91. data/spec/acceptance/net_http/real_net_http_spec.rb +0 -20
  92. data/spec/acceptance/patron/patron_spec.rb +0 -125
  93. data/spec/acceptance/patron/patron_spec_helper.rb +0 -54
  94. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +0 -313
  95. data/spec/acceptance/shared/callbacks.rb +0 -147
  96. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +0 -36
  97. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +0 -95
  98. data/spec/acceptance/shared/precedence_of_stubs.rb +0 -15
  99. data/spec/acceptance/shared/request_expectations.rb +0 -923
  100. data/spec/acceptance/shared/returning_declared_responses.rb +0 -388
  101. data/spec/acceptance/shared/stubbing_requests.rb +0 -638
  102. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +0 -135
  103. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +0 -60
  104. data/spec/acceptance/webmock_shared.rb +0 -41
  105. data/spec/fixtures/test.txt +0 -1
  106. data/spec/quality_spec.rb +0 -84
  107. data/spec/spec_helper.rb +0 -48
  108. data/spec/support/example_curl_output.txt +0 -22
  109. data/spec/support/failures.rb +0 -9
  110. data/spec/support/my_rack_app.rb +0 -53
  111. data/spec/support/network_connection.rb +0 -19
  112. data/spec/support/webmock_server.rb +0 -69
  113. data/spec/unit/api_spec.rb +0 -175
  114. data/spec/unit/errors_spec.rb +0 -129
  115. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +0 -17
  116. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +0 -12
  117. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +0 -61
  118. data/spec/unit/matchers/hash_including_matcher_spec.rb +0 -87
  119. data/spec/unit/rack_response_spec.rb +0 -112
  120. data/spec/unit/request_body_diff_spec.rb +0 -90
  121. data/spec/unit/request_execution_verifier_spec.rb +0 -208
  122. data/spec/unit/request_pattern_spec.rb +0 -596
  123. data/spec/unit/request_registry_spec.rb +0 -95
  124. data/spec/unit/request_signature_snippet_spec.rb +0 -89
  125. data/spec/unit/request_signature_spec.rb +0 -155
  126. data/spec/unit/request_stub_spec.rb +0 -199
  127. data/spec/unit/response_spec.rb +0 -282
  128. data/spec/unit/stub_registry_spec.rb +0 -103
  129. data/spec/unit/stub_request_snippet_spec.rb +0 -115
  130. data/spec/unit/util/hash_counter_spec.rb +0 -39
  131. data/spec/unit/util/hash_keys_stringifier_spec.rb +0 -27
  132. data/spec/unit/util/headers_spec.rb +0 -28
  133. data/spec/unit/util/json_spec.rb +0 -33
  134. data/spec/unit/util/query_mapper_spec.rb +0 -150
  135. data/spec/unit/util/uri_spec.rb +0 -299
  136. data/spec/unit/util/version_checker_spec.rb +0 -65
  137. data/spec/unit/webmock_spec.rb +0 -11
  138. data/test/http_request.rb +0 -24
  139. data/test/shared_test.rb +0 -108
  140. data/test/test_helper.rb +0 -23
  141. data/test/test_webmock.rb +0 -6
  142. data/webmock.gemspec +0 -46
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
 
3
5
  module RSpecMatcherDetector
@@ -24,7 +26,7 @@ module WebMock
24
26
  end
25
27
 
26
28
  def with(options = {}, &block)
27
- raise ArgumentError.new('#with method invoked with no arguments. Either options hash or block must be specified.') if options.empty? && !block_given?
29
+ 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?
28
30
  assign_options(options)
29
31
  @with_block = block
30
32
  self
@@ -80,11 +82,16 @@ module WebMock
80
82
  URIRegexpPattern.new(uri)
81
83
  elsif uri.is_a?(Addressable::Template)
82
84
  URIAddressablePattern.new(uri)
83
- else
85
+ elsif uri.respond_to?(:call)
86
+ URICallablePattern.new(uri)
87
+ elsif uri.is_a?(::URI::Generic)
88
+ URIStringPattern.new(uri.to_s)
89
+ elsif uri.is_a?(String)
84
90
  URIStringPattern.new(uri)
91
+ else
92
+ raise ArgumentError.new("URI should be a String, Regexp, Addressable::Template or a callable object. Got: #{uri.class}")
85
93
  end
86
94
  end
87
-
88
95
  end
89
96
 
90
97
 
@@ -107,11 +114,13 @@ module WebMock
107
114
  include RSpecMatcherDetector
108
115
 
109
116
  def initialize(pattern)
110
- @pattern = case pattern
111
- when Addressable::URI, Addressable::Template
117
+ @pattern = if pattern.is_a?(Addressable::URI) \
118
+ || pattern.is_a?(Addressable::Template)
119
+ pattern
120
+ elsif pattern.respond_to?(:call)
112
121
  pattern
113
122
  else
114
- WebMock::Util::URI.normalize_uri(pattern)
123
+ WebMock::Util::URI.normalize_uri(pattern)
115
124
  end
116
125
  @query_params = nil
117
126
  end
@@ -131,38 +140,44 @@ module WebMock
131
140
  end
132
141
  end
133
142
 
143
+ def matches?(uri)
144
+ pattern_matches?(uri) && query_params_matches?(uri)
145
+ end
146
+
134
147
  def to_s
135
- str = @pattern.inspect
148
+ str = pattern_inspect
136
149
  str += " with query params #{@query_params.inspect}" if @query_params
137
150
  str
138
151
  end
139
- end
140
152
 
141
- class URIRegexpPattern < URIPattern
142
- def matches?(uri)
143
- WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| u.match(@pattern) } &&
144
- (@query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query, notation: Config.instance.query_values_notation))
153
+ private
154
+
155
+ def pattern_inspect
156
+ @pattern.inspect
145
157
  end
146
158
 
147
- def to_s
148
- str = @pattern.inspect
149
- str += " with query params #{@query_params.inspect}" if @query_params
150
- str
159
+ def query_params_matches?(uri)
160
+ @query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query, notation: Config.instance.query_values_notation)
151
161
  end
152
162
  end
153
163
 
154
- class URIAddressablePattern < URIPattern
155
- def matches?(uri)
156
- if @query_params.nil?
157
- # Let Addressable check the whole URI
158
- matches_with_variations?(uri)
159
- else
160
- # WebMock checks the query, Addressable checks everything else
161
- matches_with_variations?(uri.omit(:query)) &&
162
- @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query)
163
- end
164
+ class URICallablePattern < URIPattern
165
+ private
166
+
167
+ def pattern_matches?(uri)
168
+ @pattern.call(uri)
164
169
  end
170
+ end
165
171
 
172
+ class URIRegexpPattern < URIPattern
173
+ private
174
+
175
+ def pattern_matches?(uri)
176
+ WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| u.match(@pattern) }
177
+ end
178
+ end
179
+
180
+ class URIAddressablePattern < URIPattern
166
181
  def add_query_params(query_params)
167
182
  @@add_query_params_warned ||= false
168
183
  if not @@add_query_params_warned
@@ -172,28 +187,57 @@ module WebMock
172
187
  super(query_params)
173
188
  end
174
189
 
175
- def to_s
176
- str = @pattern.pattern.inspect
177
- str += " with variables #{@pattern.variables.inspect}" if @pattern.variables
178
- str
190
+ private
191
+
192
+ def pattern_matches?(uri)
193
+ if @query_params.nil?
194
+ # Let Addressable check the whole URI
195
+ matches_with_variations?(uri)
196
+ else
197
+ # WebMock checks the query, Addressable checks everything else
198
+ matches_with_variations?(uri.omit(:query))
199
+ end
179
200
  end
180
201
 
181
- private
202
+ def pattern_inspect
203
+ @pattern.pattern.inspect
204
+ end
182
205
 
183
206
  def matches_with_variations?(uri)
184
- normalized_template = Addressable::Template.new(WebMock::Util::URI.heuristic_parse(@pattern.pattern))
207
+ template =
208
+ begin
209
+ Addressable::Template.new(WebMock::Util::URI.heuristic_parse(@pattern.pattern))
210
+ rescue Addressable::URI::InvalidURIError
211
+ Addressable::Template.new(@pattern.pattern)
212
+ end
213
+ WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u|
214
+ template_matches_uri?(template, u)
215
+ }
216
+ end
185
217
 
186
- WebMock::Util::URI.variations_of_uri_as_strings(uri, only_with_scheme: true)
187
- .any? { |u| normalized_template.match(u) }
218
+ def template_matches_uri?(template, uri)
219
+ template.match(uri)
220
+ rescue Addressable::URI::InvalidURIError
221
+ false
188
222
  end
189
223
  end
190
224
 
191
225
  class URIStringPattern < URIPattern
192
- def matches?(uri)
226
+ def add_query_params(query_params)
227
+ super
228
+ if @query_params.is_a?(Hash) || @query_params.is_a?(String)
229
+ query_hash = (WebMock::Util::QueryMapper.query_to_values(@pattern.query, notation: Config.instance.query_values_notation) || {}).merge(@query_params)
230
+ @pattern.query = WebMock::Util::QueryMapper.values_to_query(query_hash, notation: WebMock::Config.instance.query_values_notation)
231
+ @query_params = nil
232
+ end
233
+ end
234
+
235
+ private
236
+
237
+ def pattern_matches?(uri)
193
238
  if @pattern.is_a?(Addressable::URI)
194
239
  if @query_params
195
- uri.omit(:query) === @pattern &&
196
- (@query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query, notation: Config.instance.query_values_notation))
240
+ uri.omit(:query) === @pattern
197
241
  else
198
242
  uri === @pattern
199
243
  end
@@ -202,19 +246,8 @@ module WebMock
202
246
  end
203
247
  end
204
248
 
205
- def add_query_params(query_params)
206
- super
207
- if @query_params.is_a?(Hash) || @query_params.is_a?(String)
208
- query_hash = (WebMock::Util::QueryMapper.query_to_values(@pattern.query, notation: Config.instance.query_values_notation) || {}).merge(@query_params)
209
- @pattern.query = WebMock::Util::QueryMapper.values_to_query(query_hash, notation: WebMock::Config.instance.query_values_notation)
210
- @query_params = nil
211
- end
212
- end
213
-
214
- def to_s
215
- str = WebMock::Util::URI.strip_default_port_from_uri_string(@pattern.to_s)
216
- str += " with query params #{@query_params.inspect}" if @query_params
217
- str
249
+ def pattern_inspect
250
+ WebMock::Util::URI.strip_default_port_from_uri_string(@pattern.to_s)
218
251
  end
219
252
  end
220
253
 
@@ -253,7 +286,9 @@ module WebMock
253
286
  if (@pattern).is_a?(Hash)
254
287
  return true if @pattern.empty?
255
288
  matching_body_hashes?(body_as_hash(body, content_type), @pattern, content_type)
256
- elsif (@pattern).is_a?(WebMock::Matchers::HashIncludingMatcher)
289
+ elsif (@pattern).is_a?(Array)
290
+ matching_body_array?(body_as_hash(body, content_type), @pattern, content_type)
291
+ elsif (@pattern).is_a?(WebMock::Matchers::HashArgumentMatcher)
257
292
  @pattern == body_as_hash(body, content_type)
258
293
  else
259
294
  empty_string?(@pattern) && empty_string?(body) ||
@@ -267,15 +302,23 @@ module WebMock
267
302
  end
268
303
 
269
304
  private
305
+
270
306
  def body_as_hash(body, content_type)
271
- case BODY_FORMATS[content_type]
307
+ case body_format(content_type)
272
308
  when :json then
273
- WebMock::Util::JSON.parse(body)
309
+ WebMock::Util::Parsers::JSON.parse(body)
274
310
  when :xml then
275
- Crack::XML.parse(body)
311
+ WebMock::Util::Parsers::XML.parse(body)
276
312
  else
277
313
  WebMock::Util::QueryMapper.query_to_values(body, notation: Config.instance.query_values_notation)
278
314
  end
315
+ rescue WebMock::Util::Parsers::ParseError
316
+ nil
317
+ end
318
+
319
+ def body_format(content_type)
320
+ normalized_content_type = content_type.sub(/\A(application\/)[a-zA-Z0-9.-]+\+(json|xml)\Z/,'\1\2')
321
+ BODY_FORMATS[normalized_content_type]
279
322
  end
280
323
 
281
324
  def assert_non_multipart_body(content_type)
@@ -310,19 +353,33 @@ module WebMock
310
353
  def matching_body_hashes?(query_parameters, pattern, content_type)
311
354
  return false unless query_parameters.is_a?(Hash)
312
355
  return false unless query_parameters.keys.sort == pattern.keys.sort
313
- query_parameters.each do |key, actual|
356
+
357
+ query_parameters.all? do |key, actual|
314
358
  expected = pattern[key]
359
+ matching_values(actual, expected, content_type)
360
+ end
361
+ end
315
362
 
316
- if actual.is_a?(Hash) && expected.is_a?(Hash)
317
- return false unless matching_body_hashes?(actual, expected, content_type)
318
- else
319
- expected = WebMock::Util::ValuesStringifier.stringify_values(expected) if url_encoded_body?(content_type)
320
- return false unless expected === actual
321
- end
363
+ def matching_body_array?(query_parameters, pattern, content_type)
364
+ return false unless query_parameters.is_a?(Array)
365
+ return false unless query_parameters.length == pattern.length
366
+
367
+ query_parameters.each_with_index do |actual, index|
368
+ expected = pattern[index]
369
+ return false unless matching_values(actual, expected, content_type)
322
370
  end
371
+
323
372
  true
324
373
  end
325
374
 
375
+ def matching_values(actual, expected, content_type)
376
+ return matching_body_hashes?(actual, expected, content_type) if actual.is_a?(Hash) && expected.is_a?(Hash)
377
+ return matching_body_array?(actual, expected, content_type) if actual.is_a?(Array) && expected.is_a?(Array)
378
+
379
+ expected = WebMock::Util::ValuesStringifier.stringify_values(expected) if url_encoded_body?(content_type)
380
+ expected === actual
381
+ end
382
+
326
383
  def empty_string?(string)
327
384
  string.nil? || string == ""
328
385
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
 
3
5
  class RequestRegistry
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
 
3
5
  class RequestSignature
@@ -35,11 +37,11 @@ module WebMock
35
37
  alias == eql?
36
38
 
37
39
  def url_encoded?
38
- !!(headers && headers['Content-Type'] == 'application/x-www-form-urlencoded')
40
+ !!(headers&.fetch('Content-Type', nil)&.start_with?('application/x-www-form-urlencoded'))
39
41
  end
40
42
 
41
43
  def json_headers?
42
- !!(headers && headers['Content-Type'] == 'application/json')
44
+ !!(headers&.fetch('Content-Type', nil)&.start_with?('application/json'))
43
45
  end
44
46
 
45
47
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "pp"
2
4
 
3
5
  module WebMock
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  class RequestStub
3
5
 
@@ -24,6 +26,38 @@ module WebMock
24
26
  end
25
27
  alias_method :and_return, :to_return
26
28
 
29
+ def to_return_json(*response_hashes)
30
+ raise ArgumentError, '#to_return_json does not support passing a block' if block_given?
31
+
32
+ json_response_hashes = [*response_hashes].flatten.map do |resp_h|
33
+ headers, body = resp_h.values_at(:headers, :body)
34
+
35
+ json_body = if body.respond_to?(:call)
36
+ ->(request_signature) {
37
+ b = if body.respond_to?(:arity) && body.arity == 1
38
+ body.call(request_signature)
39
+ else
40
+ body.call
41
+ end
42
+ b = b.to_json unless b.is_a?(String)
43
+ b
44
+ }
45
+ elsif !body.is_a?(String)
46
+ body.to_json
47
+ else
48
+ body
49
+ end
50
+
51
+ resp_h.merge(
52
+ headers: {content_type: 'application/json'}.merge(headers.to_h),
53
+ body: json_body
54
+ )
55
+ end
56
+
57
+ to_return(json_response_hashes)
58
+ end
59
+ alias_method :and_return_json, :to_return_json
60
+
27
61
  def to_rack(app, options={})
28
62
  @responses_sequences << ResponsesSequence.new([RackResponse.new(app)])
29
63
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "pathname"
2
4
 
3
5
  module WebMock
@@ -14,8 +16,11 @@ module WebMock
14
16
 
15
17
  class Response
16
18
  def initialize(options = {})
17
- if options.is_a?(IO) || options.is_a?(String)
19
+ case options
20
+ when IO, StringIO
18
21
  self.options = read_raw_response(options)
22
+ when String
23
+ self.options = read_raw_response(StringIO.new(options))
19
24
  else
20
25
  self.options = options
21
26
  end
@@ -33,7 +38,7 @@ module WebMock
33
38
  end
34
39
 
35
40
  def body
36
- @body || ''
41
+ @body || String.new("")
37
42
  end
38
43
 
39
44
  def body=(body)
@@ -91,10 +96,10 @@ module WebMock
91
96
 
92
97
  def ==(other)
93
98
  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
99
+ self.headers === other.headers &&
100
+ self.status == other.status &&
101
+ self.exception == other.exception &&
102
+ self.should_timeout == other.should_timeout
98
103
  end
99
104
 
100
105
  private
@@ -111,16 +116,17 @@ module WebMock
111
116
  valid_types = [Proc, IO, Pathname, String, Array]
112
117
  return if @body.nil?
113
118
  return if valid_types.any? { |c| @body.is_a?(c) }
114
- raise InvalidBody, "must be one of: #{valid_types}. '#{@body.class}' given"
115
- end
116
119
 
117
- def read_raw_response(raw_response)
118
- if raw_response.is_a?(IO)
119
- string = raw_response.read
120
- raw_response.close
121
- raw_response = string
120
+ if @body.is_a?(Hash)
121
+ raise InvalidBody, "must be one of: #{valid_types}, but you've used a #{@body.class}. " \
122
+ "Please convert it by calling .to_json .to_xml, or otherwise convert it to a string."
123
+ else
124
+ raise InvalidBody, "must be one of: #{valid_types}. '#{@body.class}' given."
122
125
  end
123
- socket = ::Net::BufferedIO.new(raw_response)
126
+ end
127
+
128
+ def read_raw_response(io)
129
+ socket = ::Net::BufferedIO.new(io)
124
130
  response = ::Net::HTTPResponse.read_new(socket)
125
131
  transfer_encoding = response.delete('transfer-encoding') #chunks were already read by curl
126
132
  response.reading_body(socket, true) {}
@@ -132,6 +138,8 @@ module WebMock
132
138
  options[:body] = response.read_body
133
139
  options[:status] = [response.code.to_i, response.message]
134
140
  options
141
+ ensure
142
+ socket.close
135
143
  end
136
144
 
137
145
  InvalidBody = Class.new(StandardError)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
 
3
5
  class ResponsesSequence
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  class RequestPatternMatcher
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  class WebMockMatcher
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'webmock'
2
4
  require 'webmock/rspec/matchers/request_pattern_matcher'
3
5
  require 'webmock/rspec/matchers/webmock_matcher'
data/lib/webmock/rspec.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'webmock'
2
4
 
3
5
  # RSpec 1.x and 2.x compatibility
@@ -33,7 +35,8 @@ RSPEC_CONFIGURER.configure { |config|
33
35
  WebMock.disable!
34
36
  end
35
37
 
36
- config.after(:each) do
38
+ config.around(:each) do |example|
39
+ example.run
37
40
  WebMock.reset!
38
41
  end
39
42
  }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
 
3
5
  class StubRegistry
@@ -10,25 +12,39 @@ module WebMock
10
12
  end
11
13
 
12
14
  def global_stubs
13
- @global_stubs ||= []
15
+ @global_stubs ||= Hash.new { |h, k| h[k] = [] }
14
16
  end
15
17
 
16
18
  def reset!
17
19
  self.request_stubs = []
18
20
  end
19
21
 
20
- def register_global_stub(&block)
22
+ def register_global_stub(order = :before_local_stubs, &block)
23
+ unless %i[before_local_stubs after_local_stubs].include?(order)
24
+ raise ArgumentError.new("Wrong order. Use :before_local_stubs or :after_local_stubs")
25
+ end
26
+
21
27
  # This hash contains the responses returned by the block,
22
28
  # keyed by the exact request (using the object_id).
23
29
  # That way, there's no race condition in case #to_return
24
30
  # doesn't run immediately after stub.with.
25
31
  responses = {}
26
-
27
- stub = ::WebMock::RequestStub.new(:any, /.*/).with { |request|
28
- responses[request.object_id] = yield(request)
29
- }.to_return(lambda { |request| responses.delete(request.object_id) })
30
-
31
- global_stubs.push stub
32
+ response_lock = Mutex.new
33
+
34
+ stub = ::WebMock::RequestStub.new(:any, ->(uri) { true }).with { |request|
35
+ update_response = -> { responses[request.object_id] = yield(request) }
36
+
37
+ # The block can recurse, so only lock if we don't already own it
38
+ if response_lock.owned?
39
+ update_response.call
40
+ else
41
+ response_lock.synchronize(&update_response)
42
+ end
43
+ }.to_return(lambda { |request|
44
+ response_lock.synchronize { responses.delete(request.object_id) }
45
+ })
46
+
47
+ global_stubs[order].push stub
32
48
  end
33
49
 
34
50
  def register_request_stub(stub)
@@ -54,9 +70,10 @@ module WebMock
54
70
  private
55
71
 
56
72
  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
- }
73
+ (global_stubs[:before_local_stubs] + request_stubs + global_stubs[:after_local_stubs])
74
+ .detect { |registered_request_stub|
75
+ registered_request_stub.request_pattern.matches?(request_signature)
76
+ }
60
77
  end
61
78
 
62
79
  def evaluate_response_for_request(response, request_signature)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  class StubRequestSnippet
3
5
  def initialize(request_stub)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test/unit'
2
4
  require 'webmock'
3
5
 
@@ -8,12 +10,10 @@ module Test
8
10
  class TestCase
9
11
  include WebMock::API
10
12
 
11
- alias_method :teardown_without_webmock, :teardown
13
+ teardown
12
14
  def teardown_with_webmock
13
- teardown_without_webmock
14
15
  WebMock.reset!
15
16
  end
16
- alias_method :teardown, :teardown_with_webmock
17
17
 
18
18
  end
19
19
  end
@@ -1,29 +1,35 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thread'
2
4
 
3
5
  module WebMock
4
6
  module Util
5
7
  class HashCounter
6
8
  attr_accessor :hash
9
+
7
10
  def initialize
8
- self.hash = {}
11
+ self.hash = Hash.new(0)
9
12
  @order = {}
10
13
  @max = 0
11
14
  @lock = ::Mutex.new
12
15
  end
13
- def put key, num=1
16
+
17
+ def put(key, num=1)
14
18
  @lock.synchronize do
15
- hash[key] = (hash[key] || 0) + num
16
- @order[key] = @max = @max + 1
19
+ hash[key] += num
20
+ @order[key] = @max += 1
17
21
  end
18
22
  end
19
- def get key
23
+
24
+ def get(key)
20
25
  @lock.synchronize do
21
- hash[key] || 0
26
+ hash[key]
22
27
  end
23
28
  end
24
29
 
25
30
  def select(&block)
26
31
  return unless block_given?
32
+
27
33
  @lock.synchronize do
28
34
  hash.select(&block)
29
35
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  module Util
3
5
  class HashKeysStringifier
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  class HashValidator
3
5
  def initialize(hash)