webmock 3.5.1 → 3.7.2

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -7
  3. data/CHANGELOG.md +65 -0
  4. data/README.md +25 -1
  5. data/Rakefile +0 -2
  6. data/lib/webmock.rb +1 -0
  7. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +214 -0
  8. data/lib/webmock/http_lib_adapters/curb_adapter.rb +6 -1
  9. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +1 -1
  10. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +9 -6
  11. data/lib/webmock/http_lib_adapters/net_http.rb +3 -2
  12. data/lib/webmock/http_lib_adapters/patron_adapter.rb +1 -1
  13. data/lib/webmock/request_body_diff.rb +1 -1
  14. data/lib/webmock/request_pattern.rb +2 -3
  15. data/lib/webmock/util/query_mapper.rb +4 -2
  16. data/lib/webmock/util/uri.rb +8 -8
  17. data/lib/webmock/version.rb +1 -1
  18. data/lib/webmock/webmock.rb +5 -0
  19. data/spec/acceptance/async_http_client/async_http_client_spec.rb +349 -0
  20. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  21. data/spec/acceptance/curb/curb_spec.rb +11 -0
  22. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
  23. data/spec/acceptance/shared/callbacks.rb +2 -1
  24. data/spec/acceptance/shared/request_expectations.rb +7 -0
  25. data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
  26. data/spec/acceptance/shared/stubbing_requests.rb +5 -0
  27. data/spec/support/webmock_server.rb +1 -0
  28. data/spec/unit/request_pattern_spec.rb +6 -1
  29. data/spec/unit/util/query_mapper_spec.rb +7 -0
  30. data/spec/unit/util/uri_spec.rb +74 -2
  31. data/spec/unit/webmock_spec.rb +8 -0
  32. data/webmock.gemspec +2 -3
  33. metadata +30 -6
@@ -229,14 +229,15 @@ class PatchedStringIO < StringIO #:nodoc:
229
229
  alias_method :orig_read_nonblock, :read_nonblock
230
230
 
231
231
  def read_nonblock(size, *args)
232
- orig_read_nonblock(size)
232
+ args.reject! {|arg| !arg.is_a?(Hash)}
233
+ orig_read_nonblock(size, *args)
233
234
  end
234
235
 
235
236
  end
236
237
 
237
238
  class StubSocket #:nodoc:
238
239
 
239
- attr_accessor :read_timeout, :continue_timeout
240
+ attr_accessor :read_timeout, :continue_timeout, :write_timeout
240
241
 
241
242
  def initialize(*args)
242
243
  end
@@ -118,7 +118,7 @@ if defined?(::Patron)
118
118
  def self.build_webmock_response(patron_response)
119
119
  webmock_response = WebMock::Response.new
120
120
  reason = patron_response.status_line.
121
- scan(%r(\AHTTP/(\d+\.\d+)\s+(\d\d\d)\s*([^\r\n]+)?))[0][2]
121
+ scan(%r(\AHTTP/(\d+(?:\.\d+)?)\s+(\d\d\d)\s*([^\r\n]+)?))[0][2]
122
122
  webmock_response.status = [patron_response.status, reason]
123
123
  webmock_response.body = patron_response.body
124
124
  webmock_response.headers = patron_response.headers
@@ -12,7 +12,7 @@ module WebMock
12
12
  def body_diff
13
13
  return {} unless request_signature_diffable? && request_stub_diffable?
14
14
 
15
- HashDiff.diff(request_signature_body_hash, request_stub_body_hash)
15
+ Hashdiff.diff(request_signature_body_hash, request_stub_body_hash)
16
16
  end
17
17
 
18
18
  attr_reader :request_signature, :request_stub
@@ -24,7 +24,7 @@ module WebMock
24
24
  end
25
25
 
26
26
  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?
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?
28
28
  assign_options(options)
29
29
  @with_block = block
30
30
  self
@@ -183,8 +183,7 @@ module WebMock
183
183
  def matches_with_variations?(uri)
184
184
  normalized_template = Addressable::Template.new(WebMock::Util::URI.heuristic_parse(@pattern.pattern))
185
185
 
186
- WebMock::Util::URI.variations_of_uri_as_strings(uri, only_with_scheme: true)
187
- .any? { |u| normalized_template.match(u) }
186
+ WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| normalized_template.match(u) }
188
187
  end
189
188
  end
190
189
 
@@ -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]}
@@ -41,12 +41,12 @@ module WebMock
41
41
  uris = uris_with_trailing_slash_and_without(uris)
42
42
  end
