rack-jet_router 1.2.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/test/misc_test.rb ADDED
@@ -0,0 +1,23 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+
4
+ require_relative './shared'
5
+
6
+
7
+ Oktest.scope do
8
+
9
+
10
+ topic Rack::JetRouter::REQUEST_METHODS do
11
+
12
+ spec "[!haggu] contains available request methods." do
13
+ Rack::JetRouter::REQUEST_METHODS.each do |k, v|
14
+ ok {k}.is_a?(String)
15
+ ok {v}.is_a?(Symbol)
16
+ ok {v.to_s} == k
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+
23
+ end
@@ -0,0 +1,597 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ###
4
+ ### $Release: 1.3.1 $
5
+ ### $Copyright: copyright(c) 2015 kwatch@gmail.com $
6
+ ### $License: MIT License $
7
+ ###
8
+
9
+ require_relative './shared'
10
+
11
+
12
+ Oktest.scope do
13
+
14
+
15
+ topic Rack::JetRouter do
16
+
17
+ welcome_app = proc {|env| [200, {}, ["welcome_app"]]}
18
+ #
19
+ book_list_api = proc {|env| [200, {}, ["book_list_api"]]}
20
+ book_create_api = proc {|env| [200, {}, ["book_create_api"]]}
21
+ book_new_api = proc {|env| [200, {}, ["book_new_api"]]}
22
+ book_show_api = proc {|env| [200, {}, ["book_show_api"]]}
23
+ book_update_api = proc {|env| [200, {}, ["book_update_api"]]}
24
+ book_delete_api = proc {|env| [200, {}, ["book_delete_api"]]}
25
+ book_edit_api = proc {|env| [200, {}, ["book_edit_api"]]}
26
+ #
27
+ comment_create_api = proc {|env| [200, {}, ["comment_create_api"]]}
28
+ comment_update_api = proc {|env| [200, {}, ["comment_update_api"]]}
29
+ #
30
+ admin_book_list_app = proc {|env| [200, {}, ["admin_book_list_app"]]}
31
+ admin_book_create_app = proc {|env| [200, {}, ["admin_book_create_app"]]}
32
+ admin_book_new_app = proc {|env| [200, {}, ["admin_book_new_app"]]}
33
+ admin_book_show_app = proc {|env| [200, {}, ["admin_book_show_app"]]}
34
+ admin_book_update_app = proc {|env| [200, {}, ["admin_book_update_app"]]}
35
+ admin_book_delete_app = proc {|env| [200, {}, ["admin_book_delete_app"]]}
36
+ admin_book_edit_app = proc {|env| [200, {}, ["admin_book_edit_app"]]}
37
+ #
38
+ whole_urlpath_mapping = [
39
+ ['/' , welcome_app],
40
+ ['/index.html' , welcome_app],
41
+ ['/api' , [
42
+ ['/books' , [
43
+ ['' , book_list_api],
44
+ ['/new' , book_new_api],
45
+ ['/:id' , book_show_api],
46
+ ['/:id/edit' , book_edit_api],
47
+ ]],
48
+ ['/books/:book_id/comments', [
49
+ ['' , comment_create_api],
50
+ ['/:comment_id' , comment_update_api],
51
+ ]],
52
+ ]],
53
+ ['/admin' , [
54
+ ['/books' , [
55
+ ['' , {:GET=>admin_book_list_app, :POST=>admin_book_create_app}],
56
+ ['/:id' , {:GET=>admin_book_show_app, :PUT=>admin_book_update_app, :DELETE=>admin_book_delete_app}],
57
+ ]],
58
+ ]],
59
+ ]
60
+
61
+ def new_env(req_method, req_path, opts={})
62
+ opts[:method] = req_method.to_s
63
+ env = ::Rack::MockRequest.env_for(req_path, opts)
64
+ return env
65
+ end
66
+
67
+ before do
68
+ @router = Rack::JetRouter.new(whole_urlpath_mapping)
69
+ end
70
+
71
+
72
+ topic '#normalize_method_mapping()' do
73
+
74
+ spec "[!r7cmk] converts keys into string." do
75
+ d1 = {GET: book_list_api, POST: book_create_api}
76
+ d2 = @router.normalize_method_mapping(d1)
77
+ ok {d2} == {"GET"=>book_list_api, "POST"=>book_create_api}
78
+ #
79
+ mapping = [
80
+ ['/books', {:GET=>book_list_api, :POST=>book_create_api}]
81
+ ]
82
+ Rack::JetRouter.new(mapping).instance_exec(self) do |_|
83
+ dict = @fixed_endpoints
84
+ _.ok {dict['/books']} == {'GET'=>book_list_api, 'POST'=>book_create_api}
85
+ end
86
+ end
87
+
88
+ spec "[!z9kww] allows 'ANY' as request method." do
89
+ d1 = {ANY: book_list_api, POST: book_create_api}
90
+ d2 = @router.normalize_method_mapping(d1)
91
+ ok {d2} == {"ANY"=>book_list_api, "POST"=>book_create_api}
92
+ #
93
+ mapping = [
94
+ ['/books', {'ANY'=>book_list_api, 'POST'=>book_create_api}]
95
+ ]
96
+ Rack::JetRouter.new(mapping).instance_exec(self) do |_|
97
+ dict = @fixed_endpoints
98
+ _.ok {dict['/books']} == {'ANY'=>book_list_api, 'POST'=>book_create_api}
99
+ end
100
+ end
101
+
102
+ spec "[!k7sme] raises error when unknown request method specified." do
103
+ d1 = {LOCK: book_list_api}
104
+ pr = proc { @router.normalize_method_mapping(d1) }
105
+ ok {pr}.raise?(ArgumentError, 'LOCK: unknown request method.')
106
+ #
107
+ mapping = [
108
+ ['/books', {UNLOCK: book_list_api}]
109
+ ]
110
+ pr = proc { Rack::JetRouter.new(mapping) }
111
+ ok {pr}.raise?(ArgumentError, 'UNLOCK: unknown request method.')
112
+ end
113
+
114
+ spec "[!itfsd] returns new Hash object." do
115
+ d1 = {"GET" => book_list_api}
116
+ d2 = @router.normalize_method_mapping(d1)
117
+ ok {d2} == d1
118
+ ok {d2}.NOT.same?(d1)
119
+ end
120
+
121
+ spec "[!gd08f] if arg is an instance of Hash subclass, returns new instance of it." do
122
+ d1 = Map(GET: book_list_api)
123
+ d2 = @router.normalize_method_mapping(d1)
124
+ ok {d2}.is_a?(Map)
125
+ ok {d2} == Map.new.update({"GET"=>book_list_api})
126
+ end
127
+
128
+ end
129
+
130
+
131
+ topic '#param2rexp()' do
132
+
133
+ spec "[!6sd9b] returns regexp string according to param name." do
134
+ ok {@router.param2rexp("id")} == '[^./?]+'
135
+ ok {@router.param2rexp("user_id")} == '[^./?]+'
136
+ ok {@router.param2rexp("username")} == '[^./?]+'
137
+ end
138
+
139
+ spec "[!rfvk2] returns '\d+' if param name matched to int param regexp." do
140
+ router = Rack::JetRouter.new([], int_param: /(\A|_)id\z/)
141
+ ok {router.param2rexp("id")} == '\d+'
142
+ ok {router.param2rexp("user_id")} == '\d+'
143
+ ok {router.param2rexp("username")} == '[^./?]+'
144
+ end
145
+
146
+ end
147
+
148
+
149
+ topic '#should_redirect?' do
150
+
151
+ spec "[!dsu34] returns false when request path is '/'." do
152
+ @router.instance_exec(self) do |_|
153
+ _.ok {should_redirect?(_.new_env('GET' , '/'))} == false
154
+ _.ok {should_redirect?(_.new_env('POST' , '/'))} == false
155
+ _.ok {should_redirect?(_.new_env('PUT' , '/'))} == false
156
+ _.ok {should_redirect?(_.new_env('DELETE', '/'))} == false
157
+ _.ok {should_redirect?(_.new_env('HEAD' , '/'))} == false
158
+ _.ok {should_redirect?(_.new_env('PATCH' , '/'))} == false
159
+ end
160
+ end
161
+
162
+ spec "[!ycpqj] returns true when request method is GET or HEAD." do
163
+ @router.instance_exec(self) do |_|
164
+ _.ok {should_redirect?(_.new_env('GET' , '/index'))} == true
165
+ _.ok {should_redirect?(_.new_env('HEAD' , '/index'))} == true
166
+ end
167
+ end
168
+
169
+ spec "[!7q8xu] returns false when request method is POST, PUT or DELETE." do
170
+ @router.instance_exec(self) do |_|
171
+ _.ok {should_redirect?(_.new_env('POST' , '/index'))} == false
172
+ _.ok {should_redirect?(_.new_env('PUT' , '/index'))} == false
173
+ _.ok {should_redirect?(_.new_env('DELETE', '/index'))} == false
174
+ _.ok {should_redirect?(_.new_env('PATCH' , '/index'))} == false
175
+ end
176
+ end
177
+
178
+ end
179
+
180
+
181
+ topic '#error_not_found()' do
182
+
183
+ spec "[!mlruv] returns 404 response." do
184
+ expected = [404, {"Content-Type"=>"text/plain"}, ["404 Not Found"]]
185
+ env = new_env('GET', '/xxx')
186
+ @router.instance_exec(self) do |_|
187
+ _.ok {error_not_found(env)} == expected
188
+ end
189
+ end
190
+
191
+ end
192
+
193
+
194
+ topic '#error_not_allowed()' do
195
+
196
+ spec "[!mjigf] returns 405 response." do
197
+ expected = [405, {"Content-Type"=>"text/plain"}, ["405 Method Not Allowed"]]
198
+ env = new_env('POST', '/')
199
+ @router.instance_exec(self) do |_|
200
+ _.ok {error_not_allowed(env)} == expected
201
+ end
202
+ end
203
+
204
+ end
205
+
206
+
207
+ topic '#initialize()' do
208
+
209
+ spec "[!21mf9] 'urlpath_cache_size:' kwarg is available for backward compatibility." do
210
+ r = Rack::JetRouter.new([], urlpath_cache_size: 5)
211
+ ok {r.instance_variable_get(:@cache_size)} == 5
212
+ ok {r.instance_variable_get(:@cache_dict)} == {}
213
+ #
214
+ r = Rack::JetRouter.new([], cache_size: 7)
215
+ ok {r.instance_variable_get(:@cache_size)} == 7
216
+ ok {r.instance_variable_get(:@cache_dict)} == {}
217
+ end
218
+
219
+ spec "[!5tw57] cache is disabled when 'cache_size:' is zero." do
220
+ r = Rack::JetRouter.new([], cache_size: 0)
221
+ ok {r.instance_variable_get(:@cache_size)} == 0
222
+ ok {r.instance_variable_get(:@cache_dict)} == nil
223
+ #
224
+ r = Rack::JetRouter.new([], urlpath_cache_size: 0)
225
+ ok {r.instance_variable_get(:@cache_size)} == 0
226
+ ok {r.instance_variable_get(:@cache_dict)} == nil
227
+ #
228
+ r = Rack::JetRouter.new([])
229
+ ok {r.instance_variable_get(:@cache_size)} == 0
230
+ ok {r.instance_variable_get(:@cache_dict)} == nil
231
+ end
232
+
233
+ spec "[!x2l32] gathers all endpoints." do
234
+ all = @router.instance_eval { @all_endpoints }
235
+ ok {all} == [
236
+ ["/" , welcome_app],
237
+ ["/index.html" , welcome_app],
238
+ ["/api/books" , book_list_api],
239
+ ["/api/books/new" , book_new_api],
240
+ ["/api/books/:id" , book_show_api],
241
+ ["/api/books/:id/edit", book_edit_api],
242
+ ["/api/books/:book_id/comments" , comment_create_api],
243
+ ["/api/books/:book_id/comments/:comment_id", comment_update_api],
244
+ ["/admin/books", {
245
+ "GET" => admin_book_list_app,
246
+ "POST" => admin_book_create_app,
247
+ }],
248
+ ["/admin/books/:id", {
249
+ "GET" => admin_book_show_app,
250
+ "PUT" => admin_book_update_app,
251
+ "DELETE" => admin_book_delete_app,
252
+ }],
253
+ ]
254
+ end
255
+
256
+ spec "[!l63vu] handles urlpath pattern as fixed when no urlpath params." do
257
+ @router.instance_exec(self) do |_|
258
+ _.ok {@fixed_endpoints} == {
259
+ '/' => welcome_app,
260
+ '/index.html' => welcome_app,
261
+ '/api/books' => book_list_api,
262
+ '/api/books/new' => book_new_api,
263
+ '/admin/books' => {
264
+ 'GET'=>admin_book_list_app,
265
+ 'POST'=>admin_book_create_app,
266
+ }
267
+ }
268
+ end
269
+ #
270
+ mapping = [
271
+ ['/api/books' , book_list_api],
272
+ ]
273
+ router = Rack::JetRouter.new(mapping)
274
+ router.instance_exec(self) do |_|
275
+ _.ok {@fixed_endpoints} == {'/api/books' => book_list_api}
276
+ _.ok {@variable_endpoints} == []
277
+ end
278
+ end
279
+
280
+ spec "[!saa1a] compiles compound urlpath regexp." do
281
+ id = '[^./?]+'
282
+ z = '(\z)'
283
+ ok {@router.urlpath_rexp} == %r!\A/a(?:pi/books/#{id}(?:#{z}|/(?:edit#{z}|comments(?:#{z}|/#{id}#{z})))|dmin/books/#{id}#{z})\z!
284
+ end
285
+
286
+ spec "[!f1d7s] builds variable endpoint list." do
287
+ id = '[^./?]+'
288
+ map2 = {
289
+ 'GET' => admin_book_show_app,
290
+ 'PUT' => admin_book_update_app,
291
+ 'DELETE' => admin_book_delete_app,
292
+ }
293
+ @router.instance_exec(self) do |_|
294
+ _.ok {@variable_endpoints} == [
295
+ [%r'\A/api/books/(#{id})\z', ['id'], book_show_api, (11..-1)],
296
+ [%r'\A/api/books/(#{id})/edit\z', ['id'], book_edit_api, (11..-6)],
297
+ [%r'\A/api/books/(#{id})/comments\z', ['book_id'], comment_create_api, (11..-10)],
298
+ [%r'\A/api/books/(#{id})/comments/(#{id})\z', ['book_id', 'comment_id'], comment_update_api, nil],
299
+ [%r'\A/admin/books/(#{id})\z', ['id'], map2, (13..-1)],
300
+ ]
301
+ end
302
+ end
303
+
304
+ end
305
+
306
+
307
+ topic '#lookup()' do
308
+
309
+ spec "[!ijqws] returns mapped object and urlpath parameter values when urlpath found." do
310
+ ret = @router.lookup('/api/books/123')
311
+ ok {ret} == [book_show_api, {"id"=>"123"}]
312
+ end
313
+
314
+ spec "[!vpdzn] returns nil when urlpath not found." do
315
+ ok {@router.lookup('/api')} == nil
316
+ ok {@router.lookup('/api/book')} == nil
317
+ ok {@router.lookup('/api/books/')} == nil
318
+ end
319
+
320
+ spec "[!24khb] finds in fixed urlpaths at first." do
321
+ ok {@router.lookup('/')} == [welcome_app, nil]
322
+ ok {@router.lookup('/api/books')} == [book_list_api, nil]
323
+ dict = {'GET'=>admin_book_list_app, 'POST'=>admin_book_create_app}
324
+ ok {@router.lookup('/admin/books')} == [dict, nil]
325
+ end
326
+
327
+ spec "[!iwyzd] urlpath param value is nil when found in fixed urlpaths." do
328
+ obj, vars = @router.lookup('/')
329
+ ok {vars} == nil
330
+ obj, vars = @router.lookup('/api/books')
331
+ ok {vars} == nil
332
+ end
333
+
334
+ spec "[!upacd] finds in variable urlpath cache if it is enabled." do
335
+ mapping = [
336
+ ['/api/books/:id', book_show_api],
337
+ ]
338
+ r = Rack::JetRouter.new(mapping, cache_size: 3)
339
+ pair = r.lookup('/api/books/123')
340
+ ok {pair} == [book_show_api, {"id"=>"123"}]
341
+ r.instance_exec(self) do |_|
342
+ _.ok {@cache_dict} == {'/api/books/123'=>pair}
343
+ #
344
+ @cache_dict['/api/books/999'] = [book_list_api, {"ID"=>"111"}]
345
+ end
346
+ pair = r.lookup('/api/books/999')
347
+ ok {pair} == [book_list_api, {"ID"=>"111"}]
348
+ end
349
+
350
+ spec "[!84inr] caches result when variable urlpath cache enabled." do
351
+ mapping = [
352
+ ['/books/:id', book_show_api],
353
+ ]
354
+ r = Rack::JetRouter.new(mapping, cache_size: 3)
355
+ #
356
+ pair1 = r.lookup('/books/1'); ok {pair1} == [book_show_api, {"id"=>"1"}]
357
+ pair2 = r.lookup('/books/2'); ok {pair2} == [book_show_api, {"id"=>"2"}]
358
+ pair3 = r.lookup('/books/3'); ok {pair3} == [book_show_api, {"id"=>"3"}]
359
+ r.instance_exec(self) do |_|
360
+ _.ok {@cache_dict} == {
361
+ '/books/1'=>pair1,
362
+ '/books/2'=>pair2,
363
+ '/books/3'=>pair3,
364
+ }
365
+ end
366
+ #
367
+ pair4 = r.lookup('/books/4'); ok {pair4} == [book_show_api, {"id"=>"4"}]
368
+ r.instance_exec(self) do |_|
369
+ _.ok {@cache_dict} == {
370
+ '/books/2'=>pair2,
371
+ '/books/3'=>pair3,
372
+ '/books/4'=>pair4,
373
+ }
374
+ end
375
+ end
376
+
377
+ spec "[!1zx7t] variable urlpath cache is based on LRU." do
378
+ mapping = [
379
+ ['/books/:id', book_show_api],
380
+ ]
381
+ r = Rack::JetRouter.new(mapping, cache_size: 3)
382
+ #
383
+ pair1 = r.lookup('/books/1')
384
+ pair2 = r.lookup('/books/2')
385
+ pair3 = r.lookup('/books/3')
386
+ pair4 = r.lookup('/books/4')
387
+ r.instance_exec(self) do |_|
388
+ _.ok {@cache_dict} == {
389
+ '/books/2'=>pair2,
390
+ '/books/3'=>pair3,
391
+ '/books/4'=>pair4,
392
+ }
393
+ end
394
+ #
395
+ ok {r.lookup('/books/3')} == pair3
396
+ r.instance_exec(self) do |_|
397
+ _.ok {@cache_dict} == {
398
+ '/books/2'=>pair2,
399
+ '/books/4'=>pair4,
400
+ '/books/3'=>pair3,
401
+ }
402
+ end
403
+ #
404
+ ok {r.lookup('/books/1')} == pair1
405
+ r.instance_exec(self) do |_|
406
+ _.ok {@cache_dict} == {
407
+ '/books/4'=>pair4,
408
+ '/books/3'=>pair3,
409
+ '/books/1'=>pair1,
410
+ }
411
+ end
412
+ end
413
+
414
+ end
415
+
416
+
417
+ topic '#call()' do
418
+
419
+ spec "[!hse47] invokes app mapped to request urlpath." do
420
+ ok {@router.call(new_env(:GET, '/api/books/123'))} == [200, {}, ["book_show_api"]]
421
+ ok {@router.call(new_env(:PUT, '/admin/books/123'))} == [200, {}, ["admin_book_update_app"]]
422
+ end
423
+
424
+ spec "[!fpw8x] finds mapped app according to env['PATH_INFO']." do
425
+ ok {@router.call(new_env(:GET, '/api/books'))} == [200, {}, ["book_list_api"]]
426
+ ok {@router.call(new_env(:GET, '/api/books/123'))} == [200, {}, ["book_show_api"]]
427
+ end
428
+
429
+ spec "[!wxt2g] guesses correct urlpath and redirects to it automaticaly when request path not found." do
430
+ headers = {"Content-Type"=>"text/plain", "Location"=>"/api/books"}
431
+ content = "Redirect to /api/books"
432
+ ok {@router.call(new_env(:GET, '/api/books/'))} == [301, headers, [content]]
433
+ #
434
+ headers = {"Content-Type"=>"text/plain", "Location"=>"/api/books/78"}
435
+ content = "Redirect to /api/books/78"
436
+ ok {@router.call(new_env(:GET, '/api/books/78/'))} == [301, headers, [content]]
437
+ end
438
+
439
+ spec "[!3vsua] doesn't redict automatically when request path is '/'." do
440
+ r = Rack::JetRouter.new([['/api/books', book_list_api]])
441
+ ok {r.call(new_env(:GET, '/'))} == [404, {"Content-Type"=>"text/plain"}, ["404 Not Found"]]
442
+ end
443
+
444
+ spec "[!hyk62] adds QUERY_STRING to redirect location." do
445
+ headers = {"Content-Type"=>"text/plain", "Location"=>"/api/books?x=1&y=2"}
446
+ content = "Redirect to /api/books?x=1&y=2"
447
+ env = new_env(:GET, '/api/books/', {"QUERY_STRING"=>"x=1&y=2"})
448
+ ok {@router.call(env)} == [301, headers, [content]]
449
+ end
450
+
451
+ spec "[!30x0k] returns 404 when request urlpath not found." do
452
+ expected = [404, {"Content-Type"=>"text/plain"}, ["404 Not Found"]]
453
+ ok {@router.call(new_env(:GET, '/xxx'))} == expected
454
+ ok {@router.call(new_env(:GET, '/api/book'))} == expected
455
+ end
456
+
457
+ case_when "[!gclbs] if mapped object is a Hash..." do
458
+
459
+ spec "[!p1fzn] invokes app mapped to request method." do
460
+ ok {@router.call(new_env(:GET, '/admin/books'))} == [200, {}, ["admin_book_list_app"]]
461
+ ok {@router.call(new_env(:POST, '/admin/books'))} == [200, {}, ["admin_book_create_app"]]
462
+ ok {@router.call(new_env(:GET, '/admin/books/123'))} == [200, {}, ["admin_book_show_app"]]
463
+ ok {@router.call(new_env(:PUT, '/admin/books/123'))} == [200, {}, ["admin_book_update_app"]]
464
+ ok {@router.call(new_env(:DELETE, '/admin/books/123'))} == [200, {}, ["admin_book_delete_app"]]
465
+ end
466
+
467
+ spec "[!5m64a] returns 405 when request method is not allowed." do
468
+ expected = [405, {"Content-Type"=>"text/plain"}, ["405 Method Not Allowed"]]
469
+ ok {@router.call(new_env(:PUT, '/admin/books'))} == expected
470
+ ok {@router.call(new_env(:FOOBAR, '/admin/books'))} == expected
471
+ end
472
+
473
+ spec "[!ys1e2] uses GET method when HEAD is not mapped." do
474
+ ok {@router.call(new_env(:HEAD, '/admin/books'))} == [200, {}, ["admin_book_list_app"]]
475
+ ok {@router.call(new_env(:HEAD, '/admin/books/123'))} == [200, {}, ["admin_book_show_app"]]
476
+ end
477
+
478
+ spec "[!2hx6j] try ANY method when request method is not mapped." do
479
+ mapping = [
480
+ ['/admin/books', {:ANY=>admin_book_list_app}]
481
+ ]
482
+ r = Rack::JetRouter.new(mapping)
483
+ expected = [200, {}, ["admin_book_list_app"]]
484
+ ok {r.call(new_env(:GET, '/admin/books'))} == expected
485
+ ok {r.call(new_env(:POST, '/admin/books'))} == expected
486
+ ok {r.call(new_env(:PUT, '/admin/books'))} == expected
487
+ ok {r.call(new_env(:DELETE, '/admin/books'))} == expected
488
+ end
489
+
490
+ end
491
+
492
+ spec "[!2c32f] stores urlpath parameter values into env['rack.urlpath_params']." do
493
+ env = new_env(:GET, '/api/books')
494
+ @router.call(env)
495
+ ok {env['rack.urlpath_params']} == nil
496
+ env = new_env(:GET, '/api/books/123')
497
+ @router.call(env)
498
+ ok {env['rack.urlpath_params']} == {"id"=>"123"}
499
+ env = new_env(:GET, '/api/books/123/comments/999')
500
+ @router.call(env)
501
+ ok {env['rack.urlpath_params']} == {"book_id"=>"123", "comment_id"=>"999"}
502
+ #
503
+ env = new_env(:GET, '/admin/books')
504
+ @router.call(env)
505
+ ok {env['rack.urlpath_params']} == nil
506
+ env = new_env(:GET, '/admin/books/123')
507
+ @router.call(env)
508
+ ok {env['rack.urlpath_params']} == {"id"=>"123"}
509
+ end
510
+
511
+ end
512
+
513
+
514
+ topic '#each()' do
515
+
516
+ spec "[!ep0pw] yields pair of urlpath pattern and app." do
517
+ arr = []
518
+ @router.each do |upath, app|
519
+ arr << [upath, app]
520
+ end
521
+ ok {arr[0]} == ["/", welcome_app]
522
+ ok {arr[1]} == ["/index.html", welcome_app]
523
+ ok {arr[2]} == ["/api/books", book_list_api]
524
+ ok {arr[3]} == ["/api/books/new", book_new_api]
525
+ ok {arr[4]} == ["/api/books/:id", book_show_api]
526
+ ok {arr[5]} == ["/api/books/:id/edit", book_edit_api]
527
+ ok {arr[6]} == ["/api/books/:book_id/comments", comment_create_api]
528
+ ok {arr[7]} == ["/api/books/:book_id/comments/:comment_id", comment_update_api]
529
+ ok {arr[8]} == ["/admin/books", {"GET"=>admin_book_list_app, "POST"=>admin_book_create_app}]
530
+ ok {arr[9]} == ["/admin/books/:id", {"GET"=>admin_book_show_app, "PUT"=>admin_book_update_app, "DELETE"=>admin_book_delete_app}]
531
+ end
532
+
533
+ end
534
+
535
+
536
+ topic '#redirect_to()' do
537
+
538
+ spec "[!9z57v] returns 301 and 'Location' header." do
539
+ ret = @router.instance_eval { redirect_to("/foo") }
540
+ headers = {"Content-Type" => "text/plain", "Location" => "/foo"}
541
+ ok {ret} == [301, headers, ["Redirect to /foo"]]
542
+ end
543
+
544
+ end
545
+
546
+
547
+ topic '#store_param_values()' do
548
+
549
+ spec "[!94riv] stores urlpath param values into `env['rack.urlpath_params']`." do
550
+ env = {}
551
+ @router.instance_eval { store_param_values(env, {"id"=>123}) }
552
+ ok {env} == {'rack.urlpath_params' => {"id"=>123}}
553
+ end
554
+
555
+ spec "[!9he9h] env key can be changed by `env_key:` kwarg of 'JetRouter#initialize()'." do
556
+ router = Rack::JetRouter.new([], env_key: 'rack.params')
557
+ env = {}
558
+ router.instance_eval { store_param_values(env, {"book_id"=>123}) }
559
+ ok {env} == {'rack.params' => {"book_id"=>123}}
560
+ end
561
+
562
+ end
563
+
564
+
565
+ topic '#build_param_values()' do
566
+
567
+ case_when "[!qxcis] when 'int_param:' kwarg is specified to constructor..." do
568
+
569
+ spec "[!l6p84] converts urlpath pavam value into integer." do
570
+ router = Rack::JetRouter.new([], int_param: /(\A|_)id\z/)
571
+ router.instance_exec(self) do |_|
572
+ ret = build_param_values(["id", "name"], ["123", "foo"])
573
+ _.ok {ret} == {"id" => 123, "name" => "foo"}
574
+ end
575
+ end
576
+
577
+ end
578
+
579
+ case_when "[!vrbo5] else..." do
580
+
581
+ spec "[!yc9n8] creates new Hash object from param names and values." do
582
+ router = Rack::JetRouter.new([])
583
+ router.instance_exec(self) do |_|
584
+ ret = build_param_values(["id", "name"], ["123", "foo"])
585
+ _.ok {ret} == {"id" => "123", "name" => "foo"}
586
+ end
587
+ end
588
+
589
+ end
590
+
591
+ end
592
+
593
+
594
+ end
595
+
596
+
597
+ end
data/test/run_all.rb ADDED
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ Dir.glob(File.dirname(__FILE__) + '/*_test.rb').each do |filename|
5
+ require File.absolute_path(filename)
6
+ end
@@ -1,10 +1,15 @@
1
1
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
2
 
3
- require 'minitest/spec'
4
- require 'minitest/autorun'
5
- require 'minitest/ok'
3
+ require 'oktest'
6
4
 
7
5
  require 'rack'
8
6
  require 'rack/mock'
9
7
  require 'rack/jet_router'
10
8
 
9
+
10
+ class Map < Hash
11
+ end
12
+
13
+ def Map(**kwargs)
14
+ return Map.new.update(kwargs)
15
+ end