committee 3.3.0 → 5.0.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/lib/committee/drivers/open_api_2/driver.rb +1 -2
- data/lib/committee/drivers/open_api_2/parameter_schema_builder.rb +1 -1
- data/lib/committee/drivers.rb +22 -10
- data/lib/committee/errors.rb +12 -0
- data/lib/committee/middleware/base.rb +5 -4
- data/lib/committee/middleware/request_validation.rb +4 -18
- data/lib/committee/middleware/response_validation.rb +15 -16
- data/lib/committee/request_unpacker.rb +46 -60
- data/lib/committee/schema_validator/hyper_schema/response_validator.rb +8 -2
- data/lib/committee/schema_validator/hyper_schema.rb +41 -27
- data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +44 -37
- data/lib/committee/schema_validator/open_api_3/request_validator.rb +11 -2
- data/lib/committee/schema_validator/open_api_3/router.rb +3 -1
- data/lib/committee/schema_validator/open_api_3.rb +52 -26
- data/lib/committee/schema_validator/option.rb +14 -3
- data/lib/committee/schema_validator.rb +1 -1
- data/lib/committee/test/methods.rb +27 -16
- data/lib/committee/test/schema_coverage.rb +101 -0
- data/lib/committee/utils.rb +28 -0
- data/lib/committee/validation_error.rb +3 -2
- data/lib/committee/version.rb +5 -0
- data/lib/committee.rb +11 -4
- data/test/bin/committee_stub_test.rb +5 -1
- data/test/committee_test.rb +29 -3
- data/test/drivers/open_api_3/driver_test.rb +1 -1
- data/test/drivers_test.rb +20 -7
- data/test/middleware/base_test.rb +9 -10
- data/test/middleware/request_validation_open_api_3_test.rb +175 -18
- data/test/middleware/request_validation_test.rb +20 -28
- data/test/middleware/response_validation_open_api_3_test.rb +96 -7
- data/test/middleware/response_validation_test.rb +21 -26
- data/test/middleware/stub_test.rb +4 -0
- data/test/request_unpacker_test.rb +51 -110
- data/test/schema_validator/hyper_schema/response_validator_test.rb +10 -0
- data/test/schema_validator/hyper_schema/router_test.rb +4 -0
- data/test/schema_validator/hyper_schema/string_params_coercer_test.rb +1 -1
- data/test/schema_validator/open_api_3/operation_wrapper_test.rb +72 -20
- data/test/schema_validator/open_api_3/request_validator_test.rb +27 -0
- data/test/schema_validator/open_api_3/response_validator_test.rb +26 -5
- data/test/test/methods_new_version_test.rb +17 -5
- data/test/test/methods_test.rb +155 -31
- data/test/test/schema_coverage_test.rb +216 -0
- data/test/test_helper.rb +34 -4
- metadata +47 -15
@@ -34,11 +34,27 @@ describe Committee::Middleware::RequestValidation do
|
|
34
34
|
params = { "datetime_string" => "2016-04-01T16:00:00.000+09:00" }
|
35
35
|
|
36
36
|
check_parameter = lambda { |env|
|
37
|
+
assert_equal DateTime, env['test.query_hash']["datetime_string"].class
|
38
|
+
assert_equal String, env['rack.request.query_hash']["datetime_string"].class
|
39
|
+
[200, {}, []]
|
40
|
+
}
|
41
|
+
|
42
|
+
@app = new_rack_app_with_lambda(check_parameter, schema: open_api_3_schema, coerce_date_times: true, query_hash_key: "test.query_hash")
|
43
|
+
|
44
|
+
get "/string_params_coercer", params
|
45
|
+
assert_equal 200, last_response.status
|
46
|
+
end
|
47
|
+
|
48
|
+
it "passes given a datetime and with coerce_date_times enabled on GET endpoint overwrite query_hash" do
|
49
|
+
params = { "datetime_string" => "2016-04-01T16:00:00.000+09:00" }
|
50
|
+
|
51
|
+
check_parameter = lambda { |env|
|
52
|
+
assert_nil env['committee.query_hash']
|
37
53
|
assert_equal DateTime, env['rack.request.query_hash']["datetime_string"].class
|
38
54
|
[200, {}, []]
|
39
55
|
}
|
40
56
|
|
41
|
-
@app = new_rack_app_with_lambda(check_parameter, schema: open_api_3_schema, coerce_date_times: true)
|
57
|
+
@app = new_rack_app_with_lambda(check_parameter, schema: open_api_3_schema, coerce_date_times: true, query_hash_key: "rack.request.query_hash")
|
42
58
|
|
43
59
|
get "/string_params_coercer", params
|
44
60
|
assert_equal 200, last_response.status
|
@@ -49,7 +65,7 @@ describe Committee::Middleware::RequestValidation do
|
|
49
65
|
|
50
66
|
@app = new_rack_app(schema: open_api_3_schema, allow_get_body: true)
|
51
67
|
|
52
|
-
get "/
|
68
|
+
get "/get_endpoint_with_required_parameter", { no_problem: true }, { input: params.to_json }
|
53
69
|
assert_equal 200, last_response.status
|
54
70
|
end
|
55
71
|
|
@@ -58,7 +74,7 @@ describe Committee::Middleware::RequestValidation do
|
|
58
74
|
|
59
75
|
@app = new_rack_app(schema: open_api_3_schema, allow_get_body: false)
|
60
76
|
|
61
|
-
get "/
|
77
|
+
get "/get_endpoint_with_required_parameter", { no_problem: true }, { input: params.to_json }
|
62
78
|
assert_equal 400, last_response.status
|
63
79
|
end
|
64
80
|
|
@@ -154,7 +170,7 @@ describe Committee::Middleware::RequestValidation do
|
|
154
170
|
}
|
155
171
|
|
156
172
|
check_parameter = lambda { |env|
|
157
|
-
hash = env[
|
173
|
+
hash = env["committee.query_hash"]
|
158
174
|
assert_equal DateTime, hash['nested_array'].first['update_time'].class
|
159
175
|
assert_equal 1, hash['nested_array'].first['per_page']
|
160
176
|
|
@@ -249,8 +265,7 @@ describe Committee::Middleware::RequestValidation do
|
|
249
265
|
}
|
250
266
|
post "/characters", JSON.generate(params)
|
251
267
|
assert_equal 400, last_response.status
|
252
|
-
|
253
|
-
assert_match(/expected string, but received #{1.class}:/i, last_response.body)
|
268
|
+
assert_match(/expected string, but received Integer:/i, last_response.body)
|
254
269
|
end
|
255
270
|
|
256
271
|
it "rescues JSON errors" do
|
@@ -279,8 +294,7 @@ describe Committee::Middleware::RequestValidation do
|
|
279
294
|
header "Content-Type", "application/json"
|
280
295
|
post "/v1/characters", JSON.generate(params)
|
281
296
|
assert_equal 400, last_response.status
|
282
|
-
|
283
|
-
assert_match(/expected string, but received #{1.class}: /i, last_response.body)
|
297
|
+
assert_match(/expected string, but received Integer: /i, last_response.body)
|
284
298
|
end
|
285
299
|
|
286
300
|
it "ignores paths outside the prefix" do
|
@@ -333,7 +347,7 @@ describe Committee::Middleware::RequestValidation do
|
|
333
347
|
get "/coerce_path_params/#{not_an_integer}", nil
|
334
348
|
end
|
335
349
|
|
336
|
-
assert_match(/expected integer, but received String: abc/i, e.message)
|
350
|
+
assert_match(/expected integer, but received String: \"abc\"/i, e.message)
|
337
351
|
end
|
338
352
|
|
339
353
|
it "optionally raises an error" do
|
@@ -362,7 +376,7 @@ describe Committee::Middleware::RequestValidation do
|
|
362
376
|
|
363
377
|
it "passes through a valid request for OpenAPI3" do
|
364
378
|
check_parameter = lambda { |env|
|
365
|
-
assert_equal 3, env['
|
379
|
+
assert_equal 3, env['committee.query_hash']['limit'] #5.0.x-
|
366
380
|
[200, {}, []]
|
367
381
|
}
|
368
382
|
|
@@ -376,7 +390,7 @@ describe Committee::Middleware::RequestValidation do
|
|
376
390
|
get "/characters?limit=foo"
|
377
391
|
|
378
392
|
assert_equal 400, last_response.status
|
379
|
-
assert_match(/expected integer, but received String: foo/i, last_response.body)
|
393
|
+
assert_match(/expected integer, but received String: \\"foo\\"/i, last_response.body)
|
380
394
|
end
|
381
395
|
|
382
396
|
it "ignores errors when ignore_error: true" do
|
@@ -396,28 +410,155 @@ describe Committee::Middleware::RequestValidation do
|
|
396
410
|
get "/coerce_path_params/1"
|
397
411
|
end
|
398
412
|
|
413
|
+
describe "overwrite same parameter (old rule)" do
|
414
|
+
# (high priority) path_hash_key -> request_body_hash -> query_param
|
415
|
+
it "set query parameter to committee.params and query hash" do
|
416
|
+
@app = new_rack_app_with_lambda(lambda do |env|
|
417
|
+
assert_equal env['committee.params']['integer'], 42
|
418
|
+
assert_equal env['committee.params'][:integer], 42
|
419
|
+
assert_equal env['committee.query_hash']['integer'], 42
|
420
|
+
#assert_equal env['rack.request.query_hash'][:integer], 42 # this isn't hash indifferent hash because we use rack.request.query_hash
|
421
|
+
[204, {}, []]
|
422
|
+
end, schema: open_api_3_schema, parameter_overwite_by_rails_rule: false)
|
423
|
+
|
424
|
+
header "Content-Type", "application/json"
|
425
|
+
post '/overwrite_same_parameter?integer=42'
|
426
|
+
assert_equal 204, last_response.status
|
427
|
+
end
|
428
|
+
|
429
|
+
it "request body precedence over query parameter" do
|
430
|
+
@app = new_rack_app_with_lambda(lambda do |env|
|
431
|
+
assert_equal env['committee.params']['integer'], 21
|
432
|
+
assert_equal env['committee.params'][:integer], 21
|
433
|
+
assert_equal env['committee.request_body_hash']['integer'], 21
|
434
|
+
assert_equal env['committee.request_body_hash'][:integer], 21
|
435
|
+
assert_equal env['committee.query_hash']['integer'], 42
|
436
|
+
[204, {}, []]
|
437
|
+
end, schema: open_api_3_schema, parameter_overwite_by_rails_rule: false)
|
438
|
+
|
439
|
+
params = {integer: 21}
|
440
|
+
|
441
|
+
header "Content-Type", "application/json"
|
442
|
+
post '/overwrite_same_parameter?integer=42', JSON.generate(params)
|
443
|
+
assert_equal 204, last_response.status
|
444
|
+
end
|
445
|
+
|
446
|
+
it "path parameter precedence over request body" do
|
447
|
+
@app = new_rack_app_with_lambda(lambda do |env|
|
448
|
+
assert_equal env['committee.params']['integer'], 84
|
449
|
+
assert_equal env['committee.params'][:integer], 84
|
450
|
+
assert_equal env['committee.path_hash']['integer'], 84
|
451
|
+
assert_equal env['committee.path_hash'][:integer], 84
|
452
|
+
assert_equal env['committee.request_body_hash']['integer'], 21
|
453
|
+
assert_equal env['committee.request_body_hash'][:integer], 21
|
454
|
+
assert_equal env['committee.query_hash']['integer'], 84 # we can't use query_parameter :(
|
455
|
+
#assert_equal env['rack.request.query_hash'][:integer], 21 # this isn't hash indifferent hash because we use rack.request.query_hash
|
456
|
+
[204, {}, []]
|
457
|
+
end, schema: open_api_3_schema, parameter_overwite_by_rails_rule: false)
|
458
|
+
|
459
|
+
params = {integer: 21}
|
460
|
+
|
461
|
+
header "Content-Type", "application/json"
|
462
|
+
post '/overwrite_same_parameter/84?integer=42', JSON.generate(params)
|
463
|
+
assert_equal 204, last_response.status
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
describe "overwrite same parameter (new rule and seme to Rails)" do
|
468
|
+
# (high priority) path_hash_key -> query_param -> request_body_hash
|
469
|
+
it "set request body to committee.params and query hash" do
|
470
|
+
@app = new_rack_app_with_lambda(lambda do |env|
|
471
|
+
assert_equal env['committee.params']['integer'], 21
|
472
|
+
assert_equal env['committee.params'][:integer], 21
|
473
|
+
assert_equal env['committee.request_body_hash']['integer'], 21
|
474
|
+
assert_equal env['committee.request_body_hash'][:integer], 21
|
475
|
+
[204, {}, []]
|
476
|
+
end, schema: open_api_3_schema)
|
477
|
+
|
478
|
+
params = {integer: 21}
|
479
|
+
|
480
|
+
header "Content-Type", "application/json"
|
481
|
+
post '/overwrite_same_parameter', JSON.generate(params)
|
482
|
+
assert_equal 204, last_response.status
|
483
|
+
end
|
484
|
+
|
485
|
+
it "query parameter precedence over request body" do
|
486
|
+
@app = new_rack_app_with_lambda(lambda do |env|
|
487
|
+
assert_equal env['committee.params']['integer'], 42
|
488
|
+
assert_equal env['committee.params'][:integer], 42
|
489
|
+
assert_equal env['committee.request_body_hash']['integer'], 21
|
490
|
+
assert_equal env['committee.request_body_hash'][:integer], 21
|
491
|
+
assert_equal env['committee.query_hash']['integer'], 42
|
492
|
+
[204, {}, []]
|
493
|
+
end, schema: open_api_3_schema)
|
494
|
+
|
495
|
+
params = {integer: 21}
|
496
|
+
|
497
|
+
header "Content-Type", "application/json"
|
498
|
+
post '/overwrite_same_parameter?integer=42', JSON.generate(params)
|
499
|
+
assert_equal 204, last_response.status
|
500
|
+
end
|
501
|
+
|
502
|
+
it "path path parameter precedence over query parameter" do
|
503
|
+
@app = new_rack_app_with_lambda(lambda do |env|
|
504
|
+
assert_equal env['committee.params']['integer'], 84
|
505
|
+
assert_equal env['committee.params'][:integer], 84
|
506
|
+
assert_equal env['committee.request_body_hash']['integer'], 21
|
507
|
+
assert_equal env['committee.request_body_hash'][:integer], 21
|
508
|
+
assert_equal env['committee.query_hash']['integer'], 84 # we can't use query_parameter :(
|
509
|
+
assert_equal env['committee.path_hash']['integer'], 84
|
510
|
+
assert_equal env['committee.path_hash'][:integer], 84
|
511
|
+
[204, {}, []]
|
512
|
+
end, schema: open_api_3_schema)
|
513
|
+
|
514
|
+
params = {integer: 21}
|
515
|
+
|
516
|
+
header "Content-Type", "application/json"
|
517
|
+
post '/overwrite_same_parameter/84?integer=42', JSON.generate(params)
|
518
|
+
assert_equal 204, last_response.status
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
it "unpacker test" do
|
523
|
+
@app = new_rack_app_with_lambda(lambda do |env|
|
524
|
+
assert_equal '21', env['committee.params']['integer'] # query parameter has precedence
|
525
|
+
assert_equal '21', env['committee.params'][:integer]
|
526
|
+
assert_equal '21', env['rack.request.query_hash']['integer']
|
527
|
+
assert_equal 42, env['committee.request_body_hash']['integer']
|
528
|
+
[204, {}, []]
|
529
|
+
end, schema: open_api_3_schema, raise: true)
|
530
|
+
|
531
|
+
header "Content-Type", "application/x-www-form-urlencoded"
|
532
|
+
post '/validate?integer=21', "integer=42"
|
533
|
+
assert_equal 204, last_response.status
|
534
|
+
end
|
535
|
+
|
399
536
|
it "OpenAPI3 raise not support method" do
|
400
537
|
@app = new_rack_app(schema: open_api_3_schema)
|
401
538
|
|
402
539
|
e = assert_raises(RuntimeError) {
|
403
|
-
|
540
|
+
custom_request('TRACE', "/characters")
|
404
541
|
}
|
405
542
|
|
406
|
-
assert_equal 'Committee OpenAPI3 not support
|
407
|
-
end
|
543
|
+
assert_equal 'Committee OpenAPI3 not support trace method', e.message
|
544
|
+
end
|
408
545
|
|
409
546
|
describe 'check header' do
|
410
547
|
[
|
411
548
|
{ check_header: true, description: 'valid value', value: 1, expected: { status: 200 } },
|
412
549
|
{ check_header: true, description: 'missing value', value: nil, expected: { status: 400, error: 'missing required parameters: integer' } },
|
413
|
-
{ check_header: true, description: 'invalid value', value: 'x', expected: { status: 400, error: 'expected integer, but received String: x' } },
|
550
|
+
{ check_header: true, description: 'invalid value', value: 'x', expected: { status: 400, error: 'expected integer, but received String: \\"x\\"' } },
|
414
551
|
|
415
552
|
{ check_header: false, description: 'valid value', value: 1, expected: { status: 200 } },
|
416
553
|
{ check_header: false, description: 'missing value', value: nil, expected: { status: 200 } },
|
417
554
|
{ check_header: false, description: 'invalid value', value: 'x', expected: { status: 200 } },
|
418
|
-
].each do |
|
555
|
+
].each do |h|
|
556
|
+
check_header = h[:check_header]
|
557
|
+
description = h[:description]
|
558
|
+
value = h[:value]
|
559
|
+
expected = h[:expected]
|
419
560
|
describe "when #{check_header}" do
|
420
|
-
%w(get post put patch delete).each do |method|
|
561
|
+
%w(get post put patch delete options).each do |method|
|
421
562
|
describe method do
|
422
563
|
describe description do
|
423
564
|
it (expected[:error].nil? ? 'should pass' : 'should fail') do
|
@@ -441,7 +582,10 @@ describe Committee::Middleware::RequestValidation do
|
|
441
582
|
{ description: 'when not specified, includes everything', accept_request_filter: nil, expected: { status: 400 } },
|
442
583
|
{ description: 'when predicate matches, performs validation', accept_request_filter: -> (request) { request.path.start_with?('/v1/c') }, expected: { status: 400 } },
|
443
584
|
{ description: 'when predicate does not match, skips validation', accept_request_filter: -> (request) { request.path.start_with?('/v1/x') }, expected: { status: 200 } },
|
444
|
-
].each do |
|
585
|
+
].each do |h|
|
586
|
+
description = h[:description]
|
587
|
+
accept_request_filter = h[:accept_request_filter]
|
588
|
+
expected = h[:expected]
|
445
589
|
it description do
|
446
590
|
@app = new_rack_app(prefix: '/v1', schema: open_api_3_schema, accept_request_filter: accept_request_filter)
|
447
591
|
|
@@ -452,6 +596,16 @@ describe Committee::Middleware::RequestValidation do
|
|
452
596
|
end
|
453
597
|
end
|
454
598
|
|
599
|
+
it 'does not suppress application error' do
|
600
|
+
@app = new_rack_app_with_lambda(lambda { |_|
|
601
|
+
JSON.load('-') # invalid json
|
602
|
+
}, schema: open_api_3_schema, raise: true)
|
603
|
+
|
604
|
+
assert_raises(JSON::ParserError) do
|
605
|
+
get "/error", nil
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
455
609
|
private
|
456
610
|
|
457
611
|
def new_rack_app(options = {})
|
@@ -461,6 +615,9 @@ describe Committee::Middleware::RequestValidation do
|
|
461
615
|
end
|
462
616
|
|
463
617
|
def new_rack_app_with_lambda(check_lambda, options = {})
|
618
|
+
# TODO: delete when 5.0.0 released because default value changed
|
619
|
+
options[:parse_response_by_content_type] = true if options[:parse_response_by_content_type] == nil
|
620
|
+
|
464
621
|
Rack::Builder.new {
|
465
622
|
use Committee::Middleware::RequestValidation, options
|
466
623
|
run check_lambda
|
@@ -306,21 +306,6 @@ describe Committee::Middleware::RequestValidation do
|
|
306
306
|
assert_equal 200, last_response.status
|
307
307
|
end
|
308
308
|
|
309
|
-
it "calls error_handler (has a arg) when request is invalid" do
|
310
|
-
called_err = nil
|
311
|
-
pr = ->(e) { called_err = e }
|
312
|
-
@app = new_rack_app(schema: hyper_schema, error_handler: pr)
|
313
|
-
header "Content-Type", "application/json"
|
314
|
-
params = {
|
315
|
-
"name" => 1
|
316
|
-
}
|
317
|
-
_, err = capture_io do
|
318
|
-
post "/apps", JSON.generate(params)
|
319
|
-
end
|
320
|
-
assert_kind_of Committee::InvalidRequest, called_err
|
321
|
-
assert_match(/\[DEPRECATION\]/i, err)
|
322
|
-
end
|
323
|
-
|
324
309
|
it "calls error_handler (has two args) when request is invalid" do
|
325
310
|
called_err = nil
|
326
311
|
pr = ->(e, _env) { called_err = e }
|
@@ -341,18 +326,6 @@ describe Committee::Middleware::RequestValidation do
|
|
341
326
|
assert_match(/valid json/i, last_response.body)
|
342
327
|
end
|
343
328
|
|
344
|
-
it "calls error_handler (has a arg) when it rescues JSON errors" do
|
345
|
-
called_err = nil
|
346
|
-
pr = ->(e) { called_err = e }
|
347
|
-
@app = new_rack_app(schema: hyper_schema, error_handler: pr)
|
348
|
-
header "Content-Type", "application/json"
|
349
|
-
_, err = capture_io do
|
350
|
-
post "/apps", "{x:y}"
|
351
|
-
end
|
352
|
-
assert_kind_of JSON::ParserError, called_err
|
353
|
-
assert_match(/\[DEPRECATION\]/i, err)
|
354
|
-
end
|
355
|
-
|
356
329
|
it "calls error_handler (has two args) when it rescues JSON errors" do
|
357
330
|
called_err = nil
|
358
331
|
pr = ->(e, _env) { called_err = e }
|
@@ -435,6 +408,19 @@ describe Committee::Middleware::RequestValidation do
|
|
435
408
|
assert_equal 200, last_response.status
|
436
409
|
end
|
437
410
|
|
411
|
+
it "coerce form params" do
|
412
|
+
check_parameter = lambda { |env|
|
413
|
+
assert_equal 3, env['committee.params']['age']
|
414
|
+
assert_equal 3, env['committee.request_body_hash']['age']
|
415
|
+
[200, {}, []]
|
416
|
+
}
|
417
|
+
|
418
|
+
@app = new_rack_app_with_lambda(check_parameter, schema: open_api_2_form_schema, raise: true, allow_form_params: true, coerce_form_params: true)
|
419
|
+
header "Content-Type", "application/x-www-form-urlencoded"
|
420
|
+
post "/api/pets", "age=3&name=ab"
|
421
|
+
assert_equal 200, last_response.status
|
422
|
+
end
|
423
|
+
|
438
424
|
it "detects an invalid request for OpenAPI" do
|
439
425
|
@app = new_rack_app(schema: open_api_2_schema)
|
440
426
|
get "/api/pets?limit=foo", nil, { "HTTP_AUTH_TOKEN" => "xxx" }
|
@@ -495,7 +481,10 @@ describe Committee::Middleware::RequestValidation do
|
|
495
481
|
{ description: 'when not specified, includes everything', accept_request_filter: nil, expected: { status: 400 } },
|
496
482
|
{ description: 'when predicate matches, performs validation', accept_request_filter: -> (request) { request.path.start_with?('/v1/a') }, expected: { status: 400 } },
|
497
483
|
{ description: 'when predicate does not match, skips validation', accept_request_filter: -> (request) { request.path.start_with?('/v1/x') }, expected: { status: 200 } },
|
498
|
-
].each do |
|
484
|
+
].each do |h|
|
485
|
+
description = h[:description]
|
486
|
+
accept_request_filter = h[:accept_request_filter]
|
487
|
+
expected = h[:expected]
|
499
488
|
it description do
|
500
489
|
@app = new_rack_app(prefix: '/v1', schema: hyper_schema, accept_request_filter: accept_request_filter)
|
501
490
|
|
@@ -516,6 +505,9 @@ describe Committee::Middleware::RequestValidation do
|
|
516
505
|
|
517
506
|
|
518
507
|
def new_rack_app_with_lambda(check_lambda, options = {})
|
508
|
+
# TODO: delete when 5.0.0 released because default value changed
|
509
|
+
options[:parse_response_by_content_type] = true if options[:parse_response_by_content_type] == nil
|
510
|
+
|
519
511
|
Rack::Builder.new {
|
520
512
|
use Committee::Middleware::RequestValidation, options
|
521
513
|
run check_lambda
|
@@ -34,6 +34,14 @@ describe Committee::Middleware::ResponseValidation do
|
|
34
34
|
assert_equal 200, last_response.status
|
35
35
|
end
|
36
36
|
|
37
|
+
it "passes through a invalid json with parse_response_by_content_type option" do
|
38
|
+
@app = new_response_rack("csv response", { "Content-Type" => "test/csv"}, schema: open_api_3_schema, parse_response_by_content_type: true)
|
39
|
+
|
40
|
+
get "/csv"
|
41
|
+
|
42
|
+
assert_equal 200, last_response.status
|
43
|
+
end
|
44
|
+
|
37
45
|
it "passes through not definition" do
|
38
46
|
@app = new_response_rack(JSON.generate(CHARACTERS_RESPONSE), {}, schema: open_api_3_schema)
|
39
47
|
get "/no_data"
|
@@ -56,6 +64,12 @@ describe Committee::Middleware::ResponseValidation do
|
|
56
64
|
assert_equal 204, last_response.status
|
57
65
|
end
|
58
66
|
|
67
|
+
it "passes through a 304 (not modified) response" do
|
68
|
+
@app = new_response_rack("", {}, {schema: open_api_3_schema}, {status: 304})
|
69
|
+
post "/validate"
|
70
|
+
assert_equal 304, last_response.status
|
71
|
+
end
|
72
|
+
|
59
73
|
it "passes through a valid response with prefix" do
|
60
74
|
@app = new_response_rack(JSON.generate(CHARACTERS_RESPONSE), {}, schema: open_api_3_schema, prefix: "/v1")
|
61
75
|
get "/v1/characters"
|
@@ -78,7 +92,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
78
92
|
end
|
79
93
|
end
|
80
94
|
|
81
|
-
it "not parameter
|
95
|
+
it "not parameter request" do
|
82
96
|
@app = new_response_rack({integer: '1'}.to_json, {}, schema: open_api_3_schema, raise: true)
|
83
97
|
|
84
98
|
assert_raises(Committee::InvalidResponse) do
|
@@ -99,18 +113,36 @@ describe Committee::Middleware::ResponseValidation do
|
|
99
113
|
assert_match(/valid JSON/i, last_response.body)
|
100
114
|
end
|
101
115
|
|
116
|
+
describe "remote schema $ref" do
|
117
|
+
it "passes through a valid response" do
|
118
|
+
@app = new_response_rack(JSON.generate({ "sample" => "value" }), {}, schema: open_api_3_schema)
|
119
|
+
get "/ref-sample"
|
120
|
+
assert_equal 200, last_response.status
|
121
|
+
end
|
122
|
+
|
123
|
+
it "detects a invalid response" do
|
124
|
+
@app = new_response_rack("{}", {}, schema: open_api_3_schema)
|
125
|
+
get "/ref-sample"
|
126
|
+
assert_equal 500, last_response.status
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
102
130
|
describe 'check header' do
|
103
131
|
[
|
104
132
|
{ check_header: true, description: 'valid value', header: { 'integer' => 1 }, expected: { status: 200 } },
|
105
133
|
{ check_header: true, description: 'missing value', header: { 'integer' => nil }, expected: { error: 'headers/integer/schema does not allow null values' } },
|
106
|
-
{ check_header: true, description: 'invalid value', header: { 'integer' => 'x' }, expected: { error: 'headers/integer/schema expected integer, but received String: x' } },
|
134
|
+
{ check_header: true, description: 'invalid value', header: { 'integer' => 'x' }, expected: { error: 'headers/integer/schema expected integer, but received String: "x"' } },
|
107
135
|
|
108
136
|
{ check_header: false, description: 'valid value', header: { 'integer' => 1 }, expected: { status: 200 } },
|
109
137
|
{ check_header: false, description: 'missing value', header: { 'integer' => nil }, expected: { status: 200 } },
|
110
138
|
{ check_header: false, description: 'invalid value', header: { 'integer' => 'x' }, expected: { status: 200 } },
|
111
|
-
].each do |
|
139
|
+
].each do |h|
|
140
|
+
check_header = h[:check_header]
|
141
|
+
description = h[:description]
|
142
|
+
header = h[:header]
|
143
|
+
expected = h[:expected]
|
112
144
|
describe "when #{check_header}" do
|
113
|
-
%w(get post put patch delete).each do |method|
|
145
|
+
%w(get post put patch delete options).each do |method|
|
114
146
|
describe method do
|
115
147
|
describe description do
|
116
148
|
if expected[:error].nil?
|
@@ -151,7 +183,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
151
183
|
get "/characters"
|
152
184
|
end
|
153
185
|
|
154
|
-
assert_match(/but received String: 1/i, e.message)
|
186
|
+
assert_match(/but received String: \"1\"/i, e.message)
|
155
187
|
end
|
156
188
|
|
157
189
|
it "detects an invalid response status code with validate_success_only=true" do
|
@@ -174,7 +206,11 @@ describe Committee::Middleware::ResponseValidation do
|
|
174
206
|
{ description: 'when not specified, includes everything', accept_request_filter: nil, expected: { status: 500 } },
|
175
207
|
{ description: 'when predicate matches, performs validation', accept_request_filter: -> (request) { request.path.start_with?('/v1/c') }, expected: { status: 500 } },
|
176
208
|
{ description: 'when predicate does not match, skips validation', accept_request_filter: -> (request) { request.path.start_with?('/v1/x') }, expected: { status: 200 } },
|
177
|
-
].each do |
|
209
|
+
].each do |h|
|
210
|
+
description = h[:description]
|
211
|
+
accept_request_filter = h[:accept_request_filter]
|
212
|
+
expected = h[:expected]
|
213
|
+
|
178
214
|
it description do
|
179
215
|
@app = new_response_rack('not_json', {}, schema: open_api_3_schema, prefix: '/v1', accept_request_filter: accept_request_filter)
|
180
216
|
|
@@ -185,12 +221,65 @@ describe Committee::Middleware::ResponseValidation do
|
|
185
221
|
end
|
186
222
|
end
|
187
223
|
|
224
|
+
it 'does not suppress application error' do
|
225
|
+
@app = Rack::Builder.new {
|
226
|
+
use Committee::Middleware::ResponseValidation, {schema: open_api_3_schema, raise: true}
|
227
|
+
run lambda { |_|
|
228
|
+
JSON.load('-') # invalid json
|
229
|
+
}
|
230
|
+
}
|
231
|
+
|
232
|
+
assert_raises(JSON::ParserError) do
|
233
|
+
get "/error", nil
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
it "strict and invalid status" do
|
238
|
+
@app = new_response_rack(JSON.generate(CHARACTERS_RESPONSE), {}, {schema: open_api_3_schema, strict: true}, {status: 201})
|
239
|
+
get "/characters"
|
240
|
+
assert_equal 500, last_response.status
|
241
|
+
end
|
242
|
+
|
243
|
+
it "strict and invalid status with raise" do
|
244
|
+
@app = new_response_rack(JSON.generate(CHARACTERS_RESPONSE), {}, {schema: open_api_3_schema, strict: true, raise: true}, {status: 201})
|
245
|
+
|
246
|
+
assert_raises(Committee::InvalidResponse) do
|
247
|
+
get "/characters"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
it "strict and invalid content type" do
|
252
|
+
@app = new_response_rack("abc",
|
253
|
+
{},
|
254
|
+
{schema: open_api_3_schema, strict: true},
|
255
|
+
{content_type: 'application/text'}
|
256
|
+
)
|
257
|
+
get "/characters"
|
258
|
+
assert_equal 500, last_response.status
|
259
|
+
end
|
260
|
+
|
261
|
+
it "strict and invalid content type with raise" do
|
262
|
+
@app = new_response_rack("abc",
|
263
|
+
{},
|
264
|
+
{schema: open_api_3_schema, strict: true, raise: true},
|
265
|
+
{content_type: 'application/text'}
|
266
|
+
)
|
267
|
+
|
268
|
+
assert_raises(Committee::InvalidResponse) do
|
269
|
+
get "/characters"
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
188
273
|
private
|
189
274
|
|
190
275
|
def new_response_rack(response, headers = {}, options = {}, rack_options = {})
|
276
|
+
# TODO: delete when 5.0.0 released because default value changed
|
277
|
+
options[:parse_response_by_content_type] = true if options[:parse_response_by_content_type] == nil
|
278
|
+
|
191
279
|
status = rack_options[:status] || 200
|
280
|
+
content_type = rack_options[:content_type] || "application/json"
|
192
281
|
headers = {
|
193
|
-
"Content-Type" =>
|
282
|
+
"Content-Type" => content_type
|
194
283
|
}.merge(headers)
|
195
284
|
Rack::Builder.new {
|
196
285
|
use Committee::Middleware::ResponseValidation, options
|
@@ -15,6 +15,14 @@ describe Committee::Middleware::ResponseValidation do
|
|
15
15
|
assert_equal 200, last_response.status
|
16
16
|
end
|
17
17
|
|
18
|
+
# TODO: remove 5.0.0
|
19
|
+
it "passes through a valid response" do
|
20
|
+
# will show deprecated message
|
21
|
+
@app = new_rack_app(JSON.generate([ValidApp]), {}, schema: hyper_schema, strict: true)
|
22
|
+
get "/apps"
|
23
|
+
assert_equal 200, last_response.status
|
24
|
+
end
|
25
|
+
|
18
26
|
it "doesn't call error_handler (has a arg) when response is valid" do
|
19
27
|
called = false
|
20
28
|
pr = ->(_e) { called = true }
|
@@ -77,6 +85,12 @@ describe Committee::Middleware::ResponseValidation do
|
|
77
85
|
assert_equal 204, last_response.status
|
78
86
|
end
|
79
87
|
|
88
|
+
it "passes through a 304 (not modified) response" do
|
89
|
+
@app = new_rack_app("", {}, app_status: 304, schema: hyper_schema)
|
90
|
+
get "/apps"
|
91
|
+
assert_equal 304, last_response.status
|
92
|
+
end
|
93
|
+
|
80
94
|
it "skip validation when 4xx" do
|
81
95
|
@app = new_rack_app("[{x:y}]", {}, schema: hyper_schema, validate_success_only: true, app_status: 400)
|
82
96
|
get "/apps"
|
@@ -91,17 +105,6 @@ describe Committee::Middleware::ResponseValidation do
|
|
91
105
|
assert_match(/valid json/i, last_response.body)
|
92
106
|
end
|
93
107
|
|
94
|
-
it "calls error_handler (has a arg) when it rescues JSON errors" do
|
95
|
-
called_err = nil
|
96
|
-
pr = ->(e) { called_err = e }
|
97
|
-
@app = new_rack_app("[{x:y}]", {}, schema: hyper_schema, error_handler: pr)
|
98
|
-
_, err = capture_io do
|
99
|
-
get "/apps"
|
100
|
-
end
|
101
|
-
assert_kind_of JSON::ParserError, called_err
|
102
|
-
assert_match(/\[DEPRECATION\]/i, err)
|
103
|
-
end
|
104
|
-
|
105
108
|
it "calls error_handler (has two args) when it rescues JSON errors" do
|
106
109
|
called_err = nil
|
107
110
|
pr = ->(e, _env) { called_err = e }
|
@@ -124,20 +127,6 @@ describe Committee::Middleware::ResponseValidation do
|
|
124
127
|
end
|
125
128
|
end
|
126
129
|
|
127
|
-
it "calls error_handler (has a arg) when it rescues JSON errors" do
|
128
|
-
called_err = nil
|
129
|
-
pr = ->(e) { called_err = e }
|
130
|
-
@app = new_rack_app("[{x:y}]", {}, raise: true, schema: hyper_schema, error_handler: pr)
|
131
|
-
assert_raises(Committee::InvalidResponse) do
|
132
|
-
_, err = capture_io do
|
133
|
-
get "/apps"
|
134
|
-
end
|
135
|
-
|
136
|
-
assert_kind_of JSON::ParserError, called_err
|
137
|
-
assert_match(/\[DEPRECATION\]/i, err)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
130
|
it "calls error_handler (has two args) when it rescues JSON errors" do
|
142
131
|
called_err = nil
|
143
132
|
pr = ->(e, _env) { called_err = e }
|
@@ -167,7 +156,10 @@ describe Committee::Middleware::ResponseValidation do
|
|
167
156
|
{ description: 'when not specified, includes everything', accept_request_filter: nil, expected: { status: 500 } },
|
168
157
|
{ description: 'when predicate matches, performs validation', accept_request_filter: -> (request) { request.path.start_with?('/v1/a') }, expected: { status: 500 } },
|
169
158
|
{ description: 'when predicate does not match, skips validation', accept_request_filter: -> (request) { request.path.start_with?('/v1/x') }, expected: { status: 200 } },
|
170
|
-
].each do |
|
159
|
+
].each do |h|
|
160
|
+
description = h[:description]
|
161
|
+
accept_request_filter = h[:accept_request_filter]
|
162
|
+
expected = h[:expected]
|
171
163
|
it description do
|
172
164
|
@app = new_rack_app('not_json', {}, schema: hyper_schema, prefix: '/v1', accept_request_filter: accept_request_filter)
|
173
165
|
|
@@ -181,6 +173,9 @@ describe Committee::Middleware::ResponseValidation do
|
|
181
173
|
private
|
182
174
|
|
183
175
|
def new_rack_app(response, headers = {}, options = {})
|
176
|
+
# TODO: delete when 5.0.0 released because default value changed
|
177
|
+
options[:parse_response_by_content_type] = true if options[:parse_response_by_content_type] == nil
|
178
|
+
|
184
179
|
headers = {
|
185
180
|
"Content-Type" => "application/json"
|
186
181
|
}.merge(headers)
|
@@ -112,6 +112,10 @@ describe Committee::Middleware::Stub do
|
|
112
112
|
def new_rack_app(options = {})
|
113
113
|
response = options.delete(:response)
|
114
114
|
suppress = options.delete(:suppress)
|
115
|
+
|
116
|
+
# TODO: delete when 5.0.0 released because default value changed
|
117
|
+
options[:parse_response_by_content_type] = true if options[:parse_response_by_content_type] == nil
|
118
|
+
|
115
119
|
Rack::Builder.new {
|
116
120
|
use Committee::Middleware::Stub, options
|
117
121
|
run lambda { |env|
|