43
43
 
44
- uris = uris_encoded_and_unencoded(uris)
45
-
46
44
  if normalized_uri.port == Addressable::URI.port_mapping[normalized_uri.scheme]
47
45
  uris = uris_with_inferred_port_and_without(uris)
48
46
  end
49
47
 
48
+ uris = uris_encoded_and_unencoded(uris)
49
+
50
50
  if normalized_uri.scheme == "http" && !only_with_scheme
51
51
  uris = uris_with_scheme_and_without(uris)
52
52
  end
@@ -80,27 +80,27 @@ module WebMock
80
80
 
81
81
  def self.uris_with_inferred_port_and_without(uris)
82
82
  uris.map { |uri|
83
- uri = uri.dup.force_encoding(Encoding::ASCII_8BIT) if uri.respond_to?(:force_encoding)
84
- [ uri, uri.gsub(%r{(:80)|(:443)}, "").freeze ]
83
+ [ uri, uri.omit(:port)]
85
84
  }.flatten
86
85
  end
87
86
 
88
87
  def self.uris_encoded_and_unencoded(uris)
89
88
  uris.map do |uri|
90
- [ uri.to_s, Addressable::URI.unencode(uri, String).freeze ]
89
+ [
90
+ uri.to_s.force_encoding(Encoding::ASCII_8BIT),
91
+ Addressable::URI.unencode(uri, String).force_encoding(Encoding::ASCII_8BIT).freeze
92
+ ]
91
93
  end.flatten
92
94
  end
93
95
 
94
96
  def self.uris_with_scheme_and_without(uris)
