agiley-faraday_middleware 0.8.3.2 → 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 (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