faraday 0.6.0 → 0.9.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 +7 -0
  2. data/.document +6 -0
  3. data/CHANGELOG.md +15 -0
  4. data/CONTRIBUTING.md +36 -0
  5. data/Gemfile +25 -15
  6. data/{LICENSE → LICENSE.md} +1 -1
  7. data/README.md +187 -137
  8. data/Rakefile +38 -101
  9. data/faraday.gemspec +26 -81
  10. data/lib/faraday/adapter/em_http.rb +237 -0
  11. data/lib/faraday/adapter/em_http_ssl_patch.rb +56 -0
  12. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +66 -0
  13. data/lib/faraday/adapter/em_synchrony.rb +65 -38
  14. data/lib/faraday/adapter/excon.rb +60 -9
  15. data/lib/faraday/adapter/httpclient.rb +106 -0
  16. data/lib/faraday/adapter/net_http.rb +89 -38
  17. data/lib/faraday/adapter/net_http_persistent.rb +47 -0
  18. data/lib/faraday/adapter/patron.rb +46 -9
  19. data/lib/faraday/adapter/rack.rb +58 -0
  20. data/lib/faraday/adapter/test.rb +68 -28
  21. data/lib/faraday/adapter/typhoeus.rb +94 -16
  22. data/lib/faraday/adapter.rb +33 -25
  23. data/lib/faraday/autoload.rb +85 -0
  24. data/lib/faraday/connection.rb +342 -134
  25. data/lib/faraday/error.rb +44 -29
  26. data/lib/faraday/middleware.rb +18 -10
  27. data/lib/faraday/options.rb +350 -0
  28. data/lib/faraday/parameters.rb +193 -0
  29. data/lib/faraday/rack_builder.rb +212 -0
  30. data/lib/faraday/request/authorization.rb +42 -0
  31. data/lib/faraday/request/basic_authentication.rb +13 -0
  32. data/lib/faraday/request/instrumentation.rb +36 -0
  33. data/lib/faraday/request/multipart.rb +15 -15
  34. data/lib/faraday/request/retry.rb +118 -0
  35. data/lib/faraday/request/token_authentication.rb +15 -0
  36. data/lib/faraday/request/url_encoded.rb +8 -9
  37. data/lib/faraday/request.rb +46 -45
  38. data/lib/faraday/response/logger.rb +4 -4
  39. data/lib/faraday/response/raise_error.rb +8 -3
  40. data/lib/faraday/response.rb +20 -25
  41. data/lib/faraday/upload_io.rb +51 -7
  42. data/lib/faraday/utils.rb +240 -44
  43. data/lib/faraday.rb +218 -38
  44. data/script/console +7 -0
  45. data/script/generate_certs +42 -0
  46. data/script/package +7 -0
  47. data/script/proxy-server +42 -0
  48. data/script/release +17 -0
  49. data/script/server +36 -0
  50. data/script/test +172 -0
  51. data/test/adapters/default_test.rb +14 -0
  52. data/test/adapters/em_http_test.rb +20 -0
  53. data/test/adapters/em_synchrony_test.rb +20 -0
  54. data/test/adapters/excon_test.rb +20 -0
  55. data/test/adapters/httpclient_test.rb +21 -0
  56. data/test/adapters/integration.rb +254 -0
  57. data/test/adapters/logger_test.rb +3 -3
  58. data/test/adapters/net_http_persistent_test.rb +20 -0
  59. data/test/adapters/net_http_test.rb +6 -25
  60. data/test/adapters/patron_test.rb +20 -0
  61. data/test/adapters/rack_test.rb +31 -0
  62. data/test/adapters/test_middleware_test.rb +74 -3
  63. data/test/adapters/typhoeus_test.rb +28 -0
  64. data/test/authentication_middleware_test.rb +65 -0
  65. data/test/composite_read_io_test.rb +111 -0
  66. data/test/connection_test.rb +381 -104
  67. data/test/env_test.rb +116 -40
  68. data/test/helper.rb +61 -25
  69. data/test/live_server.rb +55 -29
  70. data/test/middleware/instrumentation_test.rb +88 -0
  71. data/test/middleware/retry_test.rb +109 -0
  72. data/test/middleware_stack_test.rb +87 -5
  73. data/test/multibyte.txt +1 -0
  74. data/test/options_test.rb +252 -0
  75. data/test/request_middleware_test.rb +78 -21
  76. data/test/response_middleware_test.rb +32 -7
  77. data/test/strawberry.rb +2 -0
  78. data/test/utils_test.rb +58 -0
  79. metadata +116 -117
  80. data/lib/faraday/adapter/action_dispatch.rb +0 -30
  81. data/lib/faraday/builder.rb +0 -137
  82. data/lib/faraday/request/json.rb +0 -31
  83. data/test/adapters/live_test.rb +0 -186
