faraday 2.0.0.alpha.pre.4 → 2.0.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.
- checksums.yaml +4 -4
- data/examples/client_spec.rb +1 -1
- data/examples/client_test.rb +1 -1
- data/lib/faraday/connection.rb +1 -1
- data/lib/faraday/error.rb +2 -8
- data/lib/faraday/rack_builder.rb +21 -18
- data/lib/faraday/request.rb +0 -2
- data/lib/faraday/version.rb +1 -1
- data/lib/faraday.rb +0 -3
- data/spec/faraday/connection_spec.rb +3 -0
- data/spec/faraday/rack_builder_spec.rb +2 -7
- data/spec/faraday/request/url_encoded_spec.rb +0 -1
- data/spec/support/helper_methods.rb +0 -37
- data/spec/support/shared_examples/adapter.rb +0 -1
- data/spec/support/shared_examples/request_method.rb +0 -13
- metadata +5 -32
- data/lib/faraday/file_part.rb +0 -122
- data/lib/faraday/param_part.rb +0 -53
- data/lib/faraday/request/multipart.rb +0 -108
- data/lib/faraday/request/retry.rb +0 -241
- data/spec/faraday/composite_read_io_spec.rb +0 -80
- data/spec/faraday/request/multipart_spec.rb +0 -302
- data/spec/faraday/request/retry_spec.rb +0 -254
@@ -1,254 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Faraday::Request::Retry do
|
4
|
-
let(:calls) { [] }
|
5
|
-
let(:times_called) { calls.size }
|
6
|
-
let(:options) { [] }
|
7
|
-
let(:conn) do
|
8
|
-
Faraday.new do |b|
|
9
|
-
b.request :retry, *options
|
10
|
-
|
11
|
-
b.adapter :test do |stub|
|
12
|
-
%w[get post].each do |method|
|
13
|
-
stub.send(method, '/unstable') do |env|
|
14
|
-
calls << env.dup
|
15
|
-
env[:body] = nil # simulate blanking out response body
|
16
|
-
callback.call
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
context 'when an unexpected error happens' do
|
24
|
-
let(:callback) { -> { raise 'boom!' } }
|
25
|
-
|
26
|
-
before { expect { conn.get('/unstable') }.to raise_error(RuntimeError) }
|
27
|
-
|
28
|
-
it { expect(times_called).to eq(1) }
|
29
|
-
|
30
|
-
context 'and this is passed as a custom exception' do
|
31
|
-
let(:options) { [{ exceptions: StandardError }] }
|
32
|
-
|
33
|
-
it { expect(times_called).to eq(3) }
|
34
|
-
end
|
35
|
-
|
36
|
-
context 'and this is passed as a string custom exception' do
|
37
|
-
let(:options) { [{ exceptions: 'StandardError' }] }
|
38
|
-
|
39
|
-
it { expect(times_called).to eq(3) }
|
40
|
-
end
|
41
|
-
|
42
|
-
context 'and a non-existent string custom exception is passed' do
|
43
|
-
let(:options) { [{ exceptions: 'WrongStandardErrorNotExisting' }] }
|
44
|
-
|
45
|
-
it { expect(times_called).to eq(1) }
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
context 'when an expected error happens' do
|
50
|
-
let(:callback) { -> { raise Errno::ETIMEDOUT } }
|
51
|
-
|
52
|
-
before do
|
53
|
-
@started = Time.now
|
54
|
-
expect { conn.get('/unstable') }.to raise_error(Errno::ETIMEDOUT)
|
55
|
-
end
|
56
|
-
|
57
|
-
it { expect(times_called).to eq(3) }
|
58
|
-
|
59
|
-
context 'and legacy max_retry set to 1' do
|
60
|
-
let(:options) { [1] }
|
61
|
-
|
62
|
-
it { expect(times_called).to eq(2) }
|
63
|
-
end
|
64
|
-
|
65
|
-
context 'and legacy max_retry set to -9' do
|
66
|
-
let(:options) { [-9] }
|
67
|
-
|
68
|
-
it { expect(times_called).to eq(1) }
|
69
|
-
end
|
70
|
-
|
71
|
-
context 'and new max_retry set to 3' do
|
72
|
-
let(:options) { [{ max: 3 }] }
|
73
|
-
|
74
|
-
it { expect(times_called).to eq(4) }
|
75
|
-
end
|
76
|
-
|
77
|
-
context 'and new max_retry set to -9' do
|
78
|
-
let(:options) { [{ max: -9 }] }
|
79
|
-
|
80
|
-
it { expect(times_called).to eq(1) }
|
81
|
-
end
|
82
|
-
|
83
|
-
context 'and both max_retry and interval are set' do
|
84
|
-
let(:options) { [{ max: 2, interval: 0.1 }] }
|
85
|
-
|
86
|
-
it { expect(Time.now - @started).to be_within(0.04).of(0.2) }
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
context 'when no exception raised' do
|
91
|
-
let(:options) { [{ max: 1, retry_statuses: 429 }] }
|
92
|
-
|
93
|
-
before { conn.get('/unstable') }
|
94
|
-
|
95
|
-
context 'and response code is in retry_statuses' do
|
96
|
-
let(:callback) { -> { [429, {}, ''] } }
|
97
|
-
|
98
|
-
it { expect(times_called).to eq(2) }
|
99
|
-
end
|
100
|
-
|
101
|
-
context 'and response code is not in retry_statuses' do
|
102
|
-
let(:callback) { -> { [503, {}, ''] } }
|
103
|
-
|
104
|
-
it { expect(times_called).to eq(1) }
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
describe '#calculate_retry_interval' do
|
109
|
-
context 'with exponential backoff' do
|
110
|
-
let(:options) { { max: 5, interval: 0.1, backoff_factor: 2 } }
|
111
|
-
let(:middleware) { Faraday::Request::Retry.new(nil, options) }
|
112
|
-
|
113
|
-
it { expect(middleware.send(:calculate_retry_interval, 5)).to eq(0.1) }
|
114
|
-
it { expect(middleware.send(:calculate_retry_interval, 4)).to eq(0.2) }
|
115
|
-
it { expect(middleware.send(:calculate_retry_interval, 3)).to eq(0.4) }
|
116
|
-
end
|
117
|
-
|
118
|
-
context 'with exponential backoff and max_interval' do
|
119
|
-
let(:options) { { max: 5, interval: 0.1, backoff_factor: 2, max_interval: 0.3 } }
|
120
|
-
let(:middleware) { Faraday::Request::Retry.new(nil, options) }
|
121
|
-
|
122
|
-
it { expect(middleware.send(:calculate_retry_interval, 5)).to eq(0.1) }
|
123
|
-
it { expect(middleware.send(:calculate_retry_interval, 4)).to eq(0.2) }
|
124
|
-
it { expect(middleware.send(:calculate_retry_interval, 3)).to eq(0.3) }
|
125
|
-
it { expect(middleware.send(:calculate_retry_interval, 2)).to eq(0.3) }
|
126
|
-
end
|
127
|
-
|
128
|
-
context 'with exponential backoff and interval_randomness' do
|
129
|
-
let(:options) { { max: 2, interval: 0.1, interval_randomness: 0.05 } }
|
130
|
-
let(:middleware) { Faraday::Request::Retry.new(nil, options) }
|
131
|
-
|
132
|
-
it { expect(middleware.send(:calculate_retry_interval, 2)).to be_between(0.1, 0.105) }
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
context 'when method is not idempotent' do
|
137
|
-
let(:callback) { -> { raise Errno::ETIMEDOUT } }
|
138
|
-
|
139
|
-
before { expect { conn.post('/unstable') }.to raise_error(Errno::ETIMEDOUT) }
|
140
|
-
|
141
|
-
it { expect(times_called).to eq(1) }
|
142
|
-
end
|
143
|
-
|
144
|
-
describe 'retry_if option' do
|
145
|
-
let(:callback) { -> { raise Errno::ETIMEDOUT } }
|
146
|
-
let(:options) { [{ retry_if: @check }] }
|
147
|
-
|
148
|
-
it 'retries if retry_if block always returns true' do
|
149
|
-
body = { foo: :bar }
|
150
|
-
@check = ->(_, _) { true }
|
151
|
-
expect { conn.post('/unstable', body) }.to raise_error(Errno::ETIMEDOUT)
|
152
|
-
expect(times_called).to eq(3)
|
153
|
-
expect(calls.all? { |env| env[:body] == body }).to be_truthy
|
154
|
-
end
|
155
|
-
|
156
|
-
it 'does not retry if retry_if block returns false checking env' do
|
157
|
-
@check = ->(env, _) { env[:method] != :post }
|
158
|
-
expect { conn.post('/unstable') }.to raise_error(Errno::ETIMEDOUT)
|
159
|
-
expect(times_called).to eq(1)
|
160
|
-
end
|
161
|
-
|
162
|
-
it 'does not retry if retry_if block returns false checking exception' do
|
163
|
-
@check = ->(_, exception) { !exception.is_a?(Errno::ETIMEDOUT) }
|
164
|
-
expect { conn.post('/unstable') }.to raise_error(Errno::ETIMEDOUT)
|
165
|
-
expect(times_called).to eq(1)
|
166
|
-
end
|
167
|
-
|
168
|
-
it 'FilePart: should rewind files on retry' do
|
169
|
-
io = StringIO.new('Test data')
|
170
|
-
filepart = Faraday::FilePart.new(io, 'application/octet/stream')
|
171
|
-
|
172
|
-
rewound = 0
|
173
|
-
rewind = -> { rewound += 1 }
|
174
|
-
|
175
|
-
@check = ->(_, _) { true }
|
176
|
-
allow(filepart).to receive(:rewind, &rewind)
|
177
|
-
expect { conn.post('/unstable', file: filepart) }.to raise_error(Errno::ETIMEDOUT)
|
178
|
-
expect(times_called).to eq(3)
|
179
|
-
expect(rewound).to eq(2)
|
180
|
-
end
|
181
|
-
|
182
|
-
it 'UploadIO: should rewind files on retry' do
|
183
|
-
io = StringIO.new('Test data')
|
184
|
-
upload_io = Faraday::FilePart.new(io, 'application/octet/stream')
|
185
|
-
|
186
|
-
rewound = 0
|
187
|
-
rewind = -> { rewound += 1 }
|
188
|
-
|
189
|
-
@check = ->(_, _) { true }
|
190
|
-
allow(upload_io).to receive(:rewind, &rewind)
|
191
|
-
expect { conn.post('/unstable', file: upload_io) }.to raise_error(Errno::ETIMEDOUT)
|
192
|
-
expect(times_called).to eq(3)
|
193
|
-
expect(rewound).to eq(2)
|
194
|
-
end
|
195
|
-
|
196
|
-
context 'when explicitly specifying methods to retry' do
|
197
|
-
let(:options) { [{ retry_if: @check, methods: [:post] }] }
|
198
|
-
|
199
|
-
it 'does not call retry_if for specified methods' do
|
200
|
-
@check = ->(_, _) { raise 'this should have never been called' }
|
201
|
-
expect { conn.post('/unstable') }.to raise_error(Errno::ETIMEDOUT)
|
202
|
-
expect(times_called).to eq(3)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
context 'with empty list of methods to retry' do
|
207
|
-
let(:options) { [{ retry_if: @check, methods: [] }] }
|
208
|
-
|
209
|
-
it 'calls retry_if for all methods' do
|
210
|
-
@check = ->(_, _) { calls.size < 2 }
|
211
|
-
expect { conn.get('/unstable') }.to raise_error(Errno::ETIMEDOUT)
|
212
|
-
expect(times_called).to eq(2)
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
describe 'retry_after header support' do
|
218
|
-
let(:callback) { -> { [504, headers, ''] } }
|
219
|
-
let(:elapsed) { Time.now - @started }
|
220
|
-
|
221
|
-
before do
|
222
|
-
@started = Time.now
|
223
|
-
conn.get('/unstable')
|
224
|
-
end
|
225
|
-
|
226
|
-
context 'when retry_after bigger than interval' do
|
227
|
-
let(:headers) { { 'Retry-After' => '0.5' } }
|
228
|
-
let(:options) { [{ max: 1, interval: 0.1, retry_statuses: 504 }] }
|
229
|
-
|
230
|
-
it { expect(elapsed).to be > 0.5 }
|
231
|
-
end
|
232
|
-
|
233
|
-
context 'when retry_after smaller than interval' do
|
234
|
-
let(:headers) { { 'Retry-After' => '0.1' } }
|
235
|
-
let(:options) { [{ max: 1, interval: 0.2, retry_statuses: 504 }] }
|
236
|
-
|
237
|
-
it { expect(elapsed).to be > 0.2 }
|
238
|
-
end
|
239
|
-
|
240
|
-
context 'when retry_after is a timestamp' do
|
241
|
-
let(:headers) { { 'Retry-After' => (Time.now.utc + 2).strftime('%a, %d %b %Y %H:%M:%S GMT') } }
|
242
|
-
let(:options) { [{ max: 1, interval: 0.1, retry_statuses: 504 }] }
|
243
|
-
|
244
|
-
it { expect(elapsed).to be > 1 }
|
245
|
-
end
|
246
|
-
|
247
|
-
context 'when retry_after is bigger than max_interval' do
|
248
|
-
let(:headers) { { 'Retry-After' => (Time.now.utc + 20).strftime('%a, %d %b %Y %H:%M:%S GMT') } }
|
249
|
-
let(:options) { [{ max: 2, interval: 0.1, max_interval: 5, retry_statuses: 504 }] }
|
250
|
-
|
251
|
-
it { expect(times_called).to eq(1) }
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|