agiley-faraday_middleware 0.8.3.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +2 -2
  2. data/.rspec +2 -1
  3. data/.travis.yml +6 -3
  4. data/Gemfile +5 -1
  5. data/Rakefile +16 -4
  6. data/faraday_middleware.gemspec +1 -1
  7. data/lib/faraday_middleware.rb +17 -13
  8. data/lib/faraday_middleware/instrumentation.rb +2 -2
  9. data/lib/faraday_middleware/rack_compatible.rb +14 -9
  10. data/lib/faraday_middleware/request/encode_json.rb +4 -2
  11. data/lib/faraday_middleware/request/method_override.rb +51 -0
  12. data/lib/faraday_middleware/request/oauth.rb +28 -5
  13. data/lib/faraday_middleware/response/caching.rb +7 -2
  14. data/lib/faraday_middleware/response/chunked.rb +29 -0
  15. data/lib/faraday_middleware/response/follow_redirects.rb +100 -11
  16. data/lib/faraday_middleware/response/mashify.rb +11 -2
  17. data/lib/faraday_middleware/response/parse_dates.rb +39 -0
  18. data/lib/faraday_middleware/response/parse_json.rb +17 -5
  19. data/lib/faraday_middleware/response/parse_marshal.rb +2 -2
  20. data/lib/faraday_middleware/response/parse_xml.rb +3 -2
  21. data/lib/faraday_middleware/response/parse_yaml.rb +3 -1
  22. data/lib/faraday_middleware/response/rashify.rb +2 -0
  23. data/lib/faraday_middleware/response_middleware.rb +2 -2
  24. data/lib/faraday_middleware/version.rb +1 -1
  25. data/spec/caching_test.rb +37 -4
  26. data/spec/chunked_spec.rb +78 -0
  27. data/spec/encode_json_spec.rb +13 -13
  28. data/spec/follow_redirects_spec.rb +203 -18
  29. data/spec/helper.rb +15 -1
  30. data/spec/mashify_spec.rb +46 -26
  31. data/spec/method_override_spec.rb +92 -0
  32. data/spec/oauth2_spec.rb +18 -18
  33. data/spec/oauth_spec.rb +67 -17
  34. data/spec/parse_dates_spec.rb +39 -0
  35. data/spec/parse_json_spec.rb +37 -19
  36. data/spec/parse_marshal_spec.rb +3 -3
  37. data/spec/parse_xml_spec.rb +13 -13
  38. data/spec/parse_yaml_spec.rb +10 -10
  39. data/spec/rashify_spec.rb +26 -23
  40. metadata +78 -21
@@ -1,7 +1,11 @@
1
1
  require 'faraday'
2
2
 
3
3
  module FaradayMiddleware
4
+ # Public: Converts parsed response bodies to a Hashie::Mash if they were of
5
+ # Hash or Array type.
4
6
  class Mashify < Faraday::Response::Middleware
7
+ attr_accessor :mash_class
8
+
5
9
  class << self
6
10
  attr_accessor :mash_class
7
11
  end
@@ -11,12 +15,17 @@ module FaradayMiddleware
11
15
  self.mash_class = ::Hashie::Mash
12
16
  end
13
17
 
18
+ def initialize(app = nil, options = {})
19
+ super(app)
20
+ self.mash_class = options[:mash_class] || self.class.mash_class
21
+ end
22
+
14
23
  def parse(body)
15
24
  case body
16
25
  when Hash
17
- self.class.mash_class.new(body)
26
+ mash_class.new(body)
18
27
  when Array
19
- body.map { |item| item.is_a?(Hash) ? self.class.mash_class.new(item) : item }
28
+ body.map { |item| parse(item) }
20
29
  else
21
30
  body
22
31
  end