data/test/env_test.rb CHANGED
@@ -1,56 +1,80 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class EnvTest < Faraday::TestCase
4
4
  def setup
5
- @conn = Faraday.new :url => 'http://sushi.com/api', :headers => {'Mime-Version' => '1.0'}
6
- @conn.options[:timeout] = 3
7
- @conn.options[:open_timeout] = 5
8
- @conn.ssl[:verify] = false
5
+ @conn = Faraday.new :url => 'http://sushi.com/api',
6
+ :headers => {'Mime-Version' => '1.0'},
7
+ :request => {:oauth => {:consumer_key => 'anonymous'}}
8
+
9
+ @conn.options.timeout = 3
10
+ @conn.options.open_timeout = 5
11
+ @conn.ssl.verify = false
9
12
  @conn.proxy 'http://proxy.com'
10
- @input = { :body => 'abc' }
11
- @env = env_for @conn do |req|
12
- req.url 'foo.json', 'a' => 1
13
- req['Server'] = 'Faraday'
14
- req.body = @input[:body]
15
- end
16
13
  end
17
14
 
18
15
  def test_request_create_stores_method
19
- assert_equal :get, @env[:method]
16
+ env = make_env(:get)
17
+ assert_equal :get, env.method
20
18
  end
21
19
 
22
- def test_request_create_stores_addressable_uri
23
- assert_equal 'http://sushi.com/api/foo.json?a=1', @env[:url].to_s
20
+ def test_request_create_stores_uri
21
+ env = make_env do |req|
22
+ req.url 'foo.json', 'a' => 1
23
+ end
24
+ assert_equal 'http://sushi.com/api/foo.json?a=1', env.url.to_s
24
25
  end
25
26
 
26
27
  def test_request_create_stores_headers
27
- headers = @env[:request_headers]
28
+ env = make_env do |req|
29
+ req['Server'] = 'Faraday'
30
+ end
31
+ headers = env.request_headers
28
32
  assert_equal '1.0', headers['mime-version']
29
33
  assert_equal 'Faraday', headers['server']
30
34
  end
31
35
 
32
36
  def test_request_create_stores_body
33
- assert_equal @input[:body], @env[:body]
37
+ env = make_env do |req|
38
+ req.body = 'hi'
39
+ end
40
+ assert_equal 'hi', env.body
34
41
  end
35
42
 
36
- def test_request_create_stores_timeout_options
37
- assert_equal 3, @env[:request][:timeout]
38
- assert_equal 5, @env[:request][:open_timeout]
43
+ def test_global_request_options
44
+ env = make_env
45
+ assert_equal 3, env.request.timeout
46
+ assert_equal 5, env.request.open_timeout
47
+ end
48
+
49
+ def test_per_request_options
50
+ env = make_env do |req|
51
+ req.options.timeout = 10
52
+ req.options.boundary = 'boo'
53
+ req.options.oauth[:consumer_secret] = 'xyz'
54
+ end
55
+ assert_equal 10, env.request.timeout
56
+ assert_equal 5, env.request.open_timeout
57
+ assert_equal 'boo', env.request.boundary
58
+
59
+ oauth_expected = {:consumer_secret => 'xyz', :consumer_key => 'anonymous'}
60
+ assert_equal oauth_expected, env.request.oauth
39
61
  end
