dnclabs-httparty 0.6.1.2010090201

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 (69) hide show
  1. data/.gitignore +7 -0
  2. data/History +216 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest +47 -0
  5. data/README.rdoc +54 -0
  6. data/Rakefile +89 -0
  7. data/VERSION +1 -0
  8. data/bin/httparty +108 -0
  9. data/cucumber.yml +1 -0
  10. data/examples/aaws.rb +32 -0
  11. data/examples/basic.rb +11 -0
  12. data/examples/custom_parsers.rb +67 -0
  13. data/examples/delicious.rb +37 -0
  14. data/examples/google.rb +16 -0
  15. data/examples/rubyurl.rb +14 -0
  16. data/examples/twitter.rb +31 -0
  17. data/examples/whoismyrep.rb +10 -0
  18. data/features/basic_authentication.feature +20 -0
  19. data/features/command_line.feature +7 -0
  20. data/features/deals_with_http_error_codes.feature +26 -0
  21. data/features/digest_authentication.feature +20 -0
  22. data/features/handles_compressed_responses.feature +19 -0
  23. data/features/handles_multiple_formats.feature +34 -0
  24. data/features/steps/env.rb +23 -0
  25. data/features/steps/httparty_response_steps.rb +26 -0
  26. data/features/steps/httparty_steps.rb +27 -0
  27. data/features/steps/mongrel_helper.rb +94 -0
  28. data/features/steps/remote_service_steps.rb +69 -0
  29. data/features/supports_redirection.feature +22 -0
  30. data/features/supports_timeout_option.feature +13 -0
  31. data/httparty.gemspec +146 -0
  32. data/lib/httparty.rb +365 -0
  33. data/lib/httparty/cookie_hash.rb +22 -0
  34. data/lib/httparty/core_extensions.rb +31 -0
  35. data/lib/httparty/exceptions.rb +26 -0
  36. data/lib/httparty/module_inheritable_attributes.rb +34 -0
  37. data/lib/httparty/net_digest_auth.rb +35 -0
  38. data/lib/httparty/parser.rb +141 -0
  39. data/lib/httparty/request.rb +231 -0
  40. data/lib/httparty/response.rb +79 -0
  41. data/spec/fixtures/delicious.xml +23 -0
  42. data/spec/fixtures/empty.xml +0 -0
  43. data/spec/fixtures/google.html +3 -0
  44. data/spec/fixtures/ssl/generate.sh +29 -0
  45. data/spec/fixtures/ssl/generated/1fe462c2.0 +15 -0
  46. data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
  47. data/spec/fixtures/ssl/generated/ca.crt +15 -0
  48. data/spec/fixtures/ssl/generated/ca.key +15 -0
  49. data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
  50. data/spec/fixtures/ssl/generated/server.crt +13 -0
  51. data/spec/fixtures/ssl/generated/server.key +15 -0
  52. data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
  53. data/spec/fixtures/twitter.json +1 -0
  54. data/spec/fixtures/twitter.xml +403 -0
  55. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  56. data/spec/httparty/cookie_hash_spec.rb +71 -0
  57. data/spec/httparty/parser_spec.rb +155 -0
  58. data/spec/httparty/request_spec.rb +430 -0
  59. data/spec/httparty/response_spec.rb +188 -0
  60. data/spec/httparty/ssl_spec.rb +54 -0
  61. data/spec/httparty_spec.rb +570 -0
  62. data/spec/spec.opts +3 -0
  63. data/spec/spec_helper.rb +20 -0
  64. data/spec/support/ssl_test_helper.rb +25 -0
  65. data/spec/support/ssl_test_server.rb +69 -0
  66. data/spec/support/stub_response.rb +30 -0
  67. data/website/css/common.css +47 -0
  68. data/website/index.html +73 -0
  69. metadata +245 -0
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <Entities total="0" results="0" page="1" page-size="25" href="https://s3-sandbox.parature.com/api/v1/5578/5633/Account" />
@@ -0,0 +1,71 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper'))
2
+
3
+ describe HTTParty::CookieHash do
4
+ before(:each) do
5
+ @cookie_hash = HTTParty::CookieHash.new
6
+ end
7
+
8
+ describe "#add_cookies" do
9
+
10
+ describe "with a hash" do
11
+ it "should add new key/value pairs to the hash" do
12
+ @cookie_hash.add_cookies(:foo => "bar")
13
+ @cookie_hash.add_cookies(:rofl => "copter")
14
+ @cookie_hash.length.should eql(2)
15
+ end
16
+
17
+ it "should overwrite any existing key" do
18
+ @cookie_hash.add_cookies(:foo => "bar")
19
+ @cookie_hash.add_cookies(:foo => "copter")
20
+ @cookie_hash.length.should eql(1)
21
+ @cookie_hash[:foo].should eql("copter")
22
+ end
23
+ end
24
+
25
+ describe "with a string" do
26
+ it "should add new key/value pairs to the hash" do
27
+ @cookie_hash.add_cookies("first=one; second=two; third")
28
+ @cookie_hash[:first].should == 'one'
29
+ @cookie_hash[:second].should == 'two'
30
+ @cookie_hash[:third].should == nil
31
+ end
32
+
33
+ it "should overwrite any existing key" do
34
+ @cookie_hash[:foo] = 'bar'
35
+ @cookie_hash.add_cookies("foo=tar")
36
+ @cookie_hash.length.should eql(1)
37
+ @cookie_hash[:foo].should eql("tar")
38
+ end
39
+ end
40
+
41
+ describe 'with other class' do
42
+ it "should error" do
43
+ lambda {
44
+ @cookie_hash.add_cookies(Array.new)
45
+ }.should raise_error
46
+ end
47
+ end
48
+ end
49
+
50
+ # The regexen are required because Hashes aren't ordered, so a test against
51
+ # a hardcoded string was randomly failing.
52
+ describe "#to_cookie_string" do
53
+ before(:each) do
54
+ @cookie_hash.add_cookies(:foo => "bar")
55
+ @cookie_hash.add_cookies(:rofl => "copter")
56
+ @s = @cookie_hash.to_cookie_string
57
+ end
58
+
59
+ it "should format the key/value pairs, delimited by semi-colons" do
60
+ @s.should match(/foo=bar/)
61
+ @s.should match(/rofl=copter/)
62
+ @s.should match(/^\w+=\w+; \w+=\w+$/)
63
+ end
64
+
65
+ it "should not include client side only cookies" do
66
+ @cookie_hash.add_cookies(:path => "/")
67
+ @s = @cookie_hash.to_cookie_string
68
+ @s.should_not match(/path=\//)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,155 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe HTTParty::Parser do
4
+ describe ".SupportedFormats" do
5
+ it "returns a hash" do
6
+ HTTParty::Parser::SupportedFormats.should be_instance_of(Hash)
7
+ end
8
+ end
9
+
10
+ describe ".call" do
11
+ it "generates an HTTParty::Parser instance with the given body and format" do
12
+ HTTParty::Parser.should_receive(:new).with('body', :plain).and_return(stub(:parse => nil))
13
+ HTTParty::Parser.call('body', :plain)
14
+ end
15
+
16
+ it "calls #parse on the parser" do
17
+ parser = mock('Parser')
18
+ parser.should_receive(:parse)
19
+ HTTParty::Parser.stub(:new => parser)
20
+ parser = HTTParty::Parser.call('body', :plain)
21
+ end
22
+ end
23
+
24
+ describe ".formats" do
25
+ it "returns the SupportedFormats constant" do
26
+ HTTParty::Parser.formats.should == HTTParty::Parser::SupportedFormats
27
+ end
28
+
29
+ it "returns the SupportedFormats constant for subclasses" do
30
+ class MyParser < HTTParty::Parser
31
+ SupportedFormats = {"application/atom+xml" => :atom}
32
+ end
33
+ MyParser.formats.should == {"application/atom+xml" => :atom}
34
+ end
35
+ end
36
+
37
+ describe ".format_from_mimetype" do
38
+ it "returns a symbol representing the format mimetype" do
39
+ HTTParty::Parser.format_from_mimetype("text/plain").should == :plain
40
+ end
41
+
42
+ it "returns nil when the mimetype is not supported" do
43
+ HTTParty::Parser.format_from_mimetype("application/atom+xml").should be_nil
44
+ end
45
+ end
46
+
47
+ describe ".supported_formats" do
48
+ it "returns a unique set of supported formats represented by symbols" do
49
+ HTTParty::Parser.supported_formats.should == HTTParty::Parser::SupportedFormats.values.uniq
50
+ end
51
+ end
52
+
53
+ describe ".supports_format?" do
54
+ it "returns true for a supported format" do
55
+ HTTParty::Parser.stub(:supported_formats => [:json])
56
+ HTTParty::Parser.supports_format?(:json).should be_true
57
+ end
58
+
59
+ it "returns false for an unsupported format" do
60
+ HTTParty::Parser.stub(:supported_formats => [])
61
+ HTTParty::Parser.supports_format?(:json).should be_false
62
+ end
63
+ end
64
+
65
+ describe "#parse" do
66
+ before do
67
+ @parser = HTTParty::Parser.new('body', :json)
68
+ end
69
+
70
+ it "attempts to parse supported formats" do
71
+ @parser.stub(:supports_format? => true)
72
+ @parser.should_receive(:parse_supported_format)
73
+ @parser.parse
74
+ end
75
+
76
+ it "returns the unparsed body when the format is unsupported" do
77
+ @parser.stub(:supports_format? => false)
78
+ @parser.parse.should == @parser.body
79
+ end
80
+
81
+ it "returns nil for an empty body" do
82
+ @parser.stub(:body => '')
83
+ @parser.parse.should be_nil
84
+ end
85
+
86
+ it "returns nil for a nil body" do
87
+ @parser.stub(:body => nil)
88
+ @parser.parse.should be_nil
89
+ end
90
+ end
91
+
92
+ describe "#supports_format?" do
93
+ it "utilizes the class method to determine if the format is supported" do
94
+ HTTParty::Parser.should_receive(:supports_format?).with(:json)
95
+ parser = HTTParty::Parser.new('body', :json)
96
+ parser.send(:supports_format?)
97
+ end
98
+ end
99
+
100
+ describe "#parse_supported_format" do
101
+ it "calls the parser for the given format" do
102
+ parser = HTTParty::Parser.new('body', :json)
103
+ parser.should_receive(:json)
104
+ parser.send(:parse_supported_format)
105
+ end
106
+
107
+ context "when a parsing method does not exist for the given format" do
108
+ it "raises an exception" do
109
+ parser = HTTParty::Parser.new('body', :atom)
110
+ expect do
111
+ parser.send(:parse_supported_format)
112
+ end.to raise_error(NotImplementedError, "HTTParty::Parser has not implemented a parsing method for the :atom format.")
113
+ end
114
+
115
+ it "raises a useful exception message for subclasses" do
116
+ atom_parser = Class.new(HTTParty::Parser) do
117
+ def self.name; 'AtomParser'; end
118
+ end
119
+ parser = atom_parser.new 'body', :atom
120
+ expect do
121
+ parser.send(:parse_supported_format)
122
+ end.to raise_error(NotImplementedError, "AtomParser has not implemented a parsing method for the :atom format.")
123
+ end
124
+ end
125
+ end
126
+
127
+ context "parsers" do
128
+ subject do
129
+ HTTParty::Parser.new('body', nil)
130
+ end
131
+
132
+ it "parses xml with Crack" do
133
+ Crack::XML.should_receive(:parse).with('body')
134
+ subject.send(:xml)
135
+ end
136
+
137
+ it "parses json with Crack" do
138
+ Crack::JSON.should_receive(:parse).with('body')
139
+ subject.send(:json)
140
+ end
141
+
142
+ it "parses yaml" do
143
+ YAML.should_receive(:load).with('body')
144
+ subject.send(:yaml)
145
+ end
146
+
147
+ it "parses html by simply returning the body" do
148
+ subject.send(:html).should == 'body'
149
+ end
150
+
151
+ it "parses plain text by simply returning the body" do
152
+ subject.send(:plain).should == 'body'
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,430 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe HTTParty::Request do
4
+ before do
5
+ @request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', :format => :xml)
6
+ end
7
+
8
+ describe "initialization" do
9
+ it "sets parser to HTTParty::Parser" do
10
+ request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
11
+ request.parser.should == HTTParty::Parser
12
+ end
13
+
14
+ it "sets parser to the optional parser" do
15
+ my_parser = lambda {}
16
+ request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', :parser => my_parser)
17
+ request.parser.should == my_parser
18
+ end
19
+ end
20
+
21
+ describe "#format" do
22
+ context "request yet to be made" do
23
+ it "returns format option" do
24
+ request = HTTParty::Request.new 'get', '/', :format => :xml
25
+ request.format.should == :xml
26
+ end
27
+
28
+ it "returns nil format" do
29
+ request = HTTParty::Request.new 'get', '/'
30
+ request.format.should be_nil
31
+ end
32
+ end
33
+
34
+ context "request has been made" do
35
+ it "returns format option" do
36
+ request = HTTParty::Request.new 'get', '/', :format => :xml
37
+ request.last_response = stub
38
+ request.format.should == :xml
39
+ end
40
+
41
+ it "returns the content-type from the last response when the option is not set" do
42
+ request = HTTParty::Request.new 'get', '/'
43
+ response = stub
44
+ response.should_receive(:[]).with('content-type').and_return('text/json')
45
+ request.last_response = response
46
+ request.format.should == :json
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ context "options" do
53
+ it "should use basic auth when configured" do
54
+ @request.options[:basic_auth] = {:username => 'foobar', :password => 'secret'}
55
+ @request.send(:setup_raw_request)
56
+ @request.instance_variable_get(:@raw_request)['authorization'].should_not be_nil
57
+ end
58
+
59
+ it "should use digest auth when configured" do
60
+ FakeWeb.register_uri(:head, "http://api.foo.com/v1",
61
+ :www_authenticate => 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false')
62
+
63
+ @request.options[:digest_auth] = {:username => 'foobar', :password => 'secret'}
64
+ @request.send(:setup_raw_request)
65
+
66
+ raw_request = @request.instance_variable_get(:@raw_request)
67
+ raw_request.instance_variable_get(:@header)['Authorization'].should_not be_nil
68
+ end
69
+ end
70
+
71
+ describe "#uri" do
72
+ context "query strings" do
73
+ it "does not add an empty query string when default_params are blank" do
74
+ @request.options[:default_params] = {}
75
+ @request.uri.query.should be_nil
76
+ end
77
+ end
78
+ end
79
+
80
+ describe 'http' do
81
+ it "should use ssl for port 443" do
82
+ request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443')
83
+ request.send(:http).use_ssl?.should == true
84
+ end
85
+
86
+ it 'should not use ssl for port 80' do
87
+ request = HTTParty::Request.new(Net::HTTP::Get, 'http://foobar.com')
88
+ request.send(:http).use_ssl?.should == false
89
+ end
90
+
91
+ it "uses ssl for https scheme with default port" do
92
+ request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com')
93
+ request.send(:http).use_ssl?.should == true
94
+ end
95
+
96
+ it "uses ssl for https scheme regardless of port" do
97
+ request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com:123456')
98
+ request.send(:http).use_ssl?.should == true
99
+ end
100
+
101
+ context "PEM certificates" do
102
+ before do
103
+ OpenSSL::X509::Certificate.stub(:new)
104
+ OpenSSL::PKey::RSA.stub(:new)
105
+ end
106
+
107
+ context "when scheme is https" do
108
+ before do
109
+ @request.stub!(:uri).and_return(URI.parse("https://google.com"))
110
+ pem = :pem_contents
111
+ @cert = mock("OpenSSL::X509::Certificate")
112
+ @key = mock("OpenSSL::PKey::RSA")
113
+ OpenSSL::X509::Certificate.should_receive(:new).with(pem).and_return(@cert)
114
+ OpenSSL::PKey::RSA.should_receive(:new).with(pem).and_return(@key)
115
+
116
+ @request.options[:pem] = pem
117
+ @pem_http = @request.send(:http)
118
+ end
119
+
120
+ it "should use a PEM certificate when provided" do
121
+ @pem_http.cert.should == @cert
122
+ @pem_http.key.should == @key
123
+ end
124
+
125
+ it "should verify the certificate when provided" do
126
+ @pem_http = @request.send(:http)
127
+ @pem_http.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
128
+ end
129
+ end
130
+
131
+ context "when scheme is not https" do
132
+ it "does not assign a PEM" do
133
+ http = Net::HTTP.new('google.com')
134
+ http.should_not_receive(:cert=)
135
+ http.should_not_receive(:key=)
136
+ Net::HTTP.stub(:new => http)
137
+
138
+ request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
139
+ request.options[:pem] = :pem_contents
140
+ request.send(:http)
141
+ end
142
+ end
143
+
144
+ context "debugging" do
145
+ before do
146
+ @http = Net::HTTP.new('google.com')
147
+ Net::HTTP.stub(:new => @http)
148
+ @request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
149
+ end
150
+
151
+ it "calls #set_debug_output when the option is provided" do
152
+ @request.options[:debug_output] = $stderr
153
+ @http.should_receive(:set_debug_output).with($stderr)
154
+ @request.send(:http)
155
+ end
156
+
157
+ it "does not set_debug_output when the option is not provided" do
158
+ @http.should_not_receive(:set_debug_output)
159
+ @request.send(:http)
160
+ end
161
+ end
162
+ end
163
+
164
+ context "when setting timeout" do
165
+ it "does nothing if the timeout option is a string" do
166
+ http = mock("http", :null_object => true)
167
+ http.should_not_receive(:open_timeout=)
168
+ http.should_not_receive(:read_timeout=)
169
+ Net::HTTP.stub(:new => http)
170
+
171
+ request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com', {:timeout => "five seconds"})
172
+ request.send(:http)
173
+ end
174
+
175
+ it "sets the timeout to 5 seconds" do
176
+ @request.options[:timeout] = 5
177
+ @request.send(:http).open_timeout.should == 5
178
+ @request.send(:http).read_timeout.should == 5
179
+ end
180
+ end
181
+ end
182
+
183
+ describe '#format_from_mimetype' do
184
+ it 'should handle text/xml' do
185
+ ["text/xml", "text/xml; charset=iso8859-1"].each do |ct|
186
+ @request.send(:format_from_mimetype, ct).should == :xml
187
+ end
188
+ end
189
+
190
+ it 'should handle application/xml' do
191
+ ["application/xml", "application/xml; charset=iso8859-1"].each do |ct|
192
+ @request.send(:format_from_mimetype, ct).should == :xml
193
+ end
194
+ end
195
+
196
+ it 'should handle text/json' do
197
+ ["text/json", "text/json; charset=iso8859-1"].each do |ct|
198
+ @request.send(:format_from_mimetype, ct).should == :json
199
+ end
200
+ end
201
+
202
+ it 'should handle application/json' do
203
+ ["application/json", "application/json; charset=iso8859-1"].each do |ct|
204
+ @request.send(:format_from_mimetype, ct).should == :json
205
+ end
206
+ end
207
+
208
+ it 'should handle text/javascript' do
209
+ ["text/javascript", "text/javascript; charset=iso8859-1"].each do |ct|
210
+ @request.send(:format_from_mimetype, ct).should == :json
211
+ end
212
+ end
213
+
214
+ it 'should handle application/javascript' do
215
+ ["application/javascript", "application/javascript; charset=iso8859-1"].each do |ct|
216
+ @request.send(:format_from_mimetype, ct).should == :json
217
+ end
218
+ end
219
+
220
+ it "returns nil for an unrecognized mimetype" do
221
+ @request.send(:format_from_mimetype, "application/atom+xml").should be_nil
222
+ end
223
+
224
+ it "returns nil when using a default parser" do
225
+ @request.options[:parser] = lambda {}
226
+ @request.send(:format_from_mimetype, "text/json").should be_nil
227
+ end
228
+ end
229
+
230
+ describe 'parsing responses' do
231
+ it 'should handle xml automatically' do
232
+ xml = %q[<books><book><id>1234</id><name>Foo Bar!</name></book></books>]
233
+ @request.options[:format] = :xml
234
+ @request.send(:parse_response, xml).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
235
+ end
236
+
237
+ it 'should handle json automatically' do
238
+ json = %q[{"books": {"book": {"name": "Foo Bar!", "id": "1234"}}}]
239
+ @request.options[:format] = :json
240
+ @request.send(:parse_response, json).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
241
+ end
242
+
243
+ it 'should handle yaml automatically' do
244
+ yaml = "books: \n book: \n name: Foo Bar!\n id: \"1234\"\n"
245
+ @request.options[:format] = :yaml
246
+ @request.send(:parse_response, yaml).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
247
+ end
248
+
249
+ it "should include any HTTP headers in the returned response" do
250
+ @request.options[:format] = :html
251
+ response = stub_response "Content"
252
+ response.initialize_http_header("key" => "value")
253
+
254
+ @request.perform.headers.should == { "key" => ["value"] }
255
+ end
256
+
257
+ describe 'with non-200 responses' do
258
+ context "3xx responses" do
259
+ it 'returns a valid object for 304 not modified' do
260
+ stub_response '', 304
261
+ resp = @request.perform
262
+ resp.code.should == 304
263
+ resp.body.should == ''
264
+ resp.should be_nil
265
+ end
266
+
267
+ it "redirects if a 300 contains a location header" do
268
+ redirect = stub_response '', 300
269
+ redirect['location'] = 'http://foo.com/foo'
270
+ ok = stub_response('<hash><foo>bar</foo></hash>', 200)
271
+ @http.stub!(:request).and_return(redirect, ok)
272
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
273
+ end
274
+
275
+ it "returns the Net::HTTP response if the 300 does not contain a location header" do
276
+ net_response = stub_response '', 300
277
+ @request.perform.should be_kind_of(Net::HTTPMultipleChoice)
278
+ end
279
+ end
280
+
281
+ it 'should return a valid object for 4xx response' do
282
+ stub_response '<foo><bar>yes</bar></foo>', 401
283
+ resp = @request.perform
284
+ resp.code.should == 401
285
+ resp.body.should == "<foo><bar>yes</bar></foo>"
286
+ resp['foo']['bar'].should == "yes"
287
+ end
288
+
289
+ it 'should return a valid object for 5xx response' do
290
+ stub_response '<foo><bar>error</bar></foo>', 500
291
+ resp = @request.perform
292
+ resp.code.should == 500
293
+ resp.body.should == "<foo><bar>error</bar></foo>"
294
+ resp['foo']['bar'].should == "error"
295
+ end
296
+ end
297
+ end
298
+
299
+ it "should not attempt to parse empty responses" do
300
+ [204, 304].each do |code|
301
+ stub_response "", code
302
+
303
+ @request.options[:format] = :xml
304
+ @request.perform.should be_nil
305
+ end
306
+ end
307
+
308
+ it "should not fail for missing mime type" do
309
+ stub_response "Content for you"
310
+ @request.options[:format] = :html
311
+ @request.perform.should == 'Content for you'
312
+ end
313
+
314
+ describe "a request that redirects" do
315
+ before(:each) do
316
+ @redirect = stub_response("", 302)
317
+ @redirect['location'] = '/foo'
318
+
319
+ @ok = stub_response('<hash><foo>bar</foo></hash>', 200)
320
+ end
321
+
322
+ describe "once" do
323
+ before(:each) do
324
+ @http.stub!(:request).and_return(@redirect, @ok)
325
+ end
326
+
327
+ it "should be handled by GET transparently" do
328
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
329
+ end
330
+
331
+ it "should be handled by POST transparently" do
332
+ @request.http_method = Net::HTTP::Post
333
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
334
+ end
335
+
336
+ it "should be handled by DELETE transparently" do
337
+ @request.http_method = Net::HTTP::Delete
338
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
339
+ end
340
+
341
+ it "should be handled by PUT transparently" do
342
+ @request.http_method = Net::HTTP::Put
343
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
344
+ end
345
+
346
+ it "should be handled by HEAD transparently" do
347
+ @request.http_method = Net::HTTP::Head
348
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
349
+ end
350
+
351
+ it "should be handled by OPTIONS transparently" do
352
+ @request.http_method = Net::HTTP::Options
353
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
354
+ end
355
+
356
+ it "should keep track of cookies between redirects" do
357
+ @redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
358
+ @request.perform
359
+ @request.options[:headers]['Cookie'].should match(/foo=bar/)
360
+ @request.options[:headers]['Cookie'].should match(/name=value/)
361
+ end
362
+
363
+ it 'should update cookies with rediects' do
364
+ @request.options[:headers] = {'Cookie'=> 'foo=bar;'}
365
+ @redirect['Set-Cookie'] = 'foo=tar;'
366
+ @request.perform
367
+ @request.options[:headers]['Cookie'].should match(/foo=tar/)
368
+ end
369
+
370
+ it 'should keep cookies between rediects' do
371
+ @request.options[:headers] = {'Cookie'=> 'keep=me'}
372
+ @redirect['Set-Cookie'] = 'foo=tar;'
373
+ @request.perform
374
+ @request.options[:headers]['Cookie'].should match(/keep=me/)
375
+ end
376
+
377
+ it 'should make resulting request a get request if it not already' do
378
+ @request.http_method = Net::HTTP::Delete
379
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
380
+ @request.http_method.should == Net::HTTP::Get
381
+ end
382
+
383
+ it 'should not make resulting request a get request if options[:maintain_method_across_redirects] is true' do
384
+ @request.options[:maintain_method_across_redirects] = true
385
+ @request.http_method = Net::HTTP::Delete
386
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
387
+ @request.http_method.should == Net::HTTP::Delete
388
+ end
389
+ end
390
+
391
+ describe "infinitely" do
392
+ before(:each) do
393
+ @http.stub!(:request).and_return(@redirect)
394
+ end
395
+
396
+ it "should raise an exception" do
397
+ lambda { @request.perform }.should raise_error(HTTParty::RedirectionTooDeep)
398
+ end
399
+ end
400
+ end
401
+
402
+ context "with POST http method" do
403
+ it "should raise argument error if query is not a hash" do
404
+ lambda {
405
+ HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :format => :xml, :query => 'astring').perform
406
+ }.should raise_error(ArgumentError)
407
+ end
408
+ end
409
+
410
+ describe "argument validation" do
411
+ it "should raise argument error if basic_auth and digest_auth are both present" do
412
+ lambda {
413
+ HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :basic_auth => {}, :digest_auth => {}).perform
414
+ }.should raise_error(ArgumentError, "only one authentication method, :basic_auth or :digest_auth may be used at a time")
415
+ end
416
+
417
+ it "should raise argument error if basic_auth is not a hash" do
418
+ lambda {
419
+ HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :basic_auth => ["foo", "bar"]).perform
420
+ }.should raise_error(ArgumentError, ":basic_auth must be a hash")
421
+ end
422
+
423
+ it "should raise argument error if digest_auth is not a hash" do
424
+ lambda {
425
+ HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :digest_auth => ["foo", "bar"]).perform
426
+ }.should raise_error(ArgumentError, ":digest_auth must be a hash")
427
+ end
428
+ end
429
+ end
430
+