faraday 1.0.1 → 1.10.3

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +104 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +3 -5
  5. data/examples/client_spec.rb +35 -3
  6. data/examples/client_test.rb +41 -2
  7. data/lib/faraday/adapter/test.rb +59 -43
  8. data/lib/faraday/adapter.rb +2 -12
  9. data/lib/faraday/autoload.rb +2 -10
  10. data/lib/faraday/connection.rb +37 -9
  11. data/lib/faraday/dependency_loader.rb +3 -1
  12. data/lib/faraday/deprecate.rb +110 -0
  13. data/lib/faraday/encoders/flat_params_encoder.rb +9 -2
  14. data/lib/faraday/encoders/nested_params_encoder.rb +7 -2
  15. data/lib/faraday/error.rb +20 -6
  16. data/lib/faraday/methods.rb +6 -0
  17. data/lib/faraday/middleware.rb +14 -4
  18. data/lib/faraday/options/proxy_options.rb +4 -0
  19. data/lib/faraday/options.rb +4 -8
  20. data/lib/faraday/rack_builder.rb +13 -12
  21. data/lib/faraday/request/authorization.rb +14 -7
  22. data/lib/faraday/request/json.rb +55 -0
  23. data/lib/faraday/request.rb +19 -11
  24. data/lib/faraday/response/json.rb +54 -0
  25. data/lib/faraday/response/logger.rb +2 -4
  26. data/lib/faraday/response/raise_error.rb +12 -1
  27. data/lib/faraday/response.rb +3 -7
  28. data/lib/faraday/utils/headers.rb +2 -2
  29. data/lib/faraday/utils.rb +2 -2
  30. data/lib/faraday/version.rb +5 -0
  31. data/lib/faraday.rb +67 -40
  32. data/spec/faraday/adapter/em_http_spec.rb +39 -37
  33. data/spec/faraday/adapter/em_synchrony_spec.rb +11 -9
  34. data/spec/faraday/adapter/test_spec.rb +377 -0
  35. data/spec/faraday/connection_spec.rb +45 -0
  36. data/spec/faraday/deprecate_spec.rb +147 -0
  37. data/spec/faraday/error_spec.rb +15 -0
  38. data/spec/faraday/middleware_spec.rb +32 -6
  39. data/spec/faraday/options/proxy_options_spec.rb +7 -0
  40. data/spec/faraday/params_encoders/flat_spec.rb +8 -0
  41. data/spec/faraday/params_encoders/nested_spec.rb +8 -0
  42. data/spec/faraday/rack_builder_spec.rb +149 -0
  43. data/spec/faraday/request/authorization_spec.rb +10 -2
  44. data/spec/faraday/request/json_spec.rb +111 -0
  45. data/spec/faraday/request_spec.rb +16 -5
  46. data/spec/faraday/response/json_spec.rb +119 -0
  47. data/spec/faraday/response/raise_error_spec.rb +63 -0
  48. data/spec/spec_helper.rb +2 -0
  49. data/spec/support/shared_examples/adapter.rb +2 -1
  50. data/spec/support/shared_examples/request_method.rb +39 -11
  51. metadata +156 -30
  52. data/lib/faraday/adapter/em_http.rb +0 -286
  53. data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -62
  54. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -69
  55. data/lib/faraday/adapter/em_synchrony.rb +0 -150
  56. data/lib/faraday/adapter/excon.rb +0 -124
  57. data/lib/faraday/adapter/httpclient.rb +0 -152
  58. data/lib/faraday/adapter/net_http.rb +0 -219
  59. data/lib/faraday/adapter/net_http_persistent.rb +0 -91
  60. data/lib/faraday/adapter/patron.rb +0 -132
  61. data/lib/faraday/adapter/rack.rb +0 -75
  62. data/lib/faraday/file_part.rb +0 -128
  63. data/lib/faraday/param_part.rb +0 -53
  64. data/lib/faraday/request/multipart.rb +0 -99
  65. data/lib/faraday/request/retry.rb +0 -239
  66. data/spec/faraday/adapter/net_http_persistent_spec.rb +0 -57
  67. data/spec/faraday/request/multipart_spec.rb +0 -274
  68. data/spec/faraday/request/retry_spec.rb +0 -242