40
62
 
41
63
  def test_request_create_stores_ssl_options
42
- assert_equal false, @env[:ssl][:verify]
64
+ env = make_env
65
+ assert_equal false, env.ssl.verify
43
66
  end
44
67
 
45
68
  def test_request_create_stores_proxy_options
46
- assert_equal 'proxy.com', @env[:request][:proxy][:uri].host
69
+ env = make_env
70
+ assert_equal 'proxy.com', env.request.proxy.host
47
71
  end
48
72
 
49
- def env_for(connection)
50
- env_setup = Faraday::Request.create do |req|
51
- yield req
52
- end
53
- env_setup.to_env_hash(connection, :get)
73
+ private
74
+
75
+ def make_env(method = :get, connection = @conn, &block)
76
+ request = connection.build_request(method, &block)
77
+ request.to_env(connection)
54
78
  end
55
79
  end
56
80
 
@@ -59,6 +83,50 @@ class HeadersTest < Faraday::TestCase
59
83
  @headers = Faraday::Utils::Headers.new
60
84
  end
61
85
 
86
+ def test_normalizes_different_capitalizations
87
+ @headers['Content-Type'] = 'application/json'
88
+ assert_equal ['Content-Type'], @headers.keys
89
+ assert_equal 'application/json', @headers['Content-Type']
90
+ assert_equal 'application/json', @headers['CONTENT-TYPE']
91
+ assert_equal 'application/json', @headers['content-type']
92
+ assert @headers.include?('content-type')
93
+
94
+ @headers['content-type'] = 'application/xml'
95
+ assert_equal ['Content-Type'], @headers.keys
96
+ assert_equal 'application/xml', @headers['Content-Type']
97
+ assert_equal 'application/xml', @headers['CONTENT-TYPE']
98
+ assert_equal 'application/xml', @headers['content-type']
99
+ end
100
+
101
+ def test_fetch_key
102
+ @headers['Content-Type'] = 'application/json'
103
+ block_called = false
104
+ assert_equal 'application/json', @headers.fetch('content-type') { block_called = true }
105
+ assert_equal 'application/json', @headers.fetch('Content-Type')
106
+ assert_equal 'application/json', @headers.fetch('CONTENT-TYPE')
107
+ assert_equal 'application/json', @headers.fetch(:content_type)
108
+ assert_equal false, block_called
109
+
110
+ assert_equal 'default', @headers.fetch('invalid', 'default')
111
+ assert_equal false, @headers.fetch('invalid', false)
112
+ assert_nil @headers.fetch('invalid', nil)
113
+
114
+ assert_equal 'Invalid key', @headers.fetch('Invalid') { |key| "#{key} key" }
115
+
116
+ expected_error = defined?(KeyError) ? KeyError : IndexError
117
+ assert_raises(expected_error) { @headers.fetch('invalid') }
118
+ end
119
+
120
+ def test_delete_key
121
+ @headers['Content-Type'] = 'application/json'
122
+ assert_equal 1, @headers.size
123
+ assert @headers.include?('content-type')
124
+ assert_equal 'application/json', @headers.delete('content-type')
125
+ assert_equal 0, @headers.size
126
+ assert !@headers.include?('content-type')
127
+ assert_equal nil, @headers.delete('content-type')
128
+ end
129
+
62
130
  def test_parse_response_headers_leaves_http_status_line_out
63
131
  @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
64
132
  assert_equal %w(Content-Type), @headers.keys
@@ -82,53 +150,61 @@ end
82
150
 
83
151
  class ResponseTest < Faraday::TestCase
84
152
  def setup
85
- @env = {
153
+ @env = Faraday::Env.from \
86
154
  :status => 404, :body => 'yikes',
87
- :response_headers => Faraday::Utils::Headers.new('Content-Type' => 'text/plain')
88
- }
155
+ :response_headers => {'Content-Type' => 'text/plain'}
89
156
  @response = Faraday::Response.new @env
