rlivsey-voorhees 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ class Address
2
+ include Voorhees::Resource
3
+ end
4
+
5
+ class LatLon
6
+ include Voorhees::Resource
7
+ end
8
+
9
+ class Message
10
+ include Voorhees::Resource
11
+ end
12
+
13
+ class User
14
+ include Voorhees::Resource
15
+ end
16
+
17
+ class NotResource
18
+ end
@@ -0,0 +1,31 @@
1
+ {
2
+ "email":"bt@example.com",
3
+ "username":"btables",
4
+ "name":"Bobby Tables",
5
+ "id":1,
6
+ "address":{
7
+ "street":"24 Monkey Close",
8
+ "city":"Somesville",
9
+ "country":"Somewhere",
10
+ "coords":{
11
+ "lat":52.9876,
12
+ "lon":12.3456
13
+ }
14
+ },
15
+ "pet":{
16
+ "kind":"dog",
17
+ "name":"spot"
18
+ },
19
+ "messages":[
20
+ {
21
+ "subject":"QWERTYUIOP",
22
+ "body":"ASDFHJKOIUFC G F IUH HGJBNM",
23
+ "id":1
24
+ },
25
+ {
26
+ "subject":"asdoifj sajf askjdfhuaerf kdshf dsaf asdf a skf dksf k.",
27
+ "body":"adsjfi asdif jaosdj fojasdf jasdjhf hsadkf ahjsdkf jasdhf jasdk;sadfjahsdf ;adfs",
28
+ "id":2
29
+ }
30
+ ]
31
+ }
@@ -0,0 +1 @@
1
+ [{"email":"bt@example.com","username":"btables","name":"Bobby Tables","id":1,"messages":[{"subject":"QWERTYUIOP","body":"ASDFHJKOIUFC G F IUH HGJBNM","id":1},{"subject":"asdoifj sajf askjdfhuaerf kdshf dsaf asdf a skf dksf k.","body":"adsjfi asdif jaosdj fojasdf jasdjhf hsadkf ahjsdkf jasdhf jasdk;sadfjahsdf ;adfs","id":2}]},{"email":"rm@example.com","username":"rmoore","name":"Roger Moore","id":2,"messages":[{"subject":"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.","body":"Lorem ipsum dolor sit amet, consectetur adipisicing elit","id":4},{"subject":"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.","body":"Excepteur sint o cat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.","id":5}]}]
@@ -0,0 +1,446 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Voorhees::Request do
4
+
5
+ before :each do
6
+ @caller_class = User
7
+ @request = Voorhees::Request.new(@caller_class)
8
+
9
+ # disable the logger
10
+ Voorhees::Config.logger = mock(:logger, :null_object => true)
11
+ end
12
+
13
+ describe "global defaults" do
14
+
15
+ before :each do
16
+ Voorhees::Config.setup do |c|
17
+ c.timeout = 1
18
+ c.retries = 50
19
+ end
20
+ end
21
+
22
+ it "should override the global defaults if overridden" do
23
+ @request.timeout = 100
24
+ @request.timeout.should == 100
25
+ end
26
+
27
+ it "should default to the global defaults if not overridden" do
28
+ @request.retries.should == 50
29
+ end
30
+
31
+ end
32
+
33
+ describe "defaults" do
34
+
35
+ it "should be included in the parameters" do
36
+ @request.defaults = {:all => true}
37
+ @request.parameters = {:order => 'surname'}
38
+ @request.parameters.should == {:all => true, :order => 'surname'}
39
+ end
40
+
41
+ it "should be overridden by matching parameters" do
42
+ @request.defaults = {:all => true, :order => 'surname'}
43
+ @request.parameters = {:all => false}
44
+ @request.parameters.should == {:all => false, :order => 'surname'}
45
+ end
46
+
47
+ end
48
+
49
+
50
+ describe "uri" do
51
+
52
+ before :each do
53
+ @base = "http://example.com"
54
+ @path = "/some/path"
55
+ Voorhees::Config.base_uri = @base
56
+ end
57
+
58
+ it "should prepend the base_uri if it's given relative path" do
59
+ @request.path = @path
60
+ @request.uri.to_s.should == "#{@base}#{@path}"
61
+ end
62
+
63
+ it "should not prepend the base_uri if it's given a full URI" do
64
+ @request.path = "#{@base}#{@path}"
65
+ @request.uri.to_s.should == "#{@base}#{@path}"
66
+ end
67
+
68
+ describe "with parameters" do
69
+
70
+ before :each do
71
+ @request.parameters = {:monkeys => 2}
72
+ end
73
+
74
+ describe "with a Net::HTTP::POST request" do
75
+
76
+ before :each do
77
+ Voorhees::Config[:http_method] = Net::HTTP::Post
78
+ end
79
+
80
+ it "should not append the query string with the parameters" do
81
+ @request.path = "#{@base}#{@path}"
82
+ @request.uri.to_s.should == "#{@base}#{@path}"
83
+ end
84
+
85
+ end
86
+
87
+ [Net::HTTP::Get, Net::HTTP::Put, Net::HTTP::Delete].each do |type|
88
+ describe "with #{type} requests" do
89
+
90
+ before :each do
91
+ Voorhees::Config[:http_method] = type
92
+ end
93
+
94
+ it "should set the query string with the parameters" do
95
+ @request.path = "#{@base}#{@path}"
96
+ @request.uri.to_s.should == "#{@base}#{@path}?monkeys=2"
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+ describe "validation" do
105
+
106
+ before :each do
107
+ @request.required = [:id, :login]
108
+ end
109
+
110
+ it "should raise Voorhees::ParameterMissingError if the params do not contain a required item" do
111
+ @request.parameters = {:id => 1}
112
+
113
+ lambda{
114
+ @request.send(:validate)
115
+ }.should raise_error(Voorhees::ParameterMissingError)
116
+ end
117
+
118
+ it "should not raise Voorhees::ParameterMissingError if the params contain all required items" do
119
+ @request.parameters = {:id => 1, :login => 'test'}
120
+
121
+ lambda{
122
+ @request.send(:validate)
123
+ }.should_not raise_error(Voorhees::ParameterMissingError)
124
+ end
125
+
126
+ end
127
+
128
+ describe "perform" do
129
+
130
+ before :each do
131
+ @host = "example.com"
132
+ @port = 8080
133
+ @path = "/endpoint"
134
+ @params = {:bananas => 5}
135
+ @hierarchy = {:address => Address}
136
+
137
+ @request.path = "http://#{@host}:#{@port}#{@path}"
138
+ @request.timeout = 10
139
+ @request.retries = 0
140
+ @request.hierarchy = @hierarchy
141
+ @request.parameters = @params
142
+
143
+ @mock_post = mock(:post, :null_object => true)
144
+ Net::HTTP::Post.stub!(:new).and_return(@mock_post)
145
+
146
+ @mock_get = mock(:get, :null_object => true)
147
+ Net::HTTP::Get.stub!(:new).and_return(@mock_get)
148
+
149
+ @mock_put = mock(:put, :null_object => true)
150
+ Net::HTTP::Put.stub!(:put).and_return(@mock_put)
151
+
152
+ @mock_delete = mock(:delete, :null_object => true)
153
+ Net::HTTP::Delete.stub!(:delete).and_return(@mock_delete)
154
+
155
+ Voorhees::Config[:http_method] = Net::HTTP::Get
156
+
157
+ body = '{"something":"result"}'
158
+ @http_response = Net::HTTPResponse::CODE_TO_OBJ["200"].new("1.1", 200, body)
159
+ @http_response.stub!(:body).and_return(body)
160
+ @json_response = JSON.parse(body)
161
+
162
+ @mock_http = MockNetHttp.new
163
+ @connection = @mock_http.connection
164
+ @connection.stub!(:request).and_return(@http_response)
165
+ Net::HTTP.stub!(:new).and_return(@mock_http)
166
+ end
167
+
168
+
169
+ def perform_catching_errors
170
+ @request.perform
171
+ rescue
172
+ end
173
+
174
+ describe "with GET request" do
175
+
176
+ before :each do
177
+ Voorhees::Config[:http_method] = Net::HTTP::Get
178
+ end
179
+
180
+ it "should perform a HTTP::Get request to the correct path" do
181
+ Net::HTTP::Get.should_receive(:new).with(@path).and_return(@mock_get)
182
+ @request.perform
183
+ end
184
+
185
+ end
186
+
187
+ describe "with POST request" do
188
+
189
+ before :each do
190
+ Voorhees::Config[:http_method] = Net::HTTP::Post
191
+ end
192
+
193
+ describe "with post_json set" do
194
+
195
+ before :each do
196
+ @param_name = "json_data"
197
+ Voorhees::Config[:post_json] = true
198
+ Voorhees::Config[:post_json_parameter] = @param_name
199
+ end
200
+
201
+ it "should set the form_data to a hash with JSON parameters as :post_json_parameter" do
202
+ @mock_post.should_receive(:form_data=).with({ @param_name => @params.to_json })
203
+ @request.perform
204
+ end
205
+
206
+ end
207
+
208
+ describe "without post_json set" do
209
+
210
+ before :each do
211
+ Voorhees::Config[:post_json] = false
212
+ end
213
+
214
+ it "should set the form_data to the parameters" do
215
+ @mock_post.should_receive(:form_data=).with(@params)
216
+ @request.perform
217
+ end
218
+ end
219
+
220
+ it "should perform a HTTP::Post request to the correct path" do
221
+ Net::HTTP::Post.should_receive(:new).with(@path).and_return(@mock_post)
222
+ @request.perform
223
+ end
224
+
225
+ end
226
+
227
+ describe "with DELETE request" do
228
+
229
+ before :each do
230
+ Voorhees::Config[:http_method] = Net::HTTP::Delete
231
+ end
232
+
233
+ it "should perform a HTTP::Get request to the correct path" do
234
+ Net::HTTP::Delete.should_receive(:new).with(@path).and_return(@mock_delete)
235
+ @request.perform
236
+ end
237
+
238
+ end
239
+
240
+ describe "with PUT request" do
241
+
242
+ before :each do
243
+ Voorhees::Config[:http_method] = Net::HTTP::Put
244
+ end
245
+
246
+ it "should perform a HTTP::Get request to the correct path" do
247
+ Net::HTTP::Put.should_receive(:new).with(@path).and_return(@mock_put)
248
+ @request.perform
249
+ end
250
+
251
+ end
252
+
253
+ it "should create a Net::HTTP object with the correct host and port" do
254
+ Net::HTTP.should_receive(:new).with(@host, @port).and_return(@mock_http)
255
+ @request.perform
256
+ end
257
+
258
+ it "should set Net::HTTP#open_timeout" do
259
+ @mock_http.should_receive(:open_timeout=).with(@request.timeout)
260
+ @request.perform
261
+ end
262
+
263
+ it "should set Net::HTTP#read_timeout" do
264
+ @mock_http.should_receive(:read_timeout=).with(@request.timeout)
265
+ @request.perform
266
+ end
267
+
268
+ it "should send one request to Net::HTTP#start" do
269
+ @connection.should_receive(:request).once.with(@mock_get)
270
+ @request.perform
271
+ end
272
+
273
+ it "should return the response from the service as a Voorhees::Response" do
274
+ @connection.stub!(:request).and_return(@http_response)
275
+ @request.perform.should be_a(Voorhees::Response)
276
+ end
277
+
278
+ it "should pass the parsed JSON, caller class and hierarcy to the response" do
279
+ @connection.stub!(:request).and_return(@http_response)
280
+
281
+ Voorhees::Response.should_receive(:new).with(@json_response, @caller_class, @hierarchy)
282
+ @request.perform
283
+ end
284
+
285
+ describe "with TimeoutError" do
286
+
287
+ it "should raise a Voorhees::TimeoutError" do
288
+ @connection.stub!(:request).and_raise(Timeout::Error.new(nil))
289
+
290
+ lambda{
291
+ @request.perform
292
+ }.should raise_error(Voorhees::TimeoutError)
293
+ end
294
+
295
+ describe "with retries" do
296
+
297
+ before :each do
298
+ @request.retries = 2
299
+ end
300
+
301
+ describe "with subsequent success" do
302
+
303
+ it "should post the request 2 times" do
304
+ @connection.should_receive(:request).with(@mock_get).exactly(1).times.ordered.and_raise(Timeout::Error.new(nil))
305
+ @connection.should_receive(:request).with(@mock_get).exactly(1).times.ordered
306
+ @request.perform
307
+ end
308
+
309
+ it "should return the response from the service" do
310
+ @connection.should_receive(:request).with(@mock_get).exactly(1).times.ordered.and_raise(Timeout::Error.new(nil))
311
+ @connection.should_receive(:request).with(@mock_get).exactly(1).times.ordered.and_return(@http_response)
312
+ @request.perform.json.should == @json_response
313
+ end
314
+
315
+ end
316
+
317
+ describe "with subseqent failure" do
318
+
319
+ before :each do
320
+ @connection.stub!(:request).and_raise(Timeout::Error.new(nil))
321
+ end
322
+
323
+ it "should post the request 3 times (original + 2 retries)" do
324
+ @connection.should_receive(:request).with(@mock_get).exactly(3).times.and_raise(Timeout::Error.new(nil))
325
+ perform_catching_errors
326
+ end
327
+
328
+ it "should raise an Voorhees::TimeoutError exception" do
329
+ lambda {
330
+ @request.perform
331
+ }.should raise_error(Voorhees::TimeoutError)
332
+ end
333
+ end
334
+ end
335
+ end
336
+
337
+
338
+ describe "with Net::HTTPNotFound" do
339
+
340
+ it "should raise a Voorhees::NotFoundError" do
341
+ @connection.stub!(:request).and_return(Net::HTTPNotFound.new(404, 1.1, "Not Found"))
342
+
343
+ lambda{
344
+ @request.perform
345
+ }.should raise_error(Voorhees::NotFoundError)
346
+ end
347
+
348
+ describe "with retries" do
349
+
350
+ before :each do
351
+ @request.retries = 2
352
+ end
353
+
354
+ it "should not retry" do
355
+ @connection.should_receive(:request).with(@mock_get).exactly(1).times.and_return(Net::HTTPNotFound.new(404, 1.1, "Not Found"))
356
+ perform_catching_errors
357
+ end
358
+ end
359
+ end
360
+
361
+
362
+ describe "with Errno::ECONNREFUSED" do
363
+
364
+ it "should raise a Voorhees::UnavailableError" do
365
+ @connection.stub!(:request).and_raise(Errno::ECONNREFUSED)
366
+
367
+ lambda{
368
+ @request.perform
369
+ }.should raise_error(Voorhees::UnavailableError)
370
+ end
371
+
372
+ it "should not sleep" do
373
+ @connection.stub!(:request).and_raise(Errno::ECONNREFUSED)
374
+ @request.should_not_receive(:sleep)
375
+ perform_catching_errors
376
+ end
377
+
378
+ describe "with retries" do
379
+
380
+ before :each do
381
+ @request.retries = 2
382
+ @request.stub!(:sleep)
383
+ end
384
+
385
+ it "should sleep for 1 second before each timeout" do
386
+ @connection.stub!(:request).and_raise(Errno::ECONNREFUSED)
387
+ @request.should_receive(:sleep).with(1)
388
+ perform_catching_errors
389
+ end
390
+
391
+ describe "with subsequent success" do
392
+
393
+ it "should post the request 2 times" do
394
+ @connection.should_receive(:request).with(@mock_get).exactly(1).times.ordered.and_raise(Errno::ECONNREFUSED)
395
+ @connection.should_receive(:request).with(@mock_get).exactly(1).times.ordered
396
+ @request.perform
397
+ end
398
+
399
+ it "should return the response from the service" do
400
+ @connection.should_receive(:request).with(@mock_get).exactly(1).times.ordered.and_raise(Errno::ECONNREFUSED)
401
+ @connection.should_receive(:request).with(@mock_get).exactly(1).times.ordered.and_return(@http_response)
402
+ @request.perform.json.should == @json_response
403
+ end
404
+ end
405
+
406
+ describe "with subsequent failure" do
407
+
408
+ before :each do
409
+ @connection.stub!(:request).and_raise(Errno::ECONNREFUSED)
410
+ end
411
+
412
+ it "should post the request 3 times (original + 2 retries)" do
413
+ @connection.should_receive(:request).with(@mock_get).exactly(3).times.and_raise(Errno::ECONNREFUSED)
414
+ perform_catching_errors
415
+ end
416
+
417
+ it "should raise an Voorhees::UnavailableError exception" do
418
+ lambda {
419
+ @request.perform
420
+ }.should raise_error(Voorhees::UnavailableError)
421
+ end
422
+ end
423
+ end
424
+ end
425
+ end
426
+ end
427
+
428
+
429
+
430
+ class MockNetHttp
431
+
432
+ attr_accessor :connection
433
+
434
+ def initialize(*args)
435
+ @connection = mock(:connection)
436
+ end
437
+
438
+ def start
439
+ yield @connection
440
+ end
441
+
442
+ def method_missing(*args)
443
+ return self
444
+ end
445
+
446
+ end