rack-jet_router 1.2.0 → 1.3.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.
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.0 $
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