jimson-temp 0.9.2

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,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