90
157
  end
91
-
158
+
92
159
  def test_finished
93
160
  assert @response.finished?
94
161
  end
95
-
162
+
96
163
  def test_error_on_finish
97
164
  assert_raises RuntimeError do
98
165
  @response.finish({})
99
166
  end
100
167
  end
101
-
168
+
102
169
  def test_not_success
103
170
  assert !@response.success?
104
171
  end
105
-
172
+
106
173
  def test_status
107
174
  assert_equal 404, @response.status
108
175
  end
109
-
176
+
110
177
  def test_body
111
178
  assert_equal 'yikes', @response.body
112
179
  end
113
-
180
+
114
181
  def test_headers
115
182
  assert_equal 'text/plain', @response.headers['Content-Type']
116
183
  assert_equal 'text/plain', @response['content-type']
117
184
  end
118
-
185
+
119
186
  def test_apply_request
120
187
  @response.apply_request :body => 'a=b', :method => :post
121
188
  assert_equal 'yikes', @response.body
122
189
  assert_equal :post, @response.env[:method]
123
190
  end
124
-
191
+
125
192
  def test_marshal
126
193
  @response = Faraday::Response.new
127
194
  @response.on_complete { }
128
- @response.finish @env.merge(:custom => 'moo')
129
-
195
+ @response.finish @env.merge(:params => 'moo')
196
+
130
197
  loaded = Marshal.load Marshal.dump(@response)
131
- assert_nil loaded.env[:custom]
198
+ assert_nil loaded.env[:params]
132
199
  assert_equal %w[body response_headers status], loaded.env.keys.map { |k| k.to_s }.sort
133
200
  end
201
+
202
+ def test_hash
203
+ hash = @response.to_hash
204
+ assert_kind_of Hash, hash
205
+ assert_equal @env.to_hash, hash
206
+ assert_equal hash[:status], @response.status
207
+ assert_equal hash[:response_headers], @response.headers
208
+ assert_equal hash[:body], @response.body
209
+ end
134
210
  end
data/test/helper.rb CHANGED
@@ -1,12 +1,18 @@
1
- require 'test/unit'
2
- unless ENV['BUNDLE_GEMFILE']
3
- require 'rubygems'
4
- require 'bundler'
5
- Bundler.setup
1
+ require 'rubygems' # rubygems/version doesn't work by itself
2
+ require 'rubygems/version' # for simplecov-html
3
+ require 'simplecov'
4
+ require 'coveralls'
5
+
6
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
7
+ SimpleCov::Formatter::HTMLFormatter,
8
+ Coveralls::SimpleCov::Formatter
9
+ ]
10
+ SimpleCov.start do
11
+ add_filter '/bundle/'
6
12
  end
7
13
 
8
- require 'webmock/test_unit'
9
- WebMock.disable_net_connect!(:allow_localhost => true)
14
+ gem 'minitest' if defined? Bundler
15
+ require 'minitest/autorun'
10
16
 
11
17
  if ENV['LEFTRIGHT']
12
18
  begin
@@ -16,30 +22,60 @@ if ENV['LEFTRIGHT']
16
22
  end
17
23
  end
18
24
 
19
- unless $LOAD_PATH.include? 'lib'
20
- $LOAD_PATH.unshift(File.dirname(__FILE__))
21
- $LOAD_PATH.unshift(File.join($LOAD_PATH.first, '..', 'lib'))
22
- end
23
- require 'faraday'
24
-
25
- begin
26
- require 'ruby-debug'
27
- rescue LoadError
28
- # ignore
29
- else
30
- Debugger.start
31
- end
25
+ require File.expand_path('../../lib/faraday', __FILE__)
26
+
27
+ require 'stringio'
28
+ require 'uri'
32
29
 
33
30
  module Faraday