@@ -0,0 +1,377 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::Adapter::Test do
4
+ let(:stubs) do
5
+ described_class::Stubs.new do |stub|
6
+ stub.get('http://domain.test/hello') do
7
+ [200, { 'Content-Type' => 'text/html' }, 'domain: hello']
8
+ end
9
+
10
+ stub.get('http://wrong.test/hello') do
11
+ [200, { 'Content-Type' => 'text/html' }, 'wrong: hello']
12
+ end
13
+
14
+ stub.get('http://wrong.test/bait') do
15
+ [404, { 'Content-Type' => 'text/html' }]
16
+ end
17
+
18
+ stub.get('/hello') do
19
+ [200, { 'Content-Type' => 'text/html' }, 'hello']
20
+ end
21
+
22
+ stub.get('/method-echo') do |env|
23
+ [200, { 'Content-Type' => 'text/html' }, env[:method].to_s]
24
+ end
25
+
26
+ stub.get(%r{\A/resources/\d+(?:\?|\z)}) do
27
+ [200, { 'Content-Type' => 'text/html' }, 'show']
28
+ end
29
+
30
+ stub.get(%r{\A/resources/(specified)\z}) do |_env, meta|
31
+ [200, { 'Content-Type' => 'text/html' }, "show #{meta[:match_data][1]}"]
32
+ end
33
+ end
34
+ end
35
+
36
+ let(:connection) do
37
+ Faraday.new do |builder|
38
+ builder.adapter :test, stubs
39
+ end
40
+ end
41
+
42
+ let(:response) { connection.get('/hello') }
43
+
44
+ context 'with simple path sets status' do
45
+ subject { response.status }
46
+
47
+ it { is_expected.to eq 200 }
48
+ end
49
+
50
+ context 'with simple path sets headers' do
51
+ subject { response.headers['Content-Type'] }
52
+
53
+ it { is_expected.to eq 'text/html' }
54
+ end
55
+
56
+ context 'with simple path sets body' do
57
+ subject { response.body }
58
+
59
+ it { is_expected.to eq 'hello' }
60
+ end
61
+
62
+ context 'with host points to the right stub' do
63
+ subject { connection.get('http://domain.test/hello').body }
64
+
65
+ it { is_expected.to eq 'domain: hello' }
66
+ end
67
+
68
+ describe 'can be called several times' do
69
+ subject { connection.get('/hello').body }
70
+
71
+ it { is_expected.to eq 'hello' }
72
+ end
73
+
74
+ describe 'can handle regular expression path' do
75
+ subject { connection.get('/resources/1').body }
76
+
77
+ it { is_expected.to eq 'show' }
78
+ end
79
+
80
+ describe 'can handle single parameter block' do
81
+ subject { connection.get('/method-echo').body }
82
+
83
+ it { is_expected.to eq 'get' }
84
+ end
85
+
86
+ describe 'can handle regular expression path with captured result' do
87
+ subject { connection.get('/resources/specified').body }
88
+
89
+ it { is_expected.to eq 'show specified' }
90
+ end
91
+
92
+ context 'with get params' do
93
+ subject { connection.get('/param?a=1').body }
94
+
95
+ before do
96
+ stubs.get('/param?a=1') { [200, {}, 'a'] }
97
+ end
98
+
99
+ it { is_expected.to eq 'a' }
100
+ end
101
+
102
+ describe 'ignoring unspecified get params' do
103
+ before do
104
+ stubs.get('/optional?a=1') { [200, {}, 'a'] }
105
+ end
106
+
107
+ context 'with multiple params' do
108
+ subject { connection.get('/optional?a=1&b=1').body }
109
+
110
+ it { is_expected.to eq 'a' }
111
+ end
112
+
113
+ context 'with single param' do
114
+ subject { connection.get('/optional?a=1').body }
115
+
116
+ it { is_expected.to eq 'a' }
117
+ end
118
+
119
+ context 'without params' do
120
+ subject(:request) { connection.get('/optional') }
121
+
122
+ it do
123
+ expect { request }.to raise_error(
124
+ Faraday::Adapter::Test::Stubs::NotFound
125
+ )
126
+ end
127
+ end
128
+ end
129
+
130
+ context 'with http headers' do
131
+ before do
132
+ stubs.get('/yo', 'X-HELLO' => 'hello') { [200, {}, 'a'] }
133
+ stubs.get('/yo') { [200, {}, 'b'] }
134
+ end
135
+
136
+ context 'with header' do
137
+ subject do
138
+ connection.get('/yo') { |env| env.headers['X-HELLO'] = 'hello' }.body
139
+ end
140
+
141
+ it { is_expected.to eq 'a' }
142
+ end
143
+
144
+ context 'without header' do
145
+ subject do
146
+ connection.get('/yo').body
147
+ end
148
+
149
+ it { is_expected.to eq 'b' }
150
+ end
151
+ end
152
+
153
+ describe 'different outcomes for the same request' do
154
+ def make_request
155
+ connection.get('/foo')
156
+ end
157
+
158
+ subject(:request) { make_request.body }
159
+
160
+ before do
161
+ stubs.get('/foo') { [200, { 'Content-Type' => 'text/html' }, 'hello'] }
162
+ stubs.get('/foo') { [200, { 'Content-Type' => 'text/html' }, 'world'] }
163
+ end
164
+
165
+ context 'the first request' do
166
+ it { is_expected.to eq 'hello' }
167
+ end
168
+
169
+ context 'the second request' do
170
+ before do
171
+ make_request
172
+ end
173
+
174
+ it { is_expected.to eq 'world' }
175
+ end
176
+ end
177
+
178
+ describe 'yielding env to stubs' do
179
+ subject { connection.get('http://foo.com/foo?a=1').body }
180
+
181
+ before do
182
+ stubs.get '/foo' do |env|
183
+ expect(env[:url].path).to eq '/foo'
184
+ expect(env[:url].host).to eq 'foo.com'
185
+ expect(env[:params]['a']).to eq '1'
186
+ expect(env[:request_headers]['Accept']).to eq 'text/plain'
187
+ [200, {}, 'a']
188
+ end
189
+
190
+ connection.headers['Accept'] = 'text/plain'
191
+ end
192
+
193
+ it { is_expected.to eq 'a' }
194
+ end
195
+
196
+ describe 'params parsing' do
197
+ subject { connection.get('http://foo.com/foo?a[b]=1').body }
198
+
199
+ context 'with default encoder' do
200
+ before do
201
+ stubs.get '/foo' do |env|
202
+ expect(env[:params]['a']['b']).to eq '1'
203
+ [200, {}, 'a']
204
+ end
205
+ end
206
+
207
+ it { is_expected.to eq 'a' }
208
+ end
209
+
210
+ context 'with nested encoder' do
211
+ before do
212
+ stubs.get '/foo' do |env|
213
+ expect(env[:params]['a']['b']).to eq '1'
214
+ [200, {}, 'a']
215
+ end
216
+
217
+ connection.options.params_encoder = Faraday::NestedParamsEncoder
218
+ end
219
+
220
+ it { is_expected.to eq 'a' }
221
+ end
222
+
223
+ context 'with flat encoder' do
224
+ before do
225
+ stubs.get '/foo' do |env|
226
+ expect(env[:params]['a[b]']).to eq '1'
227
+ [200, {}, 'a']
228
+ end
229
+
230
+ connection.options.params_encoder = Faraday::FlatParamsEncoder
231
+ end
232
+
233
+ it { is_expected.to eq 'a' }
234
+ end
235
+ end
236
+
237
+ describe 'raising an error if no stub was found' do
238
+ describe 'for request' do
239
+ subject(:request) { connection.get('/invalid') { [200, {}, []] } }
240
+
241
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
242
+ end
243
+
244
+ describe 'for specified host' do
245
+ subject(:request) { connection.get('http://domain.test/bait') }
246
+
247
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
248
+ end
249
+
250
+ describe 'for request without specified header' do
251
+ subject(:request) { connection.get('/yo') }
252
+
253
+ before do
254
+ stubs.get('/yo', 'X-HELLO' => 'hello') { [200, {}, 'a'] }
255
+ end
256
+
257
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
258
+ end
259
+ end
260
+
261
+ describe 'for request with non default params encoder' do
262
+ let(:connection) do
263
+ Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) do |builder|
264
+ builder.adapter :test, stubs
265
+ end
266
+ end
267
+ let(:stubs) do
268
+ described_class::Stubs.new do |stubs|
269
+ stubs.get('/path?a=x&a=y&a=z') { [200, {}, 'a'] }
270
+ end
271
+ end
272
+
273
+ context 'when all flat param values are correctly set' do
274
+ subject(:request) { connection.get('/path?a=x&a=y&a=z') }
275
+
276
+ it { expect(request.status).to eq 200 }
277
+ end
278
+
279
+ shared_examples 'raise NotFound when params do not satisfy the flat param values' do |params|
280
+ subject(:request) { connection.get('/path', params) }
281
+
282
+ context "with #{params.inspect}" do
283
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
284
+ end
285
+ end
286
+
287
+ it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { a: %w[x] }
288
+ it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { a: %w[x y] }
289
+ it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { a: %w[x z y] } # NOTE: The order of the value is also compared.
290
+ it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { b: %w[x y z] }
291
+ end
292
+
293
+ describe 'strict_mode' do
294
+ let(:stubs) do
295
+ described_class::Stubs.new(strict_mode: true) do |stubs|
296
+ stubs.get('/strict?a=12&b=xy', 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello') { [200, {}, 'a'] }
297
+ stubs.get('/with_user_agent?a=12&b=xy', authorization: 'Bearer m_ck', 'User-Agent' => 'My Agent') { [200, {}, 'a'] }
298
+ end
299
+ end
300
+
301
+ context 'when params and headers are exactly set' do
302
+ subject(:request) { connection.get('/strict', { a: '12', b: 'xy' }, { authorization: 'Bearer m_ck', x_c: 'hello' }) }
303
+
304
+ it { expect(request.status).to eq 200 }
305
+ end
306
+
307
+ context 'when params and headers are exactly set with a custom user agent' do
308
+ subject(:request) { connection.get('/with_user_agent', { a: '12', b: 'xy' }, { authorization: 'Bearer m_ck', 'User-Agent' => 'My Agent' }) }
309
+
310
+ it { expect(request.status).to eq 200 }
311
+ end
312
+
313
+ shared_examples 'raise NotFound when params do not satisfy the strict check' do |params|
314
+ subject(:request) { connection.get('/strict', params, { 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello' }) }
315
+
316
+ context "with #{params.inspect}" do
317
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
318
+ end
319
+ end
320
+
321
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12' }
322
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { b: 'xy' }
323
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '123', b: 'xy' }
324
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12', b: 'xyz' }
325
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12', b: 'xy', c: 'hello' }
326
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { additional: 'special', a: '12', b: 'xy', c: 'hello' }
327
+
328
+ shared_examples 'raise NotFound when headers do not satisfy the strict check' do |path, headers|
329
+ subject(:request) { connection.get(path, { a: 12, b: 'xy' }, headers) }
330
+
331
+ context "with #{headers.inspect}" do
332
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
333
+ end
334
+ end
335
+
336
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck' }
337
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { 'X-C' => 'hello' }
338
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'Hi' }
339
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Basic m_ck', 'x-c': 'hello' }
340
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', x_special: 'special' }
341
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck' }
342
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'Unknown' }
343
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent', x_special: 'special' }
344
+
345
+ context 'when strict_mode is disabled' do
346
+ before do
347
+ stubs.strict_mode = false
348
+ end
349
+
350
+ shared_examples 'does not raise NotFound even when params do not satisfy the strict check' do |params|
351
+ subject(:request) { connection.get('/strict', params, { 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello' }) }
352
+
353
+ context "with #{params.inspect}" do
354
+ it { expect(request.status).to eq 200 }
355
+ end
356
+ end
357
+
358
+ it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { a: '12', b: 'xy' }
359
+ it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { a: '12', b: 'xy', c: 'hello' }
360
+ it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { additional: 'special', a: '12', b: 'xy', c: 'hello' }
361
+
362
+ shared_examples 'does not raise NotFound even when headers do not satisfy the strict check' do |path, headers|
363
+ subject(:request) { connection.get(path, { a: 12, b: 'xy' }, headers) }
364
+
365
+ context "with #{headers.inspect}" do
366
+ it { expect(request.status).to eq 200 }
367
+ end
368
+ end
369
+
370
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello' }
371
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', x_special: 'special' }
372
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', user_agent: 'Special Agent' }
373
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent' }
374
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent', x_special: 'special' }
375
+ end
376
+ end
377
+ end
@@ -18,6 +18,13 @@ shared_examples 'initializer with url' do
18
18
  it { expect(subject.path_prefix).to eq('/fish') }
19
19
  it { expect(subject.params).to eq('a' => '1') }
20
20
  end
21
+
22
+ context 'with IPv6 address' do
23
+ let(:address) { 'http://[::1]:85/' }
24
+
25
+ it { expect(subject.host).to eq('[::1]') }
26
+ it { expect(subject.port).to eq(85) }
27
+ end
21
28
  end
22
29
 
23
30
  shared_examples 'default connection options' do
@@ -246,6 +253,13 @@ RSpec.describe Faraday::Connection do
246
253
  expect(uri.path).to eq('/sake.html')
247
254
  end
248
255
 
256
+ it 'always returns new URI instance' do
257
+ conn.url_prefix = 'http://sushi.com'
258
+ uri1 = conn.build_exclusive_url(nil)
259
+ uri2 = conn.build_exclusive_url(nil)
260
+ expect(uri1).not_to equal(uri2)
261
+ end
262
+
249
263
  context 'with url_prefixed connection' do
250
264
  let(:url) { 'http://sushi.com/sushi/' }
251
265
 
@@ -270,6 +284,29 @@ RSpec.describe Faraday::Connection do
270
284
  expect(uri.to_s).to eq('http://sushi.com/sake/')
271
285
  end
272
286
  end
287
+
288
+ context 'with colon in path' do
289
+ let(:url) { 'http://service.com' }
290
+
291
+ it 'joins url to base when used absolute path' do
292
+ conn = Faraday.new(url: url)
293
+ uri = conn.build_exclusive_url('/service:search?limit=400')
294
+ expect(uri.to_s).to eq('http://service.com/service:search?limit=400')
295
+ end
296
+
297
+ it 'joins url to base when used relative path' do
298
+ conn = Faraday.new(url: url)
299
+ uri = conn.build_exclusive_url('service:search?limit=400')
300
+ expect(uri.to_s).to eq('http://service.com/service%3Asearch?limit=400')
301
+ end
302
+
303
+ it 'joins url to base when used with path prefix' do
304
+ conn = Faraday.new(url: url)
305
+ conn.path_prefix = '/api'
306
+ uri = conn.build_exclusive_url('service:search?limit=400')
307
+ expect(uri.to_s).to eq('http://service.com/api/service%3Asearch?limit=400')
308
+ end
309
+ end
273
310
  end
274
311
 
275
312
  describe '#build_url' do
@@ -412,6 +449,14 @@ RSpec.describe Faraday::Connection do
412
449
  end
413
450
  end
414
451
 
452
+ it 'allows when url in no proxy list with url_prefix' do
453
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
454
+ conn = Faraday::Connection.new
455
+ conn.url_prefix = 'http://example.com'
456
+ expect(conn.proxy).to be_nil
457
+ end
458
+ end
459
+
415
460
  it 'allows when prefixed url is not in no proxy list' do
416
461
  with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
417
462
  conn = Faraday::Connection.new('http://prefixedexample.com')
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::DeprecatedClass do
4
+ class SampleClass < StandardError
5
+ attr_accessor :foo
6
+
7
+ def initialize(foo = nil)
8
+ @foo = foo || :foo
9
+ end
10
+ end
11
+
12
+ SampleDeprecatedClass = Faraday::DeprecatedClass.proxy_class(SampleClass)
13
+
14
+ it 'does not raise error for deprecated classes but prints an error message' do
15
+ error_message, foobar = with_warn_squelching { SampleDeprecatedClass.new(:foo_bar) }
16
+ expect(foobar).to be_a(SampleClass)
17
+ expect(foobar.foo).to eq(:foo_bar)
18
+ expect(error_message).to match(
19
+ Regexp.new(
20
+ 'NOTE: SampleDeprecatedClass.new is deprecated; '\
21
+ 'use SampleClass.new instead. It will be removed in or after version 1.0'
22
+ )
23
+ )
24
+ end
25
+
26
+ it 'does not raise an error for inherited error-namespaced classes but prints an error message' do
27
+ error_message, = with_warn_squelching { Class.new(SampleDeprecatedClass) }
28
+
29
+ expect(error_message).to match(
30
+ Regexp.new(
31
+ 'NOTE: Inheriting SampleDeprecatedClass is deprecated; '\
32
+ 'use SampleClass instead. It will be removed in or after version 1.0'
33
+ )
34
+ )
35
+ end
36
+
37
+ it 'allows backward-compatible class to be subclassed' do
38
+ expect do
39
+ with_warn_squelching { Class.new(SampleDeprecatedClass) }
40
+ end.not_to raise_error
41
+ end
42
+
43
+ it 'allows rescuing of a current error with a deprecated error' do
44
+ expect { raise SampleClass, nil }.to raise_error(SampleDeprecatedClass)
45
+ end
46
+
47
+ it 'allows rescuing of a current error with a current error' do
48
+ expect { raise SampleClass, nil }.to raise_error(SampleClass)
49
+ end
50
+
51
+ it 'allows rescuing of a deprecated error with a deprecated error' do
52
+ expect { raise SampleDeprecatedClass, nil }.to raise_error(SampleDeprecatedClass)
53
+ end
54
+
55
+ it 'allows rescuing of a deprecated error with a current error' do
56
+ expect { raise SampleDeprecatedClass, nil }.to raise_error(SampleClass)
57
+ end
58
+
59
+ describe 'match behavior' do
60
+ class SampleDeprecatedClassA < SampleDeprecatedClass; end
61
+ class SampleDeprecatedClassB < SampleDeprecatedClass; end
62
+
63
+ class SampleDeprecatedClassAX < SampleDeprecatedClassA; end
64
+
65
+ class SampleClassA < SampleClass; end
66
+
67
+ describe 'undeprecated class' do
68
+ it 'is === to instance of deprecated class' do
69
+ expect(SampleDeprecatedClass.new.is_a?(SampleClass)).to be true
70
+ end
71
+
72
+ it 'is === to instance of subclass of deprecated class' do
73
+ expect(SampleDeprecatedClassA.new.is_a?(SampleClass)).to be true
74
+ end
75
+
76
+ it 'is === to instance of subclass of subclass of deprecated class' do
77
+ expect(SampleDeprecatedClassAX.new.is_a?(SampleClass)).to be true
78
+ end
79
+ end
80
+
81
+ describe 'subclass of undeprecated class' do
82
+ it 'is not === to instance of undeprecated class' do
83
+ expect(SampleClass.new.is_a?(SampleClassA)).to be false
84
+ end
85
+
86
+ it 'is not === to instance of deprecated class' do
87
+ expect(SampleDeprecatedClass.new.is_a?(SampleClassA)).to be false
88
+ end
89
+ end
90
+
91
+ describe 'deprecated class' do
92
+ it 'is === to instance of undeprecated class' do
93
+ expect(SampleDeprecatedClass.new.is_a?(SampleClass)).to be true
94
+ end
95
+
96
+ it 'is === to instance of subclass of undeprecated class' do
97
+ expect(SampleClassA.superclass == SampleDeprecatedClass.superclass).to be true
98
+ end
99
+
100
+ it 'is === to instance of subclass of deprecated class' do
101
+ expect(SampleDeprecatedClassA.new.is_a?(SampleDeprecatedClass)).to be true
102
+ end
103
+
104
+ it 'is === to instance of subclass of subclass of deprecated class' do
105
+ expect(SampleDeprecatedClassAX.new.is_a?(SampleDeprecatedClass)).to be true
106
+ end
107
+ end
108
+
109
+ describe 'subclass of deprecated class' do
110
+ it 'is not === to instance of subclass of undeprecated class' do
111
+ expect(SampleClass.new.is_a?(SampleDeprecatedClassA)).to be false
112
+ end
113
+
114
+ it 'is not === to instance of another subclass of deprecated class' do
115
+ expect(SampleDeprecatedClassB.new.is_a?(SampleDeprecatedClassA)).to be false
116
+ end
117
+
118
+ it 'is === to instance of its subclass' do
119
+ expect(SampleDeprecatedClassAX.new.is_a?(SampleDeprecatedClassA)).to be true
120
+ end
121
+
122
+ it 'is === to instance of deprecated class' do
123
+ expect(SampleDeprecatedClassB.new.is_a?(SampleDeprecatedClass)).to be true
124
+ end
125
+ end
126
+
127
+ describe 'subclass of subclass of deprecated class' do
128
+ it 'is not === to instance of subclass of another subclass of deprecated class' do
129
+ expect(SampleDeprecatedClassB.new.is_a?(SampleDeprecatedClassAX)).to be false
130
+ end
131
+
132
+ it 'is not === to instance of its superclass' do
133
+ expect(SampleDeprecatedClass.new.is_a?(SampleDeprecatedClassA)).to be false
134
+ end
135
+ end
136
+ end
137
+
138
+ def with_warn_squelching
139
+ stderr_catcher = StringIO.new
140
+ original_stderr = $stderr
141
+ $stderr = stderr_catcher
142
+ result = yield if block_given?
143
+ [stderr_catcher.tap(&:rewind).string, result]
144
+ ensure
145
+ $stderr = original_stderr
146
+ end
147
+ end
@@ -13,6 +13,7 @@ RSpec.describe Faraday::ClientError do
13
13
  it { expect(subject.message).to eq(exception.message) }
14
14
  it { expect(subject.backtrace).to eq(exception.backtrace) }
15
15
  it { expect(subject.inspect).to eq('#<Faraday::ClientError wrapped=#<RuntimeError: test>>') }
16
+ it { expect(subject.response_status).to be_nil }
16
17
  end
17
18
 
18
19
  context 'with response hash' do
@@ -22,6 +23,7 @@ RSpec.describe Faraday::ClientError do
22
23
  it { expect(subject.response).to eq(exception) }
23
24
  it { expect(subject.message).to eq('the server responded with status 400') }
24
25
  it { expect(subject.inspect).to eq('#<Faraday::ClientError response={:status=>400}>') }
26
+ it { expect(subject.response_status).to eq(400) }
25
27
  end
26
28
 
27
29
  context 'with string' do
@@ -31,6 +33,7 @@ RSpec.describe Faraday::ClientError do
31
33
  it { expect(subject.response).to be_nil }
32
34
  it { expect(subject.message).to eq('custom message') }
33
35
  it { expect(subject.inspect).to eq('#<Faraday::ClientError #<Faraday::ClientError: custom message>>') }
36
+ it { expect(subject.response_status).to be_nil }
34
37
  end
35
38
 
36
39
  context 'with anything else #to_s' do
@@ -40,6 +43,18 @@ RSpec.describe Faraday::ClientError do
40
43
  it { expect(subject.response).to be_nil }
41
44
  it { expect(subject.message).to eq('["error1", "error2"]') }
42
45
  it { expect(subject.inspect).to eq('#<Faraday::ClientError #<Faraday::ClientError: ["error1", "error2"]>>') }
46
+ it { expect(subject.response_status).to be_nil }
47
+ end
48
+
49
+ context 'with exception string and response hash' do
50
+ let(:exception) { 'custom message' }
51
+ let(:response) { { status: 400 } }
52
+
53
+ it { expect(subject.wrapped_exception).to be_nil }
54
+ it { expect(subject.response).to eq(response) }
55
+ it { expect(subject.message).to eq('custom message') }
56
+ it { expect(subject.inspect).to eq('#<Faraday::ClientError response={:status=>400}>') }
57
+ it { expect(subject.response_status).to eq(400) }
43
58
  end
44
59
  end
45
60
  end
@@ -2,23 +2,49 @@
2
2
 
3
3
  RSpec.describe Faraday::Middleware do
4
4
  subject { described_class.new(app) }
5
+ let(:app) { double }
6
+
7
+ describe 'options' do
8
+ context 'when options are passed to the middleware' do
9
+ subject { described_class.new(app, options) }
10
+ let(:options) { { field: 'value' } }
11
+
12
+ it 'accepts options when initialized' do
13
+ expect(subject.options[:field]).to eq('value')
14
+ end
15
+ end
16
+ end
17
+
18
+ describe '#on_request' do
19
+ subject do
20
+ Class.new(described_class) do
21
+ def on_request(env)
22
+ # do nothing
23
+ end
24
+ end.new(app)
25
+ end
26
+
27
+ it 'is called by #call' do
28
+ expect(app).to receive(:call).and_return(app)
29
+ expect(app).to receive(:on_complete)
30
+ is_expected.to receive(:call).and_call_original
31
+ is_expected.to receive(:on_request)
32
+ subject.call(double)
33
+ end
34
+ end
5
35
 
6
36
  describe '#close' do
7
37
  context "with app that doesn't support \#close" do
8
- let(:app) { double }
9
-
10
38
  it 'should issue warning' do
11
- expect(subject).to receive(:warn)
39
+ is_expected.to receive(:warn)
12
40
  subject.close
13
41
  end
14
42
  end
15
43
 
16
44
  context "with app that supports \#close" do
17
- let(:app) { double }
18
-
19
45
  it 'should issue warning' do
20
46
  expect(app).to receive(:close)
21
- expect(subject).to_not receive(:warn)
47
+ is_expected.to_not receive(:warn)
22
48
  subject.close
23
49
  end
24
50
  end