faraday 1.10.4 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +198 -4
  3. data/LICENSE.md +1 -1
  4. data/README.md +34 -20
  5. data/Rakefile +6 -1
  6. data/examples/client_spec.rb +41 -19
  7. data/examples/client_test.rb +48 -22
  8. data/lib/faraday/adapter/test.rb +62 -13
  9. data/lib/faraday/adapter.rb +6 -10
  10. data/lib/faraday/connection.rb +72 -150
  11. data/lib/faraday/encoders/flat_params_encoder.rb +2 -2
  12. data/lib/faraday/encoders/nested_params_encoder.rb +14 -7
  13. data/lib/faraday/error.rb +66 -10
  14. data/lib/faraday/logging/formatter.rb +30 -17
  15. data/lib/faraday/middleware.rb +43 -2
  16. data/lib/faraday/middleware_registry.rb +17 -63
  17. data/lib/faraday/options/connection_options.rb +7 -6
  18. data/lib/faraday/options/env.rb +85 -62
  19. data/lib/faraday/options/proxy_options.rb +11 -5
  20. data/lib/faraday/options/request_options.rb +7 -6
  21. data/lib/faraday/options/ssl_options.rb +62 -45
  22. data/lib/faraday/options.rb +7 -6
  23. data/lib/faraday/rack_builder.rb +44 -45
  24. data/lib/faraday/request/authorization.rb +33 -41
  25. data/lib/faraday/request/instrumentation.rb +5 -1
  26. data/lib/faraday/request/json.rb +18 -3
  27. data/lib/faraday/request/url_encoded.rb +5 -1
  28. data/lib/faraday/request.rb +15 -30
  29. data/lib/faraday/response/json.rb +25 -5
  30. data/lib/faraday/response/logger.rb +11 -3
  31. data/lib/faraday/response/raise_error.rb +45 -18
  32. data/lib/faraday/response.rb +14 -22
  33. data/lib/faraday/utils/headers.rb +15 -4
  34. data/lib/faraday/utils.rb +11 -7
  35. data/lib/faraday/version.rb +1 -1
  36. data/lib/faraday.rb +10 -45
  37. data/spec/faraday/adapter/test_spec.rb +65 -0
  38. data/spec/faraday/connection_spec.rb +165 -93
  39. data/spec/faraday/error_spec.rb +122 -7
  40. data/spec/faraday/middleware_registry_spec.rb +31 -0
  41. data/spec/faraday/middleware_spec.rb +161 -0
  42. data/spec/faraday/options/env_spec.rb +8 -2
  43. data/spec/faraday/options/options_spec.rb +1 -1
  44. data/spec/faraday/options/proxy_options_spec.rb +35 -0
  45. data/spec/faraday/params_encoders/nested_spec.rb +10 -1
  46. data/spec/faraday/rack_builder_spec.rb +26 -54
  47. data/spec/faraday/request/authorization_spec.rb +50 -28
  48. data/spec/faraday/request/instrumentation_spec.rb +5 -7
  49. data/spec/faraday/request/json_spec.rb +88 -0
  50. data/spec/faraday/request/url_encoded_spec.rb +12 -2
  51. data/spec/faraday/request_spec.rb +5 -15
  52. data/spec/faraday/response/json_spec.rb +93 -6
  53. data/spec/faraday/response/logger_spec.rb +83 -4
  54. data/spec/faraday/response/raise_error_spec.rb +133 -16
  55. data/spec/faraday/response_spec.rb +10 -1
  56. data/spec/faraday/utils/headers_spec.rb +31 -4
  57. data/spec/faraday/utils_spec.rb +65 -1
  58. data/spec/faraday_spec.rb +10 -4
  59. data/spec/spec_helper.rb +5 -6
  60. data/spec/support/fake_safe_buffer.rb +1 -1
  61. data/spec/support/faraday_middleware_subclasses.rb +18 -0
  62. data/spec/support/helper_methods.rb +0 -37
  63. data/spec/support/shared_examples/adapter.rb +2 -2
  64. data/spec/support/shared_examples/request_method.rb +22 -21
  65. metadata +24 -149
  66. data/lib/faraday/adapter/typhoeus.rb +0 -15
  67. data/lib/faraday/autoload.rb +0 -89
  68. data/lib/faraday/dependency_loader.rb +0 -39
  69. data/lib/faraday/deprecate.rb +0 -110
  70. data/lib/faraday/request/basic_authentication.rb +0 -20
  71. data/lib/faraday/request/token_authentication.rb +0 -20
  72. data/spec/faraday/adapter/em_http_spec.rb +0 -49
  73. data/spec/faraday/adapter/em_synchrony_spec.rb +0 -18
  74. data/spec/faraday/adapter/excon_spec.rb +0 -49
  75. data/spec/faraday/adapter/httpclient_spec.rb +0 -73
  76. data/spec/faraday/adapter/net_http_spec.rb +0 -64
  77. data/spec/faraday/adapter/patron_spec.rb +0 -18
  78. data/spec/faraday/adapter/rack_spec.rb +0 -8
  79. data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
  80. data/spec/faraday/composite_read_io_spec.rb +0 -80
  81. data/spec/faraday/deprecate_spec.rb +0 -147
  82. data/spec/faraday/response/middleware_spec.rb +0 -68
  83. data/spec/support/webmock_rack_app.rb +0 -68
