esank-rest-client 1.6.7

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.
@@ -0,0 +1,38 @@
1
+ require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
2
+
3
+ require 'webmock/rspec'
4
+ include WebMock
5
+
6
+ describe RestClient do
7
+
8
+ it "a simple request" do
9
+ body = 'abc'
10
+ stub_request(:get, "www.example.com").to_return(:body => body, :status => 200)
11
+ response = RestClient.get "www.example.com"
12
+ response.code.should == 200
13
+ response.body.should == body
14
+ end
15
+
16
+ it "a simple request with gzipped content" do
17
+ stub_request(:get, "www.example.com").with(:headers => { 'Accept-Encoding' => 'gzip, deflate' }).to_return(:body => "\037\213\b\b\006'\252H\000\003t\000\313T\317UH\257\312,HM\341\002\000G\242(\r\v\000\000\000", :status => 200, :headers => { 'Content-Encoding' => 'gzip' } )
18
+ response = RestClient.get "www.example.com"
19
+ response.code.should == 200
20
+ response.body.should == "i'm gziped\n"
21
+ end
22
+
23
+ it "a 404" do
24
+ body = "Ho hai ! I'm not here !"
25
+ stub_request(:get, "www.example.com").to_return(:body => body, :status => 404)
26
+ begin
27
+ RestClient.get "www.example.com"
28
+ raise
29
+ rescue RestClient::ResourceNotFound => e
30
+ e.http_code.should == 404
31
+ e.response.code.should == 404
32
+ e.response.body.should == body
33
+ e.http_body.should == body
34
+ end
35
+ end
36
+
37
+
38
+ end
Binary file
@@ -0,0 +1,234 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'base')
2
+
3
+ describe RestClient::Payload do
4
+ context "A regular Payload" do
5
+ it "should use standard enctype as default content-type" do
6
+ RestClient::Payload::UrlEncoded.new({}).headers['Content-Type'].
7
+ should == 'application/x-www-form-urlencoded'
8
+ end
9
+
10
+ it "should form properly encoded params" do
11
+ RestClient::Payload::UrlEncoded.new({:foo => 'bar'}).to_s.
12
+ should == "foo=bar"
13
+ ["foo=bar&baz=qux", "baz=qux&foo=bar"].should include(
14
+ RestClient::Payload::UrlEncoded.new({:foo => 'bar', :baz => 'qux'}).to_s)
15
+ end
16
+
17
+ it "should escape parameters" do
18
+ RestClient::Payload::UrlEncoded.new({'foo ' => 'bar'}).to_s.
19
+ should == "foo%20=bar"
20
+ end
21
+
22
+ it "should properly handle hashes as parameter" do
23
+ RestClient::Payload::UrlEncoded.new({:foo => {:bar => 'baz'}}).to_s.
24
+ should == "foo[bar]=baz"
25
+ RestClient::Payload::UrlEncoded.new({:foo => {:bar => {:baz => 'qux'}}}).to_s.
26
+ should == "foo[bar][baz]=qux"
27
+ end
28
+
29
+ it "should handle many attributes inside a hash" do
30
+ parameters = RestClient::Payload::UrlEncoded.new({:foo => {:bar => 'baz', :baz => 'qux'}}).to_s
31
+ parameters.should include("foo[bar]=baz", "foo[baz]=qux")
32
+ end
33
+
34
+ it "should handle attributes inside a an array inside an hash" do
35
+ parameters = RestClient::Payload::UrlEncoded.new({"foo" => [{"bar" => 'baz'}, {"bar" => 'qux'}]}).to_s
36
+ parameters.should include("foo[bar]=baz", "foo[bar]=qux")
37
+ end
38
+
39
+ it "should handle attributes inside a an array inside an array inside an hash" do
40
+ parameters = RestClient::Payload::UrlEncoded.new({"foo" => [[{"bar" => 'baz'}, {"bar" => 'qux'}]]}).to_s
41
+ parameters.should include("foo[bar]=baz", "foo[bar]=qux")
42
+ end
43
+
44
+ it "should form properly use symbols as parameters" do
45
+ RestClient::Payload::UrlEncoded.new({:foo => :bar}).to_s.
46
+ should == "foo=bar"
47
+ RestClient::Payload::UrlEncoded.new({:foo => {:bar => :baz}}).to_s.
48
+ should == "foo[bar]=baz"
49
+ end
50
+
51
+ it "should properly handle arrays as repeated parameters" do
52
+ RestClient::Payload::UrlEncoded.new({:foo => ['bar']}).to_s.
53
+ should == "foo[]=bar"
54
+ RestClient::Payload::UrlEncoded.new({:foo => ['bar', 'baz']}).to_s.
55
+ should == "foo[]=bar&foo[]=baz"
56
+ end
57
+
58
+ it 'should not close if stream already closed' do
59
+ p = RestClient::Payload::UrlEncoded.new({'foo ' => 'bar'})
60
+ 3.times {p.close}
61
+ end
62
+
63
+ end
64
+
65
+ context "A multipart Payload" do
66
+ it "should use standard enctype as default content-type" do
67
+ m = RestClient::Payload::Multipart.new({})
68
+ m.stub!(:boundary).and_return(123)
69
+ m.headers['Content-Type'].should == 'multipart/form-data; boundary=123'
70
+ end
71
+
72
+ it 'should not error on close if stream already closed' do
73
+ m = RestClient::Payload::Multipart.new(:file => File.new(File.join(File.dirname(File.expand_path(__FILE__)), 'master_shake.jpg')))
74
+ 3.times {m.close}
75
+ end
76
+
77
+ it "should form properly separated multipart data" do
78
+ m = RestClient::Payload::Multipart.new([[:bar, "baz"], [:foo, "bar"]])
79
+ m.to_s.should == <<-EOS
80
+ --#{m.boundary}\r
81
+ Content-Disposition: form-data; name="bar"\r
82
+ \r
83
+ baz\r
84
+ --#{m.boundary}\r
85
+ Content-Disposition: form-data; name="foo"\r
86
+ \r
87
+ bar\r
88
+ --#{m.boundary}--\r
89
+ EOS
90
+ end
91
+
92
+ it "should not escape parameters names" do
93
+ m = RestClient::Payload::Multipart.new([["bar ", "baz"]])
94
+ m.to_s.should == <<-EOS
95
+ --#{m.boundary}\r
96
+ Content-Disposition: form-data; name="bar "\r
97
+ \r
98
+ baz\r
99
+ --#{m.boundary}--\r
100
+ EOS
101
+ end
102
+
103
+ it "should form properly separated multipart data" do
104
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
105
+ m = RestClient::Payload::Multipart.new({:foo => f})
106
+ m.to_s.should == <<-EOS
107
+ --#{m.boundary}\r
108
+ Content-Disposition: form-data; name="foo"; filename="master_shake.jpg"\r
109
+ Content-Type: image/jpeg\r
110
+ \r
111
+ #{IO.read(f.path)}\r
112
+ --#{m.boundary}--\r
113
+ EOS
114
+ end
115
+
116
+ it "should ignore the name attribute when it's not set" do
117
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
118
+ m = RestClient::Payload::Multipart.new({nil => f})
119
+ m.to_s.should == <<-EOS
120
+ --#{m.boundary}\r
121
+ Content-Disposition: form-data; filename="master_shake.jpg"\r
122
+ Content-Type: image/jpeg\r
123
+ \r
124
+ #{IO.read(f.path)}\r
125
+ --#{m.boundary}--\r
126
+ EOS
127
+ end
128
+
129
+ it "should detect optional (original) content type and filename" do
130
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
131
+ f.instance_eval "def content_type; 'text/plain'; end"
132
+ f.instance_eval "def original_filename; 'foo.txt'; end"
133
+ m = RestClient::Payload::Multipart.new({:foo => f})
134
+ m.to_s.should == <<-EOS
135
+ --#{m.boundary}\r
136
+ Content-Disposition: form-data; name="foo"; filename="foo.txt"\r
137
+ Content-Type: text/plain\r
138
+ \r
139
+ #{IO.read(f.path)}\r
140
+ --#{m.boundary}--\r
141
+ EOS
142
+ end
143
+
144
+ it "should handle hash in hash parameters" do
145
+ m = RestClient::Payload::Multipart.new({:bar => {:baz => "foo"}})
146
+ m.to_s.should == <<-EOS
147
+ --#{m.boundary}\r
148
+ Content-Disposition: form-data; name="bar[baz]"\r
149
+ \r
150
+ foo\r
151
+ --#{m.boundary}--\r
152
+ EOS
153
+
154
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
155
+ f.instance_eval "def content_type; 'text/plain'; end"
156
+ f.instance_eval "def original_filename; 'foo.txt'; end"
157
+ m = RestClient::Payload::Multipart.new({:foo => {:bar => f}})
158
+ m.to_s.should == <<-EOS
159
+ --#{m.boundary}\r
160
+ Content-Disposition: form-data; name="foo[bar]"; filename="foo.txt"\r
161
+ Content-Type: text/plain\r
162
+ \r
163
+ #{IO.read(f.path)}\r
164
+ --#{m.boundary}--\r
165
+ EOS
166
+ end
167
+
168
+ end
169
+
170
+ context "streamed payloads" do
171
+ it "should properly determine the size of file payloads" do
172
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
173
+ payload = RestClient::Payload.generate(f)
174
+ payload.size.should == 22_545
175
+ payload.length.should == 22_545
176
+ end
177
+
178
+ it "should properly determine the size of other kinds of streaming payloads" do
179
+ s = StringIO.new 'foo'
180
+ payload = RestClient::Payload.generate(s)
181
+ payload.size.should == 3
182
+ payload.length.should == 3
183
+
184
+ begin
185
+ f = Tempfile.new "rest-client"
186
+ f.write 'foo bar'
187
+
188
+ payload = RestClient::Payload.generate(f)
189
+ payload.size.should == 7
190
+ payload.length.should == 7
191
+ ensure
192
+ f.close
193
+ end
194
+ end
195
+ end
196
+
197
+ context "Payload generation" do
198
+ it "should recognize standard urlencoded params" do
199
+ RestClient::Payload.generate({"foo" => 'bar'}).should be_kind_of(RestClient::Payload::UrlEncoded)
200
+ end
201
+
202
+ it "should recognize multipart params" do
203
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
204
+ RestClient::Payload.generate({"foo" => f}).should be_kind_of(RestClient::Payload::Multipart)
205
+ end
206
+
207
+ it "should be multipart if forced" do
208
+ RestClient::Payload.generate({"foo" => "bar", :multipart => true}).should be_kind_of(RestClient::Payload::Multipart)
209
+ end
210
+
211
+ it "should return data if no of the above" do
212
+ RestClient::Payload.generate("data").should be_kind_of(RestClient::Payload::Base)
213
+ end
214
+
215
+ it "should recognize nested multipart payloads in hashes" do
216
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
217
+ RestClient::Payload.generate({"foo" => {"file" => f}}).should be_kind_of(RestClient::Payload::Multipart)
218
+ end
219
+
220
+ it "should recognize nested multipart payloads in arrays" do
221
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
222
+ RestClient::Payload.generate({"foo" => [f]}).should be_kind_of(RestClient::Payload::Multipart)
223
+ end
224
+
225
+ it "should recognize file payloads that can be streamed" do
226
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
227
+ RestClient::Payload.generate(f).should be_kind_of(RestClient::Payload::Streamed)
228
+ end
229
+
230
+ it "should recognize other payloads that can be streamed" do
231
+ RestClient::Payload.generate(StringIO.new('foo')).should be_kind_of(RestClient::Payload::Streamed)
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,17 @@
1
+ require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
2
+
3
+ describe RestClient::RawResponse do
4
+ before do
5
+ @tf = mock("Tempfile", :read => "the answer is 42", :open => true)
6
+ @net_http_res = mock('net http response')
7
+ @response = RestClient::RawResponse.new(@tf, @net_http_res, {})
8
+ end
9
+
10
+ it "behaves like string" do
11
+ @response.to_s.should == 'the answer is 42'
12
+ end
13
+
14
+ it "exposes a Tempfile" do
15
+ @response.file.should == @tf
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
2
+
3
+ require 'webmock/rspec'
4
+ include WebMock
5
+
6
+ describe RestClient::Request do
7
+
8
+ it "manage params for get requests" do
9
+ stub_request(:get, 'http://some/resource?a=b&c=d').with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Foo'=>'bar'}).to_return(:body => 'foo', :status => 200)
10
+ RestClient::Request.execute(:url => 'http://some/resource', :method => :get, :headers => {:foo => :bar, :params => {:a => :b, 'c' => 'd'}}).body.should == 'foo'
11
+
12
+ stub_request(:get, 'http://some/resource').with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Foo'=>'bar', 'params' => 'a'}).to_return(:body => 'foo', :status => 200)
13
+ RestClient::Request.execute(:url => 'http://some/resource', :method => :get, :headers => {:foo => :bar, :params => :a}).body.should == 'foo'
14
+ end
15
+
16
+ it "can use a block to process response" do
17
+ response_value = nil
18
+ block = Proc.new do |http_response|
19
+ response_value = http_response.body
20
+ end
21
+ stub_request(:get, 'http://some/resource?a=b&c=d').with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Foo'=>'bar'}).to_return(:body => 'foo', :status => 200)
22
+ RestClient::Request.execute(:url => 'http://some/resource', :method => :get, :headers => {:foo => :bar, :params => {:a => :b, 'c' => 'd'}}, :block_response => block)
23
+ response_value.should == "foo"
24
+ end
25
+
26
+ it 'closes payload if not nil' do
27
+ test_file = File.new(File.join( File.dirname(File.expand_path(__FILE__)), 'master_shake.jpg'))
28
+ initial_count = tmp_count
29
+
30
+ stub_request(:post, 'http://some/resource').with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).to_return(:body => 'foo', :status => 200)
31
+ RestClient::Request.execute(:url => 'http://some/resource', :method => :post, :payload => {:file => test_file})
32
+
33
+ tmp_count.should == initial_count
34
+ end
35
+
36
+ end
37
+
38
+ def tmp_count
39
+ Dir.glob(Dir::tmpdir + "/*").size
40
+ end
@@ -0,0 +1,554 @@
1
+ require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
2
+
3
+ require 'webmock/rspec'
4
+ include WebMock
5
+
6
+ describe RestClient::Request do
7
+ before do
8
+ @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload')
9
+
10
+ @uri = mock("uri")
11
+ @uri.stub!(:request_uri).and_return('/resource')
12
+ @uri.stub!(:host).and_return('some')
13
+ @uri.stub!(:port).and_return(80)
14
+
15
+ @net = mock("net::http base")
16
+ @http = mock("net::http connection")
17
+ Net::HTTP.stub!(:new).and_return(@net)
18
+ @net.stub!(:start).and_yield(@http)
19
+ @net.stub!(:use_ssl=)
20
+ @net.stub!(:verify_mode=)
21
+ RestClient.log = nil
22
+ end
23
+
24
+ it "accept */* mimetype, preferring xml" do
25
+ @request.default_headers[:accept].should == '*/*; q=0.5, application/xml'
26
+ end
27
+
28
+ describe "compression" do
29
+
30
+ it "decodes an uncompressed result body by passing it straight through" do
31
+ RestClient::Request.decode(nil, 'xyz').should == 'xyz'
32
+ end
33
+
34
+ it "doesn't fail for nil bodies" do
35
+ RestClient::Request.decode('gzip', nil).should be_nil
36
+ end
37
+
38
+
39
+ it "decodes a gzip body" do
40
+ RestClient::Request.decode('gzip', "\037\213\b\b\006'\252H\000\003t\000\313T\317UH\257\312,HM\341\002\000G\242(\r\v\000\000\000").should == "i'm gziped\n"
41
+ end
42
+
43
+ it "ingores gzip for empty bodies" do
44
+ RestClient::Request.decode('gzip', '').should be_empty
45
+ end
46
+
47
+ it "decodes a deflated body" do
48
+ RestClient::Request.decode('deflate', "x\234+\316\317MUHIM\313I,IMQ(I\255(\001\000A\223\006\363").should == "some deflated text"
49
+ end
50
+ end
51
+
52
+ it "processes a successful result" do
53
+ res = mock("result")
54
+ res.stub!(:code).and_return("200")
55
+ res.stub!(:body).and_return('body')
56
+ res.stub!(:[]).with('content-encoding').and_return(nil)
57
+ @request.process_result(res).body.should == 'body'
58
+ @request.process_result(res).to_s.should == 'body'
59
+ end
60
+
61
+ it "doesn't classify successful requests as failed" do
62
+ 203.upto(207) do |code|
63
+ res = mock("result")
64
+ res.stub!(:code).and_return(code.to_s)
65
+ res.stub!(:body).and_return("")
66
+ res.stub!(:[]).with('content-encoding').and_return(nil)
67
+ @request.process_result(res).should be_empty
68
+ end
69
+ end
70
+
71
+ it "parses a url into a URI object" do
72
+ URI.should_receive(:parse).with('http://example.com/resource')
73
+ @request.parse_url('http://example.com/resource')
74
+ end
75
+
76
+ it "adds http:// to the front of resources specified in the syntax example.com/resource" do
77
+ URI.should_receive(:parse).with('http://example.com/resource')
78
+ @request.parse_url('example.com/resource')
79
+ end
80
+
81
+ describe "user - password" do
82
+ it "extracts the username and password when parsing http://user:password@example.com/" do
83
+ URI.stub!(:parse).and_return(mock('uri', :user => 'joe', :password => 'pass1'))
84
+ @request.parse_url_with_auth('http://joe:pass1@example.com/resource')
85
+ @request.user.should == 'joe'
86
+ @request.password.should == 'pass1'
87
+ end
88
+
89
+ it "extracts with escaping the username and password when parsing http://user:password@example.com/" do
90
+ URI.stub!(:parse).and_return(mock('uri', :user => 'joe%20', :password => 'pass1'))
91
+ @request.parse_url_with_auth('http://joe%20:pass1@example.com/resource')
92
+ @request.user.should == 'joe '
93
+ @request.password.should == 'pass1'
94
+ end
95
+
96
+ it "doesn't overwrite user and password (which may have already been set by the Resource constructor) if there is no user/password in the url" do
97
+ URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
98
+ @request = RestClient::Request.new(:method => 'get', :url => 'example.com', :user => 'beth', :password => 'pass2')
99
+ @request.parse_url_with_auth('http://example.com/resource')
100
+ @request.user.should == 'beth'
101
+ @request.password.should == 'pass2'
102
+ end
103
+ end
104
+
105
+ it "correctly formats cookies provided to the constructor" do
106
+ URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
107
+ @request = RestClient::Request.new(:method => 'get', :url => 'example.com', :cookies => {:session_id => '1', :user_id => "someone" })
108
+ @request.should_receive(:default_headers).and_return({'Foo' => 'bar'})
109
+ @request.make_headers({}).should == { 'Foo' => 'bar', 'Cookie' => 'session_id=1; user_id=someone'}
110
+ end
111
+
112
+ it "uses netrc credentials" do
113
+ URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil, :host => 'example.com'))
114
+ File.stub!(:stat).and_return(mock('stat', :mode => 0600))
115
+ IO.stub!(:readlines).and_return(["machine example.com login a password b"])
116
+ @request.parse_url_with_auth('http://example.com/resource')
117
+ @request.user.should == 'a'
118
+ @request.password.should == 'b'
119
+ end
120
+
121
+ it "uses credentials in the url in preference to netrc" do
122
+ URI.stub!(:parse).and_return(mock('uri', :user => 'joe%20', :password => 'pass1', :host => 'example.com'))
123
+ File.stub!(:stat).and_return(mock('stat', :mode => 0600))
124
+ IO.stub!(:readlines).and_return(["machine example.com login a password b"])
125
+ @request.parse_url_with_auth('http://joe%20:pass1@example.com/resource')
126
+ @request.user.should == 'joe '
127
+ @request.password.should == 'pass1'
128
+ end
129
+
130
+ it "determines the Net::HTTP class to instantiate by the method name" do
131
+ @request.net_http_request_class(:put).should == Net::HTTP::Put
132
+ end
133
+
134
+ describe "user headers" do
135
+ it "merges user headers with the default headers" do
136
+ @request.should_receive(:default_headers).and_return({ :accept => '*/*; q=0.5, application/xml', :accept_encoding => 'gzip, deflate' })
137
+ headers = @request.make_headers("Accept" => "application/json", :accept_encoding => 'gzip')
138
+ headers.should have_key "Accept-Encoding"
139
+ headers["Accept-Encoding"].should == "gzip"
140
+ headers.should have_key "Accept"
141
+ headers["Accept"].should == "application/json"
142
+ end
143
+
144
+ it "prefers the user header when the same header exists in the defaults" do
145
+ @request.should_receive(:default_headers).and_return({ '1' => '2' })
146
+ headers = @request.make_headers('1' => '3')
147
+ headers.should have_key('1')
148
+ headers['1'].should == '3'
149
+ end
150
+
151
+ it "converts user headers to string before calling CGI::unescape which fails on non string values" do
152
+ @request.should_receive(:default_headers).and_return({ '1' => '2' })
153
+ headers = @request.make_headers('1' => 3)
154
+ headers.should have_key('1')
155
+ headers['1'].should == '3'
156
+ end
157
+ end
158
+
159
+ describe "header symbols" do
160
+
161
+ it "converts header symbols from :content_type to 'Content-Type'" do
162
+ @request.should_receive(:default_headers).and_return({})
163
+ headers = @request.make_headers(:content_type => 'abc')
164
+ headers.should have_key('Content-Type')
165
+ headers['Content-Type'].should == 'abc'
166
+ end
167
+
168
+ it "converts content-type from extension to real content-type" do
169
+ @request.should_receive(:default_headers).and_return({})
170
+ headers = @request.make_headers(:content_type => 'json')
171
+ headers.should have_key('Content-Type')
172
+ headers['Content-Type'].should == 'application/json'
173
+ end
174
+
175
+ it "converts accept from extension(s) to real content-type(s)" do
176
+ @request.should_receive(:default_headers).and_return({})
177
+ headers = @request.make_headers(:accept => 'json, mp3')
178
+ headers.should have_key('Accept')
179
+ headers['Accept'].should == 'application/json, audio/mpeg'
180
+
181
+ @request.should_receive(:default_headers).and_return({})
182
+ headers = @request.make_headers(:accept => :json)
183
+ headers.should have_key('Accept')
184
+ headers['Accept'].should == 'application/json'
185
+ end
186
+
187
+ it "only convert symbols in header" do
188
+ @request.should_receive(:default_headers).and_return({})
189
+ headers = @request.make_headers({:foo_bar => 'value', "bar_bar" => 'value'})
190
+ headers['Foo-Bar'].should == 'value'
191
+ headers['bar_bar'].should == 'value'
192
+ end
193
+
194
+ it "converts header values to strings" do
195
+ @request.make_headers('A' => 1)['A'].should == '1'
196
+ end
197
+ end
198
+
199
+ it "executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do
200
+ @request.should_receive(:parse_url_with_auth).with('http://some/resource').and_return(@uri)
201
+ klass = mock("net:http class")
202
+ @request.should_receive(:net_http_request_class).with(:put).and_return(klass)
203
+ klass.should_receive(:new).and_return('result')
204
+ @request.should_receive(:transmit).with(@uri, 'result', kind_of(RestClient::Payload::Base))
205
+ @request.execute
206
+ end
207
+
208
+ it "transmits the request with Net::HTTP" do
209
+ @http.should_receive(:request).with('req', 'payload')
210
+ @request.should_receive(:process_result)
211
+ @request.transmit(@uri, 'req', 'payload')
212
+ end
213
+
214
+ describe "payload" do
215
+ it "sends nil payloads" do
216
+ @http.should_receive(:request).with('req', nil)
217
+ @request.should_receive(:process_result)
218
+ @request.stub!(:response_log)
219
+ @request.transmit(@uri, 'req', nil)
220
+ end
221
+
222
+ it "passes non-hash payloads straight through" do
223
+ @request.process_payload("x").should == "x"
224
+ end
225
+
226
+ it "converts a hash payload to urlencoded data" do
227
+ @request.process_payload(:a => 'b c+d').should == "a=b%20c%2Bd"
228
+ end
229
+
230
+ it "accepts nested hashes in payload" do
231
+ payload = @request.process_payload(:user => { :name => 'joe', :location => { :country => 'USA', :state => 'CA' }})
232
+ payload.should include('user[name]=joe')
233
+ payload.should include('user[location][country]=USA')
234
+ payload.should include('user[location][state]=CA')
235
+ end
236
+ end
237
+
238
+ it "set urlencoded content_type header on hash payloads" do
239
+ @request.process_payload(:a => 1)
240
+ @request.headers[:content_type].should == 'application/x-www-form-urlencoded'
241
+ end
242
+
243
+ describe "credentials" do
244
+ it "sets up the credentials prior to the request" do
245
+ @http.stub!(:request)
246
+ @request.stub!(:process_result)
247
+ @request.stub!(:response_log)
248
+
249
+ @request.stub!(:user).and_return('joe')
250
+ @request.stub!(:password).and_return('mypass')
251
+ @request.should_receive(:setup_credentials).with('req')
252
+
253
+ @request.transmit(@uri, 'req', nil)
254
+ end
255
+
256
+ it "does not attempt to send any credentials if user is nil" do
257
+ @request.stub!(:user).and_return(nil)
258
+ req = mock("request")
259
+ req.should_not_receive(:basic_auth)
260
+ @request.setup_credentials(req)
261
+ end
262
+
263
+ it "setup credentials when there's a user" do
264
+ @request.stub!(:user).and_return('joe')
265
+ @request.stub!(:password).and_return('mypass')
266
+ req = mock("request")
267
+ req.should_receive(:basic_auth).with('joe', 'mypass')
268
+ @request.setup_credentials(req)
269
+ end
270
+ end
271
+
272
+ it "catches EOFError and shows the more informative ServerBrokeConnection" do
273
+ @http.stub!(:request).and_raise(EOFError)
274
+ lambda { @request.transmit(@uri, 'req', nil) }.should raise_error(RestClient::ServerBrokeConnection)
275
+ end
276
+
277
+ it "class method execute wraps constructor" do
278
+ req = mock("rest request")
279
+ RestClient::Request.should_receive(:new).with(1 => 2).and_return(req)
280
+ req.should_receive(:execute)
281
+ RestClient::Request.execute(1 => 2)
282
+ end
283
+
284
+ describe "exception" do
285
+ it "raises Unauthorized when the response is 401" do
286
+ res = mock('response', :code => '401', :[] => ['content-encoding' => ''], :body => '' )
287
+ lambda { @request.process_result(res) }.should raise_error(RestClient::Unauthorized)
288
+ end
289
+
290
+ it "raises ResourceNotFound when the response is 404" do
291
+ res = mock('response', :code => '404', :[] => ['content-encoding' => ''], :body => '' )
292
+ lambda { @request.process_result(res) }.should raise_error(RestClient::ResourceNotFound)
293
+ end
294
+
295
+ it "raises RequestFailed otherwise" do
296
+ res = mock('response', :code => '500', :[] => ['content-encoding' => ''], :body => '' )
297
+ lambda { @request.process_result(res) }.should raise_error(RestClient::InternalServerError)
298
+ end
299
+ end
300
+
301
+ describe "block usage" do
302
+ it "returns what asked to" do
303
+ res = mock('response', :code => '401', :[] => ['content-encoding' => ''], :body => '' )
304
+ @request.process_result(res){|response, request| "foo"}.should == "foo"
305
+ end
306
+ end
307
+
308
+ describe "proxy" do
309
+ it "creates a proxy class if a proxy url is given" do
310
+ RestClient.stub!(:proxy).and_return("http://example.com/")
311
+ @request.net_http_class.should include(Net::HTTP::ProxyDelta)
312
+ end
313
+
314
+ it "creates a non-proxy class if a proxy url is not given" do
315
+ @request.net_http_class.should_not include(Net::HTTP::ProxyDelta)
316
+ end
317
+ end
318
+
319
+
320
+ describe "logging" do
321
+ it "logs a get request" do
322
+ log = RestClient.log = []
323
+ RestClient::Request.new(:method => :get, :url => 'http://url').log_request
324
+ log[0].should == %Q{RestClient.get "http://url", "Accept"=>"*/*; q=0.5, application/xml", "Accept-Encoding"=>"gzip, deflate"\n}
325
+ end
326
+
327
+ it "logs a post request with a small payload" do
328
+ log = RestClient.log = []
329
+ RestClient::Request.new(:method => :post, :url => 'http://url', :payload => 'foo').log_request
330
+ log[0].should == %Q{RestClient.post "http://url", "foo", "Accept"=>"*/*; q=0.5, application/xml", "Accept-Encoding"=>"gzip, deflate", "Content-Length"=>"3"\n}
331
+ end
332
+
333
+ it "logs a post request with a large payload" do
334
+ log = RestClient.log = []
335
+ RestClient::Request.new(:method => :post, :url => 'http://url', :payload => ('x' * 1000)).log_request
336
+ log[0].should == %Q{RestClient.post "http://url", 1000 byte(s) length, "Accept"=>"*/*; q=0.5, application/xml", "Accept-Encoding"=>"gzip, deflate", "Content-Length"=>"1000"\n}
337
+ end
338
+
339
+ it "logs input headers as a hash" do
340
+ log = RestClient.log = []
341
+ RestClient::Request.new(:method => :get, :url => 'http://url', :headers => { :accept => 'text/plain' }).log_request
342
+ log[0].should == %Q{RestClient.get "http://url", "Accept"=>"text/plain", "Accept-Encoding"=>"gzip, deflate"\n}
343
+ end
344
+
345
+ it "logs a response including the status code, content type, and result body size in bytes" do
346
+ log = RestClient.log = []
347
+ res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
348
+ res.stub!(:[]).with('Content-type').and_return('text/html')
349
+ @request.log_response res
350
+ log[0].should == "# => 200 OK | text/html 4 bytes\n"
351
+ end
352
+
353
+ it "logs a response with a nil Content-type" do
354
+ log = RestClient.log = []
355
+ res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
356
+ res.stub!(:[]).with('Content-type').and_return(nil)
357
+ @request.log_response res
358
+ log[0].should == "# => 200 OK | 4 bytes\n"
359
+ end
360
+
361
+ it "logs a response with a nil body" do
362
+ log = RestClient.log = []
363
+ res = mock('result', :code => '200', :class => Net::HTTPOK, :body => nil)
364
+ res.stub!(:[]).with('Content-type').and_return('text/html; charset=utf-8')
365
+ @request.log_response res
366
+ log[0].should == "# => 200 OK | text/html 0 bytes\n"
367
+ end
368
+ end
369
+
370
+ it "strips the charset from the response content type" do
371
+ log = RestClient.log = []
372
+ res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
373
+ res.stub!(:[]).with('Content-type').and_return('text/html; charset=utf-8')
374
+ @request.log_response res
375
+ log[0].should == "# => 200 OK | text/html 4 bytes\n"
376
+ end
377
+
378
+ describe "timeout" do
379
+ it "set read_timeout" do
380
+ @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :timeout => 123)
381
+ @http.stub!(:request)
382
+ @request.stub!(:process_result)
383
+ @request.stub!(:response_log)
384
+
385
+ @net.should_receive(:read_timeout=).with(123)
386
+
387
+ @request.transmit(@uri, 'req', nil)
388
+ end
389
+
390
+ it "set open_timeout" do
391
+ @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :open_timeout => 123)
392
+ @http.stub!(:request)
393
+ @request.stub!(:process_result)
394
+ @request.stub!(:response_log)
395
+
396
+ @net.should_receive(:open_timeout=).with(123)
397
+
398
+ @request.transmit(@uri, 'req', nil)
399
+ end
400
+ end
401
+
402
+ describe "ssl" do
403
+ it "uses SSL when the URI refers to a https address" do
404
+ @uri.stub!(:is_a?).with(URI::HTTPS).and_return(true)
405
+ @net.should_receive(:use_ssl=).with(true)
406
+ @http.stub!(:request)
407
+ @request.stub!(:process_result)
408
+ @request.stub!(:response_log)
409
+ @request.transmit(@uri, 'req', 'payload')
410
+ end
411
+
412
+ it "should default to not verifying ssl certificates" do
413
+ @request.verify_ssl.should == false
414
+ end
415
+
416
+ it "should set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is false" do
417
+ @net.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
418
+ @http.stub!(:request)
419
+ @request.stub!(:process_result)
420
+ @request.stub!(:response_log)
421
+ @request.transmit(@uri, 'req', 'payload')
422
+ end
423
+
424
+ it "should not set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is true" do
425
+ @request = RestClient::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload', :verify_ssl => true)
426
+ @net.should_not_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
427
+ @http.stub!(:request)
428
+ @request.stub!(:process_result)
429
+ @request.stub!(:response_log)
430
+ @request.transmit(@uri, 'req', 'payload')
431
+ end
432
+
433
+ it "should set net.verify_mode to the passed value if verify_ssl is an OpenSSL constant" do
434
+ mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
435
+ @request = RestClient::Request.new( :method => :put,
436
+ :url => 'https://some/resource',
437
+ :payload => 'payload',
438
+ :verify_ssl => mode )
439
+ @net.should_receive(:verify_mode=).with(mode)
440
+ @net.should_receive(:verify_callback=)
441
+ @http.stub!(:request)
442
+ @request.stub!(:process_result)
443
+ @request.stub!(:response_log)
444
+ @request.transmit(@uri, 'req', 'payload')
445
+ end
446
+
447
+ it "should default to not having an ssl_client_cert" do
448
+ @request.ssl_client_cert.should be(nil)
449
+ end
450
+
451
+ it "should set the ssl_client_cert if provided" do
452
+ @request = RestClient::Request.new(
453
+ :method => :put,
454
+ :url => 'https://some/resource',
455
+ :payload => 'payload',
456
+ :ssl_client_cert => "whatsupdoc!"
457
+ )
458
+ @net.should_receive(:cert=).with("whatsupdoc!")
459
+ @http.stub!(:request)
460
+ @request.stub!(:process_result)
461
+ @request.stub!(:response_log)
462
+ @request.transmit(@uri, 'req', 'payload')
463
+ end
464
+
465
+ it "should not set the ssl_client_cert if it is not provided" do
466
+ @request = RestClient::Request.new(
467
+ :method => :put,
468
+ :url => 'https://some/resource',
469
+ :payload => 'payload'
470
+ )
471
+ @net.should_not_receive(:cert=).with("whatsupdoc!")
472
+ @http.stub!(:request)
473
+ @request.stub!(:process_result)
474
+ @request.stub!(:response_log)
475
+ @request.transmit(@uri, 'req', 'payload')
476
+ end
477
+
478
+ it "should default to not having an ssl_client_key" do
479
+ @request.ssl_client_key.should be(nil)
480
+ end
481
+
482
+ it "should set the ssl_client_key if provided" do
483
+ @request = RestClient::Request.new(
484
+ :method => :put,
485
+ :url => 'https://some/resource',
486
+ :payload => 'payload',
487
+ :ssl_client_key => "whatsupdoc!"
488
+ )
489
+ @net.should_receive(:key=).with("whatsupdoc!")
490
+ @http.stub!(:request)
491
+ @request.stub!(:process_result)
492
+ @request.stub!(:response_log)
493
+ @request.transmit(@uri, 'req', 'payload')
494
+ end
495
+
496
+ it "should not set the ssl_client_key if it is not provided" do
497
+ @request = RestClient::Request.new(
498
+ :method => :put,
499
+ :url => 'https://some/resource',
500
+ :payload => 'payload'
501
+ )
502
+ @net.should_not_receive(:key=).with("whatsupdoc!")
503
+ @http.stub!(:request)
504
+ @request.stub!(:process_result)
505
+ @request.stub!(:response_log)
506
+ @request.transmit(@uri, 'req', 'payload')
507
+ end
508
+
509
+ it "should default to not having an ssl_ca_file" do
510
+ @request.ssl_ca_file.should be(nil)
511
+ end
512
+
513
+ it "should set the ssl_ca_file if provided" do
514
+ @request = RestClient::Request.new(
515
+ :method => :put,
516
+ :url => 'https://some/resource',
517
+ :payload => 'payload',
518
+ :ssl_ca_file => "Certificate Authority File"
519
+ )
520
+ @net.should_receive(:ca_file=).with("Certificate Authority File")
521
+ @http.stub!(:request)
522
+ @request.stub!(:process_result)
523
+ @request.stub!(:response_log)
524
+ @request.transmit(@uri, 'req', 'payload')
525
+ end
526
+
527
+ it "should not set the ssl_ca_file if it is not provided" do
528
+ @request = RestClient::Request.new(
529
+ :method => :put,
530
+ :url => 'https://some/resource',
531
+ :payload => 'payload'
532
+ )
533
+ @net.should_not_receive(:ca_file=).with("Certificate Authority File")
534
+ @http.stub!(:request)
535
+ @request.stub!(:process_result)
536
+ @request.stub!(:response_log)
537
+ @request.transmit(@uri, 'req', 'payload')
538
+ end
539
+ end
540
+
541
+ it "should still return a response object for 204 No Content responses" do
542
+ @request = RestClient::Request.new(
543
+ :method => :put,
544
+ :url => 'https://some/resource',
545
+ :payload => 'payload'
546
+ )
547
+ net_http_res = Net::HTTPNoContent.new("", "204", "No Content")
548
+ net_http_res.stub!(:read_body).and_return(nil)
549
+ @http.should_receive(:request).and_return(@request.fetch_body(net_http_res))
550
+ response = @request.transmit(@uri, 'req', 'payload')
551
+ response.should_not be_nil
552
+ response.code.should == 204
553
+ end
554
+ end