heroku-sauce/vendor/ruby/1.9.1/gems/httparty-0.9.0/spec/httparty/request_spec.rb

496 lines
18 KiB
Ruby

require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
describe HTTParty::Request do
before do
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', :format => :xml)
end
describe "::NON_RAILS_QUERY_STRING_NORMALIZER" do
let(:normalizer) { HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER }
it "doesn't modify strings" do
query_string = normalizer["foo=bar&foo=baz"]
URI.unescape(query_string).should == "foo=bar&foo=baz"
end
context "when the query is an array" do
it "doesn't include brackets" do
query_string = normalizer[{:page => 1, :foo => %w(bar baz)}]
URI.unescape(query_string).should == "foo=bar&foo=baz&page=1"
end
it "URI encodes array values" do
query_string = normalizer[{:people => ["Bob Marley", "Tim & Jon"]}]
query_string.should == "people=Bob%20Marley&people=Tim%20%26%20Jon"
end
end
context "when the query is a hash" do
it "correctly handles nil values" do
query_string = normalizer[{:page => 1, :per_page => nil}]
query_string.should == "page=1&per_page"
end
end
end
describe "initialization" do
it "sets parser to HTTParty::Parser" do
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
request.parser.should == HTTParty::Parser
end
it "sets parser to the optional parser" do
my_parser = lambda {}
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', :parser => my_parser)
request.parser.should == my_parser
end
it "sets connection_adapter to HTTPParty::ConnectionAdapter" do
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
request.connection_adapter.should == HTTParty::ConnectionAdapter
end
it "sets connection_adapter to the optional connection_adapter" do
my_adapter = lambda {}
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', :connection_adapter => my_adapter)
request.connection_adapter.should == my_adapter
end
end
describe "#format" do
context "request yet to be made" do
it "returns format option" do
request = HTTParty::Request.new 'get', '/', :format => :xml
request.format.should == :xml
end
it "returns nil format" do
request = HTTParty::Request.new 'get', '/'
request.format.should be_nil
end
end
context "request has been made" do
it "returns format option" do
request = HTTParty::Request.new 'get', '/', :format => :xml
request.last_response = stub
request.format.should == :xml
end
it "returns the content-type from the last response when the option is not set" do
request = HTTParty::Request.new 'get', '/'
response = stub
response.should_receive(:[]).with('content-type').and_return('text/json')
request.last_response = response
request.format.should == :json
end
end
end
context "options" do
it "should use basic auth when configured" do
@request.options[:basic_auth] = {:username => 'foobar', :password => 'secret'}
@request.send(:setup_raw_request)
@request.instance_variable_get(:@raw_request)['authorization'].should_not be_nil
end
it "should use digest auth when configured" do
FakeWeb.register_uri(:get, "http://api.foo.com/v1",
:www_authenticate => 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false')
@request.options[:digest_auth] = {:username => 'foobar', :password => 'secret'}
@request.send(:setup_raw_request)
raw_request = @request.instance_variable_get(:@raw_request)
raw_request.instance_variable_get(:@header)['Authorization'].should_not be_nil
end
it "should use the right http method for digest authentication" do
@post_request = HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :format => :xml)
FakeWeb.register_uri(:post, "http://api.foo.com/v1", {})
http = @post_request.send(:http)
@post_request.should_receive(:http).and_return(http)
http.should_not_receive(:head).and_return({'www-authenticate' => nil})
@post_request.options[:digest_auth] = {:username => 'foobar', :password => 'secret'}
@post_request.send(:setup_raw_request)
end
end
describe "#uri" do
context "query strings" do
it "does not add an empty query string when default_params are blank" do
@request.options[:default_params] = {}
@request.uri.query.should be_nil
end
it "respects the query string normalization proc" do
empty_proc = lambda {|qs| ""}
@request.options[:query_string_normalizer] = empty_proc
@request.options[:query] = {:foo => :bar}
URI.unescape(@request.uri.query).should == ""
end
context "when representing an array" do
it "returns a Rails style query string" do
@request.options[:query] = {:foo => %w(bar baz)}
URI.unescape(@request.uri.query).should == "foo[]=bar&foo[]=baz"
end
end
end
end
describe "#setup_raw_request" do
context "when query_string_normalizer is set" do
it "sets the body to the return value of the proc" do
@request.options[:query_string_normalizer] = HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER
@request.options[:body] = {:page => 1, :foo => %w(bar baz)}
@request.send(:setup_raw_request)
body = @request.instance_variable_get(:@raw_request).body
URI.unescape(body).should == "foo=bar&foo=baz&page=1"
end
end
end
describe 'http' do
it "should get a connection from the connection_adapter" do
http = Net::HTTP.new('google.com')
adapter = mock('adapter')
request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443', :connection_adapter => adapter)
adapter.should_receive(:call).with(request.uri, request.options).and_return(http)
request.send(:http).should be http
end
end
describe '#format_from_mimetype' do
it 'should handle text/xml' do
["text/xml", "text/xml; charset=iso8859-1"].each do |ct|
@request.send(:format_from_mimetype, ct).should == :xml
end
end
it 'should handle application/xml' do
["application/xml", "application/xml; charset=iso8859-1"].each do |ct|
@request.send(:format_from_mimetype, ct).should == :xml
end
end
it 'should handle text/json' do
["text/json", "text/json; charset=iso8859-1"].each do |ct|
@request.send(:format_from_mimetype, ct).should == :json
end
end
it 'should handle application/json' do
["application/json", "application/json; charset=iso8859-1"].each do |ct|
@request.send(:format_from_mimetype, ct).should == :json
end
end
it 'should handle text/javascript' do
["text/javascript", "text/javascript; charset=iso8859-1"].each do |ct|
@request.send(:format_from_mimetype, ct).should == :json
end
end
it 'should handle application/javascript' do
["application/javascript", "application/javascript; charset=iso8859-1"].each do |ct|
@request.send(:format_from_mimetype, ct).should == :json
end
end
it "returns nil for an unrecognized mimetype" do
@request.send(:format_from_mimetype, "application/atom+xml").should be_nil
end
it "returns nil when using a default parser" do
@request.options[:parser] = lambda {}
@request.send(:format_from_mimetype, "text/json").should be_nil
end
end
describe 'parsing responses' do
it 'should handle xml automatically' do
xml = %q[<books><book><id>1234</id><name>Foo Bar!</name></book></books>]
@request.options[:format] = :xml
@request.send(:parse_response, xml).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
end
it 'should handle json automatically' do
json = %q[{"books": {"book": {"name": "Foo Bar!", "id": "1234"}}}]
@request.options[:format] = :json
@request.send(:parse_response, json).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
end
it 'should handle yaml automatically' do
yaml = "books: \n book: \n name: Foo Bar!\n id: \"1234\"\n"
@request.options[:format] = :yaml
@request.send(:parse_response, yaml).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
end
it "should include any HTTP headers in the returned response" do
@request.options[:format] = :html
response = stub_response "Content"
response.initialize_http_header("key" => "value")
@request.perform.headers.should == { "key" => ["value"] }
end
describe 'with non-200 responses' do
context "3xx responses" do
it 'returns a valid object for 304 not modified' do
stub_response '', 304
resp = @request.perform
resp.code.should == 304
resp.body.should == ''
resp.should be_nil
end
it "redirects if a 300 contains a location header" do
redirect = stub_response '', 300
redirect['location'] = 'http://foo.com/foo'
ok = stub_response('<hash><foo>bar</foo></hash>', 200)
@http.stub!(:request).and_return(redirect, ok)
response = @request.perform
response.request.base_uri.to_s.should == "http://foo.com"
response.request.path.to_s.should == "http://foo.com/foo"
response.request.uri.request_uri.should == "/foo"
response.request.uri.to_s.should == "http://foo.com/foo"
response.should == {"hash" => {"foo" => "bar"}}
end
it "redirects if a 300 contains a relative location header" do
redirect = stub_response '', 300
redirect['location'] = '/foo/bar'
ok = stub_response('<hash><foo>bar</foo></hash>', 200)
@http.stub!(:request).and_return(redirect, ok)
response = @request.perform
response.request.base_uri.to_s.should == "http://api.foo.com"
response.request.path.to_s.should == "/foo/bar"
response.request.uri.request_uri.should == "/foo/bar"
response.request.uri.to_s.should == "http://api.foo.com/foo/bar"
response.should == {"hash" => {"foo" => "bar"}}
end
it "handles multiple redirects and relative location headers on different hosts" do
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', :format => :xml)
FakeWeb.register_uri(:get, "http://test.com/redirect", :status => [300, "REDIRECT"], :location => "http://api.foo.com/v2")
FakeWeb.register_uri(:get, "http://api.foo.com/v2", :status => [300, "REDIRECT"], :location => "/v3")
FakeWeb.register_uri(:get, "http://api.foo.com/v3", :body => "<hash><foo>bar</foo></hash>")
response = @request.perform
response.request.base_uri.to_s.should == "http://api.foo.com"
response.request.path.to_s.should == "/v3"
response.request.uri.request_uri.should == "/v3"
response.request.uri.to_s.should == "http://api.foo.com/v3"
response.should == {"hash" => {"foo" => "bar"}}
end
it "returns the HTTParty::Response when the 300 does not contain a location header" do
net_response = stub_response '', 300
HTTParty::Response.should === @request.perform
end
end
it 'should return a valid object for 4xx response' do
stub_response '<foo><bar>yes</bar></foo>', 401
resp = @request.perform
resp.code.should == 401
resp.body.should == "<foo><bar>yes</bar></foo>"
resp['foo']['bar'].should == "yes"
end
it 'should return a valid object for 5xx response' do
stub_response '<foo><bar>error</bar></foo>', 500
resp = @request.perform
resp.code.should == 500
resp.body.should == "<foo><bar>error</bar></foo>"
resp['foo']['bar'].should == "error"
end
it "parses response lazily so codes can be checked prior" do
stub_response 'not xml', 500
@request.options[:format] = :xml
lambda {
response = @request.perform
response.code.should == 500
response.body.should == 'not xml'
}.should_not raise_error
end
end
end
it "should not attempt to parse empty responses" do
[204, 304].each do |code|
stub_response "", code
@request.options[:format] = :xml
@request.perform.should be_nil
end
end
it "should not fail for missing mime type" do
stub_response "Content for you"
@request.options[:format] = :html
@request.perform.should == 'Content for you'
end
describe "a request that redirects" do
before(:each) do
@redirect = stub_response("", 302)
@redirect['location'] = '/foo'
@ok = stub_response('<hash><foo>bar</foo></hash>', 200)
end
describe "once" do
before(:each) do
@http.stub!(:request).and_return(@redirect, @ok)
end
it "should be handled by GET transparently" do
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should be handled by POST transparently" do
@request.http_method = Net::HTTP::Post
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should be handled by DELETE transparently" do
@request.http_method = Net::HTTP::Delete
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should be handled by PATCH transparently" do
@request.http_method = Net::HTTP::Patch
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should be handled by PUT transparently" do
@request.http_method = Net::HTTP::Put
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should be handled by HEAD transparently" do
@request.http_method = Net::HTTP::Head
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should be handled by OPTIONS transparently" do
@request.http_method = Net::HTTP::Options
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should keep track of cookies between redirects" do
@redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
@request.perform
@request.options[:headers]['Cookie'].should match(/foo=bar/)
@request.options[:headers]['Cookie'].should match(/name=value/)
end
it 'should update cookies with rediects' do
@request.options[:headers] = {'Cookie'=> 'foo=bar;'}
@redirect['Set-Cookie'] = 'foo=tar;'
@request.perform
@request.options[:headers]['Cookie'].should match(/foo=tar/)
end
it 'should keep cookies between rediects' do
@request.options[:headers] = {'Cookie'=> 'keep=me'}
@redirect['Set-Cookie'] = 'foo=tar;'
@request.perform
@request.options[:headers]['Cookie'].should match(/keep=me/)
end
it 'should make resulting request a get request if it not already' do
@request.http_method = Net::HTTP::Delete
@request.perform.should == {"hash" => {"foo" => "bar"}}
@request.http_method.should == Net::HTTP::Get
end
it 'should not make resulting request a get request if options[:maintain_method_across_redirects] is true' do
@request.options[:maintain_method_across_redirects] = true
@request.http_method = Net::HTTP::Delete
@request.perform.should == {"hash" => {"foo" => "bar"}}
@request.http_method.should == Net::HTTP::Delete
end
end
describe "infinitely" do
before(:each) do
@http.stub!(:request).and_return(@redirect)
end
it "should raise an exception" do
lambda { @request.perform }.should raise_error(HTTParty::RedirectionTooDeep)
end
end
end
describe "#handle_deflation" do
context "context-encoding" do
before do
@request.options[:format] = :html
@last_response = mock()
@last_response.stub!(:body).and_return('')
end
it "should inflate the gzipped body with content-encoding: gzip" do
@last_response.stub!(:[]).with("content-encoding").and_return("gzip")
@request.stub!(:last_response).and_return(@last_response)
Zlib::GzipReader.should_receive(:new).and_return(StringIO.new(''))
@request.last_response.should_receive(:delete).with('content-encoding')
@request.send(:handle_deflation)
end
it "should inflate the gzipped body with content-encoding: x-gzip" do
@last_response.stub!(:[]).with("content-encoding").and_return("x-gzip")
@request.stub!(:last_response).and_return(@last_response)
Zlib::GzipReader.should_receive(:new).and_return(StringIO.new(''))
@request.last_response.should_receive(:delete).with('content-encoding')
@request.send(:handle_deflation)
end
it "should inflate the deflated body" do
@last_response.stub!(:[]).with("content-encoding").and_return("deflate")
@request.stub!(:last_response).and_return(@last_response)
Zlib::Inflate.should_receive(:inflate).and_return('')
@request.last_response.should_receive(:delete).with('content-encoding')
@request.send(:handle_deflation)
end
end
end
context "with POST http method" do
it "should raise argument error if query is not a hash" do
lambda {
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :format => :xml, :query => 'astring').perform
}.should raise_error(ArgumentError)
end
end
describe "argument validation" do
it "should raise argument error if basic_auth and digest_auth are both present" do
lambda {
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :basic_auth => {}, :digest_auth => {}).perform
}.should raise_error(ArgumentError, "only one authentication method, :basic_auth or :digest_auth may be used at a time")
end
it "should raise argument error if basic_auth is not a hash" do
lambda {
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :basic_auth => ["foo", "bar"]).perform
}.should raise_error(ArgumentError, ":basic_auth must be a hash")
end
it "should raise argument error if digest_auth is not a hash" do
lambda {
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :digest_auth => ["foo", "bar"]).perform
}.should raise_error(ArgumentError, ":digest_auth must be a hash")
end
end
end