@@ -3,7 +3,7 @@
3
3
  RSpec.describe Faraday::Request::Authorization do
4
4
  let(:conn) do
5
5
  Faraday.new do |b|
6
- b.request auth_type, *auth_config
6
+ b.request :authorization, auth_type, *auth_config
7
7
  b.adapter :test do |stub|
8
8
  stub.get('/auth-echo') do |env|
9
9
  [200, {}, env[:request_headers]['Authorization']]
@@ -14,10 +14,10 @@ RSpec.describe Faraday::Request::Authorization do
14
14
 
15
15
  shared_examples 'does not interfere with existing authentication' do
16
16
  context 'and request already has an authentication header' do
17
- let(:response) { conn.get('/auth-echo', nil, authorization: 'Token token="bar"') }
17
+ let(:response) { conn.get('/auth-echo', nil, authorization: 'OAuth oauth_token') }
18
18
 
19
19
  it 'does not interfere with existing authorization' do
20
- expect(response.body).to eq('Token token="bar"')
20
+ expect(response.body).to eq('OAuth oauth_token')
21
21
  end
22
22
  end
23
23
  end
@@ -25,7 +25,7 @@ RSpec.describe Faraday::Request::Authorization do
25
25
  let(:response) { conn.get('/auth-echo') }
26
26
 
27
27
  describe 'basic_auth' do
28
- let(:auth_type) { :basic_auth }
28
+ let(:auth_type) { :basic }
29
29
 
30
30
  context 'when passed correct params' do
31
31
  let(:auth_config) { %w[aladdin opensesame] }
@@ -44,51 +44,73 @@ RSpec.describe Faraday::Request::Authorization do
44
44
  end
45
45
  end
46
46
 
47
- describe 'token_auth' do
48
- let(:auth_type) { :token_auth }
47
+ describe 'authorization' do
48
+ let(:auth_type) { :Bearer }
49
49
 
50
- context 'when passed correct params' do
51
- let(:auth_config) { 'quux' }
50
+ context 'when passed a string' do
51
+ let(:auth_config) { ['custom'] }
52
52
 
53
- it { expect(response.body).to eq('Token token="quux"') }
53
+ it { expect(response.body).to eq('Bearer custom') }
54
54
 
55
55
  include_examples 'does not interfere with existing authentication'
56
56
  end
57
57
 
58
- context 'when other values are provided' do
59
- let(:auth_config) { ['baz', { foo: 42 }] }
58
+ context 'when passed a proc' do
59
+ let(:auth_config) { [-> { 'custom_from_proc' }] }
60
60
 
61
- it { expect(response.body).to match(/^Token /) }
62
- it { expect(response.body).to match(/token="baz"/) }
63
- it { expect(response.body).to match(/foo="42"/) }
61
+ it { expect(response.body).to eq('Bearer custom_from_proc') }
64
62
 
65
63
  include_examples 'does not interfere with existing authentication'
66
64
  end
67
- end
68
-
69
- describe 'authorization' do
70
- let(:auth_type) { :authorization }
71
65
 
72
- context 'when passed two strings' do
73
- let(:auth_config) { ['custom', 'abc def'] }
66
+ context 'when passed a callable' do
67
+ let(:callable) { double('Callable Authorizer', call: 'custom_from_callable') }
68
+ let(:auth_config) { [callable] }
74
69
 
75
- it { expect(response.body).to eq('custom abc def') }
70
+ it { expect(response.body).to eq('Bearer custom_from_callable') }
76
71
 
