keight 0.2.0 → 0.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.
- checksums.yaml +4 -4
- data/CHANGES.md +237 -0
- data/README.md +285 -81
- data/Rakefile +27 -1
- data/bench/benchmarker.rb +2 -2
- data/bin/k8rb +112 -305
- data/keight.gemspec +4 -6
- data/lib/keight.rb +860 -822
- data/test/keight_test.rb +1007 -933
- data/test/oktest.rb +2 -2
- metadata +4 -68
- data/CHANGES.txt +0 -64
- data/lib/keight/skeleton/.gitignore +0 -10
- data/lib/keight/skeleton/README.txt +0 -13
- data/lib/keight/skeleton/app/action.rb +0 -106
- data/lib/keight/skeleton/app/api/hello.rb +0 -39
- data/lib/keight/skeleton/app/form/.keep +0 -0
- data/lib/keight/skeleton/app/helper/.keep +0 -0
- data/lib/keight/skeleton/app/model.rb +0 -144
- data/lib/keight/skeleton/app/model/.keep +0 -0
- data/lib/keight/skeleton/app/page/welcome.rb +0 -17
- data/lib/keight/skeleton/app/template/_layout.html.eruby +0 -56
- data/lib/keight/skeleton/app/template/welcome.html.eruby +0 -6
- data/lib/keight/skeleton/app/usecase/.keep +0 -0
- data/lib/keight/skeleton/config.rb +0 -46
- data/lib/keight/skeleton/config.ru +0 -6
- data/lib/keight/skeleton/config/app.rb +0 -29
- data/lib/keight/skeleton/config/app_dev.rb +0 -8
- data/lib/keight/skeleton/config/app_prod.rb +0 -7
- data/lib/keight/skeleton/config/app_stg.rb +0 -5
- data/lib/keight/skeleton/config/app_test.rb +0 -8
- data/lib/keight/skeleton/config/server_puma.rb +0 -22
- data/lib/keight/skeleton/config/server_unicorn.rb +0 -21
- data/lib/keight/skeleton/config/urlpath_mapping.rb +0 -16
- data/lib/keight/skeleton/index.txt +0 -39
- data/lib/keight/skeleton/main.rb +0 -22
- data/lib/keight/skeleton/static/lib/.keep +0 -0
- data/lib/keight/skeleton/test/api/hello_test.rb +0 -27
- data/lib/keight/skeleton/test/test_helper.rb +0 -9
data/test/keight_test.rb
CHANGED
@@ -4,6 +4,7 @@ $LOAD_PATH << "lib" unless $LOAD_PATH.include?("lib")
|
|
4
4
|
$LOAD_PATH << "test" unless $LOAD_PATH.include?("test")
|
5
5
|
|
6
6
|
require 'stringio'
|
7
|
+
require 'set'
|
7
8
|
|
8
9
|
require 'rack/test_app'
|
9
10
|
require 'oktest'
|
@@ -96,6 +97,17 @@ Oktest.scope do
|
|
96
97
|
return Rack::TestApp.new_env(meth, path, opts)
|
97
98
|
end
|
98
99
|
|
100
|
+
def capture_output
|
101
|
+
stdout_bkup, stderr_bkup, $stdout, $stderr = \
|
102
|
+
$stdout, $stderr, StringIO.new, StringIO.new
|
103
|
+
begin
|
104
|
+
yield
|
105
|
+
return $stdout.string, $stderr.string
|
106
|
+
ensure
|
107
|
+
$stdout, $stderr = stdout_bkup, stderr_bkup
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
99
111
|
|
100
112
|
topic K8::Util do
|
101
113
|
|
@@ -138,12 +150,12 @@ Oktest.scope do
|
|
138
150
|
|
139
151
|
topic '.parse_query_string()' do
|
140
152
|
|
141
|
-
spec "[!fzt3w] parses query string and returns
|
153
|
+
spec "[!fzt3w] parses query string and returns Hash/dict object." do
|
142
154
|
d = K8::Util.parse_query_string("x=123&y=456&z=")
|
143
155
|
ok {d} == {"x"=>"123", "y"=>"456", "z"=>""}
|
144
156
|
end
|
145
157
|
|
146
|
-
spec "[!engr6] returns empty Hash object when query string is empty." do
|
158
|
+
spec "[!engr6] returns empty Hash/dict object when query string is empty." do
|
147
159
|
d = K8::Util.parse_query_string("")
|
148
160
|
ok {d} == {}
|
149
161
|
end
|
@@ -239,7 +251,7 @@ Oktest.scope do
|
|
239
251
|
end
|
240
252
|
|
241
253
|
|
242
|
-
topic '
|
254
|
+
topic '.http_utc_time()' do
|
243
255
|
|
244
256
|
spec "[!5k50b] converts Time object into HTTP date format string." do
|
245
257
|
require 'time'
|
@@ -258,6 +270,279 @@ Oktest.scope do
|
|
258
270
|
end
|
259
271
|
|
260
272
|
|
273
|
+
topic 'TemporaryFile' do
|
274
|
+
|
275
|
+
|
276
|
+
topic '.new_path()' do
|
277
|
+
|
278
|
+
spec "[!hvnzd] generates new temorary filepath under temporary directory." do
|
279
|
+
fpath = K8::Util::TemporaryFile.new_path('/var/tmp')
|
280
|
+
ok {fpath}.start_with?('/var/tmp/')
|
281
|
+
ok {fpath} =~ %r`\A/var/tmp/tmp-\d+\.tmp\z`
|
282
|
+
#
|
283
|
+
paths = 1000.times.map { K8::Util::TemporaryFile.new_path('/var/tmp') }
|
284
|
+
ok {Set.new(paths).size} == 1000
|
285
|
+
end
|
286
|
+
|
287
|
+
spec "[!ulb2e] uses default temporary directory path if tmpdir is not specified." do
|
288
|
+
fpath = K8::Util::TemporaryFile.new_path()
|
289
|
+
ok {fpath}.start_with?(K8::Util::TemporaryFile::TMPDIR + '/')
|
290
|
+
ok {fpath} =~ %r"\A#{Regexp.escape(K8::Util::TemporaryFile::TMPDIR)}/tmp-\d+\.tmp\z"
|
291
|
+
end
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
topic '#initialize()' do
|
297
|
+
|
298
|
+
spec "[!ljilm] generates temporary filepath automatically when filepath is not specified." do
|
299
|
+
n = 1000
|
300
|
+
paths = n.times.map { K8::Util::TemporaryFile.new().path }
|
301
|
+
ok {Set.new(paths).size} == n
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
305
|
+
|
306
|
+
|
307
|
+
topic '#each()' do
|
308
|
+
|
309
|
+
spec "[!d9suq] opens temporary file with binary mode." do
|
310
|
+
tmpf = K8::Util::TemporaryFile.new()
|
311
|
+
File.open(tmpf.path, 'wb') {|f| f.write("hogehoge") }
|
312
|
+
called = false
|
313
|
+
tmpf.each do |s|
|
314
|
+
called = true
|
315
|
+
ok {s.encoding} == Encoding::BINARY
|
316
|
+
end
|
317
|
+
ok {called} == true
|
318
|
+
end
|
319
|
+
|
320
|
+
spec "[!68xdj] reads chunk size data from temporary file per iteration." do
|
321
|
+
tmpf = K8::Util::TemporaryFile.new(chunk_size: 3)
|
322
|
+
File.open(tmpf.path, 'wb') {|f| f.write("hogehoge") }
|
323
|
+
i = 0
|
324
|
+
tmpf.each do |s|
|
325
|
+
case i
|
326
|
+
when 0; ok {s} == "hog"
|
327
|
+
when 1; ok {s} == "eho"
|
328
|
+
when 2; ok {s} == "ge"
|
329
|
+
else
|
330
|
+
raise "unreachable"
|
331
|
+
end
|
332
|
+
i += 1
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
spec "[!i0dmd] removes temporary file automatically at end of loop." do
|
337
|
+
tmpf = K8::Util::TemporaryFile.new()
|
338
|
+
File.open(tmpf.path, 'wb') {|f| f.write("hogehoge") }
|
339
|
+
ok {tmpf.path}.file_exist?
|
340
|
+
tmpf.each do |s|
|
341
|
+
ok {tmpf.path}.file_exist?
|
342
|
+
end
|
343
|
+
ok {tmpf.path}.NOT.file_exist?
|
344
|
+
end
|
345
|
+
|
346
|
+
spec "[!347an] removes temporary file even if error raised in block." do
|
347
|
+
tmpf = K8::Util::TemporaryFile.new()
|
348
|
+
File.open(tmpf.path, 'wb') {|f| f.write("hogehoge") }
|
349
|
+
ok {tmpf.path}.file_exist?
|
350
|
+
pr = proc { tmpf.each {|s| 1/0 } }
|
351
|
+
ok {pr}.raise?(ZeroDivisionError)
|
352
|
+
ok {tmpf.path}.NOT.file_exist?
|
353
|
+
end
|
354
|
+
|
355
|
+
end
|
356
|
+
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
|
361
|
+
topic K8::Util::ShellCommand do
|
362
|
+
|
363
|
+
|
364
|
+
topic '#initialize()' do
|
365
|
+
|
366
|
+
spec "[!j95pi] takes shell command and input string." do
|
367
|
+
command = "psql -AF',' dbname | gzip"
|
368
|
+
input = "select * from table1"
|
369
|
+
sc = K8::Util::ShellCommand.new(command, input: input)
|
370
|
+
ok {sc.command} == command
|
371
|
+
ok {sc.input} == input
|
372
|
+
#
|
373
|
+
sc2 = K8::Util::ShellCommand.new(command)
|
374
|
+
ok {sc2.command} == command
|
375
|
+
ok {sc2.input} == nil
|
376
|
+
end
|
377
|
+
|
378
|
+
end
|
379
|
+
|
380
|
+
|
381
|
+
topic '#start()' do
|
382
|
+
|
383
|
+
fixture :sc do
|
384
|
+
K8::Util::ShellCommand.new("echo yes")
|
385
|
+
end
|
386
|
+
|
387
|
+
spec "[!66uck] not allowed to start more than once." do |sc|
|
388
|
+
ok {proc { sc.start() }}.NOT.raise?(Exception)
|
389
|
+
ok {proc { sc.start() }}.raise?(K8::Util::ShellCommandError, 'Already started (comand: "echo yes")')
|
390
|
+
end
|
391
|
+
|
392
|
+
spec "[!9seos] invokes shell command." do |sc|
|
393
|
+
ok {sc.process_id} == nil
|
394
|
+
sc.start()
|
395
|
+
ok {sc.process_id} != nil
|
396
|
+
ok {sc.process_id}.is_a?(Fixnum)
|
397
|
+
end
|
398
|
+
|
399
|
+
spec "[!d766y] writes input string if provided to initializer." do |sc|
|
400
|
+
input = "a\nb\nc\n"
|
401
|
+
sc = K8::Util::ShellCommand.new("cat -n", input: input, chunk_size: 2)
|
402
|
+
sc.start()
|
403
|
+
buf = ""
|
404
|
+
sc.each {|s| buf << s }
|
405
|
+
ok {buf} == (" 1 a\n" + \
|
406
|
+
" 2 b\n" + \
|
407
|
+
" 3 c\n")
|
408
|
+
end
|
409
|
+
|
410
|
+
spec "[!f651x] reads first chunk data." do
|
411
|
+
sc = K8::Util::ShellCommand.new("echo abcdefg", chunk_size: 2)
|
412
|
+
t = sc.instance_variable_get('@tuple')
|
413
|
+
ok {t} == nil
|
414
|
+
sc.start()
|
415
|
+
t = sc.instance_variable_get('@tuple')
|
416
|
+
ok {t} != nil
|
417
|
+
sout, serr, waiter, chunk = t
|
418
|
+
ok {chunk} == "ab"
|
419
|
+
end
|
420
|
+
|
421
|
+
spec "[!cjstj] raises ShellCommandError when command prints something to stderr." do
|
422
|
+
sc = K8::Util::ShellCommand.new("echo abcdefg 1>&2")
|
423
|
+
capture_output do
|
424
|
+
pr = proc { sc.start() }
|
425
|
+
ok {pr}.raise?(K8::Util::ShellCommandError, "abcdefg\n")
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
spec "[!bt12n] saves stdout, stderr, command process, and first chunk data." do
|
430
|
+
sc = K8::Util::ShellCommand.new("echo abcdefg", chunk_size: 2)
|
431
|
+
sc.start()
|
432
|
+
t = sc.instance_variable_get('@tuple')
|
433
|
+
ok {t} != nil
|
434
|
+
sout, serr, waiter, chunk = t
|
435
|
+
ok {sout}.is_a?(IO)
|
436
|
+
ok {serr}.is_a?(IO)
|
437
|
+
ok {waiter}.is_a?(Process::Waiter)
|
438
|
+
ok {chunk} == "ab"
|
439
|
+
end
|
440
|
+
|
441
|
+
spec "[!kgnel] yields callback (if given) when command invoked successfully." do
|
442
|
+
sc = K8::Util::ShellCommand.new("echo abcdefg")
|
443
|
+
called = false
|
444
|
+
sc.start() do
|
445
|
+
called = true
|
446
|
+
end
|
447
|
+
ok {called} == true # called
|
448
|
+
#
|
449
|
+
capture_output do
|
450
|
+
sc = K8::Util::ShellCommand.new("echo abcdefg 1>&2")
|
451
|
+
called = false
|
452
|
+
sc.start() do
|
453
|
+
called = true
|
454
|
+
end rescue nil
|
455
|
+
ok {called} == false # not called
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
spec "[!2989u] closes both stdout and stderr when error raised." do
|
460
|
+
skip_when(true, "hard to test")
|
461
|
+
end
|
462
|
+
|
463
|
+
spec "[!fp98i] returns self." do
|
464
|
+
sc = K8::Util::ShellCommand.new("echo abcdefg")
|
465
|
+
ok {sc.start()}.same?(sc)
|
466
|
+
end
|
467
|
+
|
468
|
+
end
|
469
|
+
|
470
|
+
|
471
|
+
topic '#each()' do
|
472
|
+
|
473
|
+
fixture :sc do
|
474
|
+
K8::Util::ShellCommand.new("echo yes")
|
475
|
+
end
|
476
|
+
|
477
|
+
spec "[!ssgmm] '#start()' should be called before '#each()'." do |sc|
|
478
|
+
ok {proc { sc.each() }}.raise?(K8::Util::ShellCommandError, 'Not started yet (command: "echo yes").')
|
479
|
+
end
|
480
|
+
|
481
|
+
spec "[!vpmbw] yields each chunk data." do |sc|
|
482
|
+
sc = K8::Util::ShellCommand.new("echo abcdef", chunk_size: 2)
|
483
|
+
sc.start()
|
484
|
+
arr = []
|
485
|
+
sc.each {|s| arr << s }
|
486
|
+
ok {arr} == ["ab", "cd", "ef", "\n"]
|
487
|
+
end
|
488
|
+
|
489
|
+
spec "[!70xdy] logs stderr output." do
|
490
|
+
buf = ""
|
491
|
+
sout, serr = capture_output do
|
492
|
+
sc = K8::Util::ShellCommand.new("time echo abcdef")
|
493
|
+
sc.start()
|
494
|
+
sc.each {|s| buf << s }
|
495
|
+
end
|
496
|
+
ok {buf} == "abcdef\n"
|
497
|
+
ok {sout} == ""
|
498
|
+
ok {serr} != ""
|
499
|
+
ok {serr} == ("[ERROR] ShellCommand: \"time echo abcdef\" #-------\n" + \
|
500
|
+
" 0.00 real 0.00 user 0.00 sys\n" + \
|
501
|
+
"--------------------\n")
|
502
|
+
end
|
503
|
+
|
504
|
+
spec "[!2wll8] closes stdout and stderr, even if error raised." do
|
505
|
+
sc = K8::Util::ShellCommand.new("echo abcdef")
|
506
|
+
sc.start()
|
507
|
+
sout, serr, _, _ = sc.instance_variable_get('@tuple')
|
508
|
+
ok {sout.closed?} == false
|
509
|
+
ok {serr.closed?} == false
|
510
|
+
sc.each {|s| s }
|
511
|
+
ok {sout.closed?} == true
|
512
|
+
ok {serr.closed?} == true
|
513
|
+
#
|
514
|
+
sc = K8::Util::ShellCommand.new("time echo abcdef")
|
515
|
+
sc.start()
|
516
|
+
sout, serr, _, _ = sc.instance_variable_get('@tuple')
|
517
|
+
ok {sout.closed?} == false
|
518
|
+
ok {serr.closed?} == false
|
519
|
+
capture_output do sc.each {|s| s } end
|
520
|
+
ok {sout.closed?} == true
|
521
|
+
ok {serr.closed?} == true
|
522
|
+
end
|
523
|
+
|
524
|
+
spec "[!0ebq5] calls callback specified at initializer with error object." do
|
525
|
+
arg = false
|
526
|
+
sc = K8::Util::ShellCommand.new("echo abcdef") {|x| arg = x }
|
527
|
+
ok {arg} == false
|
528
|
+
sc.start()
|
529
|
+
ok {arg} == false
|
530
|
+
sc.each {|s| s }
|
531
|
+
ok {arg} == nil
|
532
|
+
end
|
533
|
+
|
534
|
+
spec "[!ln8we] returns self." do |sc|
|
535
|
+
sc.start()
|
536
|
+
ret = sc.each {|s| s }
|
537
|
+
ok {ret}.same?(sc)
|
538
|
+
end
|
539
|
+
|
540
|
+
end
|
541
|
+
|
542
|
+
|
543
|
+
end
|
544
|
+
|
545
|
+
|
261
546
|
end
|
262
547
|
|
263
548
|
|
@@ -325,10 +610,10 @@ Oktest.scope do
|
|
325
610
|
end
|
326
611
|
|
327
612
|
|
328
|
-
topic K8::
|
613
|
+
topic K8::RackRequest do
|
329
614
|
|
330
615
|
fixture :req do
|
331
|
-
K8::
|
616
|
+
K8::RackRequest.new(new_env("GET", "/123"))
|
332
617
|
end
|
333
618
|
|
334
619
|
fixture :data_dir do
|
@@ -348,28 +633,28 @@ Oktest.scope do
|
|
348
633
|
|
349
634
|
spec "[!yb9k9] sets @env." do
|
350
635
|
env = new_env()
|
351
|
-
req = K8::
|
636
|
+
req = K8::RackRequest.new(env)
|
352
637
|
ok {req.env}.same?(env)
|
353
638
|
end
|
354
639
|
|
355
|
-
spec "[!yo22o] sets @
|
356
|
-
req1 = K8::
|
357
|
-
ok {req1.
|
358
|
-
req2 = K8::
|
359
|
-
ok {req2.
|
640
|
+
spec "[!yo22o] sets @meth as Symbol value." do
|
641
|
+
req1 = K8::RackRequest.new(new_env("GET"))
|
642
|
+
ok {req1.meth} == :GET
|
643
|
+
req2 = K8::RackRequest.new(new_env("POST"))
|
644
|
+
ok {req2.meth} == :POST
|
360
645
|
end
|
361
646
|
|
362
647
|
spec "[!twgmi] sets @path." do
|
363
|
-
req1 = K8::
|
648
|
+
req1 = K8::RackRequest.new(new_env("GET", "/123"))
|
364
649
|
ok {req1.path} == "/123"
|
365
650
|
end
|
366
651
|
|
367
652
|
spec "[!ae8ws] uses SCRIPT_NAME as urlpath when PATH_INFO is not provided." do
|
368
653
|
env = new_env("GET", "/123", env: {'SCRIPT_NAME'=>'/index.cgi'})
|
369
654
|
env['PATH_INFO'] = ''
|
370
|
-
ok {K8::
|
655
|
+
ok {K8::RackRequest.new(env).path} == "/index.cgi"
|
371
656
|
env.delete('PATH_INFO')
|
372
|
-
ok {K8::
|
657
|
+
ok {K8::RackRequest.new(env).path} == "/index.cgi"
|
373
658
|
end
|
374
659
|
|
375
660
|
end
|
@@ -378,7 +663,7 @@ Oktest.scope do
|
|
378
663
|
topic '#method()' do
|
379
664
|
|
380
665
|
fixture :req do
|
381
|
-
K8::
|
666
|
+
K8::RackRequest.new({"REQUEST_METHOD"=>"POST"})
|
382
667
|
end
|
383
668
|
|
384
669
|
spec "[!084jo] returns current request method when argument is not specified." do
|
@@ -395,13 +680,32 @@ Oktest.scope do
|
|
395
680
|
end
|
396
681
|
|
397
682
|
|
683
|
+
topic '#path_ext()' do
|
684
|
+
|
685
|
+
spec "[!tf6yz] returns extension of request path such as '.html' or '.json'." do
|
686
|
+
req1 = K8::RackRequest.new(new_env("GET", "/api/books/123.html"))
|
687
|
+
ok {req1.path_ext} == ".html"
|
688
|
+
req2 = K8::RackRequest.new(new_env("GET", "/api/books/123.json"))
|
689
|
+
ok {req2.path_ext} == ".json"
|
690
|
+
end
|
691
|
+
|
692
|
+
spec "[!xnurj] returns empty string when no extension." do
|
693
|
+
req1 = K8::RackRequest.new(new_env("GET", "/api/books/123"))
|
694
|
+
ok {req1.path_ext} == ""
|
695
|
+
req2 = K8::RackRequest.new(new_env("GET", "/api/books.d/123"))
|
696
|
+
ok {req2.path_ext} == ""
|
697
|
+
end
|
698
|
+
|
699
|
+
end
|
700
|
+
|
701
|
+
|
398
702
|
topic '#header()' do
|
399
703
|
|
400
704
|
spec "[!1z7wj] returns http header value from environment." do
|
401
705
|
env = new_env("GET", "/",
|
402
706
|
headers: {'Accept-Encoding'=>'gzip,deflate'},
|
403
707
|
env: {'HTTP_ACCEPT_LANGUAGE'=>'en,ja'})
|
404
|
-
req = K8::
|
708
|
+
req = K8::RackRequest.new(env)
|
405
709
|
ok {req.header('Accept-Encoding')} == 'gzip,deflate'
|
406
710
|
ok {req.header('Accept-Language')} == 'en,ja'
|
407
711
|
end
|
@@ -412,7 +716,7 @@ Oktest.scope do
|
|
412
716
|
topic '#request_method' do
|
413
717
|
|
414
718
|
spec "[!y8eos] returns env['REQUEST_METHOD'] as string." do
|
415
|
-
req = K8::
|
719
|
+
req = K8::RackRequest.new(new_env(:POST, "/"))
|
416
720
|
ok {req.request_method} == "POST"
|
417
721
|
end
|
418
722
|
|
@@ -423,9 +727,9 @@ Oktest.scope do
|
|
423
727
|
|
424
728
|
spec "[!95g9o] returns env['CONTENT_TYPE']." do
|
425
729
|
ctype = "text/html"
|
426
|
-
req = K8::
|
730
|
+
req = K8::RackRequest.new(new_env("GET", "/", env: {'CONTENT_TYPE'=>ctype}))
|
427
731
|
ok {req.content_type} == ctype
|
428
|
-
req = K8::
|
732
|
+
req = K8::RackRequest.new(new_env("GET", "/", env: {}))
|
429
733
|
ok {req.content_type} == nil
|
430
734
|
end
|
431
735
|
|
@@ -435,7 +739,7 @@ Oktest.scope do
|
|
435
739
|
topic '#content_length' do
|
436
740
|
|
437
741
|
spec "[!0wbek] returns env['CONTENT_LENGHT'] as integer." do
|
438
|
-
req = K8::
|
742
|
+
req = K8::RackRequest.new(new_env("GET", "/", env: {'CONTENT_LENGTH'=>'0'}))
|
439
743
|
ok {req.content_length} == 0
|
440
744
|
req.env.delete('CONTENT_LENGTH')
|
441
745
|
ok {req.content_length} == nil
|
@@ -448,9 +752,9 @@ Oktest.scope do
|
|
448
752
|
|
449
753
|
spec "[!hsgkg] returns true when 'X-Requested-With' header is 'XMLHttpRequest'." do
|
450
754
|
env = new_env("GET", "/", headers: {'X-Requested-With'=>'XMLHttpRequest'})
|
451
|
-
ok {K8::
|
755
|
+
ok {K8::RackRequest.new(env).xhr?} == true
|
452
756
|
env = new_env("GET", "/", headers: {})
|
453
|
-
ok {K8::
|
757
|
+
ok {K8::RackRequest.new(env).xhr?} == false
|
454
758
|
end
|
455
759
|
|
456
760
|
end
|
@@ -462,20 +766,20 @@ Oktest.scope do
|
|
462
766
|
env = new_env("GET", "/",
|
463
767
|
headers: {'X-Real-IP'=>'192.168.1.23'},
|
464
768
|
env: {'REMOTE_ADDR'=>'192.168.0.1'})
|
465
|
-
ok {K8::
|
769
|
+
ok {K8::RackRequest.new(env).client_ip_addr} == '192.168.1.23'
|
466
770
|
end
|
467
771
|
|
468
772
|
spec "[!qdlyl] returns first item of 'X-Forwarded-For' header if provided." do
|
469
773
|
env = new_env("GET", "/",
|
470
774
|
headers: {'X-Forwarded-For'=>'192.168.1.1, 192.168.1.2, 192.168.1.3'},
|
471
775
|
env: {'REMOTE_ADDR'=>'192.168.0.1'})
|
472
|
-
ok {K8::
|
776
|
+
ok {K8::RackRequest.new(env).client_ip_addr} == '192.168.1.1'
|
473
777
|
end
|
474
778
|
|
475
779
|
spec "[!8nzjh] returns 'REMOTE_ADDR' if neighter 'X-Real-IP' nor 'X-Forwarded-For' provided." do
|
476
780
|
env = new_env("GET", "/",
|
477
781
|
env: {'REMOTE_ADDR'=>'192.168.0.1'})
|
478
|
-
ok {K8::
|
782
|
+
ok {K8::RackRequest.new(env).client_ip_addr} == '192.168.0.1'
|
479
783
|
end
|
480
784
|
|
481
785
|
end
|
@@ -485,15 +789,15 @@ Oktest.scope do
|
|
485
789
|
|
486
790
|
spec "[!jytwy] returns 'https' when env['HTTPS'] is 'on'." do
|
487
791
|
env = new_env("GET", "/", env: {'HTTPS'=>'on'})
|
488
|
-
ok {K8::
|
792
|
+
ok {K8::RackRequest.new(env).scheme} == 'https'
|
489
793
|
end
|
490
794
|
|
491
795
|
spec "[!zg8r2] returns env['rack.url_scheme'] ('http' or 'https')." do
|
492
796
|
env = new_env("GET", "/", env: {'HTTPS'=>'off'})
|
493
797
|
env['rack.url_scheme'] = 'http'
|
494
|
-
ok {K8::
|
798
|
+
ok {K8::RackRequest.new(env).scheme} == 'http'
|
495
799
|
env['rack.url_scheme'] = 'https'
|
496
|
-
ok {K8::
|
800
|
+
ok {K8::RackRequest.new(env).scheme} == 'https'
|
497
801
|
end
|
498
802
|
|
499
803
|
end
|
@@ -501,62 +805,102 @@ Oktest.scope do
|
|
501
805
|
|
502
806
|
topic '#params_query' do
|
503
807
|
|
504
|
-
spec "[!6ezqw] parses QUERY_STRING and returns it as Hash object." do
|
808
|
+
spec "[!6ezqw] parses QUERY_STRING and returns it as Hash/dict object." do
|
505
809
|
qstr = "x=1&y=2"
|
506
|
-
req = K8::
|
810
|
+
req = K8::RackRequest.new(new_env("GET", "/", env: {'QUERY_STRING'=>qstr}))
|
507
811
|
ok {req.params_query()} == {'x'=>'1', 'y'=>'2'}
|
508
812
|
end
|
509
813
|
|
510
814
|
spec "[!o0ws7] unquotes both keys and values." do
|
511
815
|
qstr = "arr%5Bxxx%5D=%3C%3E+%26%3B"
|
512
|
-
req = K8::
|
816
|
+
req = K8::RackRequest.new(new_env("GET", "/", env: {'QUERY_STRING'=>qstr}))
|
513
817
|
ok {req.params_query()} == {'arr[xxx]'=>'<> &;'}
|
514
818
|
end
|
515
819
|
|
820
|
+
spec "[!2fhrk] returns same value when called more than once." do
|
821
|
+
req = K8::RackRequest.new(new_env("GET", "/", query: "x=1&y=2"))
|
822
|
+
val1st = req.params_query()
|
823
|
+
val2nd = req.params_query()
|
824
|
+
ok {val2nd}.same?(val1st)
|
825
|
+
end
|
826
|
+
|
516
827
|
end
|
517
828
|
|
518
829
|
|
519
830
|
topic '#params_form' do
|
520
831
|
|
521
|
-
spec "[!
|
522
|
-
|
523
|
-
|
524
|
-
|
832
|
+
spec "[!iultp] returns same value when called more than once." do
|
833
|
+
req = K8::RackRequest.new(new_env("POST", "/", form: "x=1"))
|
834
|
+
val1st = req.params_form
|
835
|
+
val2nd = req.params_form
|
836
|
+
ok {val2nd}.same?(val1st)
|
837
|
+
end
|
838
|
+
|
839
|
+
spec "[!uq46o] raises 400 error when payload is not form data." do
|
840
|
+
|multipart_env|
|
841
|
+
env = multipart_env
|
842
|
+
req = K8::RackRequest.new(env)
|
525
843
|
pr = proc { req.params_form }
|
526
|
-
ok {pr}.raise?(K8::HttpException,
|
844
|
+
ok {pr}.raise?(K8::HttpException, /^expected form data, but Content-Type header is "multipart\/form-data;boundary=.*".$/)
|
527
845
|
end
|
528
846
|
|
529
|
-
spec "[!
|
847
|
+
spec "[!puxlr] raises 400 error when content length is too large (> 10MB)." do
|
530
848
|
env = new_env("POST", "/", form: "x=1")
|
531
|
-
env['CONTENT_LENGTH'] =
|
532
|
-
req = K8::
|
849
|
+
env['CONTENT_LENGTH'] = (10*1024*1024 + 1).to_s
|
850
|
+
req = K8::RackRequest.new(env)
|
533
851
|
pr = proc { req.params_form }
|
534
|
-
ok {pr}.raise?(K8::HttpException, 'Content-Length
|
852
|
+
ok {pr}.raise?(K8::HttpException, 'Content-Length is too large (max: 10485760, actual: 10485761).')
|
535
853
|
end
|
536
854
|
|
537
|
-
spec "[!59ad2] parses form parameters and returns it as Hash object
|
855
|
+
spec "[!59ad2] parses form parameters and returns it as Hash/dict object." do
|
538
856
|
form = "x=1&y=2&arr%5Bxxx%5D=%3C%3E+%26%3B"
|
539
|
-
req = K8::
|
857
|
+
req = K8::RackRequest.new(new_env("POST", "/", form: form))
|
540
858
|
ok {req.params_form} == {'x'=>'1', 'y'=>'2', 'arr[xxx]'=>'<> &;'}
|
541
859
|
end
|
542
860
|
|
543
|
-
spec "[!puxlr] raises error when content length is too long (> 10MB)." do
|
544
|
-
env = new_env("POST", "/", form: "x=1")
|
545
|
-
env['CONTENT_LENGTH'] = (10*1024*1024 + 1).to_s
|
546
|
-
req = K8::Request.new(env)
|
547
|
-
pr = proc { req.params_form }
|
548
|
-
ok {pr}.raise?(K8::HttpException, 'Content-Length is too long.')
|
549
|
-
end
|
550
|
-
|
551
861
|
end
|
552
862
|
|
553
863
|
|
554
864
|
topic '#params_multipart' do
|
555
865
|
|
556
|
-
spec "[!
|
866
|
+
spec "[!gbdxu] returns same values when called more than once." do
|
867
|
+
|multipart_env|
|
868
|
+
req = K8::RackRequest.new(multipart_env)
|
869
|
+
d1, d2 = req.params_multipart
|
870
|
+
d3, d4 = req.params_multipart
|
871
|
+
ok {d3}.same?(d1)
|
872
|
+
ok {d4}.same?(d2)
|
873
|
+
end
|
874
|
+
|
875
|
+
spec "[!ho5ii] raises 400 error when not multipart data." do
|
876
|
+
env = new_env("POST", "/", form: "x=1")
|
877
|
+
req = K8::RackRequest.new(env)
|
878
|
+
pr = proc { req.params_multipart }
|
879
|
+
ok {pr}.raise?(K8::HttpException, 'expected multipart data, but Content-Type header is "application/x-www-form-urlencoded".')
|
880
|
+
end
|
881
|
+
|
882
|
+
spec "[!davzs] raises 400 error when boundary is missing." do
|
883
|
+
|multipart_env, data_dir|
|
884
|
+
env = multipart_env
|
885
|
+
env['CONTENT_TYPE'] = env['CONTENT_TYPE'].split(';').first
|
886
|
+
req = K8::RackRequest.new(env)
|
887
|
+
pr = proc { req.params_multipart }
|
888
|
+
ok {pr}.raise?(K8::HttpException, 'bounday attribute of multipart required.')
|
889
|
+
end
|
890
|
+
|
891
|
+
spec "[!mtx6t] raises 400 error when content length of multipart is too large (> 100MB)." do
|
892
|
+
|multipart_env|
|
893
|
+
env = multipart_env
|
894
|
+
env['CONTENT_LENGTH'] = (100*1024*1024 + 1).to_s
|
895
|
+
req = K8::RackRequest.new(env)
|
896
|
+
pr = proc { req.params_multipart }
|
897
|
+
ok {pr}.raise?(K8::HttpException, 'Content-Length is too large (max: 104857600, actual: 104857601).')
|
898
|
+
end
|
899
|
+
|
900
|
+
spec "[!y1jng] parses multipart when multipart data posted." do
|
557
901
|
|multipart_env, data_dir|
|
558
902
|
env = multipart_env
|
559
|
-
req = K8::
|
903
|
+
req = K8::RackRequest.new(env)
|
560
904
|
form, files = req.params_multipart
|
561
905
|
ok {form} == {
|
562
906
|
"text1" => "test1",
|
@@ -585,53 +929,110 @@ Oktest.scope do
|
|
585
929
|
|
586
930
|
end
|
587
931
|
|
588
|
-
spec "[!mtx6t] raises error when content length of multipart is too long (> 100MB)." do
|
589
|
-
|multipart_env|
|
590
|
-
env = multipart_env
|
591
|
-
env['CONTENT_LENGTH'] = (100*1024*1024 + 1).to_s
|
592
|
-
req = K8::Request.new(env)
|
593
|
-
pr = proc { req.params_multipart }
|
594
|
-
ok {pr}.raise?(K8::HttpException, 'Content-Length of multipart is too long.')
|
595
|
-
end
|
596
|
-
|
597
932
|
end
|
598
933
|
|
599
934
|
|
600
935
|
topic '#params_json' do
|
601
936
|
|
937
|
+
spec "[!5kwij] returns same value when called more than once." do
|
938
|
+
req = K8::RackRequest.new(new_env("POST", "/", json: {"x"=>1}))
|
939
|
+
val1st = req.params_json
|
940
|
+
val2nd = req.params_json
|
941
|
+
ok {val2nd}.same?(val1st)
|
942
|
+
end
|
943
|
+
|
944
|
+
spec "[!qjgfz] raises 400 error when not JSON data." do
|
945
|
+
req = K8::RackRequest.new(new_env("POST", "/", form: "x=1"))
|
946
|
+
pr = proc { req.params_json }
|
947
|
+
ok {pr}.raise?(K8::HttpException, "expected JSON data, but Content-Type header is \"application/x-www-form-urlencoded\".")
|
948
|
+
end
|
949
|
+
|
950
|
+
spec "[!on107] raises error when content length of JSON is too large (> 10MB)." do
|
951
|
+
env = new_env("POST", "/", json: {"x"=>1})
|
952
|
+
env['CONTENT_LENGTH'] = (10*1024*1024 + 1).to_s
|
953
|
+
req = K8::RackRequest.new(env)
|
954
|
+
pr = proc { req.params_json }
|
955
|
+
ok {pr}.raise?(K8::HttpException, 'Content-Length is too large (max: 10485760, actual: 10485761).')
|
956
|
+
end
|
957
|
+
|
602
958
|
spec "[!ugik5] parses json data and returns it as hash object when json data is sent." do
|
603
959
|
data = '{"x":1,"y":2,"arr":["a","b","c"]}'
|
604
|
-
req = K8::
|
960
|
+
req = K8::RackRequest.new(new_env("POST", "/", json: data))
|
605
961
|
ok {req.params_json} == {"x"=>1, "y"=>2, "arr"=>["a", "b", "c"]}
|
606
962
|
end
|
607
963
|
|
608
964
|
end
|
609
965
|
|
610
966
|
|
967
|
+
topic '#get_content_length()' do
|
968
|
+
|
969
|
+
spec "[!q88w9] raises 400 error when content length is missing." do
|
970
|
+
env = new_env("POST", "/", form: "x=1")
|
971
|
+
env['CONTENT_LENGTH'] = nil
|
972
|
+
req = K8::RackRequest.new(env)
|
973
|
+
req.instance_exec(self) do |_|
|
974
|
+
pr = proc { get_content_length(100) }
|
975
|
+
_.ok {pr}.raise?(K8::HttpException, 'Content-Length header expected.')
|
976
|
+
end
|
977
|
+
end
|
978
|
+
|
979
|
+
spec "[!ls6ir] raises error when content length is too large." do
|
980
|
+
env = new_env("POST", "/", form: "x=1")
|
981
|
+
env['CONTENT_LENGTH'] = '101'
|
982
|
+
req = K8::RackRequest.new(env)
|
983
|
+
req.instance_exec(self) do |_|
|
984
|
+
pr = proc { get_content_length(100) }
|
985
|
+
_.ok {pr}.raise?(K8::HttpException, 'Content-Length is too large (max: 100, actual: 101).')
|
986
|
+
end
|
987
|
+
end
|
988
|
+
|
989
|
+
end
|
990
|
+
|
991
|
+
|
992
|
+
topic '#get_input_stream()' do
|
993
|
+
|
994
|
+
spec "[!2buc6] returns input stream." do
|
995
|
+
env = new_env("POST", "/", form: "x=1")
|
996
|
+
req = K8::RackRequest.new(env)
|
997
|
+
req.instance_exec(self) do |_|
|
998
|
+
_.ok { get_input_stream() }.same?(req.env['rack.input'])
|
999
|
+
end
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
|
611
1005
|
topic '#params' do
|
612
1006
|
|
613
1007
|
spec "[!erlc7] parses QUERY_STRING when request method is GET or HEAD." do
|
614
1008
|
qstr = "a=8&b=9"
|
615
1009
|
form = "x=1&y=2"
|
616
|
-
req = K8::
|
1010
|
+
req = K8::RackRequest.new(new_env('GET', '/', query: qstr, form: form))
|
617
1011
|
ok {req.params} == {"a"=>"8", "b"=>"9"}
|
618
1012
|
end
|
619
1013
|
|
620
|
-
spec "[!cr0zj] parses JSON when content type is 'application/json'." do
|
621
|
-
qstr = "a=8&b=9"
|
622
|
-
json = '{"n":123}'
|
623
|
-
req = K8::Request.new(new_env('POST', '/', query: qstr, json: json))
|
624
|
-
ok {req.params} == {"n"=>123}
|
625
|
-
end
|
626
|
-
|
627
1014
|
spec "[!j2lno] parses form parameters when content type is 'application/x-www-form-urlencoded'." do
|
628
1015
|
qstr = "a=8&b=9"
|
629
1016
|
form = "x=1&y=2"
|
630
|
-
req = K8::
|
1017
|
+
req = K8::RackRequest.new(new_env('POST', '/', query: qstr, form: form))
|
631
1018
|
ok {req.params} == {"x"=>"1", "y"=>"2"}
|
632
1019
|
end
|
633
1020
|
|
634
|
-
spec "[!
|
1021
|
+
spec "[!z5w4k] raises error when content type is 'multipart/form-data' (because params_multipart() returns two values)." do
|
1022
|
+
env = new_env('POST', '/', form: "x=1")
|
1023
|
+
env['CONTENT_TYPE'] = "multipart/form-data"
|
1024
|
+
req = K8::RackRequest.new(env)
|
1025
|
+
pr = proc { req.params }
|
1026
|
+
ok {pr}.raise?(K8::PayloadParseError, "don't use `@req.params' for multipart data; use `@req.params_multipart' instead.")
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
spec "[!td6fw] raises error when content type is 'application/json' (because JSON data can contain non-string values)." do
|
1030
|
+
qstr = "a=8&b=9"
|
1031
|
+
json = '{"n":123}'
|
1032
|
+
req = K8::RackRequest.new(new_env('POST', '/', query: qstr, json: json))
|
1033
|
+
pr = proc { req.params }
|
1034
|
+
ok {pr}.raise?(K8::PayloadParseError, "use `@req.json' for JSON data instead of `@req.params'.")
|
1035
|
+
end
|
635
1036
|
|
636
1037
|
end
|
637
1038
|
|
@@ -639,7 +1040,7 @@ Oktest.scope do
|
|
639
1040
|
topic '#cookies' do
|
640
1041
|
|
641
1042
|
spec "[!c9pwr] parses cookie data and returns it as hash object." do
|
642
|
-
req = K8::
|
1043
|
+
req = K8::RackRequest.new(new_env('POST', '/', cookies: "aaa=homhom; bbb=madmad"))
|
643
1044
|
ok {req.cookies} == {"aaa"=>"homhom", "bbb"=>"madmad"}
|
644
1045
|
end
|
645
1046
|
|
@@ -650,7 +1051,7 @@ Oktest.scope do
|
|
650
1051
|
|
651
1052
|
spec "[!0jdal] removes uploaded files." do
|
652
1053
|
|multipart_env|
|
653
|
-
req = K8::
|
1054
|
+
req = K8::RackRequest.new(multipart_env)
|
654
1055
|
form, files = req.params_multipart
|
655
1056
|
ok {files.empty?} == false
|
656
1057
|
tmpfile1 = files['file1'].tmp_filepath
|
@@ -669,37 +1070,66 @@ Oktest.scope do
|
|
669
1070
|
end
|
670
1071
|
|
671
1072
|
|
672
|
-
topic K8::
|
673
|
-
end
|
1073
|
+
topic K8::RackResponse do
|
674
1074
|
|
675
1075
|
|
676
|
-
|
1076
|
+
topic '#initialize()' do
|
677
1077
|
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
K8.REQUEST_CLASS = Array
|
682
|
-
ok {K8::REQUEST_CLASS} == Array
|
683
|
-
ensure
|
684
|
-
K8.REQUEST_CLASS = original
|
1078
|
+
spec "[!ehdkl] default status code is 200." do
|
1079
|
+
resp = K8::RackResponse.new
|
1080
|
+
ok {resp.status_code} == 200
|
685
1081
|
end
|
1082
|
+
|
686
1083
|
end
|
687
1084
|
|
688
|
-
end
|
689
1085
|
|
1086
|
+
topic '#status_line' do
|
1087
|
+
|
1088
|
+
spec "[!apy81] returns status line such as '200 OK'." do
|
1089
|
+
resp = K8::RackResponse.new
|
1090
|
+
ok {resp.status_line} == "200 OK"
|
1091
|
+
resp.status_code = 302
|
1092
|
+
ok {resp.status_line} == "302 Found"
|
1093
|
+
resp.status_code = 404
|
1094
|
+
ok {resp.status_line} == "404 Not Found"
|
1095
|
+
resp.status_code = 500
|
1096
|
+
ok {resp.status_line} == "500 Internal Server Error"
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
|
1102
|
+
topic '#set_cookie()' do
|
1103
|
+
|
1104
|
+
spec "[!58tby] adds 'Set-Cookie' response header." do
|
1105
|
+
resp = K8::RackResponse.new
|
1106
|
+
resp.set_cookie("hom", "HOMHOM")
|
1107
|
+
ok {resp.headers['Set-Cookie']} == "hom=HOMHOM"
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
spec "[!u9w9l] supports multiple cookies." do
|
1111
|
+
resp = K8::RackResponse.new
|
1112
|
+
resp.set_cookie("foo", "FOO")
|
1113
|
+
resp.set_cookie("bar", "BAR")
|
1114
|
+
resp.set_cookie("baz", "BAZ")
|
1115
|
+
ok {resp.headers['Set-Cookie']} == "foo=FOO\nbar=BAR\nbaz=BAZ"
|
1116
|
+
end
|
690
1117
|
|
691
|
-
|
1118
|
+
spec "[!7otip] returns cookie string." do
|
1119
|
+
resp = K8::RackResponse.new
|
1120
|
+
ok {resp.set_cookie("foo", "FOO")} == "foo=FOO"
|
1121
|
+
ok {resp.set_cookie("bar", "BAR", path: '/', httponly: true)} == "bar=BAR; Path=/; HttpOnly"
|
1122
|
+
end
|
692
1123
|
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
ok {K8::RESPONSE_CLASS} == Hash
|
698
|
-
ensure
|
699
|
-
K8.RESPONSE_CLASS = original
|
1124
|
+
spec "[!oanme] converts Time object into HTTP timestamp string." do
|
1125
|
+
resp = K8::RackResponse.new
|
1126
|
+
resp.set_cookie("hom", "HOMHOM", expires: Time.utc(2001, 11, 30, 12, 34, 56))
|
1127
|
+
ok {resp.headers['Set-Cookie']} == "hom=HOMHOM; Expires=Fri, 30 Nov 2001 12:34:56 GMT"
|
700
1128
|
end
|
1129
|
+
|
701
1130
|
end
|
702
1131
|
|
1132
|
+
|
703
1133
|
end
|
704
1134
|
|
705
1135
|
|
@@ -707,29 +1137,20 @@ Oktest.scope do
|
|
707
1137
|
|
708
1138
|
fixture :action do
|
709
1139
|
env = new_env("GET", "/books")
|
710
|
-
TestBaseAction.new(K8::
|
1140
|
+
TestBaseAction.new(K8::RackRequest.new(env), K8::RackResponse.new())
|
711
1141
|
end
|
712
1142
|
|
713
1143
|
|
714
1144
|
topic '#initialize()' do
|
715
1145
|
|
716
1146
|
spec "[!uotpb] accepts request and response objects." do
|
717
|
-
req = K8::
|
718
|
-
resp = K8::
|
1147
|
+
req = K8::RackRequest.new(new_env("GET", "/books"))
|
1148
|
+
resp = K8::RackResponse.new()
|
719
1149
|
action = K8::BaseAction.new(req, resp)
|
720
1150
|
ok {action.instance_variable_get('@req')}.same?(req)
|
721
1151
|
ok {action.instance_variable_get('@resp')}.same?(resp)
|
722
1152
|
end
|
723
1153
|
|
724
|
-
spec "[!7sfyf] sets session object." do
|
725
|
-
d = {'a'=>1}
|
726
|
-
req = K8::Request.new(new_env("GET", "/books", env: {'rack.session'=>d}))
|
727
|
-
resp = K8::Response.new()
|
728
|
-
action = K8::BaseAction.new(req, resp)
|
729
|
-
ok {action.instance_variable_get('@sess')}.same?(d)
|
730
|
-
ok {action.sess}.same?(d)
|
731
|
-
end
|
732
|
-
|
733
1154
|
end
|
734
1155
|
|
735
1156
|
|
@@ -776,7 +1197,7 @@ Oktest.scope do
|
|
776
1197
|
mapping '/{code}', :GET=>:do_show, :PUT=>:do_update
|
777
1198
|
end
|
778
1199
|
args_list = []
|
779
|
-
cls.
|
1200
|
+
cls._mappings.each do |args|
|
780
1201
|
args_list << args
|
781
1202
|
end
|
782
1203
|
ok {args_list} == [
|
@@ -794,12 +1215,12 @@ Oktest.scope do
|
|
794
1215
|
infos = BooksAction._build_action_info('/api/books')
|
795
1216
|
#
|
796
1217
|
ok {infos[:do_index]}.is_a?(K8::ActionInfo)
|
797
|
-
ok {infos[:do_index].
|
798
|
-
ok {infos[:do_index].
|
1218
|
+
ok {infos[:do_index].meth} == :GET
|
1219
|
+
ok {infos[:do_index].path} == '/api/books/'
|
799
1220
|
#
|
800
1221
|
ok {infos[:do_update]}.is_a?(K8::ActionInfo)
|
801
|
-
ok {infos[:do_update].
|
802
|
-
ok {infos[:do_update].
|
1222
|
+
ok {infos[:do_update].meth} == :PUT
|
1223
|
+
ok {infos[:do_update].path(123)} == '/api/books/123'
|
803
1224
|
end
|
804
1225
|
|
805
1226
|
end
|
@@ -812,12 +1233,12 @@ Oktest.scope do
|
|
812
1233
|
cls = BooksAction
|
813
1234
|
#
|
814
1235
|
ok {cls[:do_create]}.is_a?(K8::ActionInfo)
|
815
|
-
ok {cls[:do_create].
|
816
|
-
ok {cls[:do_create].
|
1236
|
+
ok {cls[:do_create].meth} == :POST
|
1237
|
+
ok {cls[:do_create].path} == '/api/books/'
|
817
1238
|
#
|
818
1239
|
ok {cls[:do_show]}.is_a?(K8::ActionInfo)
|
819
|
-
ok {cls[:do_show].
|
820
|
-
ok {cls[:do_show].
|
1240
|
+
ok {cls[:do_show].meth} == :GET
|
1241
|
+
ok {cls[:do_show].path(123)} == '/api/books/123'
|
821
1242
|
end
|
822
1243
|
|
823
1244
|
spec "[!6g2iw] returns nil when not mounted yet." do
|
@@ -838,7 +1259,7 @@ Oktest.scope do
|
|
838
1259
|
|
839
1260
|
fixture :action_obj do
|
840
1261
|
env = new_env("GET", "/", env: {'rack.session'=>{}})
|
841
|
-
BooksAction.new(K8::
|
1262
|
+
BooksAction.new(K8::RackRequest.new(env), K8::RackResponse.new())
|
842
1263
|
end
|
843
1264
|
|
844
1265
|
|
@@ -873,6 +1294,20 @@ Oktest.scope do
|
|
873
1294
|
end
|
874
1295
|
|
875
1296
|
|
1297
|
+
topic '#initialize()' do
|
1298
|
+
|
1299
|
+
spec "[!7sfyf] sets session object." do
|
1300
|
+
d = {'a'=>1}
|
1301
|
+
req = K8::RackRequest.new(new_env("GET", "/books", env: {'rack.session'=>d}))
|
1302
|
+
resp = K8::RackResponse.new()
|
1303
|
+
action = K8::Action.new(req, resp)
|
1304
|
+
ok {action.instance_variable_get('@sess')}.same?(d)
|
1305
|
+
ok {action.sess}.same?(d)
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
end
|
1309
|
+
|
1310
|
+
|
876
1311
|
topic '#before_action()' do
|
877
1312
|
end
|
878
1313
|
|
@@ -895,7 +1330,7 @@ Oktest.scope do
|
|
895
1330
|
|
896
1331
|
spec "[!d5v0l] handles exception when handler method defined." do
|
897
1332
|
env = new_env("POST", "/", env: {'rack.session'=>{}})
|
898
|
-
action_obj = TestExceptionAction.new(K8::
|
1333
|
+
action_obj = TestExceptionAction.new(K8::RackRequest.new(env), K8::RackResponse.new())
|
899
1334
|
result = nil
|
900
1335
|
pr = proc { result = action_obj.handle_action(:do_create, []) }
|
901
1336
|
ok {pr}.raise?(ZeroDivisionError)
|
@@ -1167,13 +1602,13 @@ Oktest.scope do
|
|
1167
1602
|
|
1168
1603
|
fixture :action_obj do
|
1169
1604
|
env = new_env('GET', '/')
|
1170
|
-
action = K8::Action.new(K8::
|
1605
|
+
action = K8::Action.new(K8::RackRequest.new(env), K8::RackResponse.new)
|
1171
1606
|
end
|
1172
1607
|
|
1173
1608
|
spec "[!8chgu] returns false when requested with 'XMLHttpRequest'." do
|
1174
1609
|
headers = {'X-Requested-With'=>'XMLHttpRequest'}
|
1175
1610
|
env = new_env('GET', '/', headers: headers)
|
1176
|
-
action = K8::Action.new(K8::
|
1611
|
+
action = K8::Action.new(K8::RackRequest.new(env), K8::RackResponse.new)
|
1177
1612
|
action.instance_exec(self) do |_|
|
1178
1613
|
_.ok {csrf_protection_required?} == false
|
1179
1614
|
end
|
@@ -1182,7 +1617,7 @@ Oktest.scope do
|
|
1182
1617
|
spec "[!vwrqv] returns true when request method is one of POST, PUT, or DELETE." do
|
1183
1618
|
['POST', 'PUT', 'DELETE'].each do |meth|
|
1184
1619
|
env = new_env(meth, '/')
|
1185
|
-
action = K8::Action.new(K8::
|
1620
|
+
action = K8::Action.new(K8::RackRequest.new(env), K8::RackResponse.new)
|
1186
1621
|
action.instance_exec(self) do |_|
|
1187
1622
|
_.ok {csrf_protection_required?} == true
|
1188
1623
|
end
|
@@ -1192,7 +1627,7 @@ Oktest.scope do
|
|
1192
1627
|
spec "[!jfhla] returns true when request method is GET or HEAD." do
|
1193
1628
|
['GET', 'HEAD'].each do |meth|
|
1194
1629
|
env = new_env(meth, '/')
|
1195
|
-
action = K8::Action.new(K8::
|
1630
|
+
action = K8::Action.new(K8::RackRequest.new(env), K8::RackResponse.new)
|
1196
1631
|
action.instance_exec(self) do |_|
|
1197
1632
|
_.ok {csrf_protection_required?} == false
|
1198
1633
|
end
|
@@ -1208,7 +1643,7 @@ Oktest.scope do
|
|
1208
1643
|
headers = {'Cookie'=>"_csrf=abc123"}
|
1209
1644
|
form = {"_csrf"=>"abc123"}
|
1210
1645
|
env = new_env('POST', '/', form: form, headers: headers)
|
1211
|
-
action = K8::Action.new(K8::
|
1646
|
+
action = K8::Action.new(K8::RackRequest.new(env), K8::RackResponse.new)
|
1212
1647
|
action.instance_exec(self) do |_|
|
1213
1648
|
pr = proc { csrf_protection() }
|
1214
1649
|
_.ok {pr}.NOT.raise?
|
@@ -1219,7 +1654,7 @@ Oktest.scope do
|
|
1219
1654
|
headers = {'Cookie'=>"_csrf=abc123"}
|
1220
1655
|
form = {"_csrf"=>"abc999"}
|
1221
1656
|
env = new_env('POST', '/', form: form, headers: headers)
|
1222
|
-
action = K8::Action.new(K8::
|
1657
|
+
action = K8::Action.new(K8::RackRequest.new(env), K8::RackResponse.new)
|
1223
1658
|
action.instance_exec(self) do |_|
|
1224
1659
|
pr = proc { csrf_protection() }
|
1225
1660
|
_.ok {pr}.raise?(K8::HttpException, "invalid csrf token")
|
@@ -1260,7 +1695,7 @@ Oktest.scope do
|
|
1260
1695
|
|
1261
1696
|
spec "[!pal33] returns csrf token in request parameter." do
|
1262
1697
|
env = new_env("POST", "/", form: {"_csrf"=>"foobar999"})
|
1263
|
-
action_obj = K8::Action.new(K8::
|
1698
|
+
action_obj = K8::Action.new(K8::RackRequest.new(env), K8::RackResponse.new)
|
1264
1699
|
action_obj.instance_exec(self) do |_|
|
1265
1700
|
_.ok {csrf_get_param()} == "foobar999"
|
1266
1701
|
end
|
@@ -1467,36 +1902,36 @@ Oktest.scope do
|
|
1467
1902
|
spec "[!btt2g] returns ActionInfoN object when number of urlpath parameter <= 4." do
|
1468
1903
|
info = K8::ActionInfo.create('GET', '/books')
|
1469
1904
|
ok {info}.is_a?(K8::ActionInfo0)
|
1470
|
-
ok {info.
|
1471
|
-
ok {->{ info.
|
1905
|
+
ok {info.path} == '/books'
|
1906
|
+
ok {->{ info.path('a') }}.raise?(ArgumentError, /^wrong number of arguments \((1 for 0|given 1, expected 0)\)$/)
|
1472
1907
|
#
|
1473
1908
|
info = K8::ActionInfo.create('GET', '/books/{id}')
|
1474
1909
|
ok {info}.is_a?(K8::ActionInfo1)
|
1475
|
-
ok {info.
|
1476
|
-
ok {->{ info.
|
1910
|
+
ok {info.path('a')} == '/books/a'
|
1911
|
+
ok {->{ info.path() }}.raise?(ArgumentError, /^wrong number of arguments \((0 for 1|given 0, expected 1)\)$/)
|
1477
1912
|
#
|
1478
1913
|
info = K8::ActionInfo.create('GET', '/books/{id}/comments/{comment_id}')
|
1479
1914
|
ok {info}.is_a?(K8::ActionInfo2)
|
1480
|
-
ok {info.
|
1481
|
-
ok {->{info.
|
1915
|
+
ok {info.path('a', 'b')} == '/books/a/comments/b'
|
1916
|
+
ok {->{info.path('a')}}.raise?(ArgumentError, /^wrong number of arguments \((1 for 2|given 1, expected 2)\)$/)
|
1482
1917
|
#
|
1483
1918
|
info = K8::ActionInfo.create('GET', '/books/{id}/{title}/{code}')
|
1484
1919
|
ok {info}.is_a?(K8::ActionInfo3)
|
1485
|
-
ok {info.
|
1486
|
-
ok {->{info.
|
1920
|
+
ok {info.path('a', 'b', 'c')} == '/books/a/b/c'
|
1921
|
+
ok {->{info.path(1,2)}}.raise?(ArgumentError, /^wrong number of arguments \((2 for 3|given 2, expected 3)\)$/)
|
1487
1922
|
#
|
1488
1923
|
info = K8::ActionInfo.create('GET', '/books/{id}/{title}/{code}/{ref}')
|
1489
1924
|
ok {info}.is_a?(K8::ActionInfo4)
|
1490
|
-
ok {info.
|
1491
|
-
ok {->{info.
|
1925
|
+
ok {info.path('a', 'b', 'c', 'd')} == '/books/a/b/c/d'
|
1926
|
+
ok {->{info.path}}.raise?(ArgumentError, /^wrong number of arguments \((0 for 4|given 0, expected 4)\)$/)
|
1492
1927
|
end
|
1493
1928
|
|
1494
1929
|
spec "[!x5yx2] returns ActionInfo object when number of urlpath parameter > 4." do
|
1495
1930
|
info = K8::ActionInfo.create('GET', '/books/{id}/{title}/{code}/{ref}/{x}')
|
1496
1931
|
ok {info}.is_a?(K8::ActionInfo)
|
1497
|
-
ok {info.
|
1932
|
+
ok {info.path('a', 'b', 'c', 'd', 'e')} == "/books/a/b/c/d/e"
|
1498
1933
|
#
|
1499
|
-
ok {->{info.
|
1934
|
+
ok {->{info.path('a','b','c')}}.raise?(ArgumentError, "too few arguments")
|
1500
1935
|
end
|
1501
1936
|
|
1502
1937
|
end
|
@@ -1520,249 +1955,82 @@ Oktest.scope do
|
|
1520
1955
|
end
|
1521
1956
|
|
1522
1957
|
|
1523
|
-
topic K8::
|
1958
|
+
topic K8::ActionMapping do
|
1524
1959
|
|
1525
1960
|
|
1526
|
-
topic '#
|
1961
|
+
topic '#initialize()' do
|
1527
1962
|
|
1528
|
-
spec "[!
|
1529
|
-
K8::
|
1530
|
-
|
1531
|
-
|
1532
|
-
_.ok {@
|
1533
|
-
_.ok {@patterns[0][0]} == /_id\z/
|
1534
|
-
_.ok {@patterns[0][1]} == '\d+'
|
1535
|
-
_.ok {@patterns[0][2]}.is_a?(Proc)
|
1536
|
-
_.ok {@patterns[0][2].call("123")} == 123
|
1963
|
+
spec "[!buj0d] prepares LRU cache if cache size specified." do
|
1964
|
+
mapping = K8::ActionMapping.new([], urlpath_cache_size: 3)
|
1965
|
+
mapping.instance_exec(self) do |_|
|
1966
|
+
_.ok {@urlpath_cache_size} == 3
|
1967
|
+
_.ok {@urlpath_lru_cache} == {}
|
1537
1968
|
end
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
topic '#unregister()' do
|
1544
|
-
|
1545
|
-
spec "[!3gplv] deletes matched record." do
|
1546
|
-
K8::DefaultPatterns.new.instance_exec(self) do |_|
|
1547
|
-
register("id", '\d+') {|x| x.to_i }
|
1548
|
-
register(/_id\z/, '\d+') {|x| x.to_i }
|
1549
|
-
_.ok {@patterns.length} == 2
|
1550
|
-
unregister(/_id\z/)
|
1551
|
-
_.ok {@patterns.length} == 1
|
1552
|
-
_.ok {@patterns[0][0]} == "id"
|
1969
|
+
#
|
1970
|
+
mapping = K8::ActionMapping.new([], urlpath_cache_size: 0)
|
1971
|
+
mapping.instance_exec(self) do |_|
|
1972
|
+
_.ok {@urlpath_cache_size} == 0
|
1973
|
+
_.ok {@urlpath_lru_cache} == nil
|
1553
1974
|
end
|
1554
1975
|
end
|
1555
1976
|
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
register(/_id\z/, '\d+') {|x| x.to_i }
|
1565
|
-
_.ok {lookup("id")}.is_a?(Array).length(2)
|
1566
|
-
_.ok {lookup("id")[0]} == '\d+'
|
1567
|
-
_.ok {lookup("id")[1].call("123")} == 123
|
1568
|
-
_.ok {lookup("book_id")[0]} == '\d+'
|
1569
|
-
_.ok {lookup("book_id")[1]}.is_a?(Proc)
|
1570
|
-
_.ok {lookup("book_id")[1].call("123")} == 123
|
1977
|
+
spec "[!wsz8g] compiles urlpath mapping passed." do
|
1978
|
+
mapping = K8::ActionMapping.new([
|
1979
|
+
['/api/books', BooksAction],
|
1980
|
+
])
|
1981
|
+
ok {mapping.urlpath_rexp} == %r'\A/api/books/\d+(?:(\z)|/edit(\z))\z'
|
1982
|
+
mapping.instance_exec(self) do |_|
|
1983
|
+
_.ok {@fixed_endpoints.keys} == ['/api/books/', '/api/books/new']
|
1984
|
+
_.ok {@variable_endpoints.map{|x| x[0]}} == ['/api/books/{id}', '/api/books/{id}/edit']
|
1571
1985
|
end
|
1572
1986
|
end
|
1573
1987
|
|
1574
|
-
spec "[!
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
_.ok {
|
1988
|
+
spec "[!34o67] keyword arg 'enable_urlpath_param_range' controls to generate range object or not." do
|
1989
|
+
arr = [['/books', BooksAction]]
|
1990
|
+
#
|
1991
|
+
mapping1 = K8::ActionMapping.new(arr, enable_urlpath_param_range: true)
|
1992
|
+
mapping1.instance_exec(self) do |_|
|
1993
|
+
tuple = @variable_endpoints.find {|a| a[0] == '/books/{id}' }
|
1994
|
+
_.ok {tuple[-1]} == (7..-1)
|
1995
|
+
end
|
1996
|
+
#
|
1997
|
+
mapping2 = K8::ActionMapping.new(arr, enable_urlpath_param_range: false)
|
1998
|
+
mapping2.instance_exec(self) do |_|
|
1999
|
+
tuple = @variable_endpoints.find {|a| a[0] == '/books/{id}' }
|
2000
|
+
_.ok {tuple[-1]} == nil
|
1581
2001
|
end
|
1582
2002
|
end
|
1583
2003
|
|
1584
2004
|
end
|
1585
2005
|
|
1586
|
-
end
|
1587
2006
|
|
2007
|
+
topic '#build()' do
|
1588
2008
|
|
1589
|
-
|
1590
|
-
|
1591
|
-
spec "[!i51id] registers '\d+' as default pattern of param 'id' or /_id\z/." do
|
1592
|
-
pat, proc_ = K8::DEFAULT_PATTERNS.lookup('id')
|
1593
|
-
ok {pat} == '\d+'
|
1594
|
-
ok {proc_.call("123")} == 123
|
1595
|
-
pat, proc_ = K8::DEFAULT_PATTERNS.lookup('book_id')
|
1596
|
-
ok {pat} == '\d+'
|
1597
|
-
ok {proc_.call("123")} == 123
|
1598
|
-
end
|
1599
|
-
|
1600
|
-
spec "[!2g08b] registers '(?:\.\w+)?' as default pattern of param 'ext'." do
|
1601
|
-
pat, proc_ = K8::DEFAULT_PATTERNS.lookup('ext')
|
1602
|
-
ok {pat} == '(?:\.\w+)?'
|
1603
|
-
ok {proc_} == nil
|
1604
|
-
end
|
1605
|
-
|
1606
|
-
spec "[!8x5mp] registers '\d\d\d\d-\d\d-\d\d' as default pattern of param 'date' or /_date\z/." do
|
1607
|
-
pat, proc_ = K8::DEFAULT_PATTERNS.lookup('date')
|
1608
|
-
ok {pat} == '\d\d\d\d-\d\d-\d\d'
|
1609
|
-
ok {proc_.call("2014-12-24")} == Date.new(2014, 12, 24)
|
1610
|
-
pat, proc_ = K8::DEFAULT_PATTERNS.lookup('birth_date')
|
1611
|
-
ok {pat} == '\d\d\d\d-\d\d-\d\d'
|
1612
|
-
ok {proc_.call("2015-02-14")} == Date.new(2015, 2, 14)
|
1613
|
-
end
|
1614
|
-
|
1615
|
-
spec "[!wg9vl] raises 404 error when invalid date (such as 2012-02-30)." do
|
1616
|
-
pat, proc_ = K8::DEFAULT_PATTERNS.lookup('date')
|
1617
|
-
pr = proc { proc_.call('2012-02-30') }
|
1618
|
-
ok {pr}.raise?(K8::HttpException, "2012-02-30: invalid date.")
|
1619
|
-
ok {pr.exception.status_code} == 404
|
1620
|
-
end
|
1621
|
-
|
1622
|
-
end
|
1623
|
-
|
1624
|
-
|
1625
|
-
topic K8::ActionMethodMapping do
|
1626
|
-
|
1627
|
-
fixture :mapping do
|
1628
|
-
mapping = K8::ActionMethodMapping.new
|
1629
|
-
mapping.map '/', :GET=>:do_index, :POST=>:do_create
|
1630
|
-
mapping.map '/{id:\d+}', :GET=>:do_show, :PUT=>:do_update
|
1631
|
-
mapping
|
1632
|
-
end
|
1633
|
-
|
1634
|
-
fixture :methods1 do
|
1635
|
-
{:GET=>:do_index, :POST=>:do_create}
|
1636
|
-
end
|
1637
|
-
|
1638
|
-
fixture :methods2 do
|
1639
|
-
{:GET=>:do_show, :PUT=>:do_update}
|
1640
|
-
end
|
1641
|
-
|
1642
|
-
|
1643
|
-
topic '#map()' do
|
1644
|
-
|
1645
|
-
spec "[!s7cs9] maps urlpath and methods." do
|
1646
|
-
|mapping|
|
1647
|
-
arr = mapping.instance_variable_get('@mappings')
|
1648
|
-
ok {arr}.is_a?(Array)
|
1649
|
-
ok {arr.length} == 2
|
1650
|
-
ok {arr[0]} == ['/', {:GET=>:do_index, :POST=>:do_create}]
|
1651
|
-
ok {arr[1]} == ['/{id:\d+}', {:GET=>:do_show, :PUT=>:do_update}]
|
1652
|
-
end
|
1653
|
-
|
1654
|
-
spec "[!o6cxr] returns self." do
|
1655
|
-
|mapping|
|
1656
|
-
ok {mapping.map '/new', :GET=>:do_new}.same?(mapping)
|
1657
|
-
end
|
1658
|
-
|
1659
|
-
end
|
1660
|
-
|
1661
|
-
|
1662
|
-
topic '#each()' do
|
1663
|
-
|
1664
|
-
spec "[!62y5q] yields each urlpath pattern and action methods." do
|
1665
|
-
|mapping, methods1, methods2|
|
1666
|
-
arr = []
|
1667
|
-
mapping.each do |urlpath_pat, action_methods|
|
1668
|
-
arr << [urlpath_pat, action_methods]
|
1669
|
-
end
|
1670
|
-
ok {arr} == [
|
1671
|
-
['/', methods1],
|
1672
|
-
['/{id:\d+}', methods2],
|
1673
|
-
]
|
1674
|
-
end
|
1675
|
-
|
1676
|
-
end
|
1677
|
-
|
1678
|
-
|
1679
|
-
end
|
1680
|
-
|
1681
|
-
|
1682
|
-
topic K8::ActionMapping do
|
1683
|
-
|
1684
|
-
|
1685
|
-
topic '#initialize()' do
|
1686
|
-
|
1687
|
-
spec "[!buj0d] prepares LRU cache if cache size specified." do
|
1688
|
-
mapping = K8::ActionMapping.new([], urlpath_cache_size: 3)
|
1689
|
-
mapping.instance_exec(self) do |_|
|
1690
|
-
_.ok {@urlpath_cache_size} == 3
|
1691
|
-
_.ok {@urlpath_lru_cache} == {}
|
1692
|
-
end
|
1693
|
-
#
|
1694
|
-
mapping = K8::ActionMapping.new([], urlpath_cache_size: 0)
|
1695
|
-
mapping.instance_exec(self) do |_|
|
1696
|
-
_.ok {@urlpath_cache_size} == 0
|
1697
|
-
_.ok {@urlpath_lru_cache} == nil
|
1698
|
-
end
|
1699
|
-
end
|
1700
|
-
|
1701
|
-
spec "[!wsz8g] compiles urlpath mapping passed." do
|
1702
|
-
mapping = K8::ActionMapping.new([
|
1703
|
-
['/api/books', BooksAction],
|
1704
|
-
])
|
1705
|
-
mapping.instance_exec(self) do |_|
|
1706
|
-
_.ok {@urlpath_rexp} == %r'\A/api/books(?:/\d+(\z)|/\d+/edit(\z))\z'
|
1707
|
-
_.ok {@fixed_endpoints.keys} == ['/api/books/', '/api/books/new']
|
1708
|
-
_.ok {@variable_endpoints.map{|x| x[0]}} == ['/api/books/{id}', '/api/books/{id}/edit']
|
1709
|
-
end
|
1710
|
-
end
|
1711
|
-
|
1712
|
-
spec "[!34o67] keyword arg 'enable_urlpath_param_range' controls to generate range object or not." do
|
1713
|
-
arr = [['/books', BooksAction]]
|
1714
|
-
#
|
1715
|
-
mapping1 = K8::ActionMapping.new(arr, enable_urlpath_param_range: true)
|
1716
|
-
mapping1.instance_exec(self) do |_|
|
1717
|
-
tuple = @variable_endpoints.find {|a| a[0] == '/books/{id}' }
|
1718
|
-
_.ok {tuple[-1]} == (7..-1)
|
1719
|
-
end
|
1720
|
-
#
|
1721
|
-
mapping2 = K8::ActionMapping.new(arr, enable_urlpath_param_range: false)
|
1722
|
-
mapping2.instance_exec(self) do |_|
|
1723
|
-
tuple = @variable_endpoints.find {|a| a[0] == '/books/{id}' }
|
1724
|
-
_.ok {tuple[-1]} == nil
|
1725
|
-
end
|
1726
|
-
end
|
1727
|
-
|
1728
|
-
end
|
1729
|
-
|
1730
|
-
|
1731
|
-
topic '#compile()' do
|
1732
|
-
|
1733
|
-
fixture :proc1 do
|
1734
|
-
proc {|x| x.to_i }
|
1735
|
-
end
|
1736
|
-
|
1737
|
-
fixture :proc2 do
|
1738
|
-
proc {|x| x.to_i }
|
2009
|
+
fixture :proc_int do
|
2010
|
+
K8::ActionMapping::URLPATH_PARAM_TYPES[0][3]
|
1739
2011
|
end
|
1740
2012
|
|
1741
2013
|
fixture :mapping do
|
1742
|
-
|proc1, proc2|
|
1743
|
-
dp = K8::DefaultPatterns.new
|
1744
|
-
dp.register('id', '\d+', &proc1)
|
1745
|
-
dp.register(/_id\z/, '\d+', &proc2)
|
1746
2014
|
K8::ActionMapping.new([
|
1747
2015
|
['/api', [
|
1748
2016
|
['/books', BooksAction],
|
1749
2017
|
['/books/{book_id}', BookCommentsAction],
|
1750
2018
|
]],
|
1751
|
-
]
|
2019
|
+
])
|
1752
2020
|
end
|
1753
2021
|
|
1754
2022
|
spec "[!6f3vl] compiles urlpath mapping." do
|
1755
2023
|
|mapping|
|
1756
|
-
mapping.
|
1757
|
-
|
1758
|
-
_.ok {@urlpath_rexp} == Regexp.compile('
|
2024
|
+
ok {mapping.urlpath_rexp}.is_a?(Regexp)
|
2025
|
+
ok {mapping.urlpath_rexp} == Regexp.compile('
|
1759
2026
|
\A/api
|
1760
2027
|
(?: /books
|
1761
|
-
|
2028
|
+
/\d+ (?: (\z) | /edit(\z) )
|
1762
2029
|
| /books/\d+
|
1763
2030
|
(?: /comments(\z) | /comments/\d+(\z) )
|
1764
2031
|
)
|
1765
2032
|
\z'.gsub(/\s/, ''))
|
2033
|
+
mapping.instance_exec(self) do |_|
|
1766
2034
|
_.ok {@fixed_endpoints.keys} == ["/api/books/", "/api/books/new"]
|
1767
2035
|
_.ok {@variable_endpoints.map{|x| x[0..2] }} == [
|
1768
2036
|
["/api/books/{id}", BooksAction, {:GET=>:do_show, :PUT=>:do_update, :DELETE=>:do_delete}],
|
@@ -1774,76 +2042,76 @@ Oktest.scope do
|
|
1774
2042
|
end
|
1775
2043
|
|
1776
2044
|
spec "[!w45ad] can compile nested array." do
|
1777
|
-
|mapping,
|
1778
|
-
mapping.
|
1779
|
-
_.ok {@urlpath_rexp} == Regexp.compile('
|
2045
|
+
|mapping, proc_int|
|
2046
|
+
ok {mapping.urlpath_rexp} == Regexp.compile('
|
1780
2047
|
\A /api
|
1781
2048
|
(?: /books
|
1782
|
-
|
2049
|
+
/\d+ (?: (\z) | /edit(\z) )
|
1783
2050
|
| /books/\d+
|
1784
2051
|
(?: /comments(\z) | /comments/\d+(\z) )
|
1785
2052
|
)
|
1786
2053
|
\z'.gsub(/\s/, ''))
|
2054
|
+
mapping.instance_exec(self) do |_|
|
1787
2055
|
_.ok {@variable_endpoints} == [
|
1788
2056
|
["/api/books/{id}",
|
1789
2057
|
BooksAction,
|
1790
2058
|
{:GET=>:do_show, :PUT=>:do_update, :DELETE=>:do_delete},
|
1791
2059
|
/\A\/api\/books\/(\d+)\z/,
|
1792
|
-
["id"], [
|
2060
|
+
["id"], [proc_int], (11..-1),
|
1793
2061
|
],
|
1794
2062
|
["/api/books/{id}/edit",
|
1795
2063
|
BooksAction,
|
1796
2064
|
{:GET=>:do_edit},
|
1797
2065
|
/\A\/api\/books\/(\d+)\/edit\z/,
|
1798
|
-
["id"], [
|
2066
|
+
["id"], [proc_int], (11..-6),
|
1799
2067
|
],
|
1800
2068
|
["/api/books/{book_id}/comments",
|
1801
2069
|
BookCommentsAction,
|
1802
2070
|
{:GET=>:do_comments},
|
1803
2071
|
/\A\/api\/books\/(\d+)\/comments\z/,
|
1804
|
-
["book_id"], [
|
2072
|
+
["book_id"], [proc_int], (11..-10),
|
1805
2073
|
],
|
1806
2074
|
["/api/books/{book_id}/comments/{comment_id}",
|
1807
2075
|
BookCommentsAction,
|
1808
2076
|
{:GET=>:do_comment},
|
1809
2077
|
/\A\/api\/books\/(\d+)\/comments\/(\d+)\z/,
|
1810
|
-
["book_id", "comment_id"], [
|
2078
|
+
["book_id", "comment_id"], [proc_int, proc_int], nil,
|
1811
2079
|
],
|
1812
2080
|
]
|
1813
2081
|
_.ok {@fixed_endpoints} == {
|
1814
|
-
"/api/books/" =>["/api/books/", BooksAction, {:GET=>:do_index, :POST=>:do_create}],
|
1815
|
-
"/api/books/new"=>["/api/books/new", BooksAction, {:GET=>:do_new}],
|
2082
|
+
"/api/books/" =>["/api/books/", BooksAction, {:GET=>:do_index, :POST=>:do_create}, []],
|
2083
|
+
"/api/books/new"=>["/api/books/new", BooksAction, {:GET=>:do_new}, []],
|
1816
2084
|
}
|
1817
2085
|
end
|
1818
2086
|
end
|
1819
2087
|
|
1820
2088
|
spec "[!z2iax] classifies urlpath contains any parameter as variable one." do
|
1821
|
-
|mapping,
|
2089
|
+
|mapping, proc_int|
|
1822
2090
|
mapping.instance_exec(self) do |_|
|
1823
2091
|
_.ok {@variable_endpoints} == [
|
1824
2092
|
["/api/books/{id}",
|
1825
2093
|
BooksAction,
|
1826
2094
|
{:GET=>:do_show, :PUT=>:do_update, :DELETE=>:do_delete},
|
1827
2095
|
/\A\/api\/books\/(\d+)\z/,
|
1828
|
-
["id"], [
|
2096
|
+
["id"], [proc_int], (11..-1),
|
1829
2097
|
],
|
1830
2098
|
["/api/books/{id}/edit",
|
1831
2099
|
BooksAction,
|
1832
2100
|
{:GET=>:do_edit},
|
1833
2101
|
/\A\/api\/books\/(\d+)\/edit\z/,
|
1834
|
-
["id"], [
|
2102
|
+
["id"], [proc_int], (11..-6),
|
1835
2103
|
],
|
1836
2104
|
["/api/books/{book_id}/comments",
|
1837
2105
|
BookCommentsAction,
|
1838
2106
|
{:GET=>:do_comments},
|
1839
2107
|
/\A\/api\/books\/(\d+)\/comments\z/,
|
1840
|
-
["book_id"], [
|
2108
|
+
["book_id"], [proc_int], (11..-10),
|
1841
2109
|
],
|
1842
2110
|
["/api/books/{book_id}/comments/{comment_id}",
|
1843
2111
|
BookCommentsAction,
|
1844
2112
|
{:GET=>:do_comment},
|
1845
2113
|
/\A\/api\/books\/(\d+)\/comments\/(\d+)\z/,
|
1846
|
-
["book_id", "comment_id"], [
|
2114
|
+
["book_id", "comment_id"], [proc_int, proc_int], nil,
|
1847
2115
|
],
|
1848
2116
|
]
|
1849
2117
|
end
|
@@ -1853,8 +2121,8 @@ Oktest.scope do
|
|
1853
2121
|
|mapping|
|
1854
2122
|
mapping.instance_exec(self) do |_|
|
1855
2123
|
_.ok {@fixed_endpoints} == {
|
1856
|
-
"/api/books/" => ["/api/books/", BooksAction, {:GET=>:do_index, :POST=>:do_create}],
|
1857
|
-
"/api/books/new" => ["/api/books/new", BooksAction, {:GET=>:do_new}],
|
2124
|
+
"/api/books/" => ["/api/books/", BooksAction, {:GET=>:do_index, :POST=>:do_create}, []],
|
2125
|
+
"/api/books/new" => ["/api/books/new", BooksAction, {:GET=>:do_new}, []],
|
1858
2126
|
}
|
1859
2127
|
end
|
1860
2128
|
end
|
@@ -1878,56 +2146,55 @@ Oktest.scope do
|
|
1878
2146
|
])
|
1879
2147
|
#
|
1880
2148
|
ok {Ex_6xwhq[:do_create]} != nil
|
1881
|
-
ok {Ex_6xwhq[:do_create].
|
1882
|
-
ok {Ex_6xwhq[:do_create].
|
2149
|
+
ok {Ex_6xwhq[:do_create].meth} == :POST
|
2150
|
+
ok {Ex_6xwhq[:do_create].path} == '/test/example4'
|
1883
2151
|
ok {Ex_6xwhq[:do_update]} != nil
|
1884
|
-
ok {Ex_6xwhq[:do_update].
|
1885
|
-
ok {Ex_6xwhq[:do_update].
|
2152
|
+
ok {Ex_6xwhq[:do_update].meth} == :PUT
|
2153
|
+
ok {Ex_6xwhq[:do_update].path(123)} == '/test/example4/123'
|
1886
2154
|
end
|
1887
2155
|
|
1888
2156
|
spec "[!wd2eb] accepts subclass of Action class." do
|
1889
|
-
|
1890
|
-
_, proc2 = K8::DEFAULT_PATTERNS.lookup('book_id')
|
2157
|
+
proc_int = K8::ActionMapping::URLPATH_PARAM_TYPES[0][3]
|
1891
2158
|
mapping = K8::ActionMapping.new([
|
1892
2159
|
['/api/books', BooksAction],
|
1893
2160
|
['/api/books/{book_id}', BookCommentsAction],
|
1894
2161
|
])
|
1895
|
-
mapping.
|
1896
|
-
_.ok {@urlpath_rexp} == Regexp.compile('
|
2162
|
+
ok {mapping.urlpath_rexp} == Regexp.compile('
|
1897
2163
|
\A (?: /api/books
|
1898
|
-
|
2164
|
+
/\d+ (?: (\z) | /edit(\z) )
|
1899
2165
|
| /api/books/\d+
|
1900
2166
|
(?: /comments(\z) | /comments/\d+(\z) )
|
1901
2167
|
)
|
1902
2168
|
\z'.gsub(/\s/, ''))
|
2169
|
+
mapping.instance_exec(self) do |_|
|
1903
2170
|
_.ok {@fixed_endpoints} == {
|
1904
|
-
"/api/books/" =>["/api/books/", BooksAction, {:GET=>:do_index, :POST=>:do_create}],
|
1905
|
-
"/api/books/new"=>["/api/books/new", BooksAction, {:GET=>:do_new}],
|
2171
|
+
"/api/books/" =>["/api/books/", BooksAction, {:GET=>:do_index, :POST=>:do_create}, []],
|
2172
|
+
"/api/books/new"=>["/api/books/new", BooksAction, {:GET=>:do_new}, []],
|
1906
2173
|
}
|
1907
2174
|
_.ok {@variable_endpoints} == [
|
1908
2175
|
["/api/books/{id}",
|
1909
2176
|
BooksAction,
|
1910
2177
|
{:GET=>:do_show, :PUT=>:do_update, :DELETE=>:do_delete},
|
1911
2178
|
/\A\/api\/books\/(\d+)\z/,
|
1912
|
-
["id"], [
|
2179
|
+
["id"], [proc_int], (11..-1),
|
1913
2180
|
],
|
1914
2181
|
["/api/books/{id}/edit",
|
1915
2182
|
BooksAction,
|
1916
2183
|
{:GET=>:do_edit},
|
1917
2184
|
/\A\/api\/books\/(\d+)\/edit\z/,
|
1918
|
-
["id"], [
|
2185
|
+
["id"], [proc_int], (11..-6),
|
1919
2186
|
],
|
1920
2187
|
["/api/books/{book_id}/comments",
|
1921
2188
|
BookCommentsAction,
|
1922
2189
|
{:GET=>:do_comments},
|
1923
2190
|
/\A\/api\/books\/(\d+)\/comments\z/,
|
1924
|
-
["book_id"], [
|
2191
|
+
["book_id"], [proc_int], (11..-10),
|
1925
2192
|
],
|
1926
2193
|
["/api/books/{book_id}/comments/{comment_id}",
|
1927
2194
|
BookCommentsAction,
|
1928
2195
|
{:GET=>:do_comment},
|
1929
2196
|
/\A\/api\/books\/(\d+)\/comments\/(\d+)\z/,
|
1930
|
-
["book_id", "comment_id"], [
|
2197
|
+
["book_id", "comment_id"], [proc_int, proc_int], nil,
|
1931
2198
|
],
|
1932
2199
|
]
|
1933
2200
|
end
|
@@ -1962,17 +2229,16 @@ Oktest.scope do
|
|
1962
2229
|
File.open(filename, 'w') {|f| f << content }
|
1963
2230
|
at_end { File.unlink filename; Dir.rmdir dirname }
|
1964
2231
|
#
|
1965
|
-
|
1966
|
-
_, proc2 = K8::DEFAULT_PATTERNS.lookup('book_id')
|
2232
|
+
proc_int = K8::ActionMapping::URLPATH_PARAM_TYPES[0][3]
|
1967
2233
|
mapping = K8::ActionMapping.new([
|
1968
2234
|
['/api/example', './test_l2kz5/sample:Ex_l2kz5::Example_l2kz5'],
|
1969
2235
|
])
|
1970
2236
|
mapping.instance_exec(self) do |_|
|
1971
2237
|
_.ok {@fixed_endpoints} == {
|
1972
|
-
"/api/example"=>["/api/example", Ex_l2kz5::Example_l2kz5, {:GET=>:do_index}],
|
2238
|
+
"/api/example"=>["/api/example", Ex_l2kz5::Example_l2kz5, {:GET=>:do_index}, []],
|
1973
2239
|
}
|
1974
2240
|
_.ok {@variable_endpoints} == [
|
1975
|
-
["/api/example/{id}", Ex_l2kz5::Example_l2kz5, {:GET=>:do_show}, /\A\/api\/example\/(\d+)\z/, ["id"], [
|
2241
|
+
["/api/example/{id}", Ex_l2kz5::Example_l2kz5, {:GET=>:do_show}, /\A\/api\/example\/(\d+)\z/, ["id"], [proc_int], (13..-1)],
|
1976
2242
|
]
|
1977
2243
|
end
|
1978
2244
|
end
|
@@ -2000,17 +2266,17 @@ Oktest.scope do
|
|
2000
2266
|
['/books/{book_id}', BookCommentsAction],
|
2001
2267
|
]],
|
2002
2268
|
])
|
2003
|
-
mapping.
|
2004
|
-
_.ok {@urlpath_rexp} == Regexp.compile('
|
2269
|
+
ok {mapping.urlpath_rexp} == Regexp.compile('
|
2005
2270
|
\A /api
|
2006
2271
|
(?: /books
|
2007
|
-
|
2272
|
+
/\d+(?:(\z)|/edit(\z))
|
2008
2273
|
| /books/\d+
|
2009
2274
|
(?:/comments(\z)|/comments/\d+(\z))
|
2010
2275
|
)
|
2011
2276
|
\z'.gsub(/\s+/, ''))
|
2012
|
-
|
2013
|
-
_.ok {@fixed_endpoints['/api/samples/
|
2277
|
+
mapping.instance_exec(self) do |_|
|
2278
|
+
_.ok {@fixed_endpoints['/api/samples/']} == ["/api/samples/", klass, {:GET=>:do_index}, []]
|
2279
|
+
_.ok {@fixed_endpoints['/api/samples/new']} == ["/api/samples/new", klass, {:GET=>:do_new}, []]
|
2014
2280
|
end
|
2015
2281
|
end
|
2016
2282
|
|
@@ -2024,90 +2290,124 @@ Oktest.scope do
|
|
2024
2290
|
['/test', klass],
|
2025
2291
|
]],
|
2026
2292
|
])
|
2027
|
-
mapping.
|
2028
|
-
|
2029
|
-
|
2293
|
+
ok {mapping.urlpath_rexp} == %r'\A/api/test/\d+(\z)\z'
|
2294
|
+
end
|
2295
|
+
|
2296
|
+
spec "[!abj34] ex: (?:/\d+(\z)|/\d+/edit(\z)) -> /d+(?:(\z)|/edit(\z))" do
|
2297
|
+
klass = Class.new(K8::Action) do
|
2298
|
+
mapping '/' , :GET=>:do_index
|
2299
|
+
mapping '/{id}' , :GET=>:do_show
|
2300
|
+
mapping '/{id}/edit' , :GET=>:do_edit
|
2301
|
+
mapping '/{id}/comments', :GET=>:do_comments
|
2302
|
+
def do_index; end
|
2303
|
+
def do_show(id); end
|
2304
|
+
def do_edit(id); end
|
2305
|
+
def do_comments(id); end
|
2030
2306
|
end
|
2307
|
+
mapping = K8::ActionMapping.new([
|
2308
|
+
['/api', [
|
2309
|
+
['/books', BooksAction],
|
2310
|
+
['/samples', klass],
|
2311
|
+
]],
|
2312
|
+
])
|
2313
|
+
ok {mapping.urlpath_rexp} == Regexp.compile('
|
2314
|
+
\A /api
|
2315
|
+
(?: /books
|
2316
|
+
/\d+(?:(\z)|/edit(\z))
|
2317
|
+
| /samples
|
2318
|
+
/\d+(?:(\z)|/edit(\z)|/comments(\z))
|
2319
|
+
)
|
2320
|
+
\z'.gsub(/\s+/, ''))
|
2031
2321
|
end
|
2032
2322
|
|
2033
|
-
|
2323
|
+
spec "[!m51yy] regards '.*' at end of urlpath pattern as extension." do
|
2324
|
+
cls = Class.new(K8::Action) do
|
2325
|
+
mapping '.*', :GET=>:do_index
|
2326
|
+
mapping '/{id}.*', :GET=>:do_show
|
2327
|
+
def do_index; end
|
2328
|
+
def do_show(id); end
|
2329
|
+
end
|
2330
|
+
am = K8::ActionMapping.new([
|
2331
|
+
['/api', [
|
2332
|
+
['/foo', cls],
|
2333
|
+
]],
|
2334
|
+
])
|
2335
|
+
ok {am.urlpath_rexp} == Regexp.compile('
|
2336
|
+
\A /api
|
2337
|
+
/foo (?: (?:\.\w+)? (\z)
|
2338
|
+
| /\d+ (?:\.\w+)? (\z)
|
2339
|
+
)
|
2340
|
+
\z'.gsub(/\s+/, ''))
|
2341
|
+
end
|
2034
2342
|
|
2343
|
+
end
|
2035
2344
|
|
2036
|
-
topic '#lookup()' do
|
2037
2345
|
|
2038
|
-
|
2039
|
-
proc {|x| x.to_i }
|
2040
|
-
end
|
2346
|
+
topic '#find()' do
|
2041
2347
|
|
2042
2348
|
fixture :mapping do
|
2043
|
-
|proc1|
|
2044
|
-
dp = K8::DefaultPatterns.new
|
2045
|
-
dp.register('id', '\d+', &proc1)
|
2046
|
-
dp.register(/_id$/, '\d+', &proc1)
|
2047
2349
|
K8::ActionMapping.new([
|
2048
2350
|
['/api', [
|
2049
2351
|
['/books', BooksAction],
|
2050
2352
|
['/books/{book_id}', BookCommentsAction],
|
2051
2353
|
]],
|
2052
|
-
],
|
2354
|
+
], urlpath_cache_size: 3)
|
2053
2355
|
end
|
2054
2356
|
|
2055
|
-
spec "[!jyxlm] returns action class
|
2357
|
+
spec "[!jyxlm] returns action class, action methods and urlpath param args." do
|
2056
2358
|
|mapping|
|
2057
|
-
tuple = mapping.
|
2058
|
-
ok {tuple} == [BooksAction, {:GET=>:do_show, :PUT=>:do_update, :DELETE=>:do_delete}, [
|
2059
|
-
tuple = mapping.
|
2060
|
-
ok {tuple} == [BookCommentsAction, {:GET=>:do_comment}, [
|
2359
|
+
tuple = mapping.find('/api/books/123')
|
2360
|
+
ok {tuple} == [BooksAction, {:GET=>:do_show, :PUT=>:do_update, :DELETE=>:do_delete}, [123]]
|
2361
|
+
tuple = mapping.find('/api/books/123/comments/999')
|
2362
|
+
ok {tuple} == [BookCommentsAction, {:GET=>:do_comment}, [123, 999]]
|
2061
2363
|
end
|
2062
2364
|
|
2063
2365
|
spec "[!j34yh] finds from fixed urlpaths at first." do
|
2064
2366
|
|mapping|
|
2065
2367
|
mapping.instance_exec(self) do |_|
|
2066
|
-
_.ok {
|
2368
|
+
_.ok {find('/books')} == nil
|
2067
2369
|
tuple = @fixed_endpoints['/api/books/']
|
2068
2370
|
_.ok {tuple} != nil
|
2069
2371
|
@fixed_endpoints['/books'] = tuple
|
2070
|
-
expected = [BooksAction, {:GET=>:do_index, :POST=>:do_create}, []
|
2071
|
-
_.ok {
|
2072
|
-
_.ok {
|
2073
|
-
_.ok {
|
2372
|
+
expected = [BooksAction, {:GET=>:do_index, :POST=>:do_create}, []]
|
2373
|
+
_.ok {find('/books')} != nil
|
2374
|
+
_.ok {find('/books')} == expected
|
2375
|
+
_.ok {find('/api/books/')} == expected
|
2074
2376
|
end
|
2075
2377
|
end
|
2076
2378
|
|
2077
2379
|
spec "[!95q61] finds from variable urlpath patterns when not found in fixed ones." do
|
2078
2380
|
|mapping|
|
2079
|
-
ok {mapping.
|
2381
|
+
ok {mapping.find('/api/books/123')} == \
|
2080
2382
|
[
|
2081
2383
|
BooksAction,
|
2082
2384
|
{:GET=>:do_show, :PUT=>:do_update, :DELETE=>:do_delete},
|
2083
|
-
["id"],
|
2084
2385
|
[123],
|
2085
2386
|
]
|
2086
|
-
ok {mapping.
|
2387
|
+
ok {mapping.find('/api/books/123/comments/999')} == \
|
2087
2388
|
[
|
2088
2389
|
BookCommentsAction,
|
2089
2390
|
{:GET=>:do_comment},
|
2090
|
-
["book_id", "comment_id"],
|
2091
2391
|
[123, 999],
|
2092
2392
|
]
|
2093
2393
|
end
|
2094
2394
|
|
2095
2395
|
spec "[!sos5i] returns nil when request path not matched to urlpath patterns." do
|
2096
2396
|
|mapping|
|
2097
|
-
ok {mapping.
|
2397
|
+
ok {mapping.find('/api/booking')} == nil
|
2098
2398
|
end
|
2099
2399
|
|
2100
2400
|
spec "[!1k1k5] converts urlpath param values by converter procs." do
|
2101
2401
|
|mapping|
|
2102
|
-
tuple = mapping.
|
2103
|
-
ok {tuple[
|
2104
|
-
tuple = mapping.
|
2105
|
-
ok {tuple[
|
2402
|
+
tuple = mapping.find('/api/books/123')
|
2403
|
+
ok {tuple[-1]} == [123] # id
|
2404
|
+
tuple = mapping.find('/api/books/123/comments/999')
|
2405
|
+
ok {tuple[-1]} == [123, 999] # book_id, comment_id
|
2106
2406
|
end
|
2107
2407
|
|
2108
2408
|
spec "[!uqwr7] stores result into cache if cache is enabled." do
|
2109
2409
|
|mapping|
|
2110
|
-
tuple = mapping.
|
2410
|
+
tuple = mapping.find('/api/books/111')
|
2111
2411
|
mapping.instance_exec(self) do |_|
|
2112
2412
|
_.ok {@urlpath_lru_cache} == {'/api/books/111' => tuple}
|
2113
2413
|
end
|
@@ -2115,11 +2415,11 @@ Oktest.scope do
|
|
2115
2415
|
|
2116
2416
|
spec "[!3ps5g] deletes item from cache when cache size over limit." do
|
2117
2417
|
|mapping|
|
2118
|
-
mapping.
|
2119
|
-
mapping.
|
2120
|
-
mapping.
|
2121
|
-
mapping.
|
2122
|
-
mapping.
|
2418
|
+
mapping.find('/api/books/1')
|
2419
|
+
mapping.find('/api/books/2')
|
2420
|
+
mapping.find('/api/books/3')
|
2421
|
+
mapping.find('/api/books/4')
|
2422
|
+
mapping.find('/api/books/5')
|
2123
2423
|
mapping.instance_exec(self) do |_|
|
2124
2424
|
_.ok {@urlpath_lru_cache.length} == 3
|
2125
2425
|
end
|
@@ -2128,18 +2428,18 @@ Oktest.scope do
|
|
2128
2428
|
spec "[!uqwr7] uses LRU as cache algorithm." do
|
2129
2429
|
|mapping|
|
2130
2430
|
mapping.instance_exec(self) do |_|
|
2131
|
-
t1 =
|
2132
|
-
t2 =
|
2133
|
-
t3 =
|
2431
|
+
t1 = find('/api/books/1')
|
2432
|
+
t2 = find('/api/books/2')
|
2433
|
+
t3 = find('/api/books/3')
|
2134
2434
|
_.ok {@urlpath_lru_cache.values} == [t1, t2, t3]
|
2135
|
-
t4 =
|
2435
|
+
t4 = find('/api/books/4')
|
2136
2436
|
_.ok {@urlpath_lru_cache.values} == [t2, t3, t4]
|
2137
|
-
t5 =
|
2437
|
+
t5 = find('/api/books/5')
|
2138
2438
|
_.ok {@urlpath_lru_cache.values} == [t3, t4, t5]
|
2139
2439
|
#
|
2140
|
-
|
2440
|
+
find('/api/books/4')
|
2141
2441
|
_.ok {@urlpath_lru_cache.values} == [t3, t5, t4]
|
2142
|
-
|
2442
|
+
find('/api/books/3')
|
2143
2443
|
_.ok {@urlpath_lru_cache.values} == [t5, t4, t3]
|
2144
2444
|
end
|
2145
2445
|
end
|
@@ -2147,103 +2447,148 @@ Oktest.scope do
|
|
2147
2447
|
end
|
2148
2448
|
|
2149
2449
|
|
2150
|
-
topic '#
|
2450
|
+
topic '#compile_urlpath()' do
|
2451
|
+
|
2452
|
+
fixture :proc_int do
|
2453
|
+
K8::ActionMapping::URLPATH_PARAM_TYPES[0][3] # for 'int' type
|
2454
|
+
end
|
2151
2455
|
|
2152
|
-
fixture :
|
2153
|
-
|
2456
|
+
fixture :proc_date do
|
2457
|
+
K8::ActionMapping::URLPATH_PARAM_TYPES[1][3] # for 'date' type
|
2154
2458
|
end
|
2155
2459
|
|
2156
|
-
fixture :
|
2157
|
-
|
2158
|
-
x = K8::DefaultPatterns.new
|
2159
|
-
x.register('id', '\d+', &proc1)
|
2160
|
-
x.register(/_id$/, '\d+', &proc1)
|
2161
|
-
x
|
2460
|
+
fixture :proc_str do
|
2461
|
+
K8::ActionMapping::URLPATH_PARAM_TYPES[2][3] # for 'str' type
|
2162
2462
|
end
|
2163
2463
|
|
2164
2464
|
spec "[!awfgs] returns regexp string, param names, and converter procs." do
|
2165
|
-
|
|
2166
|
-
mapping = K8::ActionMapping.new([]
|
2465
|
+
|proc_int|
|
2466
|
+
mapping = K8::ActionMapping.new([])
|
2167
2467
|
mapping.instance_exec(self) do |_|
|
2168
2468
|
#
|
2169
|
-
actual =
|
2170
|
-
_.ok {actual} == ['/books/\d+', ['id'], [
|
2469
|
+
actual = compile_urlpath('/books/{id}')
|
2470
|
+
_.ok {actual} == ['/books/\d+', ['id'], [proc_int]]
|
2171
2471
|
#
|
2172
|
-
actual =
|
2173
|
-
_.ok {actual} == ['/books/\d+/comments/\d+', ['book_id', 'comment_id'], [
|
2472
|
+
actual = compile_urlpath('/books/{book_id}/comments/{comment_id}')
|
2473
|
+
_.ok {actual} == ['/books/\d+/comments/\d+', ['book_id', 'comment_id'], [proc_int, proc_int]]
|
2174
2474
|
#
|
2175
|
-
actual =
|
2176
|
-
_.ok {actual} == ['/books/[0-9]+', ['id'], [
|
2475
|
+
actual = compile_urlpath('/books/{id:<[0-9]+>}')
|
2476
|
+
_.ok {actual} == ['/books/[0-9]+', ['id'], [proc_int]]
|
2177
2477
|
end
|
2178
2478
|
end
|
2179
2479
|
|
2180
2480
|
spec "[!bi7gr] captures urlpath params when 2nd argument is truthy." do
|
2181
|
-
|
|
2182
|
-
mapping = K8::ActionMapping.new([]
|
2481
|
+
|proc_int|
|
2482
|
+
mapping = K8::ActionMapping.new([])
|
2183
2483
|
mapping.instance_exec(self) do |_|
|
2184
|
-
actual =
|
2185
|
-
_.ok {actual} == ['/books/(\d+)', ['id'], [
|
2484
|
+
actual = compile_urlpath('/books/{id}', true)
|
2485
|
+
_.ok {actual} == ['/books/(\d+)', ['id'], [proc_int]]
|
2186
2486
|
#
|
2187
|
-
actual =
|
2188
|
-
_.ok {actual} == ['/books/(\d+)/comments/(\d+)', ['book_id', 'comment_id'], [
|
2487
|
+
actual = compile_urlpath('/books/{book_id}/comments/{comment_id}', true)
|
2488
|
+
_.ok {actual} == ['/books/(\d+)/comments/(\d+)', ['book_id', 'comment_id'], [proc_int, proc_int]]
|
2189
2489
|
#
|
2190
|
-
actual =
|
2191
|
-
_.ok {actual} == ['/books/([0-9]+)', ['id'], [
|
2490
|
+
actual = compile_urlpath('/books/{id:<[0-9]+>}', true)
|
2491
|
+
_.ok {actual} == ['/books/([0-9]+)', ['id'], [proc_int]]
|
2192
2492
|
end
|
2193
2493
|
end
|
2194
2494
|
|
2195
2495
|
spec "[!mprbx] ex: '/{id:x|y}' -> '/(x|y)', '/{:x|y}' -> '/(?:x|y)'" do
|
2196
|
-
|
2197
|
-
mapping = K8::ActionMapping.new([], default_patterns: default_patterns)
|
2496
|
+
mapping = K8::ActionMapping.new([])
|
2198
2497
|
mapping.instance_exec(self) do |_|
|
2199
|
-
_.ok {
|
2200
|
-
_.ok {
|
2201
|
-
_.ok {
|
2202
|
-
_.ok {
|
2498
|
+
_.ok {compile_urlpath('/item/{key:<x|y>}', true)} == ['/item/(x|y)', ['key'], [nil]]
|
2499
|
+
_.ok {compile_urlpath('/item/{key:<x|y>}', false)} == ['/item/(?:x|y)', ['key'], [nil]]
|
2500
|
+
_.ok {compile_urlpath('/item/{:<x|y>}', true)} == ['/item/(?:x|y)', [], []]
|
2501
|
+
_.ok {compile_urlpath('/item/{:<x|y>}', false)} == ['/item/(?:x|y)', [], []]
|
2203
2502
|
end
|
2204
2503
|
end
|
2205
2504
|
|
2206
2505
|
spec "[!iln54] param names and conveter procs are nil when no urlpath params." do
|
2207
|
-
|
2208
|
-
mapping = K8::ActionMapping.new([], default_patterns: default_patterns)
|
2506
|
+
mapping = K8::ActionMapping.new([])
|
2209
2507
|
mapping.instance_exec(self) do |_|
|
2210
|
-
actual =
|
2508
|
+
actual = compile_urlpath('/books/new')
|
2211
2509
|
_.ok {actual} == ['/books/new', nil, nil]
|
2212
2510
|
end
|
2213
2511
|
end
|
2214
2512
|
|
2513
|
+
spec "[!9ofdd] supports urlpath param type, for example '{id:int}'." do
|
2514
|
+
|proc_int, proc_date, proc_str|
|
2515
|
+
mapping = K8::ActionMapping.new([])
|
2516
|
+
mapping.instance_exec(self) do |_|
|
2517
|
+
actual = compile_urlpath('/books/{id:int}')
|
2518
|
+
_.ok {actual} == ['/books/\d+', ['id'], [proc_int]]
|
2519
|
+
actual = compile_urlpath('/books/{book_id:int}')
|
2520
|
+
_.ok {actual} == ['/books/\d+', ['book_id'], [proc_int]]
|
2521
|
+
actual = compile_urlpath('/books/{code:int}')
|
2522
|
+
_.ok {actual} == ['/books/\d+', ['code'], [proc_int]]
|
2523
|
+
#
|
2524
|
+
actual = compile_urlpath('/diary/{today:date}')
|
2525
|
+
_.ok {actual} == ['/diary/\d\d\d\d-\d\d-\d\d', ['today'], [proc_date]]
|
2526
|
+
#
|
2527
|
+
actual = compile_urlpath('/books/{id:str}')
|
2528
|
+
_.ok {actual} == ['/books/[^/]+', ['id'], [proc_str]]
|
2529
|
+
end
|
2530
|
+
end
|
2531
|
+
|
2215
2532
|
spec "[!lhtiz] skips empty param name." do
|
2216
|
-
|
|
2217
|
-
K8::ActionMapping.new([]
|
2218
|
-
actual =
|
2533
|
+
|proc_int|
|
2534
|
+
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2535
|
+
actual = compile_urlpath('/api/{:<\d+>}/books')
|
2219
2536
|
_.ok {actual} == ['/api/\d+/books', [], []]
|
2220
|
-
actual =
|
2221
|
-
_.ok {actual} == ['/api/\d+/books/\d+', ['id'], [
|
2537
|
+
actual = compile_urlpath('/api/{:<\d+>}/books/{id}')
|
2538
|
+
_.ok {actual} == ['/api/\d+/books/\d+', ['id'], [proc_int]]
|
2222
2539
|
end
|
2223
2540
|
end
|
2224
2541
|
|
2225
2542
|
spec "[!66zas] skips param name starting with '_'." do
|
2226
|
-
|
|
2227
|
-
K8::ActionMapping.new([]
|
2228
|
-
actual =
|
2543
|
+
|proc_int|
|
2544
|
+
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2545
|
+
actual = compile_urlpath('/api/{_ver:<\d+>}/books')
|
2229
2546
|
_.ok {actual} == ['/api/\d+/books', [], []]
|
2230
|
-
actual =
|
2231
|
-
_.ok {actual} == ['/api/\d+/books/\d+', ['id'], [
|
2547
|
+
actual = compile_urlpath('/api/{_ver:<\d+>}/books/{id}')
|
2548
|
+
_.ok {actual} == ['/api/\d+/books/\d+', ['id'], [proc_int]]
|
2232
2549
|
end
|
2233
2550
|
end
|
2234
2551
|
|
2235
2552
|
spec "[!92jcn] '{' and '}' are available in urlpath param pattern." do
|
2236
|
-
|
|
2237
|
-
K8::ActionMapping.new([]
|
2238
|
-
actual =
|
2239
|
-
_.ok {actual} == ['/blog/\d{4}-\d{2}-\d{2}', ['date'], [
|
2553
|
+
|proc_date|
|
2554
|
+
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2555
|
+
actual = compile_urlpath('/blog/{date:<\d{4}-\d{2}-\d{2}>}')
|
2556
|
+
_.ok {actual} == ['/blog/\d{4}-\d{2}-\d{2}', ['date'], [proc_date]]
|
2557
|
+
end
|
2558
|
+
end
|
2559
|
+
|
2560
|
+
spec "[!do1zi] param type is optional (ex: '{id}' or '{id:<\d+>}')." do
|
2561
|
+
|proc_int|
|
2562
|
+
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2563
|
+
actual = compile_urlpath('/books/{book_id}')
|
2564
|
+
_.ok {actual} == ['/books/\d+', ['book_id'], [proc_int]]
|
2565
|
+
actual = compile_urlpath('/books/{xxx:<\d\d\d>}')
|
2566
|
+
_.ok {actual} == ['/books/\d\d\d', ['xxx'], [nil]]
|
2567
|
+
end
|
2568
|
+
end
|
2569
|
+
|
2570
|
+
spec "[!my6as] param pattern is optional (ex: '{id}' or '{id:int}')." do
|
2571
|
+
|proc_int|
|
2572
|
+
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2573
|
+
actual = compile_urlpath('/books/{book_id}')
|
2574
|
+
_.ok {actual} == ['/books/\d+', ['book_id'], [proc_int]]
|
2575
|
+
actual = compile_urlpath('/books/{xxx:int}')
|
2576
|
+
_.ok {actual} == ['/books/\d+', ['xxx'], [proc_int]]
|
2577
|
+
end
|
2578
|
+
end
|
2579
|
+
|
2580
|
+
spec "[!3diea] '{id:<\d+>}' is ok but '{id<\d+>}' raises error." do
|
2581
|
+
|proc_int|
|
2582
|
+
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2583
|
+
pr = proc { compile_urlpath('/books/{book_id<\d+>}') }
|
2584
|
+
_.ok {pr}.raise?(K8::ActionMappingError)
|
2240
2585
|
end
|
2241
2586
|
end
|
2242
2587
|
|
2243
2588
|
end
|
2244
2589
|
|
2245
2590
|
|
2246
|
-
topic '#
|
2591
|
+
topic '#require_action_class()' do
|
2247
2592
|
|
2248
2593
|
spec "[!px9jy] requires file and finds class object." do
|
2249
2594
|
filename = 'test_px9jy.rb'
|
@@ -2251,7 +2596,7 @@ Oktest.scope do
|
|
2251
2596
|
File.open(filename, 'w') {|f| f << content }
|
2252
2597
|
at_end { File.unlink filename }
|
2253
2598
|
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2254
|
-
_.ok {
|
2599
|
+
_.ok { require_action_class './test_px9jy:Ex_px9jy' } == Ex_px9jy
|
2255
2600
|
end
|
2256
2601
|
end
|
2257
2602
|
|
@@ -2261,7 +2606,7 @@ Oktest.scope do
|
|
2261
2606
|
File.open(filename, 'w') {|f| f << content }
|
2262
2607
|
at_end { File.unlink filename }
|
2263
2608
|
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2264
|
-
pr = proc {
|
2609
|
+
pr = proc { require_action_class './test_dlcks:Ex_dlcks' }
|
2265
2610
|
_.ok {pr}.raise?(LoadError, "cannot load such file -- homhomhom")
|
2266
2611
|
_.ok {pr.exception.path} == "homhomhom"
|
2267
2612
|
end
|
@@ -2270,7 +2615,7 @@ Oktest.scope do
|
|
2270
2615
|
spec "[!mngjz] raises error when failed to load file." do
|
2271
2616
|
filename = 'test_mngjz.rb'
|
2272
2617
|
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2273
|
-
pr = proc {
|
2618
|
+
pr = proc { require_action_class './test_mngjz:Ex_mngjz' }
|
2274
2619
|
_.ok {pr}.raise?(LoadError, "'./test_mngjz:Ex_mngjz': cannot load './test_mngjz'.")
|
2275
2620
|
end
|
2276
2621
|
end
|
@@ -2281,7 +2626,7 @@ Oktest.scope do
|
|
2281
2626
|
File.open(filename, 'w') {|f| f << content }
|
2282
2627
|
at_end { File.unlink filename }
|
2283
2628
|
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2284
|
-
_.ok {
|
2629
|
+
_.ok { require_action_class './test_8n6pf:Ex_8n6pf::Sample' } == Ex_8n6pf::Sample
|
2285
2630
|
end
|
2286
2631
|
end
|
2287
2632
|
|
@@ -2291,7 +2636,7 @@ Oktest.scope do
|
|
2291
2636
|
File.open(filename, 'w') {|f| f << content }
|
2292
2637
|
at_end { File.unlink filename }
|
2293
2638
|
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2294
|
-
pr = proc {
|
2639
|
+
pr = proc { require_action_class './test_6lv7l:Ex_6lv7l::Sample' }
|
2295
2640
|
_.ok {pr}.raise?(NameError, "'./test_6lv7l:Ex_6lv7l::Sample': class not found (Ex_6lv7l::Sample).")
|
2296
2641
|
end
|
2297
2642
|
end
|
@@ -2302,7 +2647,7 @@ Oktest.scope do
|
|
2302
2647
|
File.open(filename, 'w') {|f| f << content }
|
2303
2648
|
at_end { File.unlink filename }
|
2304
2649
|
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2305
|
-
pr = proc {
|
2650
|
+
pr = proc { require_action_class './test_thf7t:Ex_thf7t' }
|
2306
2651
|
_.ok {pr}.raise?(TypeError, "'./test_thf7t:Ex_thf7t': class name expected but got \"XXX\".")
|
2307
2652
|
end
|
2308
2653
|
end
|
@@ -2313,7 +2658,7 @@ Oktest.scope do
|
|
2313
2658
|
File.open(filename, 'w') {|f| f << content }
|
2314
2659
|
at_end { File.unlink filename }
|
2315
2660
|
K8::ActionMapping.new([]).instance_exec(self) do |_|
|
2316
|
-
pr = proc {
|
2661
|
+
pr = proc { require_action_class './test_yqcgx:Ex_yqcgx' }
|
2317
2662
|
_.ok {pr}.raise?(TypeError, "'./test_yqcgx:Ex_yqcgx': expected subclass of K8::Action but not.")
|
2318
2663
|
end
|
2319
2664
|
end
|
@@ -2373,6 +2718,30 @@ Oktest.scope do
|
|
2373
2718
|
end
|
2374
2719
|
|
2375
2720
|
|
2721
|
+
topic 'K8::REQUEST_CLASS=' do
|
2722
|
+
|
2723
|
+
spec "[!7uqb4] changes default request class." do
|
2724
|
+
original = K8::RackApplication::REQUEST_CLASS
|
2725
|
+
at_end { K8::RackApplication.REQUEST_CLASS = original }
|
2726
|
+
K8::RackApplication.REQUEST_CLASS = Array
|
2727
|
+
ok {K8::RackApplication::REQUEST_CLASS} == Array
|
2728
|
+
end
|
2729
|
+
|
2730
|
+
end
|
2731
|
+
|
2732
|
+
|
2733
|
+
topic 'K8::RESPONSE_CLASS=' do
|
2734
|
+
|
2735
|
+
spec "[!c1bd0] changes default response class." do
|
2736
|
+
original = K8::RackApplication::RESPONSE_CLASS
|
2737
|
+
at_end { K8::RackApplication.RESPONSE_CLASS = original }
|
2738
|
+
K8::RackApplication.RESPONSE_CLASS = Hash
|
2739
|
+
ok {K8::RackApplication::RESPONSE_CLASS} == Hash
|
2740
|
+
end
|
2741
|
+
|
2742
|
+
end
|
2743
|
+
|
2744
|
+
|
2376
2745
|
topic '#initialize()' do
|
2377
2746
|
|
2378
2747
|
spec "[!vkp65] mounts urlpath mappings." do
|
@@ -2414,14 +2783,83 @@ Oktest.scope do
|
|
2414
2783
|
end
|
2415
2784
|
|
2416
2785
|
|
2417
|
-
topic '#
|
2786
|
+
topic '#find()' do
|
2418
2787
|
|
2419
2788
|
spec "[!o0rnr] returns action class, action methods, urlpath names and values." do
|
2420
2789
|
|app|
|
2421
|
-
ret = app.
|
2422
|
-
ok {ret} == [BooksAction, {:GET=>:do_index, :POST=>:do_create}, []
|
2423
|
-
ret = app.
|
2424
|
-
ok {ret} == [BooksAction, {:GET=>:do_show, :PUT=>:do_update, :DELETE=>:do_delete}, [
|
2790
|
+
ret = app.find('/api/books/')
|
2791
|
+
ok {ret} == [BooksAction, {:GET=>:do_index, :POST=>:do_create}, []]
|
2792
|
+
ret = app.find('/api/books/123')
|
2793
|
+
ok {ret} == [BooksAction, {:GET=>:do_show, :PUT=>:do_update, :DELETE=>:do_delete}, [123]]
|
2794
|
+
end
|
2795
|
+
|
2796
|
+
end
|
2797
|
+
|
2798
|
+
|
2799
|
+
topic '#lookup()' do
|
2800
|
+
|
2801
|
+
spec "[!7476i] uses '_method' value of query string as request method when 'POST' method." do
|
2802
|
+
|app|
|
2803
|
+
tuple = app.lookup(:POST, "/api/books/123", "_method=DELETE")
|
2804
|
+
ok {tuple} == [BooksAction, :do_delete, [123]] # found :do_delete
|
2805
|
+
end
|
2806
|
+
|
2807
|
+
spec "[!c0job] redirects only when request method is GET or HEAD." do
|
2808
|
+
|app|
|
2809
|
+
## not redirect on :POST
|
2810
|
+
pr3 = proc { app.lookup(:POST, "/api/books", "") }
|
2811
|
+
ok {pr3}.raise?(K8::HttpException)
|
2812
|
+
ex3 = pr3.exception
|
2813
|
+
ok {ex3.status_code} == 404
|
2814
|
+
end
|
2815
|
+
|
2816
|
+
spec "[!u1qfv] raises 301 when urlpath not found but found with tailing '/'." do
|
2817
|
+
|app|
|
2818
|
+
pr = proc { app.lookup(:GET, "/api/books", "") }
|
2819
|
+
ok {pr}.raise?(K8::HttpException)
|
2820
|
+
ex = pr.exception
|
2821
|
+
ok {ex.status_code} == 301
|
2822
|
+
ok {ex.response_headers} == {"Location"=>"/api/books/"}
|
2823
|
+
end
|
2824
|
+
|
2825
|
+
spec "[!kbff3] raises 301 when urlpath not found but found without tailing '/'." do
|
2826
|
+
|app|
|
2827
|
+
pr = proc { app.lookup(:GET, "/api/books/123/", "") }
|
2828
|
+
ok {pr}.raise?(K8::HttpException)
|
2829
|
+
ex = pr.exception
|
2830
|
+
ok {ex.status_code} == 301
|
2831
|
+
ok {ex.response_headers} == {"Location"=>"/api/books/123"}
|
2832
|
+
end
|
2833
|
+
|
2834
|
+
spec "[!cgxx4] adds query string to 'Location' header when redirecting." do
|
2835
|
+
|app|
|
2836
|
+
pr = proc { app.lookup(:GET, "/api/books", "x=1&y=2") }
|
2837
|
+
ok {pr}.raise?(K8::HttpException)
|
2838
|
+
ex = pr.exception
|
2839
|
+
ok {ex.status_code} == 301
|
2840
|
+
ok {ex.response_headers} == {"Location"=>"/api/books/?x=1&y=2"}
|
2841
|
+
end
|
2842
|
+
|
2843
|
+
spec "[!hdy1f] raises HTTP 404 when urlpath not found." do
|
2844
|
+
|app|
|
2845
|
+
pr = proc { app.lookup(:GET, "/api/book/comments", "") }
|
2846
|
+
ok {pr}.raise?(K8::HttpException)
|
2847
|
+
ex = pr.exception
|
2848
|
+
ok {ex.status_code} == 404
|
2849
|
+
end
|
2850
|
+
|
2851
|
+
spec "[!0znwr] uses 'GET' method to find action when request method is 'HEAD'." do
|
2852
|
+
|app|
|
2853
|
+
tuple = app.lookup(:HEAD, "/api/books/123")
|
2854
|
+
ok {tuple} == [BooksAction, :do_show, [123]]
|
2855
|
+
end
|
2856
|
+
|
2857
|
+
spec "[!bfpav] raises HTTP 405 when urlpath found but request method not allowed." do
|
2858
|
+
|app|
|
2859
|
+
pr = proc { app.lookup(:POST, "/api/books/123", "") }
|
2860
|
+
ok {pr}.raise?(K8::HttpException)
|
2861
|
+
ex = pr.exception
|
2862
|
+
ok {ex.status_code} == 405
|
2425
2863
|
end
|
2426
2864
|
|
2427
2865
|
end
|
@@ -2456,7 +2894,7 @@ Oktest.scope do
|
|
2456
2894
|
status, headers, body = app.call(env)
|
2457
2895
|
ok {status} == 301
|
2458
2896
|
ok {headers['Location']} == "/api/books/"
|
2459
|
-
ok {body} == []
|
2897
|
+
ok {body} == ["<div>\n<h2>301 Moved Permanently</h2>\n<p></p>\n</div>\n"]
|
2460
2898
|
end
|
2461
2899
|
|
2462
2900
|
spec "[!02dow] returns 301 when urlpath not found but found without tailing '/'." do
|
@@ -2465,20 +2903,7 @@ Oktest.scope do
|
|
2465
2903
|
status, headers, body = app.call(env)
|
2466
2904
|
ok {status} == 301
|
2467
2905
|
ok {headers['Location']} == "/api/books/123"
|
2468
|
-
ok {body} == []
|
2469
|
-
end
|
2470
|
-
|
2471
|
-
spec "[!2a9c9] adds query string to 'Location' header." do
|
2472
|
-
|app|
|
2473
|
-
env = new_env("GET", "/api/books", query: 'x=1&y=2')
|
2474
|
-
status, headers, body = app.call(env)
|
2475
|
-
ok {status} == 301
|
2476
|
-
ok {headers['Location']} == "/api/books/?x=1&y=2"
|
2477
|
-
#
|
2478
|
-
env = new_env("GET", "/api/books/123/", query: 'x=3&y=4')
|
2479
|
-
status, headers, body = app.call(env)
|
2480
|
-
ok {status} == 301
|
2481
|
-
ok {headers['Location']} == "/api/books/123?x=3&y=4"
|
2906
|
+
ok {body} == ["<div>\n<h2>301 Moved Permanently</h2>\n<p></p>\n</div>\n"]
|
2482
2907
|
end
|
2483
2908
|
|
2484
2909
|
spec "[!vz07j] redirects only when request method is GET or HEAD." do
|
@@ -2494,106 +2919,113 @@ Oktest.scope do
|
|
2494
2919
|
ok {headers['Location']} == nil
|
2495
2920
|
end
|
2496
2921
|
|
2497
|
-
end
|
2498
|
-
|
2499
|
-
|
2500
|
-
topic '#handle_request()' do
|
2501
|
-
|
2502
|
-
spec "[!0fgbd] finds action class and invokes action method with urlpath params." do
|
2503
|
-
|app|
|
2504
|
-
env = new_env("GET", "/api/books/123")
|
2505
|
-
app.instance_exec(self) do |_|
|
2506
|
-
tuple = handle_request(K8::Request.new(env), K8::Response.new)
|
2507
|
-
_.ok {tuple}.is_a?(Array)
|
2508
|
-
status, headers, body = tuple
|
2509
|
-
_.ok {status} == 200
|
2510
|
-
_.ok {body} == ["<show:123(Fixnum)>"]
|
2511
|
-
_.ok {headers} == {
|
2512
|
-
"Content-Length" => "18",
|
2513
|
-
"Content-Type" => "text/html; charset=utf-8",
|
2514
|
-
}
|
2515
|
-
end
|
2516
|
-
end
|
2517
|
-
|
2518
2922
|
spec "[!l6kmc] uses 'GET' method to find action when request method is 'HEAD'." do
|
2519
2923
|
|app|
|
2520
2924
|
env = new_env("HEAD", "/api/books/123")
|
2521
|
-
app.
|
2522
|
-
|
2523
|
-
|
2524
|
-
|
2525
|
-
|
2526
|
-
|
2527
|
-
|
2528
|
-
"Content-Type" => "text/html; charset=utf-8",
|
2529
|
-
}
|
2530
|
-
end
|
2925
|
+
status, headers, body = app.call(env)
|
2926
|
+
ok {status} == 200
|
2927
|
+
ok {body} == [""]
|
2928
|
+
ok {headers} == {
|
2929
|
+
"Content-Length" => "18",
|
2930
|
+
"Content-Type" => "text/html; charset=utf-8",
|
2931
|
+
}
|
2531
2932
|
end
|
2532
2933
|
|
2533
2934
|
spec "[!4vmd3] uses '_method' value of query string as request method when 'POST' method." do
|
2534
2935
|
|app|
|
2535
2936
|
env = new_env("POST", "/api/books/123", query: {"_method"=>"DELETE"})
|
2536
|
-
app.
|
2537
|
-
|
2538
|
-
|
2539
|
-
_.ok {status} == 200
|
2540
|
-
_.ok {body} == ["<delete:123(Fixnum)>"] # do_delete() caled
|
2541
|
-
end
|
2937
|
+
status, headers, body = app.call(env)
|
2938
|
+
ok {status} == 200
|
2939
|
+
ok {body} == ["<delete:123(Fixnum)>"] # do_delete() caled
|
2542
2940
|
end
|
2543
2941
|
|
2544
2942
|
spec "[!vdllr] clears request and response if possible." do
|
2545
2943
|
|app|
|
2546
|
-
|
2547
|
-
|
2548
|
-
|
2549
|
-
|
2550
|
-
|
2551
|
-
|
2944
|
+
reqclass = K8::RackApplication::REQUEST_CLASS
|
2945
|
+
respclass = K8::RackApplication::RESPONSE_CLASS
|
2946
|
+
K8::RackApplication.module_eval do
|
2947
|
+
remove_const :REQUEST_CLASS
|
2948
|
+
remove_const :RESPONSE_CLASS
|
2949
|
+
end
|
2950
|
+
$req_clear = $resp_clear = false
|
2951
|
+
K8::RackApplication::REQUEST_CLASS = Class.new(reqclass) do
|
2952
|
+
def clear; $req_clear = true; end
|
2953
|
+
end
|
2954
|
+
K8::RackApplication::RESPONSE_CLASS = Class.new(respclass) do
|
2955
|
+
def clear; $resp_clear = true; end
|
2956
|
+
end
|
2957
|
+
at_end do
|
2958
|
+
K8::RackApplication.REQUEST_CLASS = reqclass
|
2959
|
+
K8::RackApplication.RESPONSE_CLASS = respclass
|
2960
|
+
$req_clear = nil
|
2961
|
+
$resp_clear = nil
|
2962
|
+
end
|
2552
2963
|
#
|
2553
|
-
|
2554
|
-
|
2555
|
-
|
2556
|
-
|
2964
|
+
env = new_env("GET", "/")
|
2965
|
+
ok {$req_clear} == false
|
2966
|
+
ok {$resp_clear} == false
|
2967
|
+
app.call(env)
|
2968
|
+
_ = self
|
2969
|
+
K8::RackApplication::REQUEST_CLASS.class_eval do
|
2970
|
+
_.ok {$req_clear} == true
|
2557
2971
|
end
|
2558
|
-
|
2559
|
-
|
2560
|
-
spec "[!9wp9z] returns empty body when request method is HEAD." do
|
2561
|
-
|app|
|
2562
|
-
env = new_env("HEAD", "/api/books/123")
|
2563
|
-
app.instance_exec(self) do |_|
|
2564
|
-
tuple = handle_request(K8::Request.new(env), K8::Response.new)
|
2565
|
-
status, headers, body = tuple
|
2566
|
-
_.ok {body} == [""]
|
2972
|
+
K8::RackApplication::RESPONSE_CLASS.class_eval do
|
2973
|
+
_.ok {$resp_clear} == true
|
2567
2974
|
end
|
2568
2975
|
end
|
2569
2976
|
|
2570
2977
|
spec "[!rz13i] returns HTTP 404 when urlpath not found." do
|
2571
2978
|
|app|
|
2572
2979
|
env = new_env("GET", "/api/book/comments")
|
2980
|
+
status, headers, body = app.call(env)
|
2981
|
+
ok {status} == 404
|
2982
|
+
ok {headers} == {
|
2983
|
+
"Content-Length" => "44",
|
2984
|
+
"Content-Type" => "text/html;charset=utf-8",
|
2985
|
+
}
|
2986
|
+
ok {body} == ["<div>\n<h2>404 Not Found</h2>\n<p></p>\n</div>\n"]
|
2987
|
+
end
|
2988
|
+
|
2989
|
+
spec "[!rv3cf] returns HTTP 405 when urlpath found but request method not allowed." do
|
2990
|
+
|app|
|
2991
|
+
env = new_env("POST", "/api/books/123")
|
2992
|
+
status, headers, body = app.call(env)
|
2993
|
+
ok {status} == 405
|
2994
|
+
ok {headers} == {
|
2995
|
+
"Content-Length" => "53",
|
2996
|
+
"Content-Type" => "text/html;charset=utf-8",
|
2997
|
+
}
|
2998
|
+
ok {body} == ["<div>\n<h2>405 Method Not Allowed</h2>\n<p></p>\n</div>\n"]
|
2999
|
+
end
|
3000
|
+
|
3001
|
+
end
|
3002
|
+
|
3003
|
+
|
3004
|
+
topic '#handle_request()' do
|
3005
|
+
|
3006
|
+
spec "[!0fgbd] finds action class and invokes action method with urlpath params." do
|
3007
|
+
|app|
|
3008
|
+
env = new_env("GET", "/api/books/123")
|
2573
3009
|
app.instance_exec(self) do |_|
|
2574
|
-
tuple = handle_request(K8::
|
3010
|
+
tuple = handle_request(K8::RackRequest.new(env), K8::RackResponse.new)
|
3011
|
+
_.ok {tuple}.is_a?(Array)
|
2575
3012
|
status, headers, body = tuple
|
2576
|
-
_.ok {status} ==
|
2577
|
-
_.ok {body} == ["<
|
3013
|
+
_.ok {status} == 200
|
3014
|
+
_.ok {body} == ["<show:123(Fixnum)>"]
|
2578
3015
|
_.ok {headers} == {
|
2579
|
-
"Content-Length" => "
|
2580
|
-
"Content-Type" => "text/html;charset=utf-8",
|
3016
|
+
"Content-Length" => "18",
|
3017
|
+
"Content-Type" => "text/html; charset=utf-8",
|
2581
3018
|
}
|
2582
3019
|
end
|
2583
3020
|
end
|
2584
3021
|
|
2585
|
-
spec "[!
|
3022
|
+
spec "[!9wp9z] returns empty body when request method is HEAD." do
|
2586
3023
|
|app|
|
2587
|
-
env = new_env("
|
3024
|
+
env = new_env("HEAD", "/api/books/123")
|
2588
3025
|
app.instance_exec(self) do |_|
|
2589
|
-
tuple = handle_request(K8::
|
3026
|
+
tuple = handle_request(K8::RackRequest.new(env), K8::RackResponse.new)
|
2590
3027
|
status, headers, body = tuple
|
2591
|
-
_.ok {
|
2592
|
-
_.ok {body} == ["<div>\n<h2>405 Method Not Allowed</h2>\n<p></p>\n</div>\n"]
|
2593
|
-
_.ok {headers} == {
|
2594
|
-
"Content-Length" => "53",
|
2595
|
-
"Content-Type" => "text/html;charset=utf-8",
|
2596
|
-
}
|
3028
|
+
_.ok {body} == [""]
|
2597
3029
|
end
|
2598
3030
|
end
|
2599
3031
|
|
@@ -2652,364 +3084,6 @@ Oktest.scope do
|
|
2652
3084
|
end
|
2653
3085
|
|
2654
3086
|
|
2655
|
-
topic K8::SecretValue do
|
2656
|
-
|
2657
|
-
|
2658
|
-
topic '#initialize()' do
|
2659
|
-
|
2660
|
-
spec "[!fbwnh] takes environment variable name." do
|
2661
|
-
obj = K8::SecretValue.new('DB_PASS')
|
2662
|
-
ok {obj.name} == 'DB_PASS'
|
2663
|
-
end
|
2664
|
-
|
2665
|
-
end
|
2666
|
-
|
2667
|
-
|
2668
|
-
topic '#value()' do
|
2669
|
-
|
2670
|
-
spec "[!gg06v] returns environment variable value." do
|
2671
|
-
obj = K8::SecretValue.new('TEST_HOMHOM')
|
2672
|
-
ok {obj.value} == nil
|
2673
|
-
ENV['TEST_HOMHOM'] = 'homhom'
|
2674
|
-
ok {obj.value} == 'homhom'
|
2675
|
-
end
|
2676
|
-
|
2677
|
-
end
|
2678
|
-
|
2679
|
-
|
2680
|
-
topic '#to_s()' do
|
2681
|
-
|
2682
|
-
spec "[!7ymqq] returns '<SECRET>' string when name not eixst." do
|
2683
|
-
ok {K8::SecretValue.new.to_s} == "<SECRET>"
|
2684
|
-
end
|
2685
|
-
|
2686
|
-
spec "[!x6edf] returns 'ENV[<name>]' string when name exists." do
|
2687
|
-
ok {K8::SecretValue.new('DB_PASS').to_s} == "ENV['DB_PASS']"
|
2688
|
-
end
|
2689
|
-
|
2690
|
-
end
|
2691
|
-
|
2692
|
-
|
2693
|
-
topic '#inspect()' do
|
2694
|
-
|
2695
|
-
spec "[!j27ji] 'inspect()' is alias of 'to_s()'." do
|
2696
|
-
ok {K8::SecretValue.new('DB_PASS').inspect} == "ENV['DB_PASS']"
|
2697
|
-
end
|
2698
|
-
|
2699
|
-
end
|
2700
|
-
|
2701
|
-
|
2702
|
-
topic '#[](name)' do
|
2703
|
-
|
2704
|
-
spec "[!jjqmn] creates new instance object with name." do
|
2705
|
-
obj = K8::SecretValue.new['DB_PASSWORD']
|
2706
|
-
ok {obj}.is_a?(K8::SecretValue)
|
2707
|
-
ok {obj.name} == 'DB_PASSWORD'
|
2708
|
-
end
|
2709
|
-
|
2710
|
-
end
|
2711
|
-
|
2712
|
-
|
2713
|
-
end
|
2714
|
-
|
2715
|
-
|
2716
|
-
topic K8::BaseConfig do
|
2717
|
-
|
2718
|
-
|
2719
|
-
topic '#initialize()' do
|
2720
|
-
|
2721
|
-
spec "[!vvd1n] copies key and values from class object." do
|
2722
|
-
class C01 < K8::BaseConfig
|
2723
|
-
add :haruhi , 'C' , "Suzumiya"
|
2724
|
-
add :mikuru , 'E' , "Asahina"
|
2725
|
-
add :yuki , 'A' , "Nagato"
|
2726
|
-
end
|
2727
|
-
c = C01.new
|
2728
|
-
c.instance_exec(self) do |_|
|
2729
|
-
_.ok {@haruhi} == 'C'
|
2730
|
-
_.ok {@mikuru} == 'E'
|
2731
|
-
_.ok {@yuki} == 'A'
|
2732
|
-
end
|
2733
|
-
end
|
2734
|
-
|
2735
|
-
spec "[!6dilv] freezes self and class object if 'freeze:' is true." do
|
2736
|
-
class C02 < K8::BaseConfig
|
2737
|
-
add :haruhi , 'C' , "Suzumiya"
|
2738
|
-
add :mikuru , 'E' , "Asahina"
|
2739
|
-
add :yuki , 'A' , "Nagato"
|
2740
|
-
end
|
2741
|
-
## when freeze: false
|
2742
|
-
c = C02.new(freeze: false)
|
2743
|
-
pr = proc { c.instance_variable_set('@yuki', 'B') }
|
2744
|
-
ok {pr}.NOT.raise?(Exception)
|
2745
|
-
pr = proc { C02.class_eval { put :yuki, 'B' } }
|
2746
|
-
ok {pr}.NOT.raise?(Exception)
|
2747
|
-
## when freeze: true
|
2748
|
-
c = C02.new
|
2749
|
-
pr = proc { c.instance_variable_set('@yuki', 'B') }
|
2750
|
-
ok {pr}.raise?(RuntimeError, "can't modify frozen C02")
|
2751
|
-
pr = proc { C02.class_eval { put :yuki, 'B' } }
|
2752
|
-
ok {pr}.raise?(RuntimeError, "can't modify frozen class")
|
2753
|
-
end
|
2754
|
-
|
2755
|
-
case_when "[!xok12] when value is SECRET..." do
|
2756
|
-
|
2757
|
-
spec "[!a4a4p] raises error when key not specified." do
|
2758
|
-
class C03 < K8::BaseConfig
|
2759
|
-
add :db_pass , SECRET, "db password"
|
2760
|
-
end
|
2761
|
-
pr = proc { C03.new }
|
2762
|
-
ok {pr}.raise?(K8::ConfigError, "config 'db_pass' should be set, but not.")
|
2763
|
-
end
|
2764
|
-
|
2765
|
-
spec "[!w4yl7] raises error when ENV value not specified." do
|
2766
|
-
class C04 < K8::BaseConfig
|
2767
|
-
add :db_pass1 , SECRET['DB_PASS1'], "db password"
|
2768
|
-
end
|
2769
|
-
ok {ENV['DB_PASS1']} == nil
|
2770
|
-
pr = proc { C04.new }
|
2771
|
-
ok {pr}.raise?(K8::ConfigError, )
|
2772
|
-
end
|
2773
|
-
|
2774
|
-
spec "[!he20d] get value from ENV." do
|
2775
|
-
class C05 < K8::BaseConfig
|
2776
|
-
add :db_pass1 , SECRET['DB_PASS1'], "db password"
|
2777
|
-
end
|
2778
|
-
begin
|
2779
|
-
ENV['DB_PASS1'] = 'homhom'
|
2780
|
-
pr = proc { C05.new }
|
2781
|
-
ok {pr}.NOT.raise?(Exception)
|
2782
|
-
ok {C05.new.db_pass1} == 'homhom'
|
2783
|
-
ensure
|
2784
|
-
ENV['DB_PASS1'] = nil
|
2785
|
-
end
|
2786
|
-
end
|
2787
|
-
|
2788
|
-
end
|
2789
|
-
|
2790
|
-
end
|
2791
|
-
|
2792
|
-
|
2793
|
-
topic '.has?()' do
|
2794
|
-
|
2795
|
-
spec "[!dv87n] returns true iff key is set." do
|
2796
|
-
class C11 < K8::BaseConfig
|
2797
|
-
@result1 = has? :foo
|
2798
|
-
put :foo, 1
|
2799
|
-
@result2 = has? :foo
|
2800
|
-
end
|
2801
|
-
ok {C11.instance_variable_get('@result1')} == false
|
2802
|
-
ok {C11.instance_variable_get('@result2')} == true
|
2803
|
-
end
|
2804
|
-
|
2805
|
-
end
|
2806
|
-
|
2807
|
-
|
2808
|
-
topic '.put()' do
|
2809
|
-
|
2810
|
-
spec "[!h9b47] defines getter method." do
|
2811
|
-
class C21 < K8::BaseConfig
|
2812
|
-
put :hom, 123, "HomHom"
|
2813
|
-
end
|
2814
|
-
ok {C21.instance_methods}.include?(:hom)
|
2815
|
-
ok {C21.new.hom} == 123
|
2816
|
-
end
|
2817
|
-
|
2818
|
-
spec "[!ncwzt] stores key with value, description and secret flag." do
|
2819
|
-
class C22 < K8::BaseConfig
|
2820
|
-
put :hom, 123, "HomHom"
|
2821
|
-
put :hom2, SECRET, "Secret HomHom"
|
2822
|
-
end
|
2823
|
-
ok {C22.instance_variable_get('@__all')} == {
|
2824
|
-
:hom => [123, "HomHom", false],
|
2825
|
-
:hom2 => [K8::BaseConfig::SECRET, "Secret HomHom", true],
|
2826
|
-
}
|
2827
|
-
end
|
2828
|
-
|
2829
|
-
spec "[!mun1v] keeps secret flag." do
|
2830
|
-
class C23 < K8::BaseConfig
|
2831
|
-
put :haruhi , 'C' , "Suzumiya"
|
2832
|
-
put :mikuru , SECRET, "Asahina"
|
2833
|
-
put :yuki , SECRET, "Nagato"
|
2834
|
-
end
|
2835
|
-
class C23
|
2836
|
-
put :mikuru , 'F'
|
2837
|
-
end
|
2838
|
-
ok {C23.instance_variable_get('@__all')} == {
|
2839
|
-
:haruhi => ['C', "Suzumiya", false],
|
2840
|
-
:mikuru => ['F', "Asahina", true],
|
2841
|
-
:yuki => [K8::BaseConfig::SECRET, "Nagato", true],
|
2842
|
-
}
|
2843
|
-
end
|
2844
|
-
|
2845
|
-
end
|
2846
|
-
|
2847
|
-
|
2848
|
-
topic '.add()' do
|
2849
|
-
|
2850
|
-
spec "[!envke] raises error when already added." do
|
2851
|
-
class C31 < K8::BaseConfig
|
2852
|
-
add :hom, 123, "HomHom"
|
2853
|
-
@ex = nil
|
2854
|
-
begin
|
2855
|
-
add :hom, 456, "HomHom"
|
2856
|
-
rescue => ex
|
2857
|
-
@ex = ex
|
2858
|
-
end
|
2859
|
-
end
|
2860
|
-
ex = C31.instance_variable_get('@ex')
|
2861
|
-
ok {ex} != nil
|
2862
|
-
ok {ex}.is_a?(K8::ConfigError)
|
2863
|
-
ok {ex.message} == "add(:hom, 456): cannot add because already added; use set() or put() instead."
|
2864
|
-
end
|
2865
|
-
|
2866
|
-
spec "[!6cmb4] adds new key, value and desc." do
|
2867
|
-
class C32 < K8::BaseConfig
|
2868
|
-
add :hom, 123, "HomHom"
|
2869
|
-
add :hom2, 'HOM'
|
2870
|
-
end
|
2871
|
-
all = C32.instance_variable_get('@__all')
|
2872
|
-
ok {all} == {:hom=>[123, "HomHom", false], :hom2=>['HOM', nil, false]}
|
2873
|
-
end
|
2874
|
-
|
2875
|
-
end
|
2876
|
-
|
2877
|
-
|
2878
|
-
topic '.set()' do
|
2879
|
-
|
2880
|
-
spec "[!2yis0] raises error when not added yet." do
|
2881
|
-
class C41 < K8::BaseConfig
|
2882
|
-
@ex = nil
|
2883
|
-
begin
|
2884
|
-
set :hom, 123, "HomHom"
|
2885
|
-
rescue => ex
|
2886
|
-
@ex = ex
|
2887
|
-
end
|
2888
|
-
end
|
2889
|
-
ex = C41.instance_variable_get('@ex')
|
2890
|
-
ok {ex} != nil
|
2891
|
-
ok {ex}.is_a?(K8::ConfigError)
|
2892
|
-
ok {ex.message} == "set(:hom, 123): cannot set because not added yet; use add() or put() instead."
|
2893
|
-
end
|
2894
|
-
|
2895
|
-
spec "[!3060g] sets key, value and desc." do
|
2896
|
-
class C42 < K8::BaseConfig
|
2897
|
-
add :hom, 123, "HomHom"
|
2898
|
-
end
|
2899
|
-
class C42
|
2900
|
-
set :hom, 456
|
2901
|
-
end
|
2902
|
-
all = C42.instance_variable_get('@__all')
|
2903
|
-
ok {all} == {:hom=>[456, "HomHom", false]}
|
2904
|
-
end
|
2905
|
-
|
2906
|
-
end
|
2907
|
-
|
2908
|
-
|
2909
|
-
topic '.each()' do
|
2910
|
-
|
2911
|
-
spec "[!iu88i] yields with key, value, desc and secret flag." do
|
2912
|
-
class C51 < K8::BaseConfig
|
2913
|
-
add :haruhi , 'C' , "Suzumiya"
|
2914
|
-
add :mikuru , SECRET, "Asahina"
|
2915
|
-
add :yuki , 'A' , "Nagato"
|
2916
|
-
end
|
2917
|
-
class C51
|
2918
|
-
set :mikuru , 'F'
|
2919
|
-
add :sasaki , 'B'
|
2920
|
-
end
|
2921
|
-
#
|
2922
|
-
arr = []
|
2923
|
-
C51.each {|*args| arr << args }
|
2924
|
-
ok {arr} == [
|
2925
|
-
[:haruhi, 'C', "Suzumiya", false],
|
2926
|
-
[:mikuru, 'F', "Asahina", true],
|
2927
|
-
[:yuki, 'A', "Nagato", false],
|
2928
|
-
[:sasaki, 'B', nil, false],
|
2929
|
-
]
|
2930
|
-
end
|
2931
|
-
|
2932
|
-
end
|
2933
|
-
|
2934
|
-
|
2935
|
-
topic '.get()' do
|
2936
|
-
|
2937
|
-
spec "[!zlhnp] returns value corresponding to key." do
|
2938
|
-
class C61 < K8::BaseConfig
|
2939
|
-
add :haruhi , 'C' , "Suzumiya"
|
2940
|
-
add :mikuru , 'E' , "Asahina"
|
2941
|
-
add :yuki , 'A' , "Nagato"
|
2942
|
-
end
|
2943
|
-
class C61
|
2944
|
-
set :mikuru , 'F'
|
2945
|
-
add :sasaki , 'B'
|
2946
|
-
end
|
2947
|
-
ok {C61.get(:haruhi)} == 'C'
|
2948
|
-
ok {C61.get(:mikuru)} == 'F'
|
2949
|
-
ok {C61.get(:yuki)} == 'A'
|
2950
|
-
ok {C61.get(:sasaki)} == 'B'
|
2951
|
-
end
|
2952
|
-
|
2953
|
-
spec "[!o0k05] returns default value (=nil) when key is not added." do
|
2954
|
-
class C62 < K8::BaseConfig
|
2955
|
-
add :haruhi , 'C' , "Suzumiya"
|
2956
|
-
add :mikuru , 'E' , "Asahina"
|
2957
|
-
add :yuki , 'A' , "Nagato"
|
2958
|
-
end
|
2959
|
-
ok {C62.get(:sasaki)} == nil
|
2960
|
-
ok {C62.get(:sasaki, "")} == ""
|
2961
|
-
end
|
2962
|
-
|
2963
|
-
end
|
2964
|
-
|
2965
|
-
|
2966
|
-
topic '[](key)' do
|
2967
|
-
|
2968
|
-
spec "[!jn9l5] returns value corresponding to key." do
|
2969
|
-
class C71 < K8::BaseConfig
|
2970
|
-
add :haruhi , 'C' , "Suzumiya"
|
2971
|
-
add :mikuru , 'E' , "Asahina"
|
2972
|
-
add :yuki , 'A' , "Nagato"
|
2973
|
-
end
|
2974
|
-
class C71
|
2975
|
-
set :mikuru , 'F'
|
2976
|
-
add :sasaki , 'B'
|
2977
|
-
end
|
2978
|
-
c = C71.new
|
2979
|
-
ok {c[:haruhi]} == 'C'
|
2980
|
-
ok {c[:mikuru]} == 'F'
|
2981
|
-
ok {c[:yuki]} == 'A'
|
2982
|
-
ok {c[:sasaki]} == 'B'
|
2983
|
-
end
|
2984
|
-
|
2985
|
-
end
|
2986
|
-
|
2987
|
-
|
2988
|
-
topic '#get_all()' do
|
2989
|
-
|
2990
|
-
spec "[!4ik3c] returns all keys and values which keys start with prefix as hash object." do
|
2991
|
-
class C81 < K8::BaseConfig
|
2992
|
-
add :session_cookie_name , 'rack.sess'
|
2993
|
-
add :session_cookie_expires , 30*60*60
|
2994
|
-
add :session_cookie_secure , true
|
2995
|
-
add :name , 'Homhom'
|
2996
|
-
add :secure , false
|
2997
|
-
end
|
2998
|
-
#
|
2999
|
-
c = C81.new
|
3000
|
-
ok {c.get_all(:session_cookie_)} == {
|
3001
|
-
:name => 'rack.sess',
|
3002
|
-
:expires => 30*60*60,
|
3003
|
-
:secure => true,
|
3004
|
-
}
|
3005
|
-
end
|
3006
|
-
|
3007
|
-
end
|
3008
|
-
|
3009
|
-
|
3010
|
-
end
|
3011
|
-
|
3012
|
-
|
3013
3087
|
end
|
3014
3088
|
|
3015
3089
|
|