faraday 2.7.4 → 2.13.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/README.md +23 -11
  4. data/Rakefile +6 -1
  5. data/lib/faraday/adapter/test.rb +3 -3
  6. data/lib/faraday/adapter.rb +1 -1
  7. data/lib/faraday/connection.rb +27 -23
  8. data/lib/faraday/encoders/nested_params_encoder.rb +1 -1
  9. data/lib/faraday/error.rb +21 -3
  10. data/lib/faraday/logging/formatter.rb +13 -17
  11. data/lib/faraday/middleware.rb +40 -1
  12. data/lib/faraday/options/connection_options.rb +7 -6
  13. data/lib/faraday/options/env.rb +68 -69
  14. data/lib/faraday/options/proxy_options.rb +11 -5
  15. data/lib/faraday/options/request_options.rb +7 -6
  16. data/lib/faraday/options/ssl_options.rb +57 -50
  17. data/lib/faraday/options.rb +4 -3
  18. data/lib/faraday/rack_builder.rb +20 -20
  19. data/lib/faraday/request/instrumentation.rb +3 -1
  20. data/lib/faraday/request/json.rb +18 -3
  21. data/lib/faraday/request.rb +10 -7
  22. data/lib/faraday/response/json.rb +21 -1
  23. data/lib/faraday/response/logger.rb +5 -3
  24. data/lib/faraday/response/raise_error.rb +36 -17
  25. data/lib/faraday/utils/headers.rb +8 -2
  26. data/lib/faraday/utils.rb +3 -4
  27. data/lib/faraday/version.rb +1 -1
  28. data/spec/faraday/connection_spec.rb +2 -2
  29. data/spec/faraday/error_spec.rb +39 -6
  30. data/spec/faraday/middleware_spec.rb +143 -0
  31. data/spec/faraday/options/options_spec.rb +1 -1
  32. data/spec/faraday/options/proxy_options_spec.rb +35 -0
  33. data/spec/faraday/params_encoders/nested_spec.rb +2 -1
  34. data/spec/faraday/request/json_spec.rb +88 -0
  35. data/spec/faraday/response/json_spec.rb +89 -0
  36. data/spec/faraday/response/logger_spec.rb +49 -4
  37. data/spec/faraday/response/raise_error_spec.rb +104 -1
  38. data/spec/faraday/utils/headers_spec.rb +9 -0
  39. data/spec/faraday/utils_spec.rb +3 -1
  40. data/spec/faraday_spec.rb +10 -4
  41. data/spec/spec_helper.rb +6 -5
  42. data/spec/support/faraday_middleware_subclasses.rb +18 -0
  43. metadata +26 -14
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- RSpec.describe Faraday::ClientError do
3
+ RSpec.describe Faraday::Error do
4
4
  describe '.initialize' do
5
5
  subject { described_class.new(exception, response) }
6
6
  let(:response) { nil }
@@ -12,8 +12,10 @@ RSpec.describe Faraday::ClientError do
12
12
  it { expect(subject.response).to be_nil }
13
13
  it { expect(subject.message).to eq(exception.message) }
14
14
  it { expect(subject.backtrace).to eq(exception.backtrace) }
15
- it { expect(subject.inspect).to eq('#<Faraday::ClientError wrapped=#<RuntimeError: test>>') }
15
+ it { expect(subject.inspect).to eq('#<Faraday::Error wrapped=#<RuntimeError: test>>') }
16
16
  it { expect(subject.response_status).to be_nil }
17
+ it { expect(subject.response_headers).to be_nil }
18
+ it { expect(subject.response_body).to be_nil }
17
19
  end
18
20
 
19
21
  context 'with response hash' do
@@ -22,8 +24,14 @@ RSpec.describe Faraday::ClientError do
22
24
  it { expect(subject.wrapped_exception).to be_nil }
23
25
  it { expect(subject.response).to eq(exception) }
24
26
  it { expect(subject.message).to eq('the server responded with status 400') }