34
- class TestCase < Test::Unit::TestCase
35
- LIVE_SERVER = case ENV['LIVE']
36
- when /^http/ then ENV['LIVE']
37
- when nil then nil
38
- else 'http://localhost:4567'
31
+ module LiveServerConfig
32
+ def live_server=(value)
33
+ @@live_server = case value
34
+ when /^http/
35
+ URI(value)
36
+ when /./
37
+ URI('http://127.0.0.1:4567')
38
+ end
39
39
  end
40
40
 
41
+ def live_server?
42
+ defined? @@live_server
43
+ end
44
+
45
+ # Returns an object that responds to `host` and `port`.
46
+ def live_server
47
+ live_server? and @@live_server
48
+ end
49
+ end
50
+
51
+ class TestCase < MiniTest::Test
52
+ extend LiveServerConfig
53
+ self.live_server = ENV['LIVE']
54
+
41
55
  def test_default
42
56
  assert true
43
57
  end unless defined? ::MiniTest
58
+
59
+ def capture_warnings
60
+ old, $stderr = $stderr, StringIO.new
61
+ begin
62
+ yield
63
+ $stderr.string
64
+ ensure
65
+ $stderr = old
66
+ end
67
+ end
68
+
69
+ def self.jruby?
70
+ defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE
71
+ end
72
+
73
+ def self.rbx?
74
+ defined? RUBY_ENGINE and 'rbx' == RUBY_ENGINE
75
+ end
76
+
77
+ def self.ssl_mode?
78
+ ENV['SSL'] == 'yes'
79
+ end
44
80
  end
45
81
  end
data/test/live_server.rb CHANGED
@@ -1,41 +1,67 @@
1
- require 'sinatra'
2
- set :logging, false
1
+ require 'sinatra/base'
3
2
 
4
- get '/hello_world' do
5
- 'hello world'
6
- end
3
+ module Faraday
4
+ class LiveServer < Sinatra::Base
5
+ set :environment, :test
6
+ disable :logging
7
+ disable :protection
7
8
 
8
- get '/json' do
9
- content_type 'application/json'
10
- "[1,2,3]"
11
- end
9
+ [:get, :post, :put, :patch, :delete, :options].each do |method|
10
+ send(method, '/echo') do
11
+ kind = request.request_method.downcase
12
+ out = kind.dup
13
+ out << ' ?' << request.GET.inspect if request.GET.any?
14
+ out << ' ' << request.POST.inspect if request.POST.any?
12
15
 
13
- post '/file' do
14
- if params[:uploaded_file].respond_to? :each_key
15
- "file %s %s" % [
16
- params[:uploaded_file][:filename],
17
- params[:uploaded_file][:type]]
18
- else
19
- status 400
16
+ content_type 'text/plain'
17
+ return out
18
+ end
20
19
  end
21
- end
22
20
 
23
- %w[get post].each do |method|
24
- send(method, '/hello') do
25
- "hello #{params[:name]}"
21
+ get '/echo_header' do
22
+ header = "HTTP_#{params[:name].tr('-', '_').upcase}"
23
+ request.env.fetch(header) { 'NONE' }
26
24
  end
27
- end
28
25
 
29
- %w[post put].each do |method|
30
- send(method, '/echo_name') do
31
- params[:name].inspect
26
+ post '/file' do
27
+ if params[:uploaded_file].respond_to? :each_key
28
+ "file %s %s %d" % [
29
+ params[:uploaded_file][:filename],
30
+ params[:uploaded_file][:type],
31
+ params[:uploaded_file][:tempfile].size
32
+ ]
33
+ else
34
+ status 400
35
+ end
36
+ end
37
+
38
+ get '/multi' do
39
+ [200, { 'Set-Cookie' => 'one, two' }, '']
40
+ end
41
+
42
+ get '/who-am-i' do
43
+ request.env['REMOTE_ADDR']
32
44
  end
33
- end
34
45
 
