committee 5.6.1 → 5.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/committee/drivers/open_api_3/driver.rb +5 -0
- data/lib/committee/drivers.rb +2 -3
- data/lib/committee/errors.rb +11 -0
- data/lib/committee/middleware/base.rb +11 -5
- data/lib/committee/middleware/options/base.rb +107 -0
- data/lib/committee/middleware/options/request_validation.rb +80 -0
- data/lib/committee/middleware/options/response_validation.rb +46 -0
- data/lib/committee/middleware/options.rb +12 -0
- data/lib/committee/middleware/request_validation.rb +7 -1
- data/lib/committee/middleware/response_validation.rb +6 -2
- data/lib/committee/middleware.rb +1 -0
- data/lib/committee/schema_validator/hyper_schema/response_generator.rb +1 -3
- data/lib/committee/schema_validator/hyper_schema/response_validator.rb +14 -1
- data/lib/committee/schema_validator/hyper_schema/router.rb +1 -1
- data/lib/committee/schema_validator/hyper_schema.rb +2 -13
- data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +43 -13
- data/lib/committee/schema_validator/open_api_3/parameter_deserializer.rb +495 -0
- data/lib/committee/schema_validator/open_api_3/response_validator.rb +16 -2
- data/lib/committee/schema_validator/open_api_3.rb +18 -12
- data/lib/committee/schema_validator/option.rb +6 -17
- data/lib/committee/schema_validator.rb +5 -1
- data/lib/committee/test/except_parameter.rb +416 -0
- data/lib/committee/test/methods.rb +27 -2
- data/lib/committee/version.rb +1 -1
- data/lib/committee.rb +1 -1
- data/test/drivers/open_api_2/driver_test.rb +4 -16
- data/test/drivers/open_api_2/parameter_schema_builder_test.rb +4 -50
- data/test/drivers_test.rb +35 -21
- data/test/middleware/options/base_test.rb +120 -0
- data/test/middleware/options/request_validation_test.rb +177 -0
- data/test/middleware/options/response_validation_test.rb +121 -0
- data/test/middleware/request_validation_open_api_3_test.rb +113 -80
- data/test/middleware/request_validation_test.rb +13 -70
- data/test/middleware/response_validation_open_api_3_test.rb +33 -17
- data/test/middleware/response_validation_test.rb +3 -14
- data/test/request_unpacker_test.rb +2 -10
- data/test/schema_validator/hyper_schema/parameter_coercer_test.rb +1 -37
- data/test/schema_validator/hyper_schema/request_validator_test.rb +6 -30
- data/test/schema_validator/hyper_schema/router_test.rb +5 -0
- data/test/schema_validator/hyper_schema/string_params_coercer_test.rb +1 -37
- data/test/schema_validator/open_api_3/operation_wrapper_test.rb +40 -43
- data/test/schema_validator/open_api_3/parameter_deserializer_test.rb +457 -0
- data/test/schema_validator/open_api_3/request_validator_test.rb +1 -2
- data/test/schema_validator/open_api_3/response_validator_test.rb +3 -11
- data/test/schema_validator_test.rb +8 -0
- data/test/test/methods_test.rb +222 -105
- data/test/test/schema_coverage_test.rb +8 -155
- metadata +12 -2
|
@@ -84,42 +84,14 @@ describe Committee::Middleware::RequestValidation do
|
|
|
84
84
|
[200, {}, []]
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
@app = new_rack_app_with_lambda(check_parameter,
|
|
88
|
-
schema: open_api_3_schema,
|
|
89
|
-
coerce_date_times: true,
|
|
90
|
-
allow_get_body: true)
|
|
87
|
+
@app = new_rack_app_with_lambda(check_parameter, schema: open_api_3_schema, coerce_date_times: true, allow_get_body: true)
|
|
91
88
|
|
|
92
89
|
get "/string_params_coercer", { no_problem: true }, { input: params.to_json }
|
|
93
90
|
assert_equal 200, last_response.status
|
|
94
91
|
end
|
|
95
92
|
|
|
96
93
|
it "passes given a datetime and with coerce_date_times enabled on POST endpoint" do
|
|
97
|
-
params = {
|
|
98
|
-
"nested_array" => [
|
|
99
|
-
{
|
|
100
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
101
|
-
"nested_coercer_object" => {
|
|
102
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
103
|
-
},
|
|
104
|
-
"nested_no_coercer_object" => {
|
|
105
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
106
|
-
},
|
|
107
|
-
"nested_coercer_array" => [
|
|
108
|
-
{
|
|
109
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
110
|
-
}
|
|
111
|
-
],
|
|
112
|
-
"nested_no_coercer_array" => [
|
|
113
|
-
{
|
|
114
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
115
|
-
}
|
|
116
|
-
]
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
120
|
-
}
|
|
121
|
-
]
|
|
122
|
-
}
|
|
94
|
+
params = { "nested_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "nested_coercer_object" => { "update_time" => "2016-04-01T16:00:00.000+09:00", }, "nested_no_coercer_object" => { "update_time" => "2016-04-01T16:00:00.000+09:00", }, "nested_coercer_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", }], "nested_no_coercer_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", }] }, { "update_time" => "2016-04-01T16:00:00.000+09:00", }] }
|
|
123
95
|
|
|
124
96
|
check_parameter = lambda { |env|
|
|
125
97
|
nested_array = env['committee.params']["nested_array"]
|
|
@@ -166,11 +138,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
166
138
|
[200, {}, []]
|
|
167
139
|
}
|
|
168
140
|
|
|
169
|
-
@app = new_rack_app_with_lambda(check_parameter,
|
|
170
|
-
coerce_query_params: true,
|
|
171
|
-
coerce_recursive: true,
|
|
172
|
-
coerce_date_times: true,
|
|
173
|
-
schema: open_api_3_schema)
|
|
141
|
+
@app = new_rack_app_with_lambda(check_parameter, coerce_query_params: true, coerce_recursive: true, coerce_date_times: true, schema: open_api_3_schema)
|
|
174
142
|
|
|
175
143
|
get "/string_params_coercer", params
|
|
176
144
|
|
|
@@ -178,51 +146,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
178
146
|
end
|
|
179
147
|
|
|
180
148
|
it "passes given a nested datetime and with coerce_recursive=true and coerce_date_times=true on POST endpoint" do
|
|
181
|
-
params = {
|
|
182
|
-
"nested_array" => [
|
|
183
|
-
{
|
|
184
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
185
|
-
"per_page" => 1,
|
|
186
|
-
"nested_coercer_object" => {
|
|
187
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
188
|
-
"threshold" => 1.5
|
|
189
|
-
},
|
|
190
|
-
"nested_no_coercer_object" => {
|
|
191
|
-
"per_page" => 1,
|
|
192
|
-
"threshold" => 1.5
|
|
193
|
-
},
|
|
194
|
-
"nested_coercer_array" => [
|
|
195
|
-
{
|
|
196
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
197
|
-
"threshold" => 1.5
|
|
198
|
-
}
|
|
199
|
-
],
|
|
200
|
-
"nested_no_coercer_array" => [
|
|
201
|
-
{
|
|
202
|
-
"per_page" => 1,
|
|
203
|
-
"threshold" => 1.5
|
|
204
|
-
}
|
|
205
|
-
],
|
|
206
|
-
"integer_array" => [
|
|
207
|
-
1, 2, 3
|
|
208
|
-
],
|
|
209
|
-
"datetime_array" => [
|
|
210
|
-
"2016-04-01T16:00:00.000+09:00",
|
|
211
|
-
"2016-04-01T17:00:00.000+09:00",
|
|
212
|
-
"2016-04-01T18:00:00.000+09:00"
|
|
213
|
-
]
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
217
|
-
"per_page" => 1,
|
|
218
|
-
"threshold" => 1.5
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
"threshold" => 1.5,
|
|
222
|
-
"per_page" => 1
|
|
223
|
-
}
|
|
224
|
-
]
|
|
225
|
-
}
|
|
149
|
+
params = { "nested_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "per_page" => 1, "nested_coercer_object" => { "update_time" => "2016-04-01T16:00:00.000+09:00", "threshold" => 1.5 }, "nested_no_coercer_object" => { "per_page" => 1, "threshold" => 1.5 }, "nested_coercer_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "threshold" => 1.5 }], "nested_no_coercer_array" => [{ "per_page" => 1, "threshold" => 1.5 }], "integer_array" => [1, 2, 3], "datetime_array" => ["2016-04-01T16:00:00.000+09:00", "2016-04-01T17:00:00.000+09:00", "2016-04-01T18:00:00.000+09:00"] }, { "update_time" => "2016-04-01T16:00:00.000+09:00", "per_page" => 1, "threshold" => 1.5 }, { "threshold" => 1.5, "per_page" => 1 }] }
|
|
226
150
|
|
|
227
151
|
check_parameter = lambda { |env|
|
|
228
152
|
hash = env['committee.params']
|
|
@@ -330,6 +254,14 @@ describe Committee::Middleware::RequestValidation do
|
|
|
330
254
|
assert_equal 200, last_response.status
|
|
331
255
|
end
|
|
332
256
|
|
|
257
|
+
it "ignores similar prefix paths outside the prefix in strict mode" do
|
|
258
|
+
@app = new_rack_app(prefix: "/v1", schema: open_api_3_schema, strict: true)
|
|
259
|
+
params = { "string_post_1" => 1 }
|
|
260
|
+
header "Content-Type", "application/json"
|
|
261
|
+
post "/v11/characters", JSON.generate(params)
|
|
262
|
+
assert_equal 200, last_response.status
|
|
263
|
+
end
|
|
264
|
+
|
|
333
265
|
it "don't check prefix with no option" do
|
|
334
266
|
@app = new_rack_app(schema: open_api_3_schema)
|
|
335
267
|
params = { "string_post_1" => 1 }
|
|
@@ -431,6 +363,26 @@ describe Committee::Middleware::RequestValidation do
|
|
|
431
363
|
get "/coerce_path_params/1"
|
|
432
364
|
end
|
|
433
365
|
|
|
366
|
+
it "coerces path params even when query param coercion is disabled" do
|
|
367
|
+
check_parameter_string = lambda { |env|
|
|
368
|
+
assert env['committee.path_hash']['integer'].is_a?(Integer)
|
|
369
|
+
assert env['committee.params']['integer'].is_a?(Integer)
|
|
370
|
+
[200, {}, []]
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
@app = new_rack_app_with_lambda(check_parameter_string, schema: open_api_3_schema, coerce_path_params: true, coerce_query_params: false)
|
|
374
|
+
get "/coerce_path_params/1"
|
|
375
|
+
assert_equal 200, last_response.status
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
it "does not coerce path params when path coercion is disabled even if query coercion is enabled" do
|
|
379
|
+
@app = new_rack_app(schema: open_api_3_schema, coerce_path_params: false, coerce_query_params: true)
|
|
380
|
+
get "/coerce_path_params/1"
|
|
381
|
+
|
|
382
|
+
assert_equal 400, last_response.status
|
|
383
|
+
assert_match(/integer/i, last_response.body)
|
|
384
|
+
end
|
|
385
|
+
|
|
434
386
|
describe "overwrite same parameter (old rule)" do
|
|
435
387
|
# (high priority) path_hash_key -> request_body_hash -> query_param
|
|
436
388
|
it "set query parameter to committee.params and query hash" do
|
|
@@ -627,6 +579,87 @@ describe Committee::Middleware::RequestValidation do
|
|
|
627
579
|
end
|
|
628
580
|
end
|
|
629
581
|
|
|
582
|
+
describe ':strict_query_params option' do
|
|
583
|
+
it 'allows unknown query params when strict_query_params is false' do
|
|
584
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: false)
|
|
585
|
+
get "/characters", { limit: 10, unknown_param: "value" }
|
|
586
|
+
assert_equal 200, last_response.status
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
it 'rejects unknown query params when strict_query_params is true' do
|
|
590
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
591
|
+
get "/characters", { limit: 10, unknown_param: "value" }
|
|
592
|
+
assert_equal 400, last_response.status
|
|
593
|
+
assert_match(/unknown_param/, last_response.body)
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
it 'allows defined query params when strict_query_params is true' do
|
|
597
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
598
|
+
get "/characters", { limit: 10, school_name: "test" }
|
|
599
|
+
assert_equal 200, last_response.status
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
it 'handles empty query params when strict_query_params is true' do
|
|
603
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
604
|
+
get "/characters"
|
|
605
|
+
assert_equal 200, last_response.status
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
it 'rejects multiple unknown query params' do
|
|
609
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
610
|
+
get "/characters", { limit: 10, unknown1: "a", unknown2: "b" }
|
|
611
|
+
assert_equal 400, last_response.status
|
|
612
|
+
assert_match(/unknown1/, last_response.body)
|
|
613
|
+
assert_match(/unknown2/, last_response.body)
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
it 'allows partial query params (only some defined params)' do
|
|
617
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
618
|
+
get "/characters", { limit: 10 }
|
|
619
|
+
assert_equal 200, last_response.status
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
it 'works with POST requests that have query params' do
|
|
623
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
624
|
+
header "Content-Type", "application/json"
|
|
625
|
+
post "/additional_properties?first_name=test&unknown=value", JSON.generate(last_name: "test")
|
|
626
|
+
assert_equal 400, last_response.status
|
|
627
|
+
assert_match(/unknown/, last_response.body)
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
it 'allows defined query params in POST requests' do
|
|
631
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
632
|
+
header "Content-Type", "application/json"
|
|
633
|
+
post "/additional_properties?first_name=test", JSON.generate(last_name: "test")
|
|
634
|
+
assert_equal 200, last_response.status
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
it 'works with DELETE requests' do
|
|
638
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
639
|
+
delete "/characters?limit=10"
|
|
640
|
+
assert_equal 200, last_response.status
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
it 'rejects unknown query params in DELETE requests' do
|
|
644
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
645
|
+
delete "/characters?limit=10&unknown=value"
|
|
646
|
+
assert_equal 400, last_response.status
|
|
647
|
+
assert_match(/unknown/, last_response.body)
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
it 'works with HEAD requests' do
|
|
651
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
652
|
+
head "/characters?limit=10"
|
|
653
|
+
assert_equal 200, last_response.status
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
it 'rejects unknown query params in HEAD requests' do
|
|
657
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
658
|
+
head "/characters?limit=10&unknown=value"
|
|
659
|
+
assert_equal 400, last_response.status
|
|
660
|
+
end
|
|
661
|
+
end
|
|
662
|
+
|
|
630
663
|
private
|
|
631
664
|
|
|
632
665
|
def new_rack_app(options = {})
|
|
@@ -9,49 +9,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
9
9
|
@app
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
ARRAY_PROPERTY = [
|
|
13
|
-
{
|
|
14
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
15
|
-
"per_page" => 1,
|
|
16
|
-
"nested_coercer_object" => {
|
|
17
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
18
|
-
"threshold" => 1.5
|
|
19
|
-
},
|
|
20
|
-
"nested_no_coercer_object" => {
|
|
21
|
-
"per_page" => 1,
|
|
22
|
-
"threshold" => 1.5
|
|
23
|
-
},
|
|
24
|
-
"nested_coercer_array" => [
|
|
25
|
-
{
|
|
26
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
27
|
-
"threshold" => 1.5
|
|
28
|
-
}
|
|
29
|
-
],
|
|
30
|
-
"nested_no_coercer_array" => [
|
|
31
|
-
{
|
|
32
|
-
"per_page" => 1,
|
|
33
|
-
"threshold" => 1.5
|
|
34
|
-
}
|
|
35
|
-
],
|
|
36
|
-
"integer_array" => [
|
|
37
|
-
1, 2, 3
|
|
38
|
-
],
|
|
39
|
-
"datetime_array" => [
|
|
40
|
-
"2016-04-01T16:00:00.000+09:00",
|
|
41
|
-
"2016-04-01T17:00:00.000+09:00",
|
|
42
|
-
"2016-04-01T18:00:00.000+09:00"
|
|
43
|
-
]
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
47
|
-
"per_page" => 1,
|
|
48
|
-
"threshold" => 1.5
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"threshold" => 1.5,
|
|
52
|
-
"per_page" => 1
|
|
53
|
-
}
|
|
54
|
-
]
|
|
12
|
+
ARRAY_PROPERTY = [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "per_page" => 1, "nested_coercer_object" => { "update_time" => "2016-04-01T16:00:00.000+09:00", "threshold" => 1.5 }, "nested_no_coercer_object" => { "per_page" => 1, "threshold" => 1.5 }, "nested_coercer_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "threshold" => 1.5 }], "nested_no_coercer_array" => [{ "per_page" => 1, "threshold" => 1.5 }], "integer_array" => [1, 2, 3], "datetime_array" => ["2016-04-01T16:00:00.000+09:00", "2016-04-01T17:00:00.000+09:00", "2016-04-01T18:00:00.000+09:00"] }, { "update_time" => "2016-04-01T16:00:00.000+09:00", "per_page" => 1, "threshold" => 1.5 }, { "threshold" => 1.5, "per_page" => 1 }]
|
|
55
13
|
|
|
56
14
|
it "passes through a valid request" do
|
|
57
15
|
@app = new_rack_app(schema: hyper_schema)
|
|
@@ -98,11 +56,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
98
56
|
|
|
99
57
|
it "passes a nested object with recursive option" do
|
|
100
58
|
key_name = "update_time"
|
|
101
|
-
params = {
|
|
102
|
-
key_name => "2016-04-01T16:00:00.000+09:00",
|
|
103
|
-
"query" => "cloudnasium",
|
|
104
|
-
"array_property" => ARRAY_PROPERTY
|
|
105
|
-
}
|
|
59
|
+
params = { key_name => "2016-04-01T16:00:00.000+09:00", "query" => "cloudnasium", "array_property" => ARRAY_PROPERTY }
|
|
106
60
|
|
|
107
61
|
check_parameter = lambda { |env|
|
|
108
62
|
hash = env['rack.request.query_hash']
|
|
@@ -116,11 +70,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
116
70
|
[200, {}, []]
|
|
117
71
|
}
|
|
118
72
|
|
|
119
|
-
@app = new_rack_app_with_lambda(check_parameter,
|
|
120
|
-
coerce_query_params: true,
|
|
121
|
-
coerce_recursive: true,
|
|
122
|
-
coerce_date_times: true,
|
|
123
|
-
schema: hyper_schema)
|
|
73
|
+
@app = new_rack_app_with_lambda(check_parameter, coerce_query_params: true, coerce_recursive: true, coerce_date_times: true, schema: hyper_schema)
|
|
124
74
|
|
|
125
75
|
get "/search/apps", params
|
|
126
76
|
assert_equal 200, last_response.status
|
|
@@ -128,11 +78,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
128
78
|
|
|
129
79
|
it "passes a nested object with coerce_recursive false" do
|
|
130
80
|
key_name = "update_time"
|
|
131
|
-
params = {
|
|
132
|
-
key_name => "2016-04-01T16:00:00.000+09:00",
|
|
133
|
-
"query" => "cloudnasium",
|
|
134
|
-
"array_property" => ARRAY_PROPERTY
|
|
135
|
-
}
|
|
81
|
+
params = { key_name => "2016-04-01T16:00:00.000+09:00", "query" => "cloudnasium", "array_property" => ARRAY_PROPERTY }
|
|
136
82
|
|
|
137
83
|
@app = new_rack_app(coerce_query_params: true, coerce_date_times: true, coerce_recursive: false, schema: hyper_schema)
|
|
138
84
|
|
|
@@ -142,11 +88,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
142
88
|
|
|
143
89
|
it "passes a nested object with coerce_recursive default(true)" do
|
|
144
90
|
key_name = "update_time"
|
|
145
|
-
params = {
|
|
146
|
-
key_name => "2016-04-01T16:00:00.000+09:00",
|
|
147
|
-
"query" => "cloudnasium",
|
|
148
|
-
"array_property" => ARRAY_PROPERTY
|
|
149
|
-
}
|
|
91
|
+
params = { key_name => "2016-04-01T16:00:00.000+09:00", "query" => "cloudnasium", "array_property" => ARRAY_PROPERTY }
|
|
150
92
|
|
|
151
93
|
@app = new_rack_app(coerce_query_params: true, coerce_date_times: true, schema: hyper_schema)
|
|
152
94
|
|
|
@@ -321,6 +263,13 @@ describe Committee::Middleware::RequestValidation do
|
|
|
321
263
|
assert_equal 200, last_response.status
|
|
322
264
|
end
|
|
323
265
|
|
|
266
|
+
it "ignores similar prefix paths outside the prefix in strict mode" do
|
|
267
|
+
@app = new_rack_app(prefix: "/v1", schema: hyper_schema, strict: true)
|
|
268
|
+
header "Content-Type", "application/json"
|
|
269
|
+
post "/v11/apps", JSON.generate({ "name" => 1 })
|
|
270
|
+
assert_equal 200, last_response.status
|
|
271
|
+
end
|
|
272
|
+
|
|
324
273
|
it "routes to paths not in schema" do
|
|
325
274
|
@app = new_rack_app(schema: hyper_schema)
|
|
326
275
|
get "/not-a-resource"
|
|
@@ -429,13 +378,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
429
378
|
end
|
|
430
379
|
|
|
431
380
|
it "not exist path and options" do
|
|
432
|
-
options = {
|
|
433
|
-
coerce_form_params: true,
|
|
434
|
-
coerce_date_times: true,
|
|
435
|
-
coerce_query_params: true,
|
|
436
|
-
coerce_path_params: true,
|
|
437
|
-
coerce_recursive: true
|
|
438
|
-
}
|
|
381
|
+
options = { coerce_form_params: true, coerce_date_times: true, coerce_query_params: true, coerce_path_params: true, coerce_recursive: true }
|
|
439
382
|
|
|
440
383
|
@app = new_rack_app({ schema: hyper_schema }.merge(options))
|
|
441
384
|
header "Content-Type", "application/x-www-form-urlencoded"
|
|
@@ -187,12 +187,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
187
187
|
|
|
188
188
|
describe 'validate error option' do
|
|
189
189
|
it "detects an invalid response status code" do
|
|
190
|
-
@app = new_response_rack({ integer: '1' }.to_json,
|
|
191
|
-
{},
|
|
192
|
-
app_status: 400,
|
|
193
|
-
schema: open_api_3_schema,
|
|
194
|
-
raise: true,
|
|
195
|
-
validate_success_only: false)
|
|
190
|
+
@app = new_response_rack({ integer: '1' }.to_json, {}, app_status: 400, schema: open_api_3_schema, raise: true, validate_success_only: false)
|
|
196
191
|
|
|
197
192
|
e = assert_raises(Committee::InvalidResponse) do
|
|
198
193
|
get "/characters"
|
|
@@ -202,12 +197,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
202
197
|
end
|
|
203
198
|
|
|
204
199
|
it "detects an invalid response status code with validate_success_only=true" do
|
|
205
|
-
@app = new_response_rack({ string_1: :honoka }.to_json,
|
|
206
|
-
{},
|
|
207
|
-
app_status: 400,
|
|
208
|
-
schema: open_api_3_schema,
|
|
209
|
-
raise: true,
|
|
210
|
-
validate_success_only: true)
|
|
200
|
+
@app = new_response_rack({ string_1: :honoka }.to_json, {}, app_status: 400, schema: open_api_3_schema, raise: true, validate_success_only: true)
|
|
211
201
|
|
|
212
202
|
get "/characters"
|
|
213
203
|
|
|
@@ -235,6 +225,36 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
235
225
|
end
|
|
236
226
|
end
|
|
237
227
|
|
|
228
|
+
describe 'response type validation' do
|
|
229
|
+
it "detects string value for number field when coerce_response_values is false" do
|
|
230
|
+
@app = new_response_rack({ integer: '726' }.to_json, {}, app_status: 400, schema: open_api_3_schema, raise: true, validate_success_only: false, coerce_response_values: false)
|
|
231
|
+
|
|
232
|
+
e = assert_raises(Committee::InvalidResponse) do
|
|
233
|
+
get "/characters"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
assert_match(/expected integer, but received String/i, e.message)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it "passes string value for number field when coerce_response_values is true" do
|
|
240
|
+
@app = new_response_rack({ integer: '726' }.to_json, {}, app_status: 400, schema: open_api_3_schema, raise: true, validate_success_only: false, coerce_response_values: true)
|
|
241
|
+
|
|
242
|
+
get "/characters"
|
|
243
|
+
|
|
244
|
+
assert_equal 400, last_response.status
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it "detects string value for number field by default (coerce_response_values defaults to false)" do
|
|
248
|
+
@app = new_response_rack({ integer: '726' }.to_json, {}, app_status: 400, schema: open_api_3_schema, raise: true, validate_success_only: false)
|
|
249
|
+
|
|
250
|
+
e = assert_raises(Committee::InvalidResponse) do
|
|
251
|
+
get "/characters"
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
assert_match(/expected integer, but received String/i, e.message)
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
238
258
|
it 'does not suppress application error' do
|
|
239
259
|
@app = Rack::Builder.new {
|
|
240
260
|
use Committee::Middleware::ResponseValidation, { schema: open_api_3_schema, raise: true }
|
|
@@ -269,11 +289,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
269
289
|
end
|
|
270
290
|
|
|
271
291
|
it "strict and invalid content type with raise" do
|
|
272
|
-
@app = new_response_rack("abc",
|
|
273
|
-
{},
|
|
274
|
-
{ schema: open_api_3_schema, strict: true, raise: true },
|
|
275
|
-
{ content_type: 'application/text' }
|
|
276
|
-
)
|
|
292
|
+
@app = new_response_rack("abc", {}, { schema: open_api_3_schema, strict: true, raise: true }, { content_type: 'application/text' })
|
|
277
293
|
|
|
278
294
|
assert_raises(Committee::InvalidResponse) do
|
|
279
295
|
get "/characters"
|
|
@@ -199,10 +199,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
199
199
|
describe 'streaming response' do
|
|
200
200
|
describe "text/event-stream; e.g. server-sent events" do
|
|
201
201
|
it 'validates the response stream as a string' do
|
|
202
|
-
options = {
|
|
203
|
-
schema: open_api_3_streaming_response_schema,
|
|
204
|
-
streaming_content_parsers: { 'text/event-stream' => ->(body) { body } },
|
|
205
|
-
}
|
|
202
|
+
options = { schema: open_api_3_streaming_response_schema, streaming_content_parsers: { 'text/event-stream' => ->(body) { body } }, }
|
|
206
203
|
status = 200
|
|
207
204
|
headers = { 'content-type' => 'text/event-stream' }
|
|
208
205
|
@app = Rack::Builder.new {
|
|
@@ -221,11 +218,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
221
218
|
it "successfully validates the response as a special stream using a customized parser" do
|
|
222
219
|
error_handler_called = false
|
|
223
220
|
error_handler = ->(_e, _env) { error_handler_called = true }
|
|
224
|
-
options = {
|
|
225
|
-
schema: open_api_3_streaming_response_schema,
|
|
226
|
-
streaming_content_parsers: { 'application/x-json-stream' => ->(body) { JSON.parse!(body) } },
|
|
227
|
-
error_handler: error_handler,
|
|
228
|
-
}
|
|
221
|
+
options = { schema: open_api_3_streaming_response_schema, streaming_content_parsers: { 'application/x-json-stream' => ->(body) { JSON.parse!(body) } }, error_handler: error_handler, }
|
|
229
222
|
status = 200
|
|
230
223
|
headers = { 'content-type' => 'application/x-json-stream' }
|
|
231
224
|
@app = Rack::Builder.new {
|
|
@@ -243,11 +236,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
243
236
|
it "fails to validate the response as a special stream using a customized parser due to a schema mismatch" do
|
|
244
237
|
error_handler_called = false
|
|
245
238
|
error_handler = ->(_e, _env) { error_handler_called = true }
|
|
246
|
-
options = {
|
|
247
|
-
schema: open_api_3_streaming_response_schema,
|
|
248
|
-
streaming_content_parsers: { 'application/x-json-stream' => ->(body) { JSON.parse!(body) } },
|
|
249
|
-
error_handler: error_handler,
|
|
250
|
-
}
|
|
239
|
+
options = { schema: open_api_3_streaming_response_schema, streaming_content_parsers: { 'application/x-json-stream' => ->(body) { JSON.parse!(body) } }, error_handler: error_handler, }
|
|
251
240
|
status = 200
|
|
252
241
|
headers = { 'content-type' => 'application/x-json-stream' }
|
|
253
242
|
@app = Rack::Builder.new {
|
|
@@ -137,22 +137,14 @@ describe Committee::RequestUnpacker do
|
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
it "includes request body when`use_get_body` is true" do
|
|
140
|
-
env = {
|
|
141
|
-
"rack.input" => StringIO.new('{"x":1, "y":2}'),
|
|
142
|
-
"REQUEST_METHOD" => "GET",
|
|
143
|
-
"QUERY_STRING" => "data=value&x=aaa",
|
|
144
|
-
}
|
|
140
|
+
env = { "rack.input" => StringIO.new('{"x":1, "y":2}'), "REQUEST_METHOD" => "GET", "QUERY_STRING" => "data=value&x=aaa", }
|
|
145
141
|
request = Rack::Request.new(env)
|
|
146
142
|
unpacker = Committee::RequestUnpacker.new({ allow_query_params: true, allow_get_body: true })
|
|
147
143
|
assert_equal([{ 'x' => 1, 'y' => 2 }, false], unpacker.unpack_request_params(request))
|
|
148
144
|
end
|
|
149
145
|
|
|
150
146
|
it "doesn't include request body when `use_get_body` is false" do
|
|
151
|
-
env = {
|
|
152
|
-
"rack.input" => StringIO.new('{"x":1, "y":2}'),
|
|
153
|
-
"REQUEST_METHOD" => "GET",
|
|
154
|
-
"QUERY_STRING" => "data=value&x=aaa",
|
|
155
|
-
}
|
|
147
|
+
env = { "rack.input" => StringIO.new('{"x":1, "y":2}'), "REQUEST_METHOD" => "GET", "QUERY_STRING" => "data=value&x=aaa", }
|
|
156
148
|
request = Rack::Request.new(env)
|
|
157
149
|
unpacker = Committee::RequestUnpacker.new({ allow_query_params: true, use_get_body: false })
|
|
158
150
|
assert_equal({ 'data' => 'value', 'x' => 'aaa' }, unpacker.unpack_query_params(request))
|
|
@@ -35,43 +35,7 @@ describe Committee::SchemaValidator::HyperSchema::ParameterCoercer do
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
it "pass array property" do
|
|
38
|
-
params = {
|
|
39
|
-
"array_property" => [
|
|
40
|
-
{
|
|
41
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
42
|
-
"per_page" => 1,
|
|
43
|
-
"nested_coercer_object" => {
|
|
44
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
45
|
-
"threshold" => 1.5
|
|
46
|
-
},
|
|
47
|
-
"nested_no_coercer_object" => {
|
|
48
|
-
"per_page" => 1,
|
|
49
|
-
"threshold" => 1.5
|
|
50
|
-
},
|
|
51
|
-
"nested_coercer_array" => [
|
|
52
|
-
{
|
|
53
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
54
|
-
"threshold" => 1.5
|
|
55
|
-
}
|
|
56
|
-
],
|
|
57
|
-
"nested_no_coercer_array" => [
|
|
58
|
-
{
|
|
59
|
-
"per_page" => 1,
|
|
60
|
-
"threshold" => 1.5
|
|
61
|
-
}
|
|
62
|
-
]
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
66
|
-
"per_page" => 1,
|
|
67
|
-
"threshold" => 1.5
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
"threshold" => 1.5,
|
|
71
|
-
"per_page" => 1
|
|
72
|
-
}
|
|
73
|
-
],
|
|
74
|
-
}
|
|
38
|
+
params = { "array_property" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "per_page" => 1, "nested_coercer_object" => { "update_time" => "2016-04-01T16:00:00.000+09:00", "threshold" => 1.5 }, "nested_no_coercer_object" => { "per_page" => 1, "threshold" => 1.5 }, "nested_coercer_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "threshold" => 1.5 }], "nested_no_coercer_array" => [{ "per_page" => 1, "threshold" => 1.5 }] }, { "update_time" => "2016-04-01T16:00:00.000+09:00", "per_page" => 1, "threshold" => 1.5 }, { "threshold" => 1.5, "per_page" => 1 }], }
|
|
75
39
|
call(params, coerce_date_times: true, coerce_recursive: true)
|
|
76
40
|
|
|
77
41
|
first_data = params["array_property"][0]
|
|
@@ -11,11 +11,7 @@ describe Committee::SchemaValidator::HyperSchema::RequestValidator do
|
|
|
11
11
|
@schema.expand_references!
|
|
12
12
|
# POST /apps/:id
|
|
13
13
|
@link = @schema.properties["app"].links[0]
|
|
14
|
-
@request = Rack::Request.new({
|
|
15
|
-
"CONTENT_TYPE" => "application/json",
|
|
16
|
-
"rack.input" => StringIO.new("{}"),
|
|
17
|
-
"REQUEST_METHOD" => "POST"
|
|
18
|
-
})
|
|
14
|
+
@request = Rack::Request.new({ "CONTENT_TYPE" => "application/json", "rack.input" => StringIO.new("{}"), "REQUEST_METHOD" => "POST" })
|
|
19
15
|
end
|
|
20
16
|
|
|
21
17
|
it "passes through a valid request with Content-Type options" do
|
|
@@ -29,22 +25,14 @@ describe Committee::SchemaValidator::HyperSchema::RequestValidator do
|
|
|
29
25
|
end
|
|
30
26
|
|
|
31
27
|
it "passes through a valid request with Content-Type options" do
|
|
32
|
-
@request =
|
|
33
|
-
Rack::Request.new({
|
|
34
|
-
"CONTENT_TYPE" => "application/json; charset=utf-8",
|
|
35
|
-
"rack.input" => StringIO.new("{}"),
|
|
36
|
-
})
|
|
28
|
+
@request = Rack::Request.new({ "CONTENT_TYPE" => "application/json; charset=utf-8", "rack.input" => StringIO.new("{}"), })
|
|
37
29
|
data = { "name" => "heroku-api", }
|
|
38
30
|
call(data)
|
|
39
31
|
end
|
|
40
32
|
|
|
41
33
|
it "detects an invalid request Content-Type" do
|
|
42
34
|
e = assert_raises(Committee::InvalidRequest) {
|
|
43
|
-
@request =
|
|
44
|
-
Rack::Request.new({
|
|
45
|
-
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
|
46
|
-
"rack.input" => StringIO.new("{}"),
|
|
47
|
-
})
|
|
35
|
+
@request = Rack::Request.new({ "CONTENT_TYPE" => "application/x-www-form-urlencoded", "rack.input" => StringIO.new("{}"), })
|
|
48
36
|
call({})
|
|
49
37
|
}
|
|
50
38
|
message = %{"Content-Type" request header must be set to "application/json".}
|
|
@@ -52,11 +40,7 @@ describe Committee::SchemaValidator::HyperSchema::RequestValidator do
|
|
|
52
40
|
end
|
|
53
41
|
|
|
54
42
|
it "allows skipping content_type check" do
|
|
55
|
-
@request =
|
|
56
|
-
Rack::Request.new({
|
|
57
|
-
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
|
58
|
-
"rack.input" => StringIO.new("{}"),
|
|
59
|
-
})
|
|
43
|
+
@request = Rack::Request.new({ "CONTENT_TYPE" => "application/x-www-form-urlencoded", "rack.input" => StringIO.new("{}"), })
|
|
60
44
|
call({}, {}, check_content_type: false)
|
|
61
45
|
end
|
|
62
46
|
|
|
@@ -73,11 +57,7 @@ describe Committee::SchemaValidator::HyperSchema::RequestValidator do
|
|
|
73
57
|
end
|
|
74
58
|
|
|
75
59
|
it "allows an invalid Content-Type with an empty body" do
|
|
76
|
-
@request =
|
|
77
|
-
Rack::Request.new({
|
|
78
|
-
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
|
79
|
-
"rack.input" => StringIO.new(""),
|
|
80
|
-
})
|
|
60
|
+
@request = Rack::Request.new({ "CONTENT_TYPE" => "application/x-www-form-urlencoded", "rack.input" => StringIO.new(""), })
|
|
81
61
|
call({})
|
|
82
62
|
end
|
|
83
63
|
|
|
@@ -103,11 +83,7 @@ describe Committee::SchemaValidator::HyperSchema::RequestValidator do
|
|
|
103
83
|
before do
|
|
104
84
|
@schema = open_api_2_schema
|
|
105
85
|
@link = @schema.routes['GET'][0][1]
|
|
106
|
-
@request = Rack::Request.new({
|
|
107
|
-
"CONTENT_TYPE" => "application/json",
|
|
108
|
-
"rack.input" => StringIO.new("{}"),
|
|
109
|
-
"REQUEST_METHOD" => "POST"
|
|
110
|
-
})
|
|
86
|
+
@request = Rack::Request.new({ "CONTENT_TYPE" => "application/json", "rack.input" => StringIO.new("{}"), "REQUEST_METHOD" => "POST" })
|
|
111
87
|
end
|
|
112
88
|
|
|
113
89
|
it "passes through a valid request" do
|
|
@@ -43,6 +43,11 @@ describe Committee::SchemaValidator::HyperSchema::Router do
|
|
|
43
43
|
refute_nil link
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
it "does not include a similar prefix path segment" do
|
|
47
|
+
link, _ = hyper_schema_router(prefix: "/kpi").includes?("/kpi2/apps")
|
|
48
|
+
assert_nil link
|
|
49
|
+
end
|
|
50
|
+
|
|
46
51
|
it "provides named parameters" do
|
|
47
52
|
link, param_matches = open_api_2_router.find_link("GET", "/api/pets/fido")
|
|
48
53
|
refute_nil link
|