25
- it { expect(subject.inspect).to eq('#<Faraday::ClientError response={:status=>400}>') }
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
26
32
  it { expect(subject.response_status).to eq(400) }
33
+ it { expect(subject.response_headers).to be_nil }
34
+ it { expect(subject.response_body).to be_nil }
27
35
  end
28
36
 
29
37
  context 'with string' do
@@ -32,8 +40,10 @@ RSpec.describe Faraday::ClientError do
32
40
  it { expect(subject.wrapped_exception).to be_nil }
33
41
  it { expect(subject.response).to be_nil }
34
42
  it { expect(subject.message).to eq('custom message') }
35
- it { expect(subject.inspect).to eq('#<Faraday::ClientError #<Faraday::ClientError: custom message>>') }
43
+ it { expect(subject.inspect).to eq('#<Faraday::Error #<Faraday::Error: custom message>>') }
36
44
  it { expect(subject.response_status).to be_nil }
45
+ it { expect(subject.response_headers).to be_nil }
46
+ it { expect(subject.response_body).to be_nil }
37
47
  end
38
48
 
39
49
  context 'with anything else #to_s' do
@@ -42,8 +52,10 @@ RSpec.describe Faraday::ClientError do
42
52
  it { expect(subject.wrapped_exception).to be_nil }
43
53
  it { expect(subject.response).to be_nil }
44
54
  it { expect(subject.message).to eq('["error1", "error2"]') }
45
- it { expect(subject.inspect).to eq('#<Faraday::ClientError #<Faraday::ClientError: ["error1", "error2"]>>') }
55
+ it { expect(subject.inspect).to eq('#<Faraday::Error #<Faraday::Error: ["error1", "error2"]>>') }
46
56
  it { expect(subject.response_status).to be_nil }
57
+ it { expect(subject.response_headers).to be_nil }
58
+ it { expect(subject.response_body).to be_nil }
47
59
  end
48
60
 
49
61
  context 'with exception string and response hash' do
@@ -53,8 +65,29 @@ RSpec.describe Faraday::ClientError do
53
65
  it { expect(subject.wrapped_exception).to be_nil }
54
66
  it { expect(subject.response).to eq(response) }
55
67
  it { expect(subject.message).to eq('custom message') }
56
- it { expect(subject.inspect).to eq('#<Faraday::ClientError response={:status=>400}>') }
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
57
73
  it { expect(subject.response_status).to eq(400) }
74
+ it { expect(subject.response_headers).to be_nil }
75
+ it { expect(subject.response_body).to be_nil }
76
+ end
77
+
78
+ context 'with exception and response object' do
79
+ let(:exception) { RuntimeError.new('test') }
80
+ let(:body) { { test: 'test' } }
81
+ let(:headers) { { 'Content-Type' => 'application/json' } }
82
+ let(:response) { Faraday::Response.new(status: 400, response_headers: headers, response_body: body) }
83
+
84
+ it { expect(subject.wrapped_exception).to eq(exception) }
85
+ it { expect(subject.response).to eq(response) }
86
+ it { expect(subject.message).to eq(exception.message) }
87
+ it { expect(subject.backtrace).to eq(exception.backtrace) }
88
+ it { expect(subject.response_status).to eq(400) }
89
+ it { expect(subject.response_headers).to eq(headers) }
90
+ it { expect(subject.response_body).to eq(body) }
58
91
  end
59
92
  end
60
93
  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
@@ -2,7 +2,7 @@
2
2
 
3
3
  RSpec.describe Faraday::Options do
4
4
  SubOptions = Class.new(Faraday::Options.new(:sub_a, :sub_b))
5
- class ParentOptions < Faraday::Options.new(:a, :b, :c)
5
+ ParentOptions = Faraday::Options.new(:a, :b, :c) do
6
6
  options c: SubOptions
7
7
  end
8
8
 
@@ -27,11 +27,46 @@ 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
33
60
  expect(proxy.password).to be_nil
34
61
  end