@@ -0,0 +1,39 @@
1
+ require "time"
2
+ require "faraday"
3
+
4
+ module FaradayMiddleware
5
+ # Parse dates from response body
6
+ class ParseDates < ::Faraday::Response::Middleware
7
+ ISO_DATE_FORMAT = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\Z/m
8
+
9
+ def initialize(app, options = {})
10
+ @regexp = options[:match] || ISO_DATE_FORMAT
11
+ super(app)
12
+ end
13
+
14
+ def call(env)
15
+ response = @app.call(env)
16
+ parse_dates! response.env[:body]
17
+ response
18
+ end
19
+
20
+ private
21
+
22
+ def parse_dates!(value)
23
+ case value
24
+ when Hash
25
+ value.each do |key, element|
26
+ value[key] = parse_dates!(element)
27
+ end
28
+ when Array
29
+ value.each_with_index do |element, index|
30
+ value[index] = parse_dates!(element)
31
+ end
32
+ when @regexp
33
+ Time.parse(value)
34
+ else
35
+ value
36
+ end
37
+ end
38
+ end
39
+ end
@@ -3,11 +3,13 @@ require 'faraday_middleware/response_middleware'
3
3
  module FaradayMiddleware
4
4
  # Public: Parse response bodies as JSON.
5
5
  class ParseJson < ResponseMiddleware
6
- dependency 'json'
6
+ dependency do
7
+ require 'json' unless defined?(::JSON)
8
+ end
7
9
 
8
- define_parser { |body|
9
- JSON.parse body unless body.empty?
10
- }
10
+ define_parser do |body|
11
+ ::JSON.parse body unless body.strip.empty?
12
+ end
11
13
 
12
14
  # Public: Override the content-type of the response with "application/json"
13
15
  # if the response body looks like it might be JSON, i.e. starts with an
@@ -26,9 +28,19 @@ module FaradayMiddleware
26
28
  end
27
29
 
