webmock 3.5.1 → 3.11.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +15 -12
  3. data/CHANGELOG.md +211 -0
  4. data/README.md +92 -26
  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 +10 -1
  9. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +1 -1
  10. data/lib/webmock/http_lib_adapters/excon_adapter.rb +3 -0
  11. data/lib/webmock/http_lib_adapters/http_rb/client.rb +4 -1
  12. data/lib/webmock/http_lib_adapters/http_rb/response.rb +11 -1
  13. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +2 -2
  14. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +23 -6
  15. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +25 -14
  16. data/lib/webmock/http_lib_adapters/net_http.rb +35 -17
  17. data/lib/webmock/http_lib_adapters/patron_adapter.rb +1 -1
  18. data/lib/webmock/request_body_diff.rb +1 -1
  19. data/lib/webmock/request_pattern.rb +76 -48
  20. data/lib/webmock/response.rb +11 -5
  21. data/lib/webmock/rspec.rb +2 -1
  22. data/lib/webmock/stub_registry.rb +26 -11
  23. data/lib/webmock/test_unit.rb +1 -3
  24. data/lib/webmock/util/query_mapper.rb +4 -2
  25. data/lib/webmock/util/uri.rb +8 -8
  26. data/lib/webmock/version.rb +1 -1
  27. data/lib/webmock/webmock.rb +10 -3
  28. data/spec/acceptance/async_http_client/async_http_client_spec.rb +353 -0
  29. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  30. data/spec/acceptance/curb/curb_spec.rb +23 -5
  31. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
  32. data/spec/acceptance/excon/excon_spec_helper.rb +2 -0
  33. data/spec/acceptance/http_rb/http_rb_spec.rb +11 -0
  34. data/spec/acceptance/manticore/manticore_spec.rb +19 -0
  35. data/spec/acceptance/net_http/net_http_spec.rb +12 -0
  36. data/spec/acceptance/shared/callbacks.rb +2 -1
  37. data/spec/acceptance/shared/request_expectations.rb +7 -0
  38. data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
  39. data/spec/acceptance/shared/stubbing_requests.rb +40 -0
  40. data/spec/support/webmock_server.rb +1 -0
  41. data/spec/unit/request_pattern_spec.rb +119 -3
  42. data/spec/unit/response_spec.rb +22 -18
  43. data/spec/unit/util/query_mapper_spec.rb +7 -0
  44. data/spec/unit/util/uri_spec.rb +74 -2
  45. data/spec/unit/webmock_spec.rb +54 -5
  46. data/test/test_webmock.rb +6 -0
  47. data/webmock.gemspec +9 -2
  48. metadata +39 -10
@@ -1,3 +1,3 @@
1
1
  module WebMock
2
- VERSION = '3.5.1' unless defined?(::WebMock::VERSION)
2
+ VERSION = '3.11.0' unless defined?(::WebMock::VERSION)
3
3
  end
@@ -53,12 +53,19 @@ 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)
62
+ return !!Config.instance.allow_net_connect if uri.nil?
63
+
57
64
  if uri.is_a?(String)
58
65
  uri = WebMock::Util::URI.normalize_uri(uri)
59
66
  end
60
67
 
61
- Config.instance.allow_net_connect ||
68
+ !!Config.instance.allow_net_connect ||
62
69
  ( Config.instance.allow_localhost && WebMock::Util::URI.is_uri_localhost?(uri) ||
63
70
  Config.instance.allow && net_connect_explicit_allowed?(Config.instance.allow, uri) )
64
71
  end
@@ -133,8 +140,8 @@ module WebMock
133
140
  puts WebMock::RequestExecutionVerifier.executed_requests_message
134
141
  end
135
142
 
136
- def self.globally_stub_request(&block)
137
- WebMock::StubRegistry.instance.register_global_stub(&block)
143
+ def self.globally_stub_request(order = :before_local_stubs, &block)
144
+ WebMock::StubRegistry.instance.register_global_stub(order, &block)
138
145
  end
139
146
 