62
+
63
+ it 'treats empty string as nil' do
64
+ proxy = nil
65
+ proxy_string = proxy.to_s # => empty string
66
+ options = Faraday::ProxyOptions.from proxy_string
67
+ expect(options).to be_a_kind_of(Faraday::ProxyOptions)
68
+ expect(options.inspect).to eq('#<Faraday::ProxyOptions (empty)>')
69
+ end
35
70
  end
36
71
 
37
72
  it 'allows hash access' do
@@ -62,7 +62,8 @@ RSpec.describe Faraday::NestedParamsEncoder do
62
62
  it 'encodes rack compat' do
63
63
  params = { a: [{ one: '1', two: '2' }, '3', ''] }
64
64
  result = Faraday::Utils.unescape(Faraday::NestedParamsEncoder.encode(params)).split('&')
65
- expected = Rack::Utils.build_nested_query(params).split('&')
65
+ escaped = Rack::Utils.build_nested_query(params)
66
+ expected = Rack::Utils.unescape(escaped).split('&')
66
67
  expect(result).to match_array(expected)
67
68
  end
68
69
 
@@ -73,6 +73,30 @@ RSpec.describe Faraday::Request::Json do
73
73
  end
74
74
  end
75
75
 
76
+ context 'true body' do
77
+ let(:result) { process(true) }
78
+
79
+ it 'encodes body' do
80
+ expect(result_body).to eq('true')
81
+ end
82
+
83
+ it 'adds content type' do
84
+ expect(result_type).to eq('application/json')
85
+ end
86
+ end
87
+
88
+ context 'false body' do
89
+ let(:result) { process(false) }
90
+
91
+ it 'encodes body' do
92
+ expect(result_body).to eq('false')
93
+ end
94
+
95
+ it 'adds content type' do
96
+ expect(result_type).to eq('application/json')
97
+ end
98
+ end
99
+
76
100
  context 'object body with json type' do
77
101
  let(:result) { process({ a: 1 }, 'application/json; charset=utf-8') }
78
102
 
@@ -108,4 +132,68 @@ RSpec.describe Faraday::Request::Json do
108
132
  expect(result_type).to eq('application/xml; charset=utf-8')
109
133
  end
110
134
  end
135
+
136
+ context 'with encoder' do
137
+ let(:encoder) do
138
+ double('Encoder').tap do |e|
139
+ allow(e).to receive(:dump) { |s, opts| JSON.generate(s, opts) }
140
+ end
141
+ end
142
+
143
+ let(:result) { process(a: 1) }
144
+
145
+ context 'when encoder is passed as object' do
146
+ let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }, { encoder: encoder }) }
147
+
148
+ it 'calls specified JSON encoder\'s dump method' do
149
+ expect(encoder).to receive(:dump).with({ a: 1 })
150
+
151
+ result
152
+ end
153
+
154
+ it 'encodes body' do
155
+ expect(result_body).to eq('{"a":1}')
156
+ end
157
+
158
+ it 'adds content type' do
159
+ expect(result_type).to eq('application/json')
160
+ end
161
+ end
162
+
163
+ context 'when encoder is passed as an object-method pair' do
164
+ let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }, { encoder: [encoder, :dump] }) }
165
+
166
+ it 'calls specified JSON encoder' do
167
+ expect(encoder).to receive(:dump).with({ a: 1 })
168
+
169
+ result
170
+ end
171
+
172
+ it 'encodes body' do
173
+ expect(result_body).to eq('{"a":1}')
174
+ end
175
+
176
+ it 'adds content type' do
177
+ expect(result_type).to eq('application/json')
178
+ end
179
+ end
180
+
181
+ context 'when encoder is not passed' do
182
+ let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }) }
183
+
184
+ it 'calls JSON.generate' do
185
+ expect(JSON).to receive(:generate).with({ a: 1 })
186
+
187
+ result
188
+ end
189
+
190
+ it 'encodes body' do
191
+ expect(result_body).to eq('{"a":1}')
192
+ end
193
+
194
+ it 'adds content type' do
195
+ expect(result_type).to eq('application/json')
196
+ end
197
+ end
198
+ end
111
199
  end
