rest-client-maestro 1.7.2.maestro

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +8 -0
  2. data/.rspec +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +8 -0
  5. data/AUTHORS +69 -0
  6. data/Gemfile +7 -0
  7. data/README.rdoc +322 -0
  8. data/Rakefile +49 -0
  9. data/bin/restclient +93 -0
  10. data/history.md +134 -0
  11. data/lib/rest-client.rb +2 -0
  12. data/lib/rest_client.rb +2 -0
  13. data/lib/restclient/abstract_response.rb +106 -0
  14. data/lib/restclient/exceptions.rb +198 -0
  15. data/lib/restclient/net_http_ext.rb +55 -0
  16. data/lib/restclient/payload.rb +242 -0
  17. data/lib/restclient/raw_response.rb +34 -0
  18. data/lib/restclient/request.rb +346 -0
  19. data/lib/restclient/resource.rb +169 -0
  20. data/lib/restclient/response.rb +26 -0
  21. data/lib/restclient.rb +174 -0
  22. data/rest-client-maestro.gemspec +23 -0
  23. data/spec/integration/capath_equifax/578d5c04.0 +19 -0
  24. data/spec/integration/capath_equifax/594f1775.0 +19 -0
  25. data/spec/integration/capath_equifax/README +8 -0
  26. data/spec/integration/capath_equifax/equifax.crt +19 -0
  27. data/spec/integration/capath_verisign/415660c1.0 +14 -0
  28. data/spec/integration/capath_verisign/7651b327.0 +14 -0
  29. data/spec/integration/capath_verisign/README +8 -0
  30. data/spec/integration/capath_verisign/verisign.crt +14 -0
  31. data/spec/integration/certs/equifax.crt +19 -0
  32. data/spec/integration/certs/verisign.crt +14 -0
  33. data/spec/integration/integration_spec.rb +35 -0
  34. data/spec/integration/request_spec.rb +63 -0
  35. data/spec/spec_helper.rb +12 -0
  36. data/spec/unit/abstract_response_spec.rb +85 -0
  37. data/spec/unit/exceptions_spec.rb +95 -0
  38. data/spec/unit/master_shake.jpg +0 -0
  39. data/spec/unit/payload_spec.rb +245 -0
  40. data/spec/unit/raw_response_spec.rb +17 -0
  41. data/spec/unit/request2_spec.rb +32 -0
  42. data/spec/unit/request_spec.rb +621 -0
  43. data/spec/unit/resource_spec.rb +133 -0
  44. data/spec/unit/response_spec.rb +166 -0
  45. data/spec/unit/restclient_spec.rb +73 -0
  46. metadata +220 -0
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ describe RestClient::AbstractResponse do
4
+
5
+ class MyAbstractResponse
6
+
7
+ include RestClient::AbstractResponse
8
+
9
+ attr_accessor :size
10
+
11
+ def initialize net_http_res, args
12
+ @net_http_res = net_http_res
13
+ @args = args
14
+ end
15
+
16
+ end
17
+
18
+ before do
19
+ @net_http_res = double('net http response')
20
+ @response = MyAbstractResponse.new(@net_http_res, {})
21
+ end
22
+
23
+ it "fetches the numeric response code" do
24
+ @net_http_res.should_receive(:code).and_return('200')
25
+ @response.code.should eq 200
26
+ end
27
+
28
+ it "has a nice description" do
29
+ @net_http_res.should_receive(:to_hash).and_return({'Content-Type' => ['application/pdf']})
30
+ @net_http_res.should_receive(:code).and_return('200')
31
+ @response.description.should eq "200 OK | application/pdf bytes\n"
32
+ end
33
+
34
+ it "beautifies the headers by turning the keys to symbols" do
35
+ h = RestClient::AbstractResponse.beautify_headers('content-type' => [ 'x' ])
36
+ h.keys.first.should eq :content_type
37
+ end
38
+
39
+ it "beautifies the headers by turning the values to strings instead of one-element arrays" do
40
+ h = RestClient::AbstractResponse.beautify_headers('x' => [ 'text/html' ] )
41
+ h.values.first.should eq 'text/html'
42
+ end
43
+
44
+ it "fetches the headers" do
45
+ @net_http_res.should_receive(:to_hash).and_return('content-type' => [ 'text/html' ])
46
+ @response.headers.should eq({ :content_type => 'text/html' })
47
+ end
48
+
49
+ it "extracts cookies from response headers" do
50
+ @net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
51
+ @response.cookies.should eq({ 'session_id' => '1' })
52
+ end
53
+
54
+ it "extract strange cookies" do
55
+ @net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=ZJ/HQVH6YE+rVkTpn0zvTQ==; path=/'])
56
+ @response.cookies.should eq({ 'session_id' => 'ZJ%2FHQVH6YE+rVkTpn0zvTQ%3D%3D' })
57
+ end
58
+
59
+ it "doesn't escape cookies" do
60
+ @net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca; path=/'])
61
+ @response.cookies.should eq({ 'session_id' => 'BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca' })
62
+ end
63
+
64
+ it "can access the net http result directly" do
65
+ @response.net_http_res.should eq @net_http_res
66
+ end
67
+
68
+ describe "#return!" do
69
+ it "should return the response itself on 200-codes" do
70
+ @net_http_res.should_receive(:code).and_return('200')
71
+ @response.return!.should be_equal(@response)
72
+ end
73
+
74
+ it "should raise RequestFailed on unknown codes" do
75
+ @net_http_res.should_receive(:code).and_return('1000')
76
+ lambda { @response.return! }.should raise_error RestClient::RequestFailed
77
+ end
78
+
79
+ it "should raise an error on a redirection after non-GET/HEAD requests" do
80
+ @net_http_res.should_receive(:code).and_return('301')
81
+ @response.args.merge(:method => :put)
82
+ lambda { @response.return! }.should raise_error RestClient::RequestFailed
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe RestClient::Exception do
4
+ it "returns a 'message' equal to the class name if the message is not set, because 'message' should not be nil" do
5
+ e = RestClient::Exception.new
6
+ e.message.should eq "RestClient::Exception"
7
+ end
8
+
9
+ it "returns the 'message' that was set" do
10
+ e = RestClient::Exception.new
11
+ message = "An explicitly set message"
12
+ e.message = message
13
+ e.message.should eq message
14
+ end
15
+
16
+ it "sets the exception message to ErrorMessage" do
17
+ RestClient::ResourceNotFound.new.message.should eq 'Resource Not Found'
18
+ end
19
+
20
+ it "contains exceptions in RestClient" do
21
+ RestClient::Unauthorized.new.should be_a_kind_of(RestClient::Exception)
22
+ RestClient::ServerBrokeConnection.new.should be_a_kind_of(RestClient::Exception)
23
+ end
24
+ end
25
+
26
+ describe RestClient::ServerBrokeConnection do
27
+ it "should have a default message of 'Server broke connection'" do
28
+ e = RestClient::ServerBrokeConnection.new
29
+ e.message.should eq 'Server broke connection'
30
+ end
31
+ end
32
+
33
+ describe RestClient::RequestFailed do
34
+ before do
35
+ @response = double('HTTP Response', :code => '502')
36
+ end
37
+
38
+ it "stores the http response on the exception" do
39
+ response = "response"
40
+ begin
41
+ raise RestClient::RequestFailed, response
42
+ rescue RestClient::RequestFailed => e
43
+ e.response.should eq response
44
+ end
45
+ end
46
+
47
+ it "http_code convenience method for fetching the code as an integer" do
48
+ RestClient::RequestFailed.new(@response).http_code.should eq 502
49
+ end
50
+
51
+ it "http_body convenience method for fetching the body (decoding when necessary)" do
52
+ RestClient::RequestFailed.new(@response).http_code.should eq 502
53
+ RestClient::RequestFailed.new(@response).message.should eq 'HTTP status code 502'
54
+ end
55
+
56
+ it "shows the status code in the message" do
57
+ RestClient::RequestFailed.new(@response).to_s.should match(/502/)
58
+ end
59
+ end
60
+
61
+ describe RestClient::ResourceNotFound do
62
+ it "also has the http response attached" do
63
+ response = "response"
64
+ begin
65
+ raise RestClient::ResourceNotFound, response
66
+ rescue RestClient::ResourceNotFound => e
67
+ e.response.should eq response
68
+ end
69
+ end
70
+ end
71
+
72
+ describe "backwards compatibility" do
73
+ it "alias RestClient::Request::Redirect to RestClient::Redirect" do
74
+ RestClient::Request::Redirect.should eq RestClient::Redirect
75
+ end
76
+
77
+ it "alias RestClient::Request::Unauthorized to RestClient::Unauthorized" do
78
+ RestClient::Request::Unauthorized.should eq RestClient::Unauthorized
79
+ end
80
+
81
+ it "alias RestClient::Request::RequestFailed to RestClient::RequestFailed" do
82
+ RestClient::Request::RequestFailed.should eq RestClient::RequestFailed
83
+ end
84
+
85
+ it "make the exception's response act like an Net::HTTPResponse" do
86
+ body = "body"
87
+ stub_request(:get, "www.example.com").to_return(:body => body, :status => 404)
88
+ begin
89
+ RestClient.get "www.example.com"
90
+ raise
91
+ rescue RestClient::ResourceNotFound => e
92
+ e.response.body.should eq body
93
+ end
94
+ end
95
+ end
Binary file
@@ -0,0 +1,245 @@
1
+ # encoding: binary
2
+
3
+ require 'spec_helper'
4
+
5
+ describe RestClient::Payload do
6
+ context "A regular Payload" do
7
+ it "should use standard enctype as default content-type" do
8
+ RestClient::Payload::UrlEncoded.new({}).headers['Content-Type'].
9
+ should eq 'application/x-www-form-urlencoded'
10
+ end
11
+
12
+ it "should form properly encoded params" do
13
+ RestClient::Payload::UrlEncoded.new({:foo => 'bar'}).to_s.
14
+ should eq "foo=bar"
15
+ ["foo=bar&baz=qux", "baz=qux&foo=bar"].should include(
16
+ RestClient::Payload::UrlEncoded.new({:foo => 'bar', :baz => 'qux'}).to_s)
17
+ end
18
+
19
+ it "should escape parameters" do
20
+ RestClient::Payload::UrlEncoded.new({'foo ' => 'bar'}).to_s.
21
+ should eq "foo%20=bar"
22
+ end
23
+
24
+ it "should properly handle hashes as parameter" do
25
+ RestClient::Payload::UrlEncoded.new({:foo => {:bar => 'baz'}}).to_s.
26
+ should eq "foo[bar]=baz"
27
+ RestClient::Payload::UrlEncoded.new({:foo => {:bar => {:baz => 'qux'}}}).to_s.
28
+ should eq "foo[bar][baz]=qux"
29
+ end
30
+
31
+ it "should handle many attributes inside a hash" do
32
+ parameters = RestClient::Payload::UrlEncoded.new({:foo => {:bar => 'baz', :baz => 'qux'}}).to_s
33
+ parameters.should include("foo[bar]=baz", "foo[baz]=qux")
34
+ end
35
+
36
+ it "should handle attributes inside a an array inside an hash" do
37
+ parameters = RestClient::Payload::UrlEncoded.new({"foo" => [{"bar" => 'baz'}, {"bar" => 'qux'}]}).to_s
38
+ parameters.should include("foo[bar]=baz", "foo[bar]=qux")
39
+ end
40
+
41
+ it "should handle attributes inside a an array inside an array inside an hash" do
42
+ parameters = RestClient::Payload::UrlEncoded.new({"foo" => [[{"bar" => 'baz'}, {"bar" => 'qux'}]]}).to_s
43
+ parameters.should include("foo[bar]=baz", "foo[bar]=qux")
44
+ end
45
+
46
+ it "should form properly use symbols as parameters" do
47
+ RestClient::Payload::UrlEncoded.new({:foo => :bar}).to_s.
48
+ should eq "foo=bar"
49
+ RestClient::Payload::UrlEncoded.new({:foo => {:bar => :baz}}).to_s.
50
+ should eq "foo[bar]=baz"
51
+ end
52
+
53
+ it "should properly handle arrays as repeated parameters" do
54
+ RestClient::Payload::UrlEncoded.new({:foo => ['bar']}).to_s.
55
+ should eq "foo[]=bar"
56
+ RestClient::Payload::UrlEncoded.new({:foo => ['bar', 'baz']}).to_s.
57
+ should eq "foo[]=bar&foo[]=baz"
58
+ end
59
+
60
+ it 'should not close if stream already closed' do
61
+ p = RestClient::Payload::UrlEncoded.new({'foo ' => 'bar'})
62
+ 3.times {p.close}
63
+ end
64
+
65
+ end
66
+
67
+ context "A multipart Payload" do
68
+ it "should use standard enctype as default content-type" do
69
+ m = RestClient::Payload::Multipart.new({})
70
+ m.stub(:boundary).and_return(123)
71
+ m.headers['Content-Type'].should eq 'multipart/form-data; boundary=123'
72
+ end
73
+
74
+ it 'should not error on close if stream already closed' do
75
+ m = RestClient::Payload::Multipart.new(:file => File.new(File.join(File.dirname(File.expand_path(__FILE__)), 'master_shake.jpg')))
76
+ 3.times {m.close}
77
+ end
78
+
79
+ it "should form properly separated multipart data" do
80
+ m = RestClient::Payload::Multipart.new([[:bar, "baz"], [:foo, "bar"]])
81
+ m.to_s.should eq <<-EOS
82
+ --#{m.boundary}\r
83
+ Content-Disposition: form-data; name="bar"\r
84
+ \r
85
+ baz\r
86
+ --#{m.boundary}\r
87
+ Content-Disposition: form-data; name="foo"\r
88
+ \r
89
+ bar\r
90
+ --#{m.boundary}--\r
91
+ EOS
92
+ end
93
+
94
+ it "should not escape parameters names" do
95
+ m = RestClient::Payload::Multipart.new([["bar ", "baz"]])
96
+ m.to_s.should eq <<-EOS
97
+ --#{m.boundary}\r
98
+ Content-Disposition: form-data; name="bar "\r
99
+ \r
100
+ baz\r
101
+ --#{m.boundary}--\r
102
+ EOS
103
+ end
104
+
105
+ it "should form properly separated multipart data" do
106
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
107
+ m = RestClient::Payload::Multipart.new({:foo => f})
108
+ m.to_s.should eq <<-EOS
109
+ --#{m.boundary}\r
110
+ Content-Disposition: form-data; name="foo"; filename="master_shake.jpg"\r
111
+ Content-Type: image/jpeg\r
112
+ \r
113
+ #{File.open(f.path, 'rb'){|bin| bin.read}}\r
114
+ --#{m.boundary}--\r
115
+ EOS
116
+ end
117
+
118
+ it "should ignore the name attribute when it's not set" do
119
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
120
+ m = RestClient::Payload::Multipart.new({nil => f})
121
+ m.to_s.should eq <<-EOS
122
+ --#{m.boundary}\r
123
+ Content-Disposition: form-data; filename="master_shake.jpg"\r
124
+ Content-Type: image/jpeg\r
125
+ \r
126
+ #{File.open(f.path, 'rb'){|bin| bin.read}}\r
127
+ --#{m.boundary}--\r
128
+ EOS
129
+ end
130
+
131
+ it "should detect optional (original) content type and filename" do
132
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
133
+ f.instance_eval "def content_type; 'text/plain'; end"
134
+ f.instance_eval "def original_filename; 'foo.txt'; end"
135
+ m = RestClient::Payload::Multipart.new({:foo => f})
136
+ m.to_s.should eq <<-EOS
137
+ --#{m.boundary}\r
138
+ Content-Disposition: form-data; name="foo"; filename="foo.txt"\r
139
+ Content-Type: text/plain\r
140
+ \r
141
+ #{File.open(f.path, 'rb'){|bin| bin.read}}\r
142
+ --#{m.boundary}--\r
143
+ EOS
144
+ end
145
+
146
+ it "should handle hash in hash parameters" do
147
+ m = RestClient::Payload::Multipart.new({:bar => {:baz => "foo"}})
148
+ m.to_s.should eq <<-EOS
149
+ --#{m.boundary}\r
150
+ Content-Disposition: form-data; name="bar[baz]"\r
151
+ \r
152
+ foo\r
153
+ --#{m.boundary}--\r
154
+ EOS
155
+
156
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
157
+ f.instance_eval "def content_type; 'text/plain'; end"
158
+ f.instance_eval "def original_filename; 'foo.txt'; end"
159
+ m = RestClient::Payload::Multipart.new({:foo => {:bar => f}})
160
+ m.to_s.should eq <<-EOS
161
+ --#{m.boundary}\r
162
+ Content-Disposition: form-data; name="foo[bar]"; filename="foo.txt"\r
163
+ Content-Type: text/plain\r
164
+ \r
165
+ #{File.open(f.path, 'rb'){|bin| bin.read}}\r
166
+ --#{m.boundary}--\r
167
+ EOS
168
+ end
169
+
170
+ end
171
+
172
+ context "streamed payloads" do
173
+ it "should properly determine the size of file payloads" do
174
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
175
+ payload = RestClient::Payload.generate(f)
176
+ payload.size.should eq 76_988
177
+ payload.length.should eq 76_988
178
+ end
179
+
180
+ it "should properly determine the size of other kinds of streaming payloads" do
181
+ s = StringIO.new 'foo'
182
+ payload = RestClient::Payload.generate(s)
183
+ payload.size.should eq 3
184
+ payload.length.should eq 3
185
+
186
+ begin
187
+ f = Tempfile.new "rest-client"
188
+ f.write 'foo bar'
189
+
190
+ payload = RestClient::Payload.generate(f)
191
+ payload.size.should eq 7
192
+ payload.length.should eq 7
193
+ ensure
194
+ f.close
195
+ end
196
+ end
197
+ end
198
+
199
+ context "Payload generation" do
200
+ it "should recognize standard urlencoded params" do
201
+ RestClient::Payload.generate({"foo" => 'bar'}).should be_kind_of(RestClient::Payload::UrlEncoded)
202
+ end
203
+
204
+ it "should recognize multipart params" do
205
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
206
+ RestClient::Payload.generate({"foo" => f}).should be_kind_of(RestClient::Payload::Multipart)
207
+ end
208
+
209
+ it "should be multipart if forced" do
210
+ RestClient::Payload.generate({"foo" => "bar", :multipart => true}).should be_kind_of(RestClient::Payload::Multipart)
211
+ end
212
+
213
+ it "should return data if no of the above" do
214
+ RestClient::Payload.generate("data").should be_kind_of(RestClient::Payload::Base)
215
+ end
216
+
217
+ it "should recognize nested multipart payloads in hashes" do
218
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
219
+ RestClient::Payload.generate({"foo" => {"file" => f}}).should be_kind_of(RestClient::Payload::Multipart)
220
+ end
221
+
222
+ it "should recognize nested multipart payloads in arrays" do
223
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
224
+ RestClient::Payload.generate({"foo" => [f]}).should be_kind_of(RestClient::Payload::Multipart)
225
+ end
226
+
227
+ it "should recognize file payloads that can be streamed" do
228
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
229
+ RestClient::Payload.generate(f).should be_kind_of(RestClient::Payload::Streamed)
230
+ end
231
+
232
+ it "should recognize other payloads that can be streamed" do
233
+ RestClient::Payload.generate(StringIO.new('foo')).should be_kind_of(RestClient::Payload::Streamed)
234
+ end
235
+
236
+ # hashery gem introduces Hash#read convenience method. Existence of #read method used to determine of content is streameable :/
237
+ it "shouldn't treat hashes as streameable" do
238
+ RestClient::Payload.generate({"foo" => 'bar'}).should be_kind_of(RestClient::Payload::UrlEncoded)
239
+ end
240
+ end
241
+
242
+ class HashMapForTesting < Hash
243
+ alias :read :[]
244
+ end
245
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe RestClient::RawResponse do
4
+ before do
5
+ @tf = double("Tempfile", :read => "the answer is 42", :open => true)
6
+ @net_http_res = double('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 eq 'the answer is 42'
12
+ end
13
+
14
+ it "exposes a Tempfile" do
15
+ @response.file.should eq @tf
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe RestClient::Request do
4
+
5
+ it "manage params for get requests" do
6
+ 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)
7
+ RestClient::Request.execute(:url => 'http://some/resource', :method => :get, :headers => {:foo => :bar, :params => {:a => :b, 'c' => 'd'}}).body.should eq 'foo'
8
+
9
+ 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)
10
+ RestClient::Request.execute(:url => 'http://some/resource', :method => :get, :headers => {:foo => :bar, :params => :a}).body.should eq 'foo'
11
+ end
12
+
13
+ it "can use a block to process response" do
14
+ response_value = nil
15
+ block = Proc.new do |http_response|
16
+ response_value = http_response.body
17
+ end
18
+ 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)
19
+ RestClient::Request.execute(:url => 'http://some/resource', :method => :get, :headers => {:foo => :bar, :params => {:a => :b, 'c' => 'd'}}, :block_response => block)
20
+ response_value.should eq "foo"
21
+ end
22
+
23
+ it 'closes payload if not nil' do
24
+ test_file = File.new(File.join( File.dirname(File.expand_path(__FILE__)), 'master_shake.jpg'))
25
+
26
+ stub_request(:post, 'http://some/resource').with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).to_return(:body => 'foo', :status => 200)
27
+ RestClient::Request.execute(:url => 'http://some/resource', :method => :post, :payload => {:file => test_file})
28
+
29
+ test_file.closed?.should be_true
30
+ end
31
+
32
+ end