faraday 2.9.2 → 2.14.1
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/CHANGELOG.md +1 -1
- data/lib/faraday/connection.rb +14 -5
- data/lib/faraday/encoders/flat_params_encoder.rb +2 -2
- data/lib/faraday/error.rb +46 -5
- data/lib/faraday/logging/formatter.rb +7 -7
- data/lib/faraday/middleware.rb +40 -1
- data/lib/faraday/options/env.rb +1 -1
- data/lib/faraday/options/proxy_options.rb +4 -2
- data/lib/faraday/options/ssl_options.rb +8 -2
- data/lib/faraday/rack_builder.rb +21 -24
- data/lib/faraday/response/logger.rb +5 -3
- data/lib/faraday/response/raise_error.rb +17 -17
- data/lib/faraday/response.rb +7 -3
- data/lib/faraday/version.rb +1 -1
- data/lib/faraday.rb +2 -1
- data/spec/faraday/connection_spec.rb +33 -0
- data/spec/faraday/error_spec.rb +93 -3
- data/spec/faraday/middleware_spec.rb +143 -0
- data/spec/faraday/options/proxy_options_spec.rb +27 -0
- data/spec/faraday/response/logger_spec.rb +45 -4
- data/spec/faraday/response/raise_error_spec.rb +97 -22
- data/spec/faraday/response_spec.rb +7 -0
- data/spec/faraday/utils_spec.rb +3 -1
- data/spec/support/faraday_middleware_subclasses.rb +18 -0
- metadata +36 -10
data/spec/faraday/error_spec.rb
CHANGED
|
@@ -23,8 +23,12 @@ RSpec.describe Faraday::Error do
|
|
|
23
23
|
|
|
24
24
|
it { expect(subject.wrapped_exception).to be_nil }
|
|
25
25
|
it { expect(subject.response).to eq(exception) }
|
|
26
|
-
it { expect(subject.message).to eq('the server responded with status 400') }
|
|
27
|
-
|
|
26
|
+
it { expect(subject.message).to eq('the server responded with status 400 - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
|
|
27
|
+
if RUBY_VERSION >= '3.4'
|
|
28
|
+
it { expect(subject.inspect).to eq('#<Faraday::Error response={status: 400}>') }
|
|
29
|
+
else
|
|
30
|
+
it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
|
|
31
|
+
end
|
|
28
32
|
it { expect(subject.response_status).to eq(400) }
|
|
29
33
|
it { expect(subject.response_headers).to be_nil }
|
|
30
34
|
it { expect(subject.response_body).to be_nil }
|
|
@@ -61,7 +65,11 @@ RSpec.describe Faraday::Error do
|
|
|
61
65
|
it { expect(subject.wrapped_exception).to be_nil }
|
|
62
66
|
it { expect(subject.response).to eq(response) }
|
|
63
67
|
it { expect(subject.message).to eq('custom message') }
|
|
64
|
-
|
|
68
|
+
if RUBY_VERSION >= '3.4'
|
|
69
|
+
it { expect(subject.inspect).to eq('#<Faraday::Error response={status: 400}>') }
|
|
70
|
+
else
|
|
71
|
+
it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
|
|
72
|
+
end
|
|
65
73
|
it { expect(subject.response_status).to eq(400) }
|
|
66
74
|
it { expect(subject.response_headers).to be_nil }
|
|
67
75
|
it { expect(subject.response_body).to be_nil }
|
|
@@ -81,5 +89,87 @@ RSpec.describe Faraday::Error do
|
|
|
81
89
|
it { expect(subject.response_headers).to eq(headers) }
|
|
82
90
|
it { expect(subject.response_body).to eq(body) }
|
|
83
91
|
end
|
|
92
|
+
|
|
93
|
+
context 'with hash missing status key' do
|
|
94
|
+
let(:exception) { { body: 'error body' } }
|
|
95
|
+
|
|
96
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
97
|
+
it { expect(subject.response).to eq(exception) }
|
|
98
|
+
it { expect(subject.message).to eq('the server responded with status - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context 'with hash with status but missing request data' do
|
|
102
|
+
let(:exception) { { status: 404, body: 'not found' } } # missing request key
|
|
103
|
+
|
|
104
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
105
|
+
it { expect(subject.response).to eq(exception) }
|
|
106
|
+
it { expect(subject.message).to eq('the server responded with status 404 - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context 'with hash with status and request but missing method in request' do
|
|
110
|
+
let(:exception) { { status: 404, body: 'not found', request: { url: 'http://example.com/test' } } } # missing method
|
|
111
|
+
|
|
112
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
113
|
+
it { expect(subject.response).to eq(exception) }
|
|
114
|
+
it { expect(subject.message).to eq('the server responded with status 404 for http://example.com/test') }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
context 'with hash with status and request but missing url in request' do
|
|
118
|
+
let(:exception) { { status: 404, body: 'not found', request: { method: :get } } } # missing url
|
|
119
|
+
|
|
120
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
121
|
+
it { expect(subject.response).to eq(exception) }
|
|
122
|
+
it { expect(subject.message).to eq('the server responded with status 404 for GET ') }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
context 'with properly formed Faraday::Env' do
|
|
126
|
+
# This represents the normal case - a well-formed Faraday::Env object
|
|
127
|
+
# with all the standard properties populated as they would be during
|
|
128
|
+
# a typical HTTP request/response cycle
|
|
129
|
+
let(:exception) { Faraday::Env.new }
|
|
130
|
+
|
|
131
|
+
before do
|
|
132
|
+
exception.status = 500
|
|
133
|
+
exception.method = :post
|
|
134
|
+
exception.url = URI('https://api.example.com/users')
|
|
135
|
+
exception.request = Faraday::RequestOptions.new
|
|
136
|
+
exception.response_headers = { 'content-type' => 'application/json' }
|
|
137
|
+
exception.response_body = '{"error": "Internal server error"}'
|
|
138
|
+
exception.request_headers = { 'authorization' => 'Bearer token123' }
|
|
139
|
+
exception.request_body = '{"name": "John"}'
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
143
|
+
it { expect(subject.response).to eq(exception) }
|
|
144
|
+
it { expect(subject.message).to eq('the server responded with status 500 for POST https://api.example.com/users') }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
context 'with Faraday::Env missing status key' do
|
|
148
|
+
let(:exception) { Faraday::Env.new }
|
|
149
|
+
|
|
150
|
+
before do
|
|
151
|
+
exception[:body] = 'error body'
|
|
152
|
+
# Intentionally not setting status
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
156
|
+
it { expect(subject.response).to eq(exception) }
|
|
157
|
+
it { expect(subject.message).to eq('the server responded with status for ') }
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
context 'with Faraday::Env with direct method and url properties' do
|
|
161
|
+
let(:exception) { Faraday::Env.new }
|
|
162
|
+
|
|
163
|
+
before do
|
|
164
|
+
exception.status = 404
|
|
165
|
+
exception.method = :get
|
|
166
|
+
exception.url = URI('http://example.com/test')
|
|
167
|
+
exception[:body] = 'not found'
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
171
|
+
it { expect(subject.response).to eq(exception) }
|
|
172
|
+
it { expect(subject.message).to eq('the server responded with status 404 for GET http://example.com/test') }
|
|
173
|
+
end
|
|
84
174
|
end
|
|
85
175
|
end
|
|
@@ -67,4 +67,147 @@ RSpec.describe Faraday::Middleware do
|
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
|
+
|
|
71
|
+
describe '::default_options' do
|
|
72
|
+
let(:subclass_no_options) { FaradayMiddlewareSubclasses::SubclassNoOptions }
|
|
73
|
+
let(:subclass_one_option) { FaradayMiddlewareSubclasses::SubclassOneOption }
|
|
74
|
+
let(:subclass_two_options) { FaradayMiddlewareSubclasses::SubclassTwoOptions }
|
|
75
|
+
|
|
76
|
+
def build_conn(resp_middleware)
|
|
77
|
+
Faraday.new do |c|
|
|
78
|
+
c.adapter :test do |stub|
|
|
79
|
+
stub.get('/success') { [200, {}, 'ok'] }
|
|
80
|
+
end
|
|
81
|
+
c.response resp_middleware
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
RSpec.shared_context 'reset @default_options' do
|
|
86
|
+
before(:each) do
|
|
87
|
+
FaradayMiddlewareSubclasses::SubclassNoOptions.instance_variable_set(:@default_options, nil)
|
|
88
|
+
FaradayMiddlewareSubclasses::SubclassOneOption.instance_variable_set(:@default_options, nil)
|
|
89
|
+
FaradayMiddlewareSubclasses::SubclassTwoOptions.instance_variable_set(:@default_options, nil)
|
|
90
|
+
Faraday::Middleware.instance_variable_set(:@default_options, nil)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
after(:all) do
|
|
95
|
+
FaradayMiddlewareSubclasses::SubclassNoOptions.instance_variable_set(:@default_options, nil)
|
|
96
|
+
FaradayMiddlewareSubclasses::SubclassOneOption.instance_variable_set(:@default_options, nil)
|
|
97
|
+
FaradayMiddlewareSubclasses::SubclassTwoOptions.instance_variable_set(:@default_options, nil)
|
|
98
|
+
Faraday::Middleware.instance_variable_set(:@default_options, nil)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context 'with subclass DEFAULT_OPTIONS defined' do
|
|
102
|
+
include_context 'reset @default_options'
|
|
103
|
+
|
|
104
|
+
context 'and without application options configured' do
|
|
105
|
+
let(:resp1) { build_conn(:one_option).get('/success') }
|
|
106
|
+
|
|
107
|
+
it 'has only subclass defaults' do
|
|
108
|
+
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
|
109
|
+
expect(subclass_no_options.default_options).to eq(subclass_no_options::DEFAULT_OPTIONS)
|
|
110
|
+
expect(subclass_one_option.default_options).to eq(subclass_one_option::DEFAULT_OPTIONS)
|
|
111
|
+
expect(subclass_two_options.default_options).to eq(subclass_two_options::DEFAULT_OPTIONS)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it { expect(resp1.body).to eq('ok') }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
context "and with one application's options changed" do
|
|
118
|
+
let(:resp2) { build_conn(:two_options).get('/success') }
|
|
119
|
+
|
|
120
|
+
before(:each) do
|
|
121
|
+
FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options = { some_option: false }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it 'only updates default options of target subclass' do
|
|
125
|
+
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
|
126
|
+
expect(subclass_no_options.default_options).to eq(subclass_no_options::DEFAULT_OPTIONS)
|
|
127
|
+
expect(subclass_one_option.default_options).to eq(subclass_one_option::DEFAULT_OPTIONS)
|
|
128
|
+
expect(subclass_two_options.default_options).to eq({ some_option: false, some_other_option: false })
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it { expect(resp2.body).to eq('ok') }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
context "and with two applications' options changed" do
|
|
135
|
+
let(:resp1) { build_conn(:one_option).get('/success') }
|
|
136
|
+
let(:resp2) { build_conn(:two_options).get('/success') }
|
|
137
|
+
|
|
138
|
+
before(:each) do
|
|
139
|
+
FaradayMiddlewareSubclasses::SubclassOneOption.default_options = { some_other_option: true }
|
|
140
|
+
FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options = { some_option: false }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'updates subclasses and parent independent of each other' do
|
|
144
|
+
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
|
145
|
+
expect(subclass_no_options.default_options).to eq(subclass_no_options::DEFAULT_OPTIONS)
|
|
146
|
+
expect(subclass_one_option.default_options).to eq({ some_other_option: true })
|
|
147
|
+
expect(subclass_two_options.default_options).to eq({ some_option: false, some_other_option: false })
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it { expect(resp1.body).to eq('ok') }
|
|
151
|
+
it { expect(resp2.body).to eq('ok') }
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
context 'with FARADAY::MIDDLEWARE DEFAULT_OPTIONS and with Subclass DEFAULT_OPTIONS' do
|
|
156
|
+
before(:each) do
|
|
157
|
+
stub_const('Faraday::Middleware::DEFAULT_OPTIONS', { its_magic: false })
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Must stub Faraday::Middleware::DEFAULT_OPTIONS before resetting default options
|
|
161
|
+
include_context 'reset @default_options'
|
|
162
|
+
|
|
163
|
+
context 'and without application options configured' do
|
|
164
|
+
let(:resp1) { build_conn(:one_option).get('/success') }
|
|
165
|
+
|
|
166
|
+
it 'has only subclass defaults' do
|
|
167
|
+
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
|
168
|
+
expect(FaradayMiddlewareSubclasses::SubclassNoOptions.default_options).to eq({ its_magic: false })
|
|
169
|
+
expect(FaradayMiddlewareSubclasses::SubclassOneOption.default_options).to eq({ its_magic: false, some_other_option: false })
|
|
170
|
+
expect(FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options).to eq({ its_magic: false, some_option: true, some_other_option: false })
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it { expect(resp1.body).to eq('ok') }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
context "and with two applications' options changed" do
|
|
177
|
+
let(:resp1) { build_conn(:one_option).get('/success') }
|
|
178
|
+
let(:resp2) { build_conn(:two_options).get('/success') }
|
|
179
|
+
|
|
180
|
+
before(:each) do
|
|
181
|
+
FaradayMiddlewareSubclasses::SubclassOneOption.default_options = { some_other_option: true }
|
|
182
|
+
FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options = { some_option: false }
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it 'updates subclasses and parent independent of each other' do
|
|
186
|
+
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
|
187
|
+
expect(FaradayMiddlewareSubclasses::SubclassNoOptions.default_options).to eq({ its_magic: false })
|
|
188
|
+
expect(FaradayMiddlewareSubclasses::SubclassOneOption.default_options).to eq({ its_magic: false, some_other_option: true })
|
|
189
|
+
expect(FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options).to eq({ its_magic: false, some_option: false, some_other_option: false })
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it { expect(resp1.body).to eq('ok') }
|
|
193
|
+
it { expect(resp2.body).to eq('ok') }
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
describe 'default_options input validation' do
|
|
198
|
+
include_context 'reset @default_options'
|
|
199
|
+
|
|
200
|
+
it 'raises error if Faraday::Middleware option does not exist' do
|
|
201
|
+
expect { Faraday::Middleware.default_options = { something_special: true } }.to raise_error(Faraday::InitializationError) do |e|
|
|
202
|
+
expect(e.message).to eq('Invalid options provided. Keys not found in Faraday::Middleware::DEFAULT_OPTIONS: something_special')
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
it 'raises error if subclass option does not exist' do
|
|
207
|
+
expect { subclass_one_option.default_options = { this_is_a_typo: true } }.to raise_error(Faraday::InitializationError) do |e|
|
|
208
|
+
expect(e.message).to eq('Invalid options provided. Keys not found in FaradayMiddlewareSubclasses::SubclassOneOption::DEFAULT_OPTIONS: this_is_a_typo')
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
70
213
|
end
|
|
@@ -27,6 +27,33 @@ RSpec.describe Faraday::ProxyOptions do
|
|
|
27
27
|
expect(options.inspect).to eq('#<Faraday::ProxyOptions (empty)>')
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
it 'works with hash' do
|
|
31
|
+
hash = { user: 'user', password: 'pass', uri: 'http://@example.org' }
|
|
32
|
+
options = Faraday::ProxyOptions.from(hash)
|
|
33
|
+
expect(options.user).to eq('user')
|
|
34
|
+
expect(options.password).to eq('pass')
|
|
35
|
+
expect(options.uri).to be_a_kind_of(URI)
|
|
36
|
+
expect(options.path).to eq('')
|
|
37
|
+
expect(options.port).to eq(80)
|
|
38
|
+
expect(options.host).to eq('example.org')
|
|
39
|
+
expect(options.scheme).to eq('http')
|
|
40
|
+
expect(options.inspect).to match('#<Faraday::ProxyOptions uri=')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'works with option' do
|
|
44
|
+
opt_arg = { user: 'user', password: 'pass', uri: 'http://@example.org' }
|
|
45
|
+
option = Faraday::ConnectionOptions.from(proxy: opt_arg)
|
|
46
|
+
options = Faraday::ProxyOptions.from(option.proxy)
|
|
47
|
+
expect(options.user).to eq('user')
|
|
48
|
+
expect(options.password).to eq('pass')
|
|
49
|
+
expect(options.uri).to be_a_kind_of(URI)
|
|
50
|
+
expect(options.path).to eq('')
|
|
51
|
+
expect(options.port).to eq(80)
|
|
52
|
+
expect(options.host).to eq('example.org')
|
|
53
|
+
expect(options.scheme).to eq('http')
|
|
54
|
+
expect(options.inspect).to match('#<Faraday::ProxyOptions uri=')
|
|
55
|
+
end
|
|
56
|
+
|
|
30
57
|
it 'works with no auth' do
|
|
31
58
|
proxy = Faraday::ProxyOptions.from 'http://example.org'
|
|
32
59
|
expect(proxy.user).to be_nil
|
|
@@ -21,6 +21,7 @@ RSpec.describe Faraday::Response::Logger do
|
|
|
21
21
|
stubs.post('/ohai') { [200, { 'Content-Type' => 'text/html' }, 'fred'] }
|
|
22
22
|
stubs.post('/ohyes') { [200, { 'Content-Type' => 'text/html' }, 'pebbles'] }
|
|
23
23
|
stubs.get('/rubbles') { [200, { 'Content-Type' => 'application/json' }, rubbles] }
|
|
24
|
+
stubs.get('/8bit') { [200, { 'Content-Type' => 'text/html' }, (+'café!').force_encoding(Encoding::ASCII_8BIT)] }
|
|
24
25
|
stubs.get('/filtered_body') { [200, { 'Content-Type' => 'text/html' }, 'soylent green is people'] }
|
|
25
26
|
stubs.get('/filtered_headers') { [200, { 'Content-Type' => 'text/html' }, 'headers response'] }
|
|
26
27
|
stubs.get('/filtered_params') { [200, { 'Content-Type' => 'text/html' }, 'params response'] }
|
|
@@ -55,6 +56,26 @@ RSpec.describe Faraday::Response::Logger do
|
|
|
55
56
|
end
|
|
56
57
|
end
|
|
57
58
|
|
|
59
|
+
context 'when logger with program name' do
|
|
60
|
+
let(:logger) { Logger.new(string_io, progname: 'my_best_program') }
|
|
61
|
+
|
|
62
|
+
it 'logs with program name' do
|
|
63
|
+
conn.get '/hello'
|
|
64
|
+
|
|
65
|
+
expect(string_io.string).to match('-- my_best_program: request:')
|
|
66
|
+
expect(string_io.string).to match('-- my_best_program: response:')
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context 'when logger without program name' do
|
|
71
|
+
it 'logs without program name' do
|
|
72
|
+
conn.get '/hello'
|
|
73
|
+
|
|
74
|
+
expect(string_io.string).to match('-- : request:')
|
|
75
|
+
expect(string_io.string).to match('-- : response:')
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
58
79
|
context 'with default formatter' do
|
|
59
80
|
let(:formatter) { instance_double(Faraday::Logging::Formatter, request: true, response: true, filter: []) }
|
|
60
81
|
|
|
@@ -169,7 +190,7 @@ RSpec.describe Faraday::Response::Logger do
|
|
|
169
190
|
context 'when logging request body' do
|
|
170
191
|
let(:logger_options) { { bodies: { request: true } } }
|
|
171
192
|
|
|
172
|
-
it '
|
|
193
|
+
it 'logs only request body' do
|
|
173
194
|
conn.post '/ohyes', 'name=Tamago', accept: 'text/html'
|
|
174
195
|
expect(string_io.string).to match(%(name=Tamago))
|
|
175
196
|
expect(string_io.string).not_to match(%(pebbles))
|
|
@@ -179,7 +200,7 @@ RSpec.describe Faraday::Response::Logger do
|
|
|
179
200
|
context 'when logging response body' do
|
|
180
201
|
let(:logger_options) { { bodies: { response: true } } }
|
|
181
202
|
|
|
182
|
-
it '
|
|
203
|
+
it 'logs only response body' do
|
|
183
204
|
conn.post '/ohyes', 'name=Hamachi', accept: 'text/html'
|
|
184
205
|
expect(string_io.string).to match(%(pebbles))
|
|
185
206
|
expect(string_io.string).not_to match(%(name=Hamachi))
|
|
@@ -189,13 +210,13 @@ RSpec.describe Faraday::Response::Logger do
|
|
|
189
210
|
context 'when logging request and response bodies' do
|
|
190
211
|
let(:logger_options) { { bodies: true } }
|
|
191
212
|
|
|
192
|
-
it '
|
|
213
|
+
it 'logs request and response body' do
|
|
193
214
|
conn.post '/ohyes', 'name=Ebi', accept: 'text/html'
|
|
194
215
|
expect(string_io.string).to match(%(name=Ebi))
|
|
195
216
|
expect(string_io.string).to match(%(pebbles))
|
|
196
217
|
end
|
|
197
218
|
|
|
198
|
-
it '
|
|
219
|
+
it 'logs response body object' do
|
|
199
220
|
conn.get '/rubbles', nil, accept: 'text/html'
|
|
200
221
|
expect(string_io.string).to match(%([\"Barney\", \"Betty\", \"Bam Bam\"]\n))
|
|
201
222
|
end
|
|
@@ -208,6 +229,26 @@ RSpec.describe Faraday::Response::Logger do
|
|
|
208
229
|
end
|
|
209
230
|
end
|
|
210
231
|
|
|
232
|
+
context 'when bodies are logged by default' do
|
|
233
|
+
before do
|
|
234
|
+
described_class.default_options = { bodies: true }
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
it 'logs response body' do
|
|
238
|
+
conn.post '/ohai'
|
|
239
|
+
expect(string_io.string).to match(%(fred))
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it 'converts to UTF-8' do
|
|
243
|
+
conn.get '/8bit'
|
|
244
|
+
expect(string_io.string).to match(%(caf��!))
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
after do
|
|
248
|
+
described_class.default_options = { bodies: false }
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
211
252
|
context 'when logging errors' do
|
|
212
253
|
let(:logger_options) { { errors: true } }
|
|
213
254
|
|
|
@@ -13,7 +13,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
13
13
|
stub.get('proxy-error') { [407, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
14
14
|
stub.get('request-timeout') { [408, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
15
15
|
stub.get('conflict') { [409, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
16
|
-
stub.get('unprocessable-
|
|
16
|
+
stub.get('unprocessable-content') { [422, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
17
17
|
stub.get('too-many-requests') { [429, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
18
18
|
stub.get('4xx') { [499, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
19
19
|
stub.get('nil-status') { [nil, { 'X-Reason' => 'nil' }, 'fail'] }
|
|
@@ -28,7 +28,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
28
28
|
|
|
29
29
|
it 'raises Faraday::BadRequestError for 400 responses' do
|
|
30
30
|
expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError) do |ex|
|
|
31
|
-
expect(ex.message).to eq('the server responded with status 400')
|
|
31
|
+
expect(ex.message).to eq('the server responded with status 400 for GET http:/bad-request')
|
|
32
32
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
33
33
|
expect(ex.response[:status]).to eq(400)
|
|
34
34
|
expect(ex.response_status).to eq(400)
|
|
@@ -39,7 +39,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
39
39
|
|
|
40
40
|
it 'raises Faraday::UnauthorizedError for 401 responses' do
|
|
41
41
|
expect { conn.get('unauthorized') }.to raise_error(Faraday::UnauthorizedError) do |ex|
|
|
42
|
-
expect(ex.message).to eq('the server responded with status 401')
|
|
42
|
+
expect(ex.message).to eq('the server responded with status 401 for GET http:/unauthorized')
|
|
43
43
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
44
44
|
expect(ex.response[:status]).to eq(401)
|
|
45
45
|
expect(ex.response_status).to eq(401)
|
|
@@ -50,7 +50,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
50
50
|
|
|
51
51
|
it 'raises Faraday::ForbiddenError for 403 responses' do
|
|
52
52
|
expect { conn.get('forbidden') }.to raise_error(Faraday::ForbiddenError) do |ex|
|
|
53
|
-
expect(ex.message).to eq('the server responded with status 403')
|
|
53
|
+
expect(ex.message).to eq('the server responded with status 403 for GET http:/forbidden')
|
|
54
54
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
55
55
|
expect(ex.response[:status]).to eq(403)
|
|
56
56
|
expect(ex.response_status).to eq(403)
|
|
@@ -61,7 +61,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
61
61
|
|
|
62
62
|
it 'raises Faraday::ResourceNotFound for 404 responses' do
|
|
63
63
|
expect { conn.get('not-found') }.to raise_error(Faraday::ResourceNotFound) do |ex|
|
|
64
|
-
expect(ex.message).to eq('the server responded with status 404')
|
|
64
|
+
expect(ex.message).to eq('the server responded with status 404 for GET http:/not-found')
|
|
65
65
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
66
66
|
expect(ex.response[:status]).to eq(404)
|
|
67
67
|
expect(ex.response_status).to eq(404)
|
|
@@ -83,7 +83,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
83
83
|
|
|
84
84
|
it 'raises Faraday::RequestTimeoutError for 408 responses' do
|
|
85
85
|
expect { conn.get('request-timeout') }.to raise_error(Faraday::RequestTimeoutError) do |ex|
|
|
86
|
-
expect(ex.message).to eq('the server responded with status 408')
|
|
86
|
+
expect(ex.message).to eq('the server responded with status 408 for GET http:/request-timeout')
|
|
87
87
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
88
88
|
expect(ex.response[:status]).to eq(408)
|
|
89
89
|
expect(ex.response_status).to eq(408)
|
|
@@ -94,7 +94,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
94
94
|
|
|
95
95
|
it 'raises Faraday::ConflictError for 409 responses' do
|
|
96
96
|
expect { conn.get('conflict') }.to raise_error(Faraday::ConflictError) do |ex|
|
|
97
|
-
expect(ex.message).to eq('the server responded with status 409')
|
|
97
|
+
expect(ex.message).to eq('the server responded with status 409 for GET http:/conflict')
|
|
98
98
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
99
99
|
expect(ex.response[:status]).to eq(409)
|
|
100
100
|
expect(ex.response_status).to eq(409)
|
|
@@ -103,9 +103,20 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
-
it 'raises Faraday::UnprocessableEntityError for 422 responses' do
|
|
107
|
-
expect { conn.get('unprocessable-
|
|
108
|
-
expect(ex.message).to eq('the server responded with status 422')
|
|
106
|
+
it 'raises legacy Faraday::UnprocessableEntityError for 422 responses' do
|
|
107
|
+
expect { conn.get('unprocessable-content') }.to raise_error(Faraday::UnprocessableEntityError) do |ex|
|
|
108
|
+
expect(ex.message).to eq('the server responded with status 422 for GET http:/unprocessable-content')
|
|
109
|
+
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
110
|
+
expect(ex.response[:status]).to eq(422)
|
|
111
|
+
expect(ex.response_status).to eq(422)
|
|
112
|
+
expect(ex.response_body).to eq('keep looking')
|
|
113
|
+
expect(ex.response_headers['X-Reason']).to eq('because')
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'raises Faraday::UnprocessableContentError for 422 responses' do
|
|
118
|
+
expect { conn.get('unprocessable-content') }.to raise_error(Faraday::UnprocessableContentError) do |ex|
|
|
119
|
+
expect(ex.message).to eq('the server responded with status 422 for GET http:/unprocessable-content')
|
|
109
120
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
110
121
|
expect(ex.response[:status]).to eq(422)
|
|
111
122
|
expect(ex.response_status).to eq(422)
|
|
@@ -116,7 +127,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
116
127
|
|
|
117
128
|
it 'raises Faraday::TooManyRequestsError for 429 responses' do
|
|
118
129
|
expect { conn.get('too-many-requests') }.to raise_error(Faraday::TooManyRequestsError) do |ex|
|
|
119
|
-
expect(ex.message).to eq('the server responded with status 429')
|
|
130
|
+
expect(ex.message).to eq('the server responded with status 429 for GET http:/too-many-requests')
|
|
120
131
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
121
132
|
expect(ex.response[:status]).to eq(429)
|
|
122
133
|
expect(ex.response_status).to eq(429)
|
|
@@ -138,7 +149,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
138
149
|
|
|
139
150
|
it 'raises Faraday::ClientError for other 4xx responses' do
|
|
140
151
|
expect { conn.get('4xx') }.to raise_error(Faraday::ClientError) do |ex|
|
|
141
|
-
expect(ex.message).to eq('the server responded with status 499')
|
|
152
|
+
expect(ex.message).to eq('the server responded with status 499 for GET http:/4xx')
|
|
142
153
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
143
154
|
expect(ex.response[:status]).to eq(499)
|
|
144
155
|
expect(ex.response_status).to eq(499)
|
|
@@ -149,7 +160,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
149
160
|
|
|
150
161
|
it 'raises Faraday::ServerError for 500 responses' do
|
|
151
162
|
expect { conn.get('server-error') }.to raise_error(Faraday::ServerError) do |ex|
|
|
152
|
-
expect(ex.message).to eq('the server responded with status 500')
|
|
163
|
+
expect(ex.message).to eq('the server responded with status 500 for GET http:/server-error')
|
|
153
164
|
expect(ex.response[:headers]['X-Error']).to eq('bailout')
|
|
154
165
|
expect(ex.response[:status]).to eq(500)
|
|
155
166
|
expect(ex.response_status).to eq(500)
|
|
@@ -194,18 +205,82 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
194
205
|
end
|
|
195
206
|
end
|
|
196
207
|
|
|
197
|
-
|
|
198
|
-
|
|
208
|
+
describe 'DEFAULT_OPTION: include_request' do
|
|
209
|
+
before(:each) do
|
|
210
|
+
Faraday::Response::RaiseError.instance_variable_set(:@default_options, nil)
|
|
211
|
+
Faraday::Middleware.instance_variable_set(:@default_options, nil)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
after(:all) do
|
|
215
|
+
Faraday::Response::RaiseError.instance_variable_set(:@default_options, nil)
|
|
216
|
+
Faraday::Middleware.instance_variable_set(:@default_options, nil)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
context 'when RaiseError DEFAULT_OPTION (include_request: true) is used' do
|
|
220
|
+
it 'includes request info in the exception' do
|
|
221
|
+
expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
|
|
222
|
+
expect(ex.response.keys).to contain_exactly(
|
|
223
|
+
:status,
|
|
224
|
+
:headers,
|
|
225
|
+
:body,
|
|
226
|
+
:request
|
|
227
|
+
)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
context 'when application sets default_options `include_request: false`' do
|
|
233
|
+
before(:each) do
|
|
234
|
+
Faraday::Response::RaiseError.default_options = { include_request: false }
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
context 'and when include_request option is omitted' do
|
|
238
|
+
it 'does not include request info in the exception' do
|
|
239
|
+
expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
|
|
240
|
+
expect(ex.response.keys).to contain_exactly(
|
|
241
|
+
:status,
|
|
242
|
+
:headers,
|
|
243
|
+
:body
|
|
244
|
+
)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
199
248
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
249
|
+
context 'and when include_request option is explicitly set for instance' do
|
|
250
|
+
let(:middleware_options) { { include_request: true } }
|
|
251
|
+
|
|
252
|
+
it 'includes request info in the exception' do
|
|
253
|
+
expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
|
|
254
|
+
expect(ex.response.keys).to contain_exactly(
|
|
255
|
+
:status,
|
|
256
|
+
:headers,
|
|
257
|
+
:body,
|
|
258
|
+
:request
|
|
259
|
+
)
|
|
260
|
+
end
|
|
261
|
+
end
|
|
207
262
|
end
|
|
208
263
|
end
|
|
209
264
|
end
|
|
210
265
|
end
|
|
266
|
+
|
|
267
|
+
describe 'allowing certain status codes' do
|
|
268
|
+
let(:conn) do
|
|
269
|
+
Faraday.new do |b|
|
|
270
|
+
b.response :raise_error, allowed_statuses: [404]
|
|
271
|
+
b.adapter :test do |stub|
|
|
272
|
+
stub.get('bad-request') { [400, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
273
|
+
stub.get('not-found') { [404, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
it 'raises an error for status codes that are not explicitly allowed' do
|
|
279
|
+
expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it 'does not raise an error for allowed status codes' do
|
|
283
|
+
expect { conn.get('not-found') }.not_to raise_error
|
|
284
|
+
end
|
|
285
|
+
end
|
|
211
286
|
end
|
|
@@ -13,6 +13,7 @@ RSpec.describe Faraday::Response do
|
|
|
13
13
|
it { expect(subject.success?).to be_falsey }
|
|
14
14
|
it { expect(subject.status).to eq(404) }
|
|
15
15
|
it { expect(subject.body).to eq('yikes') }
|
|
16
|
+
it { expect(subject.url).to eq(URI('https://lostisland.github.io/faraday')) }
|
|
16
17
|
it { expect(subject.headers['Content-Type']).to eq('text/plain') }
|
|
17
18
|
it { expect(subject['content-type']).to eq('text/plain') }
|
|
18
19
|
|
|
@@ -31,6 +32,12 @@ RSpec.describe Faraday::Response do
|
|
|
31
32
|
it { expect(hash[:response_headers]).to eq(subject.headers) }
|
|
32
33
|
it { expect(hash[:body]).to eq(subject.body) }
|
|
33
34
|
it { expect(hash[:url]).to eq(subject.env.url) }
|
|
35
|
+
|
|
36
|
+
context 'when response is not finished' do
|
|
37
|
+
subject { Faraday::Response.new.to_hash }
|
|
38
|
+
|
|
39
|
+
it { is_expected.to eq({ status: nil, body: nil, response_headers: {}, url: nil }) }
|
|
40
|
+
end
|
|
34
41
|
end
|
|
35
42
|
|
|
36
43
|
describe 'marshal serialization support' do
|
data/spec/faraday/utils_spec.rb
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FaradayMiddlewareSubclasses
|
|
4
|
+
class SubclassNoOptions < Faraday::Middleware
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class SubclassOneOption < Faraday::Middleware
|
|
8
|
+
DEFAULT_OPTIONS = { some_other_option: false }.freeze
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class SubclassTwoOptions < Faraday::Middleware
|
|
12
|
+
DEFAULT_OPTIONS = { some_option: true, some_other_option: false }.freeze
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Faraday::Response.register_middleware(no_options: FaradayMiddlewareSubclasses::SubclassNoOptions)
|
|
17
|
+
Faraday::Response.register_middleware(one_option: FaradayMiddlewareSubclasses::SubclassOneOption)
|
|
18
|
+
Faraday::Response.register_middleware(two_options: FaradayMiddlewareSubclasses::SubclassTwoOptions)
|