140
147
  %w(
@@ -0,0 +1,353 @@
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
+ let(:default_response_headers) { {} }
140
+
141
+ before do
142
+ stub_request(
143
+ :get, "#{scheme}://www.example.com"
144
+ ).and_return(
145
+ body: 'BODY'
146
+ )
147
+ end
148
+
149
+ subject do
150
+ make_request(:get, "#{scheme}://www.example.com", protocol: protocol)
151
+ end
152
+
153
+ shared_examples :common do
154
+ specify do
155
+ expect(subject).to eq(
156
+ status: 200,
157
+ headers: default_response_headers,
158
+ body: 'BODY'
159
+ )
160
+ end
161
+ end
162
+
163
+ context 'http scheme' do
164
+ let(:scheme) { 'http' }
165
+
166
+ context 'default protocol' do
167
+ let(:protocol) { nil }
168
+
169
+ include_examples :common
170
+ end
171
+
172
+ context 'HTTP10 protocol' do
173
+ let(:protocol) { Async::HTTP::Protocol::HTTP10 }
174
+ let(:default_response_headers) { {"connection"=>["keep-alive"]} }
175
+
176
+ include_examples :common
177
+ end
178
+
179
+ context 'HTTP11 protocol' do
180
+ let(:protocol) { Async::HTTP::Protocol::HTTP11 }
181
+
182
+ include_examples :common
183
+ end
184
+
185
+ context 'HTTP2 protocol' do
186
+ let(:protocol) { Async::HTTP::Protocol::HTTP2 }
187
+
188
+ include_examples :common
189
+ end
190
+ end
191
+
192
+ context 'https scheme' do
193
+ let(:scheme) { 'https' }
194
+
195
+ context 'default protocol' do
196
+ let(:protocol) { nil }
197
+
198
+ include_examples :common
199
+ end
200
+
201
+ context 'HTTP10 protocol' do
202
+ let(:protocol) { Async::HTTP::Protocol::HTTP10 }
203
+ let(:default_response_headers) { {"connection"=>["keep-alive"]} }
204
+
205
+ include_examples :common
206
+ end
207
+
208
+ context 'HTTP11 protocol' do
209
+ let(:protocol) { Async::HTTP::Protocol::HTTP11 }
210
+
211
+ include_examples :common
212
+ end
213
+
214
+ context 'HTTP2 protocol' do
215
+ let(:protocol) { Async::HTTP::Protocol::HTTP2 }
216
+
217
+ include_examples :common
218
+ end
219
+
220
+ context 'HTTPS protocol' do
221
+ let(:protocol) { Async::HTTP::Protocol::HTTPS }
222
+
223
+ include_examples :common
224
+ end
225
+ end
226
+ end
227
+
228
+ context 'multiple requests' do
229
+ let(:endpoint) { Async::HTTP::Endpoint.parse('http://www.example.com') }
230
+ let(:requests_count) { 3 }
231
+
232
+ shared_examples :common do
233
+ before do
234
+ requests_count.times do |index|
235
+ stub_request(
236
+ :get, "http://www.example.com/foo#{index}"
237
+ ).to_return(
238
+ status: 200 + index,
239
+ headers: {'X-Token' => "foo#{index}"},
240
+ body: "FOO#{index}"
241
+ )
242
+ end
243
+ end
244
+
245
+ specify do
246
+ expect(subject).to eq(
247
+ 0 => {
248
+ status: 200,
249
+ headers: {'x-token' => ['foo0']},
250
+ body: 'FOO0'
251
+ },
252
+ 1 => {
253
+ status: 201,
254
+ headers: {'x-token' => ['foo1']},
255
+ body: 'FOO1'
256
+ },
257
+ 2 => {
258
+ status: 202,
259
+ headers: {'x-token' => ['foo2']},
260
+ body: 'FOO2'
261
+ }
262
+ )
263
+ end
264
+ end
265
+
266
+ context 'sequential' do
267
+ subject do
268
+ responses = {}
269
+ Async do |task|
270
+ Async::HTTP::Client.open(endpoint, protocol) do |client|
271
+ requests_count.times do |index|
272
+ response = client.get "/foo#{index}"
273
+ responses[index] = response_to_hash(response)
274
+ end
275
+ end
276
+ end
277
+ responses
278
+ end
279
+
280
+ context 'HTTP1 protocol' do
281
+ let(:protocol) { Async::HTTP::Protocol::HTTP1 }
282
+
283
+ include_examples :common
284
+ end
285
+
286
+ context 'HTTP2 protocol' do
287
+ let(:protocol) { Async::HTTP::Protocol::HTTP2 }
288
+
289
+ include_examples :common
290
+ end
291
+ end
292
+
293
+ context 'asynchronous' do
294
+ subject do
295
+ responses = {}
296
+ Async do |task|
297
+ Async::HTTP::Client.open(endpoint, protocol) do |client|
298
+ tasks = requests_count.times.map do |index|
299
+ task.async do
300
+ response = client.get "/foo#{index}"
301
+ responses[index] = response_to_hash(response)
302
+ end
303
+ end
304
+
305
+ tasks.map(&:wait)
306
+ end
307
+ end
308
+ responses
309
+ end
310
+
311
+ context 'HTTP1 protocol' do
312
+ let(:protocol) { Async::HTTP::Protocol::HTTP1 }
313
+
314
+ include_examples :common
315
+ end
316
+
317
+ context 'HTTP2 protocol' do
318
+ let(:protocol) { Async::HTTP::Protocol::HTTP2 }
319
+
320
+ include_examples :common
321
+ end
322
+ end
323
+ end
324
+
325
+ def make_request(method, url, protocol: nil, headers: {}, body: nil)
326
+ Async do
327
+ endpoint = Async::HTTP::Endpoint.parse(url)
328
+
329
+ begin
330
+ Async::HTTP::Client.open(endpoint, protocol || endpoint.protocol) do |client|
331
+ response = client.send(
332
+ method,
333
+ endpoint.path,
334
+ headers,
335
+ body
336
+ )
337
+ response_to_hash(response)
338
+ end
339
+ rescue Async::TimeoutError => e
340
+ e
341
+ end
342
+ end.wait
343
+ end
344
+
345
+ def response_to_hash(response)
346
+ {
347
+ status: response.status,
348
+ headers: response.headers.to_h,
349
+ body: response.read
350
+ }
351
+ end
352
+ end
353
+ end
@@ -0,0 +1,73 @@
1
+ module AsyncHttpClientSpecHelper
2
+ def http_request(method, url, options = {}, &block)
3
+ endpoint = Async::HTTP::Endpoint.parse(url)
4
+
5
+ path = endpoint.path
6
+ path = path + "?" + options[:query] if options[:query]
7
+
8
+ headers = (options[:headers] || {}).each_with_object([]) do |(k, v), o|
9
+ Array(v).each do |v|
10
+ o.push [k, v]
11
+ end
12
+ end
13
+ headers.push(
14
+ ['authorization', 'Basic ' + Base64.strict_encode64(options[:basic_auth].join(':'))]
15
+ ) if options[:basic_auth]
16
+
17
+ body = options[:body]
18
+
19
+ Async do
20
+ begin
21
+ Async::HTTP::Client.open(endpoint) do |client|
22
+ response = client.send(
23
+ method,
24
+ path,
25
+ headers,
26
+ body
27
+ )
28
+
29
+ OpenStruct.new(
30
+ build_hash_response(response)
31
+ )
32
+ end
33
+ rescue Exception => e
34
+ e
35
+ end
36
+ end.wait
37
+ end
38
+
39
+ def client_timeout_exception_class
40
+ Async::TimeoutError
41
+ end
42
+
43
+ def connection_refused_exception_class
44
+ Errno::ECONNREFUSED
45
+ end
46
+
47
+ def http_library
48
+ :async_http_client
49
+ end
50
+
51
+ private
52
+
53
+ def build_hash_response(response)
54
+ {
55
+
56
+ status: response.status.to_s,
57
+ message: Protocol::HTTP1::Reason::DESCRIPTIONS[response.status],
58
+ headers: build_response_headers(response),
59
+ body: response.read
60
+ }
61
+ end
62
+
63
+ def build_response_headers(response)
64
+ response.headers.each.each_with_object({}) do |(k, v), o|
65
+ o[k] ||= []
66
+ o[k] << v
67
+ end.tap do |o|
68
+ o.each do |k, v|
69
+ o[k] = v.join(', ')
70
+ end
71
+ end
72
+ end
73
+ end