@@ -114,4 +114,93 @@ RSpec.describe Faraday::Response::Json, type: :response do
114
114
  expect(response.body).to eq(result)
115
115
  end
116
116
  end
117
+
118
+ context 'with decoder' do
119
+ let(:decoder) do
120
+ double('Decoder').tap do |e|
121
+ allow(e).to receive(:load) { |s, opts| JSON.parse(s, opts) }
122
+ end
123
+ end
124
+
125
+ let(:body) { '{"a": 1}' }
126
+ let(:result) { { a: 1 } }
127
+
128
+ context 'when decoder is passed as object' do
129
+ let(:options) do
130
+ {
131
+ parser_options: {
132
+ decoder: decoder,
133
+ option: :option_value,
134
+ symbolize_names: true
135
+ }
136
+ }
137
+ end
138
+
139
+ it 'passes relevant options to specified decoder\'s load method' do
140
+ expect(decoder).to receive(:load)
141
+ .with(body, { option: :option_value, symbolize_names: true })
142
+ .and_return(result)
143
+
144
+ response = process(body)
145
+ expect(response.body).to eq(result)
146
+ end
147
+ end
148
+
149
+ context 'when decoder is passed as an object-method pair' do
150
+ let(:options) do
151
+ {
152
+ parser_options: {
153
+ decoder: [decoder, :load],
154
+ option: :option_value,
155
+ symbolize_names: true
156
+ }
157
+ }
158
+ end
159
+
160
+ it 'passes relevant options to specified decoder\'s method' do
161
+ expect(decoder).to receive(:load)
162
+ .with(body, { option: :option_value, symbolize_names: true })
163
+ .and_return(result)
164
+
165
+ response = process(body)
166
+ expect(response.body).to eq(result)
167
+ end
168
+ end
169
+
170
+ context 'when decoder is not passed' do
171
+ let(:options) do
172
+ {
173
+ parser_options: {
174
+ symbolize_names: true
175
+ }
176
+ }
177
+ end
178
+
179
+ it 'passes relevant options to JSON parse' do
180
+ expect(JSON).to receive(:parse)
181
+ .with(body, { symbolize_names: true })
182
+ .and_return(result)
183
+
184
+ response = process(body)
185
+ expect(response.body).to eq(result)
186
+ end
187
+
188
+ it 'passes relevant options to JSON parse even when nil responds to :load' do
189
+ original_allow_message_expectations_on_nil = RSpec::Mocks.configuration.allow_message_expectations_on_nil
190
+ RSpec::Mocks.configuration.allow_message_expectations_on_nil = true
191
+ allow(nil).to receive(:respond_to?)
192
+ .with(:load)
193
+ .and_return(true)
194
+
195
+ expect(JSON).to receive(:parse)
196
+ .with(body, { symbolize_names: true })
197
+ .and_return(result)
198
+
199
+ response = process(body)
200
+ expect(response.body).to eq(result)
201
+ ensure
202
+ RSpec::Mocks.configuration.allow_message_expectations_on_nil = original_allow_message_expectations_on_nil
203
+ end
204
+ end
205
+ end
117
206
  end
@@ -25,6 +25,7 @@ RSpec.describe Faraday::Response::Logger do
25
25
  stubs.get('/filtered_headers') { [200, { 'Content-Type' => 'text/html' }, 'headers response'] }
26
26
  stubs.get('/filtered_params') { [200, { 'Content-Type' => 'text/html' }, 'params response'] }
27
27
  stubs.get('/filtered_url') { [200, { 'Content-Type' => 'text/html' }, 'url response'] }
28
+ stubs.get('/connection_failed') { raise Faraday::ConnectionFailed, 'Failed to open TCP connection' }
28
29
  end
29
30
  end
30
31
  end
@@ -54,6 +55,26 @@ RSpec.describe Faraday::Response::Logger do
54
55
  end
55
56
  end
56
57
 
