voorhees 0.2.0

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