95
97
  uris.map { |uri|
96
- uri = uri.dup.force_encoding(Encoding::ASCII_8BIT) if uri.respond_to?(:force_encoding)
97
98
  [ uri, uri.gsub(%r{^https?://},"").freeze ]
98
99
  }.flatten
99
100
  end
100
101
 
101
102
  def self.uris_with_trailing_slash_and_without(uris)
102
- uris = uris.map { |uri|
103
- uri = uri.dup.force_encoding(Encoding::ASCII_8BIT) if uri.respond_to?(:force_encoding)
103
+ uris.map { |uri|
104
104
  [ uri, uri.omit(:path).freeze ]
105
105
  }.flatten
106
106
  end
@@ -1,3 +1,3 @@
1
1
  module WebMock
2
- VERSION = '3.5.1' unless defined?(::WebMock::VERSION)
2
+ VERSION = '3.7.2' unless defined?(::WebMock::VERSION)
3
3
  end
@@ -53,6 +53,11 @@ module WebMock
53
53
  Config.instance.net_http_connect_on_start = options[:net_http_connect_on_start]
54
54
  end
55
55
 
56
+ class << self
57
+ alias :enable_net_connect! :allow_net_connect!
58
+ alias :disallow_net_connect! :disable_net_connect!
59
+ end
60
+
56
61
  def self.net_connect_allowed?(uri = nil)
57
62
  if uri.is_a?(String)
58
63
  uri = WebMock::Util::URI.normalize_uri(uri)
@@ -0,0 +1,349 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'acceptance/webmock_shared'
4
+ require_relative './async_http_client_spec_helper'
5
+
6
+ require 'protocol/http/body/file'
7
+
8
+ Async.logger.debug! if ENV['ASYNC_LOGGER_DEBUG']
9
+
10
+ unless RUBY_PLATFORM =~ /java/
11
+ describe 'Async::HTTP::Client' do
12
+ include AsyncHttpClientSpecHelper
13
+
14
+ include_context "with WebMock",
15
+ :no_status_message,
16
+ :no_url_auth,
17
+ :no_content_length_header
18
+
19
+ it 'works' do
20
+ stub_request(:get, 'http://www.example.com')
21
+ expect(make_request(:get, 'http://www.example.com')).to eq(
22
+ status: 200,
23
+ headers: {},
24
+ body: nil
25
+ )
26
+ end
27
+
28
+ it 'works with request path' do
29
+ stub_request(:get, 'http://www.example.com/foo')
30
+ expect(make_request(:get, 'http://www.example.com/foo')).to eq(
31
+ status: 200,
32
+ headers: {},
33
+ body: nil
34
+ )
35
+ end
36
+
37
+ it 'works with request query' do
38
+ stub_request(:get, 'http://www.example.com/').with(
39
+ query: {
40
+ 'foo' => 'bar'
41
+ }
42
+ )
43
+ expect(make_request(:get, 'http://www.example.com/?foo=bar')).to eq(
44
+ status: 200,
45
+ headers: {},
46
+ body: nil
47
+ )
48
+ end
49
+
50
+ it 'works with request headers' do
51
+ stub_request(:get, 'http://www.example.com').with(
52
+ headers: {
53
+ 'X-Token' => 'Token'
54
+ }
55
+ )
56
+ expect(
57
+ make_request :get, 'http://www.example.com',
58
+ headers: {
59
+ 'X-Token' => 'Token'
60
+ }
61
+ ).to eq(
62
+ status: 200,
63
+ headers: {},
64
+ body: nil
65
+ )
66
+ end
67
+
68
+ it 'works with request body as text' do
69
+ stub_request(:post, 'http://www.example.com').with(
70
+ body: 'x'*10_000
71
+ )
72
+ expect(
73
+ make_request :post, 'http://www.example.com',
74
+ body: 'x'*10_000
75
+ ).to eq(
76
+ status: 200,
77
+ headers: {},
78
+ body: nil
79
+ )
80
+ end
81
+
82
+ it 'works with request body as file' do
83
+ stub_request(:post, "www.example.com").with(
84
+ body: File.read(__FILE__)
85
+ )
86
+ expect(
87
+ make_request :post, "http://www.example.com",
88
+ body: ::Protocol::HTTP::Body::File.open(__FILE__, block_size: 32)
89
+ ).to eq(
90
+ status: 200,
91
+ headers: {},
92
+ body: nil
93
+ )
94
+ end
95
+
96
+ it 'works with response status' do
97
+ stub_request(:get, 'http://www.example.com').to_return(
98
+ status: 400
99
+ )
100
+ expect(make_request(:get, 'http://www.example.com')).to eq(
101
+ status: 400,
102
+ headers: {},
103
+ body: nil
104
+ )
105
+ end
106
+
107
+ it 'works with response headers' do
108
+ stub_request(:get, 'http://www.example.com').to_return(
109
+ headers: {
110
+ 'X-Token' => 'TOKEN'
111
+ }
112
+ )
113
+ expect(make_request(:get, 'http://www.example.com')).to eq(
114
+ status: 200,
115
+ headers: {
116
+ 'x-token' => ['TOKEN']
117
+ },
118
+ body: nil
119
+ )
120
+ end
121
+
122
+ it 'works with response body' do
123
+ stub_request(:get, 'http://www.example.com').to_return(
124
+ body: 'abc'
125
+ )
126
+ expect(make_request(:get, 'http://www.example.com')).to eq(
127
+ status: 200,
128
+ headers: {},
129
+ body: 'abc'
130
+ )
131
+ end
132
+
133
+ it 'works with to_timeout' do
134
+ stub_request(:get, 'http://www.example.com').to_timeout
135
+ expect { make_request(:get, 'http://www.example.com') }.to raise_error Async::TimeoutError
136
+ end
137
+
138
+ context 'scheme and protocol' do
139
+ before do
140
+ stub_request(
141
+ :get, "#{scheme}://www.example.com"
142
+ ).and_return(
143
+ body: 'BODY'
144
+ )
145
+ end
146
+
147
+ subject do
148
+ make_request(:get, "#{scheme}://www.example.com", protocol: protocol)
149
+ end
150
+
151
+ shared_examples :common do
152
+ specify do
153
+ expect(subject).to eq(
154
+ status: 200,
155
+ headers: {},
156
+ body: 'BODY'
157
+ )
158
+ end
159
+ end
160
+
161
+ context 'http scheme' do
162
+ let(:scheme) { 'http' }
163
+
164
+ context 'default protocol' do
165
+ let(:protocol) { nil }
166
+
167
+ include_examples :common
168
+ end
169
+
170
+ context 'HTTP10 protocol' do
171
+ let(:protocol) { Async::HTTP::Protocol::HTTP10 }
172
+
173
+ include_examples :common
174
+ end
175
+
176
+ context 'HTTP11 protocol' do
177
+ let(:protocol) { Async::HTTP::Protocol::HTTP11 }
178
+
179
+ include_examples :common
180
+ end
181
+
182
+ context 'HTTP2 protocol' do
183
+ let(:protocol) { Async::HTTP::Protocol::HTTP2 }
184
+
185
+ include_examples :common
186
+ end
187
+ end
188
+
189
+ context 'https scheme' do
190
+ let(:scheme) { 'https' }
191
+
192
+ context 'default protocol' do
193
+ let(:protocol) { nil }
194
+
195
+ include_examples :common
196
+ end
197
+
198
+ context 'HTTP10 protocol' do
199
+ let(:protocol) { Async::HTTP::Protocol::HTTP10 }
200
+
201
+ include_examples :common
202
+ end
203
+
204
+ context 'HTTP11 protocol' do
205
+ let(:protocol) { Async::HTTP::Protocol::HTTP11 }
206
+
207
+ include_examples :common
208
+ end
209
+
210
+ context 'HTTP2 protocol' do
211
+ let(:protocol) { Async::HTTP::Protocol::HTTP2 }
212
+
213
+ include_examples :common
214
+ end
215
+
216
+ context 'HTTPS protocol' do
217
+ let(:protocol) { Async::HTTP::Protocol::HTTPS }
218
+
219
+ include_examples :common
220
+ end
221
+ end
222
+ end
223
+
224
+ context 'multiple requests' do
225
+ let(:endpoint) { Async::HTTP::Endpoint.parse('http://www.example.com') }
226
+ let(:requests_count) { 3 }
227
+
228
+ shared_examples :common do
229
+ before do
230
+ requests_count.times do |index|
231
+ stub_request(
232
+ :get, "http://www.example.com/foo#{index}"
233
+ ).to_return(
234
+ status: 200 + index,
235
+ headers: {'X-Token' => "foo#{index}"},
236
+ body: "FOO#{index}"
237
+ )
238
+ end
239
+ end
240
+
241
+ specify do
242
+ expect(subject).to eq(
243
+ 0 => {
244
+ status: 200,
245
+ headers: {'x-token' => ['foo0']},
246
+ body: 'FOO0'
247
+ },
248
+ 1 => {
249
+ status: 201,
250
+ headers: {'x-token' => ['foo1']},
251
+ body: 'FOO1'
252
+ },
253
+ 2 => {
254
+ status: 202,
255
+ headers: {'x-token' => ['foo2']},
256
+ body: 'FOO2'
257
+ }
258
+ )
259
+ end
260
+ end
261
+
262
+ context 'sequential' do
263
+ subject do
264
+ responses = {}
265
+ Async do |task|
266
+ Async::HTTP::Client.open(endpoint, protocol) do |client|
267
+ requests_count.times do |index|
268
+ response = client.get "/foo#{index}"
269
+ responses[index] = response_to_hash(response)
270
+ end
271
+ end
272
+ end
273
+ responses
274
+ end
275
+
276
+ context 'HTTP1 protocol' do
277
+ let(:protocol) { Async::HTTP::Protocol::HTTP1 }
278
+
279
+ include_examples :common
280
+ end
281
+
282
+ context 'HTTP2 protocol' do
283
+ let(:protocol) { Async::HTTP::Protocol::HTTP2 }
284
+
285
+ include_examples :common
286
+ end
287
+ end
288
+
289
+ context 'asynchronous' do
290
+ subject do
291
+ responses = {}
292
+ Async do |task|
293
+ Async::HTTP::Client.open(endpoint, protocol) do |client|
294
+ tasks = requests_count.times.map do |index|
295
+ task.async do
296
+ response = client.get "/foo#{index}"
297
+ responses[index] = response_to_hash(response)
298
+ end
299
+ end
300
+
301
+ tasks.map(&:wait)
302
+ end
303
+ end
304
+ responses
305
+ end
306
+
307
+ context 'HTTP1 protocol' do
308
+ let(:protocol) { Async::HTTP::Protocol::HTTP1 }
309
+
310
+ include_examples :common
311
+ end
312
+
313
+ context 'HTTP2 protocol' do
314
+ let(:protocol) { Async::HTTP::Protocol::HTTP2 }
315
+
316
+ include_examples :common
317
+ end
318
+ end
319
+ end
320
+
321
+ def make_request(method, url, protocol: nil, headers: {}, body: nil)
322
+ Async do
323
+ endpoint = Async::HTTP::Endpoint.parse(url)
324
+
325
+ begin
326
+ Async::HTTP::Client.open(endpoint, protocol || endpoint.protocol) do |client|
327
+ response = client.send(
328
+ method,
329
+ endpoint.path,
330
+ headers,
331
+ body
332
+ )
333
+ response_to_hash(response)
334
+ end
335
+ rescue Async::TimeoutError => e
336
+ e
337
+ end
338
+ end.wait
339
+ end
340
+
341
+ def response_to_hash(response)
342
+ {
343
+ status: response.status,
344
+ headers: response.headers.to_h,
345
+ body: response.read
346
+ }
347
+ end
348
+ end
349
+ end