58
+ context 'when logger with program name' do
59
+ let(:logger) { Logger.new(string_io, progname: 'my_best_program') }
60
+
61
+ it 'logs with program name' do
62
+ conn.get '/hello'
63
+
64
+ expect(string_io.string).to match('-- my_best_program: request:')
65
+ expect(string_io.string).to match('-- my_best_program: response:')
66
+ end
67
+ end
68
+
69
+ context 'when logger without program name' do
70
+ it 'logs without program name' do
71
+ conn.get '/hello'
72
+
73
+ expect(string_io.string).to match('-- : request:')
74
+ expect(string_io.string).to match('-- : response:')
75
+ end
76
+ end
77
+
57
78
  context 'with default formatter' do
58
79
  let(:formatter) { instance_double(Faraday::Logging::Formatter, request: true, response: true, filter: []) }
59
80
 
@@ -168,7 +189,7 @@ RSpec.describe Faraday::Response::Logger do
168
189
  context 'when logging request body' do
169
190
  let(:logger_options) { { bodies: { request: true } } }
170
191
 
171
- it 'log only request body' do
192
+ it 'logs only request body' do
172
193
  conn.post '/ohyes', 'name=Tamago', accept: 'text/html'
173
194
  expect(string_io.string).to match(%(name=Tamago))
174
195
  expect(string_io.string).not_to match(%(pebbles))
@@ -178,7 +199,7 @@ RSpec.describe Faraday::Response::Logger do
178
199
  context 'when logging response body' do
179
200
  let(:logger_options) { { bodies: { response: true } } }
180
201
 
181
- it 'log only response body' do
202
+ it 'logs only response body' do
182
203
  conn.post '/ohyes', 'name=Hamachi', accept: 'text/html'
183
204
  expect(string_io.string).to match(%(pebbles))
184
205
  expect(string_io.string).not_to match(%(name=Hamachi))
@@ -188,13 +209,13 @@ RSpec.describe Faraday::Response::Logger do
188
209
  context 'when logging request and response bodies' do
189
210
  let(:logger_options) { { bodies: true } }
190
211
 
191
- it 'log request and response body' do
212
+ it 'logs request and response body' do
192
213
  conn.post '/ohyes', 'name=Ebi', accept: 'text/html'
193
214
  expect(string_io.string).to match(%(name=Ebi))
194
215
  expect(string_io.string).to match(%(pebbles))
195
216
  end
196
217
 
197
- it 'log response body object' do
218
+ it 'logs response body object' do
198
219
  conn.get '/rubbles', nil, accept: 'text/html'
199
220
  expect(string_io.string).to match(%([\"Barney\", \"Betty\", \"Bam Bam\"]\n))
200
221
  end
@@ -207,6 +228,21 @@ RSpec.describe Faraday::Response::Logger do
207
228
  end
208
229
  end
209
230
 
231
+ context 'when bodies are logged by default' do
232
+ before do
233
+ described_class.default_options = { bodies: true }
234
+ end
235
+
236
+ it 'logs response body' do
237
+ conn.post '/ohai'
238
+ expect(string_io.string).to match(%(fred))
239
+ end
240
+
241
+ after do
242
+ described_class.default_options = { bodies: false }
243
+ end
244
+ end
245
+
210
246
  context 'when logging errors' do
211
247
  let(:logger_options) { { errors: true } }
212
248
 
@@ -216,6 +252,15 @@ RSpec.describe Faraday::Response::Logger do
216
252
  end
217
253
  end
218
254
 
255
+ context 'when logging headers and errors' do
256
+ let(:logger_options) { { headers: true, errors: true } }
257
+
258
+ it 'logs error message' do
259
+ expect { conn.get '/connection_failed' }.to raise_error(Faraday::ConnectionFailed)
260
+ expect(string_io.string).to match(%(Failed to open TCP connection))
261
+ end
262
+ end
263
+
219
264
  context 'when using log_level' do
220
265
  let(:logger_options) { { bodies: true, log_level: :debug } }
221
266