28
30
  BRACKETS = %w- [ { -
31
+ WHITESPACE = [ " ", "\n", "\r", "\t" ]
29
32
 
30
33
  def parse_response?(env)
31
- super and BRACKETS.include? env[:body][0,1]
34
+ super and BRACKETS.include? first_char(env[:body])
35
+ end
36
+
37
+ def first_char(body)
38
+ idx = -1
39
+ begin
40
+ char = body[idx += 1]
41
+ char = char.chr if char
42
+ end while char and WHITESPACE.include? char
43
+ char
32
44
  end
33
45
  end
34
46
  end
@@ -3,9 +3,9 @@ require 'faraday_middleware/response_middleware'
3
3
  module FaradayMiddleware
4
4
  # Public: Restore marshalled Ruby objects in response bodies.
5
5
  class ParseMarshal < ResponseMiddleware
6
- define_parser { |body|
6
+ define_parser do |body|
7
7
  ::Marshal.load body unless body.empty?
8
- }
8
+ end
9
9
  end
10
10
  end
11
11
 
@@ -1,12 +1,13 @@
1
1
  require 'faraday_middleware/response_middleware'
2
2
 
3
3
  module FaradayMiddleware
4
+ # Public: parses response bodies with MultiXml.
4
5
  class ParseXml < ResponseMiddleware
5
6
  dependency 'multi_xml'
6
7
 
7
- define_parser { |body|
8
+ define_parser do |body|
8
9
  ::MultiXml.parse(body)
9
- }
10
+ end
10
11
  end
11
12
  end
12
13
 
@@ -5,7 +5,9 @@ module FaradayMiddleware
5
5
  class ParseYaml < ResponseMiddleware
6
6
  dependency 'yaml'
7
7
 
8
- define_parser { |body| ::YAML.load body }
8
+ define_parser do |body|
9
+ ::YAML.load body
10
+ end
9
11
  end
10
12
  end
11
13
 
@@ -1,6 +1,8 @@
1
1
  require 'faraday_middleware/response/mashify'
2
2
 
3
3
  module FaradayMiddleware
4
+ # Public: Converts parsed response bodies to a Hashie::Rash if they were of
5
+ # Hash or Array type.
4
6
  class Rashify < Mashify
5
7
  dependency do
6
8
  require 'rash'
@@ -26,8 +26,8 @@ module FaradayMiddleware
26
26
  @content_types = Array(options[:content_type])
27
27
  end
28
28
 
29
- def call(env)
30
- @app.call(env).on_complete do
29
+ def call(environment)
30
+ @app.call(environment).on_complete do |env|
31
31
  if process_response_type?(response_type(env)) and parse_response?(env)
32
32
  process_response(env)
33
33
  end
@@ -1,3 +1,3 @@
1
1
  module FaradayMiddleware
2
- VERSION = "0.8.3.2"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -2,6 +2,7 @@ require 'test/unit'
2
2
  require 'forwardable'
3
3
  require 'fileutils'
4
4
  require 'rack/cache'
5
+ require 'faraday'
5
6
  require 'faraday_middleware/response/caching'
6
7
  require 'faraday_middleware/rack_compatible'
7
8
 
@@ -22,6 +23,16 @@ class CachingTest < Test::Unit::TestCase
22
23
  end
23
24
  end
24
25
 
26
+ class Lint < Struct.new(:app)
27
+ def call(env)
28
+ app.call(env).on_complete do
29
+ raise "no headers" unless env[:response_headers].is_a? Hash
30
+ raise "no response" unless env[:response].is_a? Faraday::Response
31
+ raise "env not identical" unless env[:response].env.object_id == env.object_id
32
+ end
33
+ end
34
+ end
35
+
25
36
  def setup
26
37
  @cache = TestCache.new
27
38
 
@@ -31,6 +42,7 @@ class CachingTest < Test::Unit::TestCase
31
42
  }
32
43
 
33
44
  @conn = Faraday.new do |b|
45
+ b.use Lint
34
46
  b.use FaradayMiddleware::Caching, @cache
35
47
  b.adapter :test do |stub|
36
48
  stub.get('/', &response)
@@ -55,7 +67,7 @@ class CachingTest < Test::Unit::TestCase
55
67
  get('/') # make cache
56
68
  response = get('/')
57
69
  assert_equal :get, response.env[:method]
58
- assert_equal '/', response.env[:url].to_s
70
+ assert_equal '/', response.env[:url].request_uri
59
71
  end
60
72
 
61
73
  def test_cache_query_params
@@ -76,10 +88,22 @@ end
76
88
  class HttpCachingTest < Test::Unit::TestCase
77
89
  include FileUtils
78
90
 
79
- CACHE_DIR = File.expand_path('../cache', __FILE__)
91
+ CACHE_DIR = File.expand_path('../../tmp/cache', __FILE__)
92
+
93
+ # middleware to check whether "rack.errors" is free of error reports
94
+ class RackErrorsComplainer < Struct.new(:app)
95
+ def call(env)
96
+ response = app.call(env)
97
+ error_stream = env['rack.errors'].string
98
+ raise %(unexpected error in 'rack.errors') if error_stream.include? 'error'
99
+ response
100
+ end
101
+ end
80
102
 
81
103
  def setup
82
104
  rm_r CACHE_DIR if File.exists? CACHE_DIR
105
+ # force reinitializing cache dirs
106
+ Rack::Cache::Storage.instance.clear
83
107
 
84
108
  request_count = 0
85
109
  response = lambda { |env|
@@ -90,6 +114,8 @@ class HttpCachingTest < Test::Unit::TestCase
90
114
  }
91
115
 
92
116
  @conn = Faraday.new do |b|
117
+ b.use RackErrorsComplainer
118
+
93
119
  b.use FaradayMiddleware::RackCompatible, Rack::Cache::Context,
94
120
  :metastore => "file:#{CACHE_DIR}/rack/meta",
95
121
  :entitystore => "file:#{CACHE_DIR}/rack/body",
@@ -106,10 +132,17 @@ class HttpCachingTest < Test::Unit::TestCase
106
132
  def_delegators :@conn, :get, :post
107
133
 
108
134
  def test_cache_get
109
- assert_equal 'request:1', get('/', :user_agent => 'test').body
135
+ response = get('/', :user_agent => 'test')
136
+ assert_equal 'request:1', response.body
137
+ assert_equal :get, response.env[:method]
138
+ assert_equal 200, response.status
139
+
110
140
  response = get('/', :user_agent => 'test')
111
141
  assert_equal 'request:1', response.body
112
142
  assert_equal 'text/plain', response['content-type']
143
+ assert_equal :get, response.env[:method]
144
+ assert response.env[:request].respond_to?(:fetch)
145
+ assert_equal 200, response.status
113
146
 
114
147
  assert_equal 'request:2', post('/').body
115
148
  end
@@ -119,4 +152,4 @@ class HttpCachingTest < Test::Unit::TestCase
119
152
  assert_equal 'request:2', post('/').body
120
153
  assert_equal 'request:3', post('/').body
121
154
  end
122
- end unless defined? RUBY_ENGINE and "rbx" == RUBY_ENGINE # rbx bug #1522
155
+ end unless defined? RUBY_ENGINE and "rbx" == RUBY_ENGINE # rbx bug #1522
@@ -0,0 +1,78 @@
1
+ require 'helper'
2
+ require 'faraday_middleware/response/chunked'
3
+
4
+ describe FaradayMiddleware::Chunked, :type => :response do
5
+ context "no transfer-encoding" do
6
+ it "doesn't change nil body" do
7
+ expect(process(nil).body).to be_nil
8
+ end
9
+
10
+ it "doesn't change an empty body" do
11
+ expect(process('').body).to eq('')
12
+ end
13
+
14
+ it "doesn't change a normal body" do
15
+ expect(process('asdf').body).to eq('asdf')
16
+ end
17
+ end
18
+
19
+ context "transfer-encoding gzip" do
20
+ let(:headers) { {"transfer-encoding" => "gzip"}}
21
+
22
+ it "doesn't change nil body" do
23
+ expect(process(nil).body).to be_nil
24
+ end
25
+
26
+ it "doesn't change an empty body" do
27
+ expect(process('').body).to eq('')
28
+ end
29
+
30
+ it "doesn't change a normal body" do
31
+ expect(process('asdf').body).to eq('asdf')
32
+ end
33
+ end
34
+
35
+ context "transfer-encoding chunked" do
36
+ let(:headers) { {"transfer-encoding" => "chunked"}}
37
+
38
+ it "doesn't change nil body" do
39
+ expect(process(nil).body).to be_nil
40
+ end
41
+
42
+ it "doesn't change an empty body" do
43
+ expect(process('').body).to eq('')
44
+ end
45
+
46
+ it "parses a basic chunked body" do
47
+ expect(process("10\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj')
48
+ end
49
+
50
+ it "parses a chunked body with no ending chunk" do
51
+ expect(process("10\r\nasdfghjklasdfghj\r\n").body).to eq('asdfghjklasdfghj')
52
+ end
53
+
54
+ it "parses a chunked body with no trailing CRLF on the data chunk" do
55
+ expect(process("10\r\nasdfghjklasdfghj0\r\n").body).to eq('asdfghjklasdfghj')
56
+ end
57
+
58
+ it "parses a chunked body with an extension" do
59
+ expect(process("10;foo=bar\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj')
60
+ end
61
+
62
+ it "parses a chunked body with two extensions" do
63
+ expect(process("10;foo=bar;bar=baz\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj')
64
+ end
65
+
66
+ it "parses a chunked body with two chunks" do
67
+ expect(process("8\r\nasdfghjk\r\n8\r\nlasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj')
68
+ end
69
+ end
70
+
71
+ context "transfer-encoding chunked,chunked" do
72
+ let(:headers) { {"transfer-encoding" => "chunked,chunked"}}
73
+
74
+ it "parses a basic chunked body" do
75
+ expect(process("10\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj')
76
+ end
77
+ end
78
+ end
@@ -17,11 +17,11 @@ describe FaradayMiddleware::EncodeJson do
17
17
  let(:result) { process(nil) }
18
18
 
19
19
  it "doesn't change body" do
20
- result_body.should be_nil
20
+ expect(result_body).to be_nil
21
21
  end
22
22
 
23
23
  it "doesn't add content type" do
24
- result_type.should be_nil
24
+ expect(result_type).to be_nil
25
25
  end
26
26
  end
27
27
 
@@ -29,11 +29,11 @@ describe FaradayMiddleware::EncodeJson do
29
29
  let(:result) { process('') }
30
30
 
31
31
  it "doesn't change body" do
32
- result_body.should be_empty
32
+ expect(result_body).to be_empty
33
33
  end
34
34
 
35
35
  it "doesn't add content type" do
36
- result_type.should be_nil
36
+ expect(result_type).to be_nil
37
37
  end
38
38
  end
39
39
 
@@ -41,11 +41,11 @@ describe FaradayMiddleware::EncodeJson do
41
41
  let(:result) { process('{"a":1}') }
42
42
 
43
43
  it "doesn't change body" do
44
- result_body.should eql('{"a":1}')
44
+ expect(result_body).to eq('{"a":1}')
45
45
  end
46
46
 
47
47
  it "adds content type" do
48
- result_type.should eql('application/json')
48
+ expect(result_type).to eq('application/json')
49
49
  end
50
50
  end
51
51
 
@@ -53,11 +53,11 @@ describe FaradayMiddleware::EncodeJson do
53
53
  let(:result) { process({:a => 1}) }
54
54
 
55
55
  it "encodes body" do
56
- result_body.should eql('{"a":1}')
56
+ expect(result_body).to eq('{"a":1}')
57
57
  end
58
58
 
59
59
  it "adds content type" do
60
- result_type.should eql('application/json')
60
+ expect(result_type).to eq('application/json')
61
61
  end
62
62
  end
63
63
 
@@ -65,7 +65,7 @@ describe FaradayMiddleware::EncodeJson do
65
65
  let(:result) { process({}) }
66
66
 
67
67
  it "encodes body" do
68
- result_body.should eql('{}')
68
+ expect(result_body).to eq('{}')
69
69
  end
70
70
  end
71
71
 
@@ -73,11 +73,11 @@ describe FaradayMiddleware::EncodeJson do
73
73
  let(:result) { process({:a => 1}, 'application/json; charset=utf-8') }
74
74
 
75
75
  it "encodes body" do
76
- result_body.should eql('{"a":1}')
76
+ expect(result_body).to eq('{"a":1}')
77
77
  end
78
78
 
79
79
  it "doesn't change content type" do
80
- result_type.should eql('application/json; charset=utf-8')
80
+ expect(result_type).to eq('application/json; charset=utf-8')
81
81
  end
82
82
  end
83
83
 
@@ -85,11 +85,11 @@ describe FaradayMiddleware::EncodeJson do
85
85
  let(:result) { process({:a => 1}, 'application/xml; charset=utf-8') }
86
86
 
87
87
  it "doesn't change body" do
88
- result_body.should eql({:a => 1})
88
+ expect(result_body).to eq({:a => 1})
89
89
  end
90
90
 
91
91
  it "doesn't change content type" do
92
- result_type.should eql('application/xml; charset=utf-8')
92
+ expect(result_type).to eq('application/xml; charset=utf-8')
93
93
  end
94
94
  end
95
95
  end
@@ -1,33 +1,218 @@
1
1
  require 'helper'
2
2
  require 'faraday_middleware/response/follow_redirects'
3
3
  require 'faraday'
4
- require 'forwardable'
4
+
5
+ # expose a method in Test adapter that should have been public
6
+ Faraday::Adapter::Test::Stubs.class_eval { public :new_stub }
5
7
 
6
8
  describe FaradayMiddleware::FollowRedirects do
7
- let(:connection) {
8
- Faraday.new do |c|
9
- c.use described_class
10
- c.adapter :test do |stub|
11
- stub.get('/') { [301, {'Location' => '/found'}, ''] }
12
- stub.post('/create') { [302, {'Location' => '/'}, ''] }
13
- stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
14
- stub.get('/loop') { [302, {'Location' => '/loop'}, ''] }
9
+ let(:middleware_options) { Hash.new }
10
+
11
+ shared_examples_for "a successful redirection" do |status_code|
12
+ it "follows the redirection for a GET request" do
13
+ expect(connection do |stub|
14
+ stub.get('/permanent') { [status_code, {'Location' => '/found'}, ''] }
15
+ stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
16
+ end.get('/permanent').body).to eq 'fin'
17
+ end
18
+
19
+ it "follows the redirection for a HEAD request" do
20
+ expect(connection do |stub|
21
+ stub.head('/permanent') { [status_code, {'Location' => '/found'}, ''] }
22
+ stub.head('/found') { [200, {'Content-Type' => 'text/plain'}, ''] }
23
+ end.head('/permanent').status).to eq 200
24
+ end
25
+
26
+ it "follows the redirection for a OPTIONS request" do
27
+ expect(connection do |stub|
28
+ stub.new_stub(:options, '/permanent') { [status_code, {'Location' => '/found'}, ''] }
29
+ stub.new_stub(:options, '/found') { [200, {'Content-Type' => 'text/plain'}, ''] }
30
+ end.run_request(:options, '/permanent', nil, nil).status).to eq 200
31
+ end
32
+ end
33
+
34
+ shared_examples_for "a forced GET redirection" do |status_code|
35
+ [:put, :post, :delete, :patch].each do |method|
36
+ it "a #{method.to_s.upcase} request is converted to a GET" do
37
+ expect(connection do |stub|
38
+ stub.new_stub(method, '/redirect') {
39
+ [status_code, {'Location' => '/found'}, 'elsewhere']
40
+ }
41
+ stub.get('/found') { |env|
42
+ body = env[:body] and body.empty? && (body = nil)
43
+ [200, {'Content-Type' => 'text/plain'}, body.inspect]
44
+ }
45
+ end.run_request(method, '/redirect', 'request data', nil).body).to eq('nil')
46
+ end
47
+ end
48
+ end
49
+
50
+ shared_examples_for "a replayed redirection" do |status_code|
51
+ it "redirects with the original request headers" do
52
+ conn = connection do |stub|
53
+ stub.get('/redirect') {
54
+ [status_code, {'Location' => '/found'}, '']
55
+ }
56
+ stub.get('/found') { |env|
57
+ [200, {'Content-Type' => 'text/plain'}, env[:request_headers]['X-Test-Value']]
58
+ }
59
+ end
60
+
61
+ response = conn.get('/redirect') { |req|
62
+ req.headers['X-Test-Value'] = 'success'
63
+ }
64
+
65
+ expect(response.body).to eq('success')
66
+ end
67
+
68
+ [:put, :post, :delete, :patch].each do |method|
69
+ it "replays a #{method.to_s.upcase} request" do
70
+ expect(connection do |stub|
71
+ stub.new_stub(method, '/redirect') { [status_code, {'Location' => '/found'}, ''] }
72
+ stub.new_stub(method, '/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
73
+ end.run_request(method, '/redirect', nil, nil).body).to eq 'fin'
15
74
  end
16
75
  end
17
- }
18
76
 
19
- extend Forwardable
20
- def_delegators :connection, :get, :post
77
+ [:put, :post, :patch].each do |method|
78
+ it "forwards request body for a #{method.to_s.upcase} request" do
79
+ conn = connection do |stub|
80
+ stub.new_stub(method, '/redirect') {
81
+ [status_code, {'Location' => '/found'}, '']
82
+ }
83
+ stub.new_stub(method, '/found') { |env|
84
+ [200, {'Content-Type' => 'text/plain'}, env[:body]]
85
+ }
86
+ end
21
87
 
22
- it "follows redirect" do
23
- get('/').body.should eql('fin')
88
+ response = conn.run_request(method, '/redirect', 'original data', nil)
89
+ expect(response.body).to eq('original data')
90
+ end
91
+ end
24
92
  end
25
93
 
26
- it "follows redirect twice" do
27
- post('/create').body.should eql('fin')
94
+
95
+ it "returns non-redirect response results" do
96
+ expect(connection do |stub|
97
+ stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
98
+ end.get('/found').body).to eq 'fin'
99
+ end
100
+
101
+ it "follows a single redirection" do
102
+ expect(connection do |stub|
103
+ stub.get('/') { [301, {'Location' => '/found'}, ''] }
104
+ stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
105
+ end.get('/').body).to eq 'fin'
28
106
  end
29
107
 
30
- it "raises exception on loop" do
31
- expect { get('/loop') }.to raise_error(FaradayMiddleware::RedirectLimitReached)
108
+ it "follows many redirections" do
109
+ expect(connection do |stub|
110
+ stub.get('/') { [301, {'Location' => '/redirect1'}, ''] }
111
+ stub.get('/redirect1') { [301, {'Location' => '/redirect2'}, ''] }
112
+ stub.get('/redirect2') { [301, {'Location' => '/found'}, ''] }
113
+ stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
114
+ end.get('/').body).to eq 'fin'
115
+ end
116
+
117
+ it "raises a FaradayMiddleware::RedirectLimitReached after 3 redirections (by default)" do
118
+ conn = connection do |stub|
119
+ stub.get('/') { [301, {'Location' => '/redirect1'}, ''] }
120
+ stub.get('/redirect1') { [301, {'Location' => '/redirect2'}, ''] }
121
+ stub.get('/redirect2') { [301, {'Location' => '/redirect3'}, ''] }
122
+ stub.get('/redirect3') { [301, {'Location' => '/found'}, ''] }
123
+ stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
124
+ end
125
+
126
+ expect{ conn.get('/') }.to raise_error(FaradayMiddleware::RedirectLimitReached)
127
+ end
128
+
129
+ it "raises a FaradayMiddleware::RedirectLimitReached after the initialized limit" do
130
+ conn = connection(:limit => 1) do |stub|
131
+ stub.get('/') { [301, {'Location' => '/redirect1'}, ''] }
132
+ stub.get('/redirect1') { [301, {'Location' => '/found'}, ''] }
133
+ stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
134
+ end
135
+
136
+ expect{ conn.get('/') }.to raise_error(FaradayMiddleware::RedirectLimitReached)
137
+ end
138
+
139
+ context "when cookies option" do
140
+
141
+ let(:cookies) { 'cookie1=abcdefg; cookie2=1234567; cookie3=awesome' }
142
+
143
+ context "is :all" do
144
+ it "puts all cookies from the response into the next request" do
145
+ expect(connection(:cookies => :all) do |stub|
146
+ stub.get('/') { [301, {'Location' => '/found', 'Cookies' => cookies }, ''] }
147
+ stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, ''] }
148
+ end.get('/').env[:request_headers][:cookies]).to eq(cookies)
149
+ end
150
+
151
+ it "not set cookies header on request when response has no cookies" do
152
+ expect(connection(:cookies => :all) do |stub|
153
+ stub.get('/') { [301, {'Location' => '/found'}, ''] }
154
+ stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, ''] }
155
+ end.get('/').env[:request_headers].has_key?('Cookies')).to eq(false)
156
+ end
157
+ end
158
+
159
+ context "is an array of cookie names" do
160
+ it "puts selected cookies from the response into the next request" do
161
+ expect(connection(:cookies => ['cookie2']) do |stub|
162
+ stub.get('/') { [301, {'Location' => '/found', 'Cookies' => cookies }, ''] }
163
+ stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, ''] }
164
+ end.get('/').env[:request_headers][:cookies]).to eq('cookie2=1234567')
165
+ end
166
+ end
167
+ end
168
+
169
+ context "for an HTTP 301 response" do
170
+ it_behaves_like 'a successful redirection', 301
171
+ it_behaves_like 'a forced GET redirection', 301
172
+ end
173
+
174
+ context "for an HTTP 302 response" do
175
+ it_behaves_like 'a successful redirection', 302
176
+
177
+ context "by default" do
178
+ it_behaves_like 'a forced GET redirection', 302
179
+ end
180
+
181
+ context "with standards compliancy enabled" do
182
+ let(:middleware_options) { { :standards_compliant => true } }
183
+ it_behaves_like 'a replayed redirection', 302
184
+ end
185
+ end
186
+
187
+ context "for an HTTP 303 response" do
188
+ it_behaves_like 'a successful redirection', 303
189
+ it_behaves_like 'a forced GET redirection', 303
190
+ end
191
+
192
+ context "for an HTTP 307 response" do
193
+ it_behaves_like 'a successful redirection', 307
194
+ it_behaves_like 'a replayed redirection', 307
195
+ end
196
+
197
+ # checks env hash in request phase for basic validity
198
+ class Lint < Struct.new(:app)
199
+ def call(env)
200
+ if env[:status] or env[:response] or env[:response_headers]
201
+ raise "invalid request: #{env.inspect}"
202
+ end
203
+ app.call(env)
204
+ end
205
+ end
206
+
207
+ private
208
+
209
+ def connection(options = middleware_options)
210
+ Faraday.new do |c|
211
+ c.use described_class, options
212
+ c.use Lint
213
+ c.adapter :test do |stub|
214
+ yield(stub) if block_given?
215
+ end
216
+ end
32
217
  end
33
218
  end