35
- delete '/delete_with_json' do
36
- %/{"deleted":true}/
46
+ get '/slow' do
47
+ sleep 10
48
+ [200, {}, 'ok']
49
+ end
50
+
51
+ get '/204' do
52
+ status 204 # no content
53
+ end
54
+
55
+ get '/ssl' do
56
+ request.secure?.to_s
57
+ end
58
+
59
+ error do |e|
60
+ "#{e.class}\n#{e.to_s}\n#{e.backtrace.join("\n")}"
61
+ end
62
+ end
37
63
  end
38
64
 
39
- get '/multi' do
40
- [200, { 'Set-Cookie' => %w[ one two ] }, '']
65
+ if $0 == __FILE__
66
+ Faraday::LiveServer.run!
41
67
  end
@@ -0,0 +1,88 @@
1
+ require File.expand_path("../../helper", __FILE__)
2
+
3
+ module Middleware
4
+ class InstrumentationTest < Faraday::TestCase
5
+ def setup
6
+ @instrumenter = FakeInstrumenter.new
7
+ end
8
+
9
+ def test_default_name
10
+ assert_equal 'request.faraday', options.name
11
+ end
12
+
13
+ def test_default_instrumenter
14
+ begin
15
+ instrumenter = options.instrumenter
16
+ rescue NameError => err
17
+ assert_match 'ActiveSupport', err.to_s
18
+ else
19
+ assert_equal ActiveSupport::Notifications, instrumenter
20
+ end
21
+ end
22
+
23
+ def test_name
24
+ assert_equal 'booya', options(:name => 'booya').name
25
+ end
26
+
27
+ def test_instrumenter
28
+ assert_equal :boom, options(:instrumenter => :boom).instrumenter
29
+ end
30
+
31
+ def test_instrumentation_with_default_name
32
+ assert_equal 0, @instrumenter.instrumentations.size
33
+
34
+ faraday = conn
35
+ res = faraday.get '/'
36
+ assert_equal 'ok', res.body
37
+
38
+ assert_equal 1, @instrumenter.instrumentations.size
39
+ name, env = @instrumenter.instrumentations.first
40
+ assert_equal 'request.faraday', name
41
+ assert_equal '/', env[:url].path
42
+ end
43
+
44
+ def test_instrumentation
45
+ assert_equal 0, @instrumenter.instrumentations.size
46
+
47
+ faraday = conn :name => 'booya'
48
+ res = faraday.get '/'
49
+ assert_equal 'ok', res.body
50
+
51
+ assert_equal 1, @instrumenter.instrumentations.size
52
+ name, env = @instrumenter.instrumentations.first
53
+ assert_equal 'booya', name
54
+ assert_equal '/', env[:url].path
55
+ end
56
+
57
+ class FakeInstrumenter
58
+ attr_reader :instrumentations
59
+
60
+ def initialize
61
+ @instrumentations = []
62
+ end
63
+
64
+ def instrument(name, env)
65
+ @instrumentations << [name, env]
66
+ yield
67
+ end
68
+ end
69
+
70
+ def options(hash = nil)
71
+ Faraday::Request::Instrumentation::Options.from hash
72
+ end
73
+
74
+ def conn(hash = nil)
75
+ hash ||= {}
76
+ hash[:instrumenter] = @instrumenter
77
+
78
+ Faraday.new do |f|
79
+ f.request :instrumentation, hash
80
+ f.adapter :test do |stub|
81
+ stub.get '/' do
82
+ [200, {}, 'ok']
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,109 @@
1
+ require File.expand_path("../../helper", __FILE__)
2
+
3
+ module Middleware
4
+ class RetryTest < Faraday::TestCase
5
+ def setup
6
+ @times_called = 0
7
+ end
8
+
9
+ def conn(*retry_args)
10
+ Faraday.new do |b|
11
+ b.request :retry, *retry_args
12
+ b.adapter :test do |stub|
13
+ stub.post('/unstable') {
14
+ @times_called += 1
15
+ @explode.call @times_called
16
+ }
17
+ end
18
+ end
19
+ end
20
+
21
+ def test_unhandled_error
22
+ @explode = lambda {|n| raise "boom!" }
23
+ assert_raises(RuntimeError) { conn.post("/unstable") }
24
+ assert_equal 1, @times_called
25
+ end
26
+
27
+ def test_handled_error
28
+ @explode = lambda {|n| raise Errno::ETIMEDOUT }
29
+ assert_raises(Errno::ETIMEDOUT) { conn.post("/unstable") }
30
+ assert_equal 3, @times_called
31
+ end
32
+
33
+ def test_legacy_max_retries
34
+ @explode = lambda {|n| raise Errno::ETIMEDOUT }
35
+ assert_raises(Errno::ETIMEDOUT) { conn(1).post("/unstable") }
36
+ assert_equal 2, @times_called
37
+ end
38
+
39
+ def test_new_max_retries
40
+ @explode = lambda {|n| raise Errno::ETIMEDOUT }
41
+ assert_raises(Errno::ETIMEDOUT) { conn(:max => 3).post("/unstable") }
42
+ assert_equal 4, @times_called
43
+ end
44
+
45
+ def test_interval
46
+ @explode = lambda {|n| raise Errno::ETIMEDOUT }
47
+ started = Time.now
48
+ assert_raises(Errno::ETIMEDOUT) {
49
+ conn(:max => 2, :interval => 0.1).post("/unstable")
50
+ }
51
+ assert_in_delta 0.2, Time.now - started, 0.04
52
+ end
53
+
54
+ def test_calls_sleep_amount
55
+ explode_app = MiniTest::Mock.new
56
+ explode_app.expect(:call, nil, [{:body=>nil}])
57
+ def explode_app.call(env)
58
+ raise Errno::ETIMEDOUT
59
+ end
60
+
61
+ retry_middleware = Faraday::Request::Retry.new(explode_app)
62
+ class << retry_middleware
63
+ attr_accessor :sleep_amount_retries
64
+
65
+ def sleep_amount(retries)
66
+ self.sleep_amount_retries.delete(retries)
67
+ 0
68
+ end
69
+ end
70
+ retry_middleware.sleep_amount_retries = [2, 1]
71
+
72
+ assert_raises(Errno::ETIMEDOUT) {
73
+ retry_middleware.call({})
74
+ }
75
+
76
+ assert_empty retry_middleware.sleep_amount_retries
77
+ end
78
+
79
+ def test_exponential_backoff
80
+ middleware = Faraday::Request::Retry.new(nil, :max => 5, :interval => 0.1, :backoff_factor => 2)
81
+ assert_equal middleware.sleep_amount(5), 0.1
82
+ assert_equal middleware.sleep_amount(4), 0.2
83
+ assert_equal middleware.sleep_amount(3), 0.4
84
+ end
85
+
86
+ def test_random_additional_interval_amount
87
+ middleware = Faraday::Request::Retry.new(nil, :max => 2, :interval => 0.1, :interval_randomness => 1.0)
88
+ sleep_amount = middleware.sleep_amount(2)
89
+ assert_operator sleep_amount, :>=, 0.1
90
+ assert_operator sleep_amount, :<=, 0.2
91
+ middleware = Faraday::Request::Retry.new(nil, :max => 2, :interval => 0.1, :interval_randomness => 0.5)
92
+ sleep_amount = middleware.sleep_amount(2)
93
+ assert_operator sleep_amount, :>=, 0.1
94
+ assert_operator sleep_amount, :<=, 0.15
95
+ middleware = Faraday::Request::Retry.new(nil, :max => 2, :interval => 0.1, :interval_randomness => 0.25)
96
+ sleep_amount = middleware.sleep_amount(2)
97
+ assert_operator sleep_amount, :>=, 0.1
98
+ assert_operator sleep_amount, :<=, 0.125
99
+ end
100
+
101
+ def test_custom_exceptions
102
+ @explode = lambda {|n| raise "boom!" }
103
+ assert_raises(RuntimeError) {
104
+ conn(:exceptions => StandardError).post("/unstable")
105
+ }
106
+ assert_equal 3, @times_called
107
+ end
108
+ end
109
+ end