77
72
  include_examples 'does not interfere with existing authentication'
78
73
  end
79
74
 
80
- context 'when passed a string and a hash' do
81
- let(:auth_config) { ['baz', { foo: 42 }] }
75
+ context 'with an argument' do
76
+ let(:response) { conn.get('/auth-echo', nil, 'middle' => 'crunchy surprise') }
82
77
 
83
- it { expect(response.body).to eq('baz foo="42"') }
78
+ context 'when passed a proc' do
79
+ let(:auth_config) { [proc { |env| "proc #{env.request_headers['middle']}" }] }
84
80
 
85
- include_examples 'does not interfere with existing authentication'
81
+ it { expect(response.body).to eq('Bearer proc crunchy surprise') }
82
+
83
+ include_examples 'does not interfere with existing authentication'
84
+ end
85
+
86
+ context 'when passed a lambda' do
87
+ let(:auth_config) { [->(env) { "lambda #{env.request_headers['middle']}" }] }
88
+
89
+ it { expect(response.body).to eq('Bearer lambda crunchy surprise') }
90
+
91
+ include_examples 'does not interfere with existing authentication'
92
+ end
93
+
94
+ context 'when passed a callable with an argument' do
95
+ let(:callable) do
96
+ Class.new do
97
+ def call(env)
98
+ "callable #{env.request_headers['middle']}"
99
+ end
100
+ end.new
101
+ end
102
+ let(:auth_config) { [callable] }
103
+
104
+ it { expect(response.body).to eq('Bearer callable crunchy surprise') }
105
+
106
+ include_examples 'does not interfere with existing authentication'
107
+ end
86
108
  end
87
109
 
88
- context 'when passed a string and a proc' do
89
- let(:auth_config) { ['Bearer', -> { 'custom_from_proc' }] }
110
+ context 'when passed too many arguments' do
111
+ let(:auth_config) { %w[baz foo] }
90
112
 
91
- it { expect(response.body).to eq('Bearer custom_from_proc') }
113
+ it { expect { response }.to raise_error(ArgumentError) }
92
114
 
93
115
  include_examples 'does not interfere with existing authentication'
94
116
  end
@@ -30,13 +30,11 @@ RSpec.describe Faraday::Request::Instrumentation do
30
30
 
31
31
  it { expect(options.name).to eq('request.faraday') }
32
32
  it 'defaults to ActiveSupport::Notifications' do
33
- begin
34
- res = options.instrumenter
35
- rescue NameError => e
36
- expect(e.to_s).to match('ActiveSupport')
37
- else
38
- expect(res).to eq(ActiveSupport::Notifications)
39
- end
33
+ res = options.instrumenter
34
+ rescue NameError => e
35
+ expect(e.to_s).to match('ActiveSupport')
36
+ else
37
+ expect(res).to eq(ActiveSupport::Notifications)
40
38
  end
41
39
 
42
40
  it 'instruments with default name' do
@@ -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
@@ -1,14 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'stringio'
4
+
3
5
  RSpec.describe Faraday::Request::UrlEncoded do
4
6
  let(:conn) do
5
7
  Faraday.new do |b|
6
- b.request :multipart
7
8
  b.request :url_encoded
8
9
  b.adapter :test do |stub|
9
10
  stub.post('/echo') do |env|
10
11
  posted_as = env[:request_headers]['Content-Type']
11
- [200, { 'Content-Type' => posted_as }, env[:body]]
12
+ body = env[:body]
13
+ if body.respond_to?(:read)
14
+ body = body.read
15
+ end
16
+ [200, { 'Content-Type' => posted_as }, body]
12
17
  end
13
18
  end
14
19
  end
@@ -68,6 +73,11 @@ RSpec.describe Faraday::Request::UrlEncoded do
68
73
  expect(response.body).to eq('a%5Bb%5D%5Bc%5D%5B%5D=d')
69
74
  end
70
75
 
76
+ it 'works with files' do
77
+ response = conn.post('/echo', StringIO.new('str=apple'))
78
+ expect(response.body).to eq('str=apple')
79
+ end
80
+
71
81
  context 'customising default_space_encoding' do
72
82
  around do |example|
73
83
  Faraday::Utils.default_space_encoding = '%20'
@@ -2,7 +2,7 @@
2
2
 
3
3
  RSpec.describe Faraday::Request do
4
4
  let(:conn) do
5
- Faraday.new(url: 'http://sushi.com/api',
5
+ Faraday.new(url: 'http://httpbingo.org/api',
6
6
  headers: { 'Mime-Version' => '1.0' },
7
7
  request: { oauth: { consumer_key: 'anonymous' } })
8
8
  end
@@ -14,6 +14,7 @@ RSpec.describe Faraday::Request do
14
14
  context 'when nothing particular is configured' do
15
15
  it { expect(subject.http_method).to eq(:get) }
16
16
  it { expect(subject.to_env(conn).ssl.verify).to be_falsey }
17
+ it { expect(subject.to_env(conn).ssl.verify_hostname).to be_falsey }
17
18
  end
18
19
 
19
20
  context 'when HTTP method is post' do
@@ -22,23 +23,12 @@ RSpec.describe Faraday::Request do
22
23
  it { expect(subject.http_method).to eq(:post) }
23
24
  end
24
25
 
25
- describe 'deprecate method for HTTP method' do
26
- let(:http_method) { :post }
27
- let(:expected_warning) do
28
- %r{NOTE: Faraday::Request#method is deprecated; use http_method instead\. It will be removed in or after version 2.0 \nFaraday::Request#method called from .+/spec/faraday/request_spec.rb:\d+.}
29
- end
30
-
31
- it { expect(subject.method).to eq(:post) }
32
-
33
- it { expect { subject.method }.to output(expected_warning).to_stderr }
34
- end
35
-
36
26
  context 'when setting the url on setup with a URI' do
37
27
  let(:block) { proc { |req| req.url URI.parse('foo.json?a=1') } }
38
28
 
39
29
  it { expect(subject.path).to eq(URI.parse('foo.json')) }
40
30
  it { expect(subject.params).to eq('a' => '1') }
41
- it { expect(subject.to_env(conn).url.to_s).to eq('http://sushi.com/api/foo.json?a=1') }
31
+ it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1') }
42
32
  end
43
33
 
44
34
  context 'when setting the url on setup with a string path and params' do
@@ -46,7 +36,7 @@ RSpec.describe Faraday::Request do
46
36
 
47
37
  it { expect(subject.path).to eq('foo.json') }
48
38
  it { expect(subject.params).to eq('a' => 1) }
49
- it { expect(subject.to_env(conn).url.to_s).to eq('http://sushi.com/api/foo.json?a=1') }
39
+ it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1') }
50
40
  end
51
41
 
52
42
  context 'when setting the url on setup with a path including params' do
@@ -54,7 +44,7 @@ RSpec.describe Faraday::Request do
54
44
 
55
45
  it { expect(subject.path).to eq('foo.json') }
56
46
  it { expect(subject.params).to eq('a' => '1', 'b' => '2') }
57
- it { expect(subject.to_env(conn).url.to_s).to eq('http://sushi.com/api/foo.json?a=1&b=2') }
47
+ it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1&b=2') }
58
48
  end
59
49
 
60
50
  context 'when setting a header on setup with []= syntax' do
@@ -76,12 +76,10 @@ RSpec.describe Faraday::Response::Json, type: :response do
76
76
  end
77
77
 
78
78
  it 'includes the response on the ParsingError instance' do
79
- begin
80
- process('{') { |env| env[:response] = Faraday::Response.new }
81
- raise 'Parsing should have failed.'
82
- rescue Faraday::ParsingError => e
83
- expect(e.response).to be_a(Faraday::Response)
84
- end
79
+ process('{') { |env| env[:response] = Faraday::Response.new }
80
+ raise 'Parsing should have failed.'
81
+ rescue Faraday::ParsingError => e
82
+ expect(e.response).to be_a(Faraday::Response)
85
83
  end
86
84
 
87
85
  context 'HEAD responses' do
@@ -116,4 +114,93 @@ RSpec.describe Faraday::Response::Json, type: :response do
116
114
  expect(response.body).to eq(result)
117
115
  end
118
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
119
206
  end
@@ -21,10 +21,12 @@ 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'] }
27
28
  stubs.get('/filtered_url') { [200, { 'Content-Type' => 'text/html' }, 'url response'] }
29
+ stubs.get('/connection_failed') { raise Faraday::ConnectionFailed, 'Failed to open TCP connection' }
28
30
  end
29
31
  end
30
32
  end
@@ -54,6 +56,26 @@ RSpec.describe Faraday::Response::Logger do
54
56
  end
55
57
  end
56
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
+
57
79
  context 'with default formatter' do
58
80
  let(:formatter) { instance_double(Faraday::Logging::Formatter, request: true, response: true, filter: []) }
59
81
 
@@ -64,6 +86,15 @@ RSpec.describe Faraday::Response::Logger do
64
86
  expect(formatter).to receive(:response).with(an_instance_of(Faraday::Env))
65
87
  conn.get '/hello'
66
88
  end
89
+
90
+ context 'when no route' do
91
+ it 'delegates logging to the formatter' do
92
+ expect(formatter).to receive(:request).with(an_instance_of(Faraday::Env))
93
+ expect(formatter).to receive(:exception).with(an_instance_of(Faraday::Adapter::Test::Stubs::NotFound))
94
+
95
+ expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound)
96
+ end
97
+ end
67
98
  end
68
99
 
69
100
  context 'with custom formatter' do
@@ -94,6 +125,16 @@ RSpec.describe Faraday::Response::Logger do
94
125
  expect(string_io.string).to match('GET http:/hello')
95
126
  end
96
127
 
128
+ it 'logs status' do
129
+ conn.get '/hello', nil, accept: 'text/html'
130
+ expect(string_io.string).to match('Status 200')
131
+ end
132
+
133
+ it 'does not log error message by default' do
134
+ expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound)
135
+ expect(string_io.string).not_to match(%(no stubbed request for get http:/noroute))
136
+ end
137
+
97
138
  it 'logs request headers by default' do
98
139
  conn.get '/hello', nil, accept: 'text/html'
99
140
  expect(string_io.string).to match(%(Accept: "text/html))
@@ -149,7 +190,7 @@ RSpec.describe Faraday::Response::Logger do
149
190
  context 'when logging request body' do
150
191
  let(:logger_options) { { bodies: { request: true } } }
151
192
 
152
- it 'log only request body' do
193
+ it 'logs only request body' do
153
194
  conn.post '/ohyes', 'name=Tamago', accept: 'text/html'
154
195
  expect(string_io.string).to match(%(name=Tamago))
155
196
  expect(string_io.string).not_to match(%(pebbles))
@@ -159,7 +200,7 @@ RSpec.describe Faraday::Response::Logger do
159
200
  context 'when logging response body' do
160
201
  let(:logger_options) { { bodies: { response: true } } }
161
202
 
162
- it 'log only response body' do
203
+ it 'logs only response body' do
163
204
  conn.post '/ohyes', 'name=Hamachi', accept: 'text/html'
164
205
  expect(string_io.string).to match(%(pebbles))
165
206
  expect(string_io.string).not_to match(%(name=Hamachi))
@@ -169,13 +210,13 @@ RSpec.describe Faraday::Response::Logger do
169
210
  context 'when logging request and response bodies' do
170
211
  let(:logger_options) { { bodies: true } }
171
212
 
172
- it 'log request and response body' do
213
+ it 'logs request and response body' do
173
214
  conn.post '/ohyes', 'name=Ebi', accept: 'text/html'
174
215
  expect(string_io.string).to match(%(name=Ebi))
175
216
  expect(string_io.string).to match(%(pebbles))
176
217
  end
177
218
 
178
- it 'log response body object' do
219
+ it 'logs response body object' do
179
220
  conn.get '/rubbles', nil, accept: 'text/html'
180
221
  expect(string_io.string).to match(%([\"Barney\", \"Betty\", \"Bam Bam\"]\n))
181
222
  end
@@ -188,6 +229,44 @@ RSpec.describe Faraday::Response::Logger do
188
229
  end
189
230
  end
190
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
+
252
+ context 'when logging errors' do
253
+ let(:logger_options) { { errors: true } }
254
+
255
+ it 'logs error message' do
256
+ expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound)
257
+ expect(string_io.string).to match(%(no stubbed request for get http:/noroute))
258
+ end
259
+ end
260
+
261
+ context 'when logging headers and errors' do
262
+ let(:logger_options) { { headers: true, errors: true } }
263
+
264
+ it 'logs error message' do
265
+ expect { conn.get '/connection_failed' }.to raise_error(Faraday::ConnectionFailed)
266
+ expect(string_io.string).to match(%(Failed to open TCP connection))
267
+ end
268
+ end
269
+
191
270
  context 'when using log_level' do
192
271
  let(:logger_options) { { bodies: true, log_level: :debug } }
193
272