jimson-temp 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ module Jimson
4
+ describe Handler do
5
+
6
+ class FooHandler
7
+ extend Jimson::Handler
8
+
9
+ jimson_expose :to_s, :bye
10
+
11
+ jimson_exclude :hi, :bye
12
+
13
+ def hi
14
+ 'hi'
15
+ end
16
+
17
+ def bye
18
+ 'bye'
19
+ end
20
+
21
+ def to_s
22
+ 'foo'
23
+ end
24
+
25
+ def so_exposed
26
+ "I'm so exposed!"
27
+ end
28
+ end
29
+
30
+ let(:foo) { FooHandler.new }
31
+
32
+ describe "#jimson_expose" do
33
+ it "exposes a method even if it was defined on Object" do
34
+ foo.class.jimson_exposed_methods.should include('to_s')
35
+ end
36
+ end
37
+
38
+ describe "#jimson_exclude" do
39
+ context "when a method was not explicitly exposed" do
40
+ it "excludes the method" do
41
+ foo.class.jimson_exposed_methods.should_not include('hi')
42
+ end
43
+ end
44
+ context "when a method was explicitly exposed" do
45
+ it "does not exclude the method" do
46
+ foo.class.jimson_exposed_methods.should include('bye')
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#jimson_exposed_methods" do
52
+ it "doesn't include methods defined on Object" do
53
+ foo.class.jimson_exposed_methods.should_not include('object_id')
54
+ end
55
+ it "includes methods defined on the extending class but not on Object" do
56
+ foo.class.jimson_exposed_methods.should include('so_exposed')
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ module Jimson
4
+ describe Router do
5
+
6
+ let(:router) { Router.new }
7
+
8
+ class RouterFooHandler
9
+ extend Jimson::Handler
10
+
11
+ def hi
12
+ 'hi'
13
+ end
14
+ end
15
+
16
+ class RouterBarHandler
17
+ extend Jimson::Handler
18
+
19
+ def bye
20
+ 'bye'
21
+ end
22
+ end
23
+
24
+ class RouterBazHandler
25
+ extend Jimson::Handler
26
+
27
+ def meh
28
+ 'mehkayla'
29
+ end
30
+ end
31
+
32
+
33
+ describe '#draw' do
34
+ context 'when given non-nested namespaces' do
35
+ it 'takes a block with a DSL to set the root and namespaces' do
36
+ router.draw do
37
+ root RouterFooHandler
38
+ namespace 'ns', RouterBarHandler
39
+ end
40
+
41
+ router.handler_for_method('hi').should be_a(RouterFooHandler)
42
+ router.handler_for_method('ns.hi').should be_a(RouterBarHandler)
43
+ end
44
+ end
45
+
46
+ context 'when given nested namespaces' do
47
+ it 'takes a block with a DSL to set the root and namespaces' do
48
+ router.draw do
49
+ root RouterFooHandler
50
+ namespace 'ns1' do
51
+ root RouterBazHandler
52
+ namespace 'ns2', RouterBarHandler
53
+ end
54
+ end
55
+
56
+ router.handler_for_method('hi').should be_a(RouterFooHandler)
57
+ router.handler_for_method('ns1.hi').should be_a(RouterBazHandler)
58
+ router.handler_for_method('ns1.ns2.hi').should be_a(RouterBarHandler)
59
+ end
60
+ end
61
+ end
62
+
63
+ describe '#jimson_methods' do
64
+ it 'returns an array of namespaced method names from all registered handlers' do
65
+ router.draw do
66
+ root RouterFooHandler
67
+ namespace 'foo', RouterBarHandler
68
+ end
69
+
70
+ router.jimson_methods.sort.should == ['hi', 'foo.bye'].sort
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,466 @@
1
+ require 'spec_helper'
2
+ require 'rack/test'
3
+
4
+ module Jimson
5
+ describe Server do
6
+ include Rack::Test::Methods
7
+
8
+ class TestHandler
9
+ extend Jimson::Handler
10
+
11
+ def subtract(a, b = nil)
12
+ if a.is_a?(Hash)
13
+ return a['minuend'] - a['subtrahend']
14
+ else
15
+ return a - b
16
+ end
17
+ end
18
+
19
+ def sum(a,b,c)
20
+ a + b + c
21
+ end
22
+
23
+ def car(array)
24
+ array.first
25
+ end
26
+
27
+ def notify_hello(*args)
28
+ # notification, doesn't do anything
29
+ end
30
+
31
+ def update(*args)
32
+ # notification, doesn't do anything
33
+ end
34
+
35
+ def get_data
36
+ ['hello', 5]
37
+ end
38
+
39
+ def ugly_method
40
+ raise RuntimeError
41
+ end
42
+ end
43
+
44
+ class OtherHandler
45
+ extend Jimson::Handler
46
+
47
+ def multiply(a,b)
48
+ a * b
49
+ end
50
+ end
51
+
52
+ INVALID_RESPONSE_EXPECTATION = {
53
+ 'jsonrpc' => '2.0',
54
+ 'error' => {
55
+ 'code' => -32600,
56
+ 'message' => 'The JSON sent is not a valid Request object.'
57
+ },
58
+ 'id' => nil
59
+ }
60
+ let(:router) do
61
+ router = Router.new.draw do
62
+ root TestHandler.new
63
+ namespace 'other', OtherHandler.new
64
+ end
65
+ end
66
+
67
+ let(:app) do
68
+ Server.new(router, :environment => "production")
69
+ end
70
+
71
+ def post_json(hash)
72
+ post '/', MultiJson.encode(hash), {'Content-Type' => 'application/json'}
73
+ end
74
+
75
+ before(:each) do
76
+ @url = SPEC_URL
77
+ end
78
+
79
+ it "exposes the given options" do
80
+ app.opts.should == { :environment => "production" }
81
+ end
82
+
83
+ describe "receiving a request with positional parameters" do
84
+ context "when no errors occur" do
85
+ it "returns a response with 'result'" do
86
+ req = {
87
+ 'jsonrpc' => '2.0',
88
+ 'method' => 'subtract',
89
+ 'params' => [24, 20],
90
+ 'id' => 1
91
+ }
92
+ post_json(req)
93
+
94
+ last_response.should be_ok
95
+ resp = MultiJson.decode(last_response.body)
96
+ resp.should == {
97
+ 'jsonrpc' => '2.0',
98
+ 'result' => 4,
99
+ 'id' => 1
100
+ }
101
+ end
102
+
103
+ it "handles an array in the parameters" do
104
+ req = {
105
+ 'jsonrpc' => '2.0',
106
+ 'method' => 'car',
107
+ 'params' => [['a', 'b']],
108
+ 'id' => 1
109
+ }
110
+ post_json(req)
111
+
112
+ last_response.should be_ok
113
+ resp = MultiJson.decode(last_response.body)
114
+ resp.should == {
115
+ 'jsonrpc' => '2.0',
116
+ 'result' => 'a',
117
+ 'id' => 1
118
+ }
119
+ end
120
+
121
+ it "handles bignums" do
122
+ req = {
123
+ 'jsonrpc' => '2.0',
124
+ 'method' => 'subtract',
125
+ 'params' => [24, 20],
126
+ 'id' => 123456789_123456789_123456789
127
+ }
128
+ post_json(req)
129
+
130
+ last_response.should be_ok
131
+ resp = MultiJson.decode(last_response.body)
132
+ resp.should == {
133
+ 'jsonrpc' => '2.0',
134
+ 'result' => 4,
135
+ 'id' => 123456789_123456789_123456789
136
+ }
137
+ end
138
+ end
139
+ end
140
+
141
+ describe "receiving a request with named parameters" do
142
+ context "when no errors occur" do
143
+ it "returns a response with 'result'" do
144
+ req = {
145
+ 'jsonrpc' => '2.0',
146
+ 'method' => 'subtract',
147
+ 'params' => {'subtrahend'=> 20, 'minuend' => 24},
148
+ 'id' => 1
149
+ }
150
+ post_json(req)
151
+
152
+ last_response.should be_ok
153
+ resp = MultiJson.decode(last_response.body)
154
+ resp.should == {
155
+ 'jsonrpc' => '2.0',
156
+ 'result' => 4,
157
+ 'id' => 1
158
+ }
159
+ end
160
+ end
161
+ end
162
+
163
+ describe "receiving a notification" do
164
+ context "when no errors occur" do
165
+ it "returns no response" do
166
+ req = {
167
+ 'jsonrpc' => '2.0',
168
+ 'method' => 'update',
169
+ 'params' => [1,2,3,4,5]
170
+ }
171
+ post_json(req)
172
+ last_response.body.should be_empty
173
+ end
174
+ end
175
+ end
176
+
177
+ describe "receiving a call for a non-existent method" do
178
+ it "returns an error response" do
179
+ req = {
180
+ 'jsonrpc' => '2.0',
181
+ 'method' => 'foobar',
182
+ 'id' => 1
183
+ }
184
+ post_json(req)
185
+
186
+ resp = MultiJson.decode(last_response.body)
187
+ resp.should == {
188
+ 'jsonrpc' => '2.0',
189
+ 'error' => {
190
+ 'code' => -32601,
191
+ 'message' => "Method 'foobar' not found."
192
+ },
193
+ 'id' => 1
194
+ }
195
+ end
196
+ end
197
+
198
+ describe "receiving a call for a method which exists but is not exposed" do
199
+ it "returns an error response" do
200
+ req = {
201
+ 'jsonrpc' => '2.0',
202
+ 'method' => 'object_id',
203
+ 'id' => 1
204
+ }
205
+ post_json(req)
206
+
207
+ resp = MultiJson.decode(last_response.body)
208
+ resp.should == {
209
+ 'jsonrpc' => '2.0',
210
+ 'error' => {
211
+ 'code' => -32601,
212
+ 'message' => "Method 'object_id' not found."
213
+ },
214
+ 'id' => 1
215
+ }
216
+ end
217
+ end
218
+
219
+ describe "receiving a call with the wrong number of params" do
220
+ it "returns an error response" do
221
+ req = {
222
+ 'jsonrpc' => '2.0',
223
+ 'method' => 'subtract',
224
+ 'params' => [1,2,3],
225
+ 'id' => 1
226
+ }
227
+ post_json(req)
228
+
229
+ resp = MultiJson.decode(last_response.body)
230
+ resp.should == {
231
+ 'jsonrpc' => '2.0',
232
+ 'error' => {
233
+ 'code' => -32602,
234
+ 'message' => 'Invalid method parameter(s).'
235
+ },
236
+ 'id' => 1
237
+ }
238
+ end
239
+ end
240
+
241
+ describe "receiving a call for ugly method" do
242
+ context "by default" do
243
+ it "returns only global error without stack trace" do
244
+ req = {
245
+ 'jsonrpc' => '2.0',
246
+ 'method' => 'ugly_method',
247
+ 'id' => 1
248
+ }
249
+ post_json(req)
250
+
251
+ resp = MultiJson.decode(last_response.body)
252
+ resp.should == {
253
+ 'jsonrpc' => '2.0',
254
+ 'error' => {
255
+ 'code' => -32099,
256
+ 'message' => 'Server application error'
257
+ },
258
+ 'id' => 1
259
+ }
260
+ end
261
+ end
262
+
263
+ context "with 'show_errors' enabled" do
264
+ it "returns an error name and first line of the stack trace" do
265
+ req = {
266
+ 'jsonrpc' => '2.0',
267
+ 'method' => 'ugly_method',
268
+ 'id' => 1
269
+ }
270
+
271
+ app = Server.new(router, :environment => "production", :show_errors => true)
272
+
273
+ # have to make a new Rack::Test browser since this server is different than the normal one
274
+ browser = Rack::Test::Session.new(Rack::MockSession.new(app))
275
+ browser.post '/', MultiJson.encode(req), {'Content-Type' => 'application/json'}
276
+
277
+ resp = MultiJson.decode(browser.last_response.body)
278
+ resp.should == {
279
+ 'jsonrpc' => '2.0',
280
+ 'error' => {
281
+ 'code' => -32099,
282
+ 'message' => "Server application error: RuntimeError at #{__FILE__}:40:in `ugly_method'"
283
+ },
284
+ 'id' => 1
285
+ }
286
+ end
287
+ end
288
+ end
289
+
290
+ describe "receiving invalid JSON" do
291
+ it "returns an error response" do
292
+ req = MultiJson.encode({
293
+ 'jsonrpc' => '2.0',
294
+ 'method' => 'foobar',
295
+ 'id' => 1
296
+ })
297
+ req += '}' # make the json invalid
298
+ post '/', req, {'Content-type' => 'application/json'}
299
+
300
+ resp = MultiJson.decode(last_response.body)
301
+ resp.should == {
302
+ 'jsonrpc' => '2.0',
303
+ 'error' => {
304
+ 'code' => -32700,
305
+ 'message' => 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.'
306
+ },
307
+ 'id' => nil
308
+ }
309
+ end
310
+ end
311
+
312
+ describe "receiving an invalid request" do
313
+ context "when the request is not a batch" do
314
+ it "returns an error response" do
315
+ req = {
316
+ 'jsonrpc' => '2.0',
317
+ 'method' => 1 # method as int is invalid
318
+ }
319
+ post_json(req)
320
+ resp = MultiJson.decode(last_response.body)
321
+ resp.should == INVALID_RESPONSE_EXPECTATION
322
+ end
323
+ end
324
+
325
+ context "when the request is an empty batch" do
326
+ it "returns an error response" do
327
+ req = []
328
+ post_json(req)
329
+ resp = MultiJson.decode(last_response.body)
330
+ resp.should == INVALID_RESPONSE_EXPECTATION
331
+ end
332
+ end
333
+
334
+ context "when the request is an invalid batch" do
335
+ it "returns an error response" do
336
+ req = [1,2]
337
+ post_json(req)
338
+ resp = MultiJson.decode(last_response.body)
339
+ resp.should == [INVALID_RESPONSE_EXPECTATION, INVALID_RESPONSE_EXPECTATION]
340
+ end
341
+ end
342
+ end
343
+
344
+ describe "receiving a valid batch request" do
345
+ context "when not all requests are notifications" do
346
+ it "returns an array of responses" do
347
+ reqs = [
348
+ {'jsonrpc' => '2.0', 'method' => 'sum', 'params' => [1,2,4], 'id' => '1'},
349
+ {'jsonrpc' => '2.0', 'method' => 'notify_hello', 'params' => [7]},
350
+ {'jsonrpc' => '2.0', 'method' => 'subtract', 'params' => [42,23], 'id' => '2'},
351
+ {'foo' => 'boo'},
352
+ {'jsonrpc' => '2.0', 'method' => 'foo.get', 'params' => {'name' => 'myself'}, 'id' => '5'},
353
+ {'jsonrpc' => '2.0', 'method' => 'get_data', 'id' => '9'}
354
+ ]
355
+ post_json(reqs)
356
+ resp = MultiJson.decode(last_response.body)
357
+ resp.should == [
358
+ {'jsonrpc' => '2.0', 'result' => 7, 'id' => '1'},
359
+ {'jsonrpc' => '2.0', 'result' => 19, 'id' => '2'},
360
+ {'jsonrpc' => '2.0', 'error' => {'code' => -32600, 'message' => 'The JSON sent is not a valid Request object.'}, 'id' => nil},
361
+ {'jsonrpc' => '2.0', 'error' => {'code' => -32601, 'message' => "Method 'foo.get' not found."}, 'id' => '5'},
362
+ {'jsonrpc' => '2.0', 'result' => ['hello', 5], 'id' => '9'}
363
+ ]
364
+ end
365
+ end
366
+
367
+ context "when all the requests are notifications" do
368
+ it "returns no response" do
369
+ req = [
370
+ {
371
+ 'jsonrpc' => '2.0',
372
+ 'method' => 'update',
373
+ 'params' => [1,2,3,4,5]
374
+ },
375
+ {
376
+ 'jsonrpc' => '2.0',
377
+ 'method' => 'update',
378
+ 'params' => [1,2,3,4,5]
379
+ }
380
+ ]
381
+ post_json(req)
382
+ last_response.body.should be_empty
383
+ end
384
+ end
385
+ end
386
+
387
+ describe "receiving a 'system.' request" do
388
+ context "when the request is 'isAlive'" do
389
+ it "returns response 'true'" do
390
+ req = {
391
+ 'jsonrpc' => '2.0',
392
+ 'method' => 'system.isAlive',
393
+ 'params' => [],
394
+ 'id' => 1
395
+ }
396
+ post_json(req)
397
+
398
+ last_response.should be_ok
399
+ resp = MultiJson.decode(last_response.body)
400
+ resp.should == {
401
+ 'jsonrpc' => '2.0',
402
+ 'result' => true,
403
+ 'id' => 1
404
+ }
405
+ end
406
+ end
407
+ context "when the request is 'system.listMethods'" do
408
+ it "returns response with all jimson_exposed_methods on the handler(s) as strings" do
409
+ req = {
410
+ 'jsonrpc' => '2.0',
411
+ 'method' => 'system.listMethods',
412
+ 'params' => [],
413
+ 'id' => 1
414
+ }
415
+ post_json(req)
416
+
417
+ last_response.should be_ok
418
+ resp = MultiJson.decode(last_response.body)
419
+ resp['jsonrpc'].should == '2.0'
420
+ resp['id'].should == 1
421
+ expected = ['get_data', 'notify_hello', 'subtract', 'sum', 'car', 'ugly_method', 'update', 'system.isAlive', 'system.listMethods', 'other.multiply']
422
+ (resp['result'] - expected).should == []
423
+ end
424
+ end
425
+ end
426
+
427
+ describe ".with_routes" do
428
+ it "creates a server with a router by passing the block to Router#draw" do
429
+ app = Server.with_routes do
430
+ root TestHandler.new
431
+ namespace 'foo', OtherHandler.new
432
+ end
433
+
434
+ # have to make a new Rack::Test browser since this server is different than the normal one
435
+ browser = Rack::Test::Session.new(Rack::MockSession.new(app))
436
+
437
+ req = {
438
+ 'jsonrpc' => '2.0',
439
+ 'method' => 'foo.multiply',
440
+ 'params' => [2, 3],
441
+ 'id' => 1
442
+ }
443
+ browser.post '/', MultiJson.encode(req), {'Content-Type' => 'application/json'}
444
+
445
+ browser.last_response.should be_ok
446
+ resp = MultiJson.decode(browser.last_response.body)
447
+ resp.should == {
448
+ 'jsonrpc' => '2.0',
449
+ 'result' => 6,
450
+ 'id' => 1
451
+ }
452
+ end
453
+
454
+ context "when opts are given" do
455
+ it "passes the opts to the new server" do
456
+ app = Server.with_routes(:show_errors => true) do
457
+ root TestHandler.new
458
+ namespace 'foo', OtherHandler.new
459
+ end
460
+
461
+ app.show_errors.should be_true
462
+ end
463
+ end
464
+ end
465
+ end
466
+ end