committee_firetail 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.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/bin/committee-stub +23 -0
  3. data/lib/committee/bin/committee_stub.rb +67 -0
  4. data/lib/committee/drivers/driver.rb +47 -0
  5. data/lib/committee/drivers/hyper_schema/driver.rb +105 -0
  6. data/lib/committee/drivers/hyper_schema/link.rb +68 -0
  7. data/lib/committee/drivers/hyper_schema/schema.rb +22 -0
  8. data/lib/committee/drivers/hyper_schema.rb +12 -0
  9. data/lib/committee/drivers/open_api_2/driver.rb +252 -0
  10. data/lib/committee/drivers/open_api_2/header_schema_builder.rb +33 -0
  11. data/lib/committee/drivers/open_api_2/link.rb +36 -0
  12. data/lib/committee/drivers/open_api_2/parameter_schema_builder.rb +83 -0
  13. data/lib/committee/drivers/open_api_2/schema.rb +26 -0
  14. data/lib/committee/drivers/open_api_2/schema_builder.rb +33 -0
  15. data/lib/committee/drivers/open_api_2.rb +13 -0
  16. data/lib/committee/drivers/open_api_3/driver.rb +51 -0
  17. data/lib/committee/drivers/open_api_3/schema.rb +41 -0
  18. data/lib/committee/drivers/open_api_3.rb +11 -0
  19. data/lib/committee/drivers/schema.rb +23 -0
  20. data/lib/committee/drivers.rb +84 -0
  21. data/lib/committee/errors.rb +36 -0
  22. data/lib/committee/middleware/base.rb +57 -0
  23. data/lib/committee/middleware/request_validation.rb +41 -0
  24. data/lib/committee/middleware/response_validation.rb +58 -0
  25. data/lib/committee/middleware/stub.rb +75 -0
  26. data/lib/committee/middleware.rb +11 -0
  27. data/lib/committee/request_unpacker.rb +91 -0
  28. data/lib/committee/schema_validator/hyper_schema/parameter_coercer.rb +79 -0
  29. data/lib/committee/schema_validator/hyper_schema/request_validator.rb +55 -0
  30. data/lib/committee/schema_validator/hyper_schema/response_generator.rb +102 -0
  31. data/lib/committee/schema_validator/hyper_schema/response_validator.rb +89 -0
  32. data/lib/committee/schema_validator/hyper_schema/router.rb +46 -0
  33. data/lib/committee/schema_validator/hyper_schema/string_params_coercer.rb +105 -0
  34. data/lib/committee/schema_validator/hyper_schema.rb +119 -0
  35. data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +139 -0
  36. data/lib/committee/schema_validator/open_api_3/request_validator.rb +52 -0
  37. data/lib/committee/schema_validator/open_api_3/response_validator.rb +29 -0
  38. data/lib/committee/schema_validator/open_api_3/router.rb +45 -0
  39. data/lib/committee/schema_validator/open_api_3.rb +120 -0
  40. data/lib/committee/schema_validator/option.rb +60 -0
  41. data/lib/committee/schema_validator.rb +23 -0
  42. data/lib/committee/test/methods.rb +84 -0
  43. data/lib/committee/test/schema_coverage.rb +101 -0
  44. data/lib/committee/utils.rb +28 -0
  45. data/lib/committee/validation_error.rb +26 -0
  46. data/lib/committee/version.rb +5 -0
  47. data/lib/committee.rb +40 -0
  48. data/test/bin/committee_stub_test.rb +57 -0
  49. data/test/bin_test.rb +25 -0
  50. data/test/committee_test.rb +77 -0
  51. data/test/drivers/hyper_schema/driver_test.rb +49 -0
  52. data/test/drivers/hyper_schema/link_test.rb +56 -0
  53. data/test/drivers/open_api_2/driver_test.rb +156 -0
  54. data/test/drivers/open_api_2/header_schema_builder_test.rb +26 -0
  55. data/test/drivers/open_api_2/link_test.rb +52 -0
  56. data/test/drivers/open_api_2/parameter_schema_builder_test.rb +195 -0
  57. data/test/drivers/open_api_3/driver_test.rb +84 -0
  58. data/test/drivers_test.rb +154 -0
  59. data/test/middleware/base_test.rb +130 -0
  60. data/test/middleware/request_validation_open_api_3_test.rb +626 -0
  61. data/test/middleware/request_validation_test.rb +516 -0
  62. data/test/middleware/response_validation_open_api_3_test.rb +291 -0
  63. data/test/middleware/response_validation_test.rb +189 -0
  64. data/test/middleware/stub_test.rb +145 -0
  65. data/test/request_unpacker_test.rb +200 -0
  66. data/test/schema_validator/hyper_schema/parameter_coercer_test.rb +111 -0
  67. data/test/schema_validator/hyper_schema/request_validator_test.rb +151 -0
  68. data/test/schema_validator/hyper_schema/response_generator_test.rb +142 -0
  69. data/test/schema_validator/hyper_schema/response_validator_test.rb +118 -0
  70. data/test/schema_validator/hyper_schema/router_test.rb +88 -0
  71. data/test/schema_validator/hyper_schema/string_params_coercer_test.rb +137 -0
  72. data/test/schema_validator/open_api_3/operation_wrapper_test.rb +218 -0
  73. data/test/schema_validator/open_api_3/request_validator_test.rb +110 -0
  74. data/test/schema_validator/open_api_3/response_validator_test.rb +92 -0
  75. data/test/test/methods_new_version_test.rb +97 -0
  76. data/test/test/methods_test.rb +363 -0
  77. data/test/test/schema_coverage_test.rb +216 -0
  78. data/test/test_helper.rb +120 -0
  79. data/test/validation_error_test.rb +25 -0
  80. metadata +328 -0
@@ -0,0 +1,626 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe Committee::Middleware::RequestValidation do
6
+ include Rack::Test::Methods
7
+
8
+ def app
9
+ @app
10
+ end
11
+
12
+ it "OpenAPI3 pass through a valid request" do
13
+ @app = new_rack_app(schema: open_api_3_schema)
14
+ params = {
15
+ "string_post_1" => "cloudnasium"
16
+ }
17
+ header "Content-Type", "application/json"
18
+ post "/characters", JSON.generate(params)
19
+
20
+ assert_equal 200, last_response.status
21
+ end
22
+
23
+ it "not parameter request" do
24
+ check_parameter_string = lambda { |_|
25
+ [200, {integer: 1}, []]
26
+ }
27
+
28
+ @app = new_rack_app_with_lambda(check_parameter_string, schema: open_api_3_schema)
29
+
30
+ put "/validate_no_parameter", {no_schema: 'no'}
31
+ end
32
+
33
+ it "passes given a datetime and with coerce_date_times enabled on GET endpoint" do
34
+ params = { "datetime_string" => "2016-04-01T16:00:00.000+09:00" }
35
+
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']
53
+ assert_equal DateTime, env['rack.request.query_hash']["datetime_string"].class
54
+ [200, {}, []]
55
+ }
56
+
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")
58
+
59
+ get "/string_params_coercer", params
60
+ assert_equal 200, last_response.status
61
+ end
62
+
63
+ it "passes given a valid parameter on GET endpoint with request body and allow_get_body=true" do
64
+ params = { "data" => "abc" }
65
+
66
+ @app = new_rack_app(schema: open_api_3_schema, allow_get_body: true)
67
+
68
+ get "/get_endpoint_with_required_parameter", { no_problem: true }, { input: params.to_json }
69
+ assert_equal 200, last_response.status
70
+ end
71
+
72
+ it "errors given valid parameter on GET endpoint with request body and allow_get_body=false" do
73
+ params = { "data" => "abc" }
74
+
75
+ @app = new_rack_app(schema: open_api_3_schema, allow_get_body: false)
76
+
77
+ get "/get_endpoint_with_required_parameter", { no_problem: true }, { input: params.to_json }
78
+ assert_equal 400, last_response.status
79
+ end
80
+
81
+ it "passes given a datetime and with coerce_date_times enabled on GET endpoint with request body" do
82
+ params = { "datetime_string" => "2016-04-01T16:00:00.000+09:00" }
83
+
84
+ check_parameter = lambda { |env|
85
+ assert_equal DateTime, env['committee.params']["datetime_string"].class
86
+ [200, {}, []]
87
+ }
88
+
89
+ @app = new_rack_app_with_lambda(check_parameter,
90
+ schema: open_api_3_schema,
91
+ coerce_date_times: true,
92
+ allow_get_body: true)
93
+
94
+ get "/string_params_coercer", { no_problem: true }, { input: params.to_json }
95
+ assert_equal 200, last_response.status
96
+ end
97
+
98
+ it "passes given a datetime and with coerce_date_times enabled on POST endpoint" do
99
+ params = {
100
+ "nested_array" => [
101
+ {
102
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
103
+ "nested_coercer_object" => {
104
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
105
+ },
106
+ "nested_no_coercer_object" => {
107
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
108
+ },
109
+ "nested_coercer_array" => [
110
+ {
111
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
112
+ }
113
+ ],
114
+ "nested_no_coercer_array" => [
115
+ {
116
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
117
+ }
118
+ ]
119
+ },
120
+ {
121
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
122
+ }
123
+ ]
124
+ }
125
+
126
+ check_parameter = lambda { |env|
127
+ nested_array = env['committee.params']["nested_array"]
128
+ first_data = nested_array[0]
129
+ assert_kind_of DateTime, first_data["update_time"]
130
+
131
+ second_data = nested_array[1]
132
+ assert_kind_of DateTime, second_data["update_time"]
133
+
134
+ assert_kind_of DateTime, first_data["nested_coercer_object"]["update_time"]
135
+
136
+ assert_kind_of String, first_data["nested_no_coercer_object"]["update_time"]
137
+
138
+ assert_kind_of DateTime, first_data["nested_coercer_array"].first["update_time"]
139
+ assert_kind_of String, first_data["nested_no_coercer_array"].first["update_time"]
140
+ [200, {}, []]
141
+ }
142
+
143
+ @app = new_rack_app_with_lambda(check_parameter, schema: open_api_3_schema, coerce_date_times: true, coerce_recursive: true)
144
+
145
+ header "Content-Type", "application/json"
146
+ post "/string_params_coercer", JSON.generate(params)
147
+
148
+ assert_equal 200, last_response.status
149
+ end
150
+
151
+ it "passes given an invalid datetime string with coerce_date_times enabled" do
152
+ @app = new_rack_app(schema: open_api_3_schema, coerce_date_times: true)
153
+ params = {
154
+ "datetime_string" => "invalid_datetime_format"
155
+ }
156
+ get "/string_params_coercer", params
157
+
158
+ assert_equal 400, last_response.status
159
+ assert_match(/invalid_datetime/i, last_response.body)
160
+ end
161
+
162
+ it "passes a nested object with recursive option" do
163
+ params = {
164
+ "nested_array" => [
165
+ {
166
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
167
+ "per_page" => "1",
168
+ }
169
+ ],
170
+ }
171
+
172
+ check_parameter = lambda { |env|
173
+ hash = env["committee.query_hash"]
174
+ assert_equal DateTime, hash['nested_array'].first['update_time'].class
175
+ assert_equal 1, hash['nested_array'].first['per_page']
176
+
177
+ [200, {}, []]
178
+ }
179
+
180
+ @app = new_rack_app_with_lambda(check_parameter,
181
+ coerce_query_params: true,
182
+ coerce_recursive: true,
183
+ coerce_date_times: true,
184
+ schema: open_api_3_schema)
185
+
186
+ get "/string_params_coercer", params
187
+
188
+ assert_equal 200, last_response.status
189
+ end
190
+
191
+ it "passes given a nested datetime and with coerce_recursive=true and coerce_date_times=true on POST endpoint" do
192
+ params = {
193
+ "nested_array" => [
194
+ {
195
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
196
+ "per_page" => 1,
197
+ "nested_coercer_object" => {
198
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
199
+ "threshold" => 1.5
200
+ },
201
+ "nested_no_coercer_object" => {
202
+ "per_page" => 1,
203
+ "threshold" => 1.5
204
+ },
205
+ "nested_coercer_array" => [
206
+ {
207
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
208
+ "threshold" => 1.5
209
+ }
210
+ ],
211
+ "nested_no_coercer_array" => [
212
+ {
213
+ "per_page" => 1,
214
+ "threshold" => 1.5
215
+ }
216
+ ],
217
+ "integer_array" => [
218
+ 1, 2, 3
219
+ ],
220
+ "datetime_array" => [
221
+ "2016-04-01T16:00:00.000+09:00",
222
+ "2016-04-01T17:00:00.000+09:00",
223
+ "2016-04-01T18:00:00.000+09:00"
224
+ ]
225
+ },
226
+ {
227
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
228
+ "per_page" => 1,
229
+ "threshold" => 1.5
230
+ },
231
+ {
232
+ "threshold" => 1.5,
233
+ "per_page" => 1
234
+ }
235
+ ]
236
+ }
237
+
238
+ check_parameter = lambda { |env|
239
+ hash = env['committee.params']
240
+ array = hash['nested_array']
241
+
242
+ assert_equal DateTime, array.first['update_time'].class
243
+ assert_equal 1, array.first['per_page']
244
+ assert_equal DateTime, array.first['nested_coercer_object']['update_time'].class
245
+ assert_equal 1, array.first['nested_no_coercer_object']['per_page']
246
+ assert_equal 2, array.first['integer_array'][1]
247
+ assert_equal DateTime, array.first['datetime_array'][0].class
248
+ [200, {}, []]
249
+ }
250
+
251
+ @app = new_rack_app_with_lambda(check_parameter,
252
+ coerce_date_times: true,
253
+ schema: open_api_3_schema)
254
+
255
+ header "Content-Type", "application/json"
256
+ post "/string_params_coercer", JSON.generate(params)
257
+ assert_equal 200, last_response.status
258
+ end
259
+
260
+ it "OpenAPI3 detects an invalid request" do
261
+ @app = new_rack_app(schema: open_api_3_schema, strict: true)
262
+ header "Content-Type", "application/json"
263
+ params = {
264
+ "string_post_1" => 1
265
+ }
266
+ post "/characters", JSON.generate(params)
267
+ assert_equal 400, last_response.status
268
+ assert_match(/expected string, but received Integer:/i, last_response.body)
269
+ end
270
+
271
+ it "rescues JSON errors" do
272
+ @app = new_rack_app(schema: open_api_3_schema)
273
+ header "Content-Type", "application/json"
274
+ post "/characters", "{x:y}"
275
+ assert_equal 400, last_response.status
276
+ assert_match(/valid json/i, last_response.body)
277
+ end
278
+
279
+ it "take a prefix" do
280
+ @app = new_rack_app(prefix: "/v1", schema: open_api_3_schema)
281
+ params = {
282
+ "string_post_1" => "cloudnasium"
283
+ }
284
+ header "Content-Type", "application/json"
285
+ post "/v1/characters", JSON.generate(params)
286
+ assert_equal 200, last_response.status
287
+ end
288
+
289
+ it "take a prefix with invalid data" do
290
+ @app = new_rack_app(prefix: "/v1", schema: open_api_3_schema)
291
+ params = {
292
+ "string_post_1" => 1
293
+ }
294
+ header "Content-Type", "application/json"
295
+ post "/v1/characters", JSON.generate(params)
296
+ assert_equal 400, last_response.status
297
+ assert_match(/expected string, but received Integer: /i, last_response.body)
298
+ end
299
+
300
+ it "ignores paths outside the prefix" do
301
+ @app = new_rack_app(prefix: "/v1", schema: open_api_3_schema)
302
+ params = {
303
+ "string_post_1" => 1
304
+ }
305
+ header "Content-Type", "application/json"
306
+ post "/characters", JSON.generate(params)
307
+ assert_equal 200, last_response.status
308
+ end
309
+
310
+ it "don't check prefix with no option" do
311
+ @app = new_rack_app(schema: open_api_3_schema)
312
+ params = {
313
+ "string_post_1" => 1
314
+ }
315
+ header "Content-Type", "application/json"
316
+ post "/v1/characters", JSON.generate(params)
317
+ assert_equal 200, last_response.status
318
+ end
319
+
320
+ it "OpenAPI3 pass not exist href" do
321
+ @app = new_rack_app(schema: open_api_3_schema)
322
+ get "/unknown"
323
+ assert_equal 200, last_response.status
324
+ end
325
+
326
+ it "OpenAPI3 pass not exist href in strict mode" do
327
+ @app = new_rack_app(schema: open_api_3_schema, strict: true)
328
+ get "/unknown"
329
+ assert_equal 404, last_response.status
330
+ end
331
+
332
+ it "OpenAPI3 parser not exist required key" do
333
+ @app = new_rack_app(raise: true, schema: open_api_3_schema)
334
+
335
+ e = assert_raises(Committee::InvalidRequest) do
336
+ get "/validate", nil
337
+ end
338
+
339
+ assert_match(/missing required parameters: query_string/i, e.message)
340
+ end
341
+
342
+ it "raises error when required path parameter is invalid" do
343
+ @app = new_rack_app(raise: true, schema: open_api_3_schema)
344
+
345
+ e = assert_raises(Committee::InvalidRequest) do
346
+ not_an_integer = 'abc'
347
+ get "/coerce_path_params/#{not_an_integer}", nil
348
+ end
349
+
350
+ assert_match(/expected integer, but received String: \"abc\"/i, e.message)
351
+ end
352
+
353
+ it "optionally raises an error" do
354
+ @app = new_rack_app(raise: true, schema: open_api_3_schema)
355
+ header "Content-Type", "application/json"
356
+ assert_raises(Committee::InvalidRequest) do
357
+ post "/characters", "{x:y}"
358
+ end
359
+ end
360
+
361
+ it "optionally coerces query params" do
362
+ @app = new_rack_app(coerce_query_params: true, schema: open_api_3_schema)
363
+ header "Content-Type", "application/json"
364
+ get "/string_params_coercer", {"integer_1" => "1"}
365
+ assert_equal 200, last_response.status
366
+ end
367
+
368
+ it "still raises an error if query param coercion is not possible" do
369
+ @app = new_rack_app(coerce_query_params: false, schema: open_api_3_schema)
370
+ header "Content-Type", "application/json"
371
+ get "/string_params_coercer", {"integer_1" => "1"}
372
+
373
+ assert_equal 400, last_response.status
374
+ assert_match(/expected integer, but received String:/i, last_response.body)
375
+ end
376
+
377
+ it "passes through a valid request for OpenAPI3" do
378
+ check_parameter = lambda { |env|
379
+ assert_equal 3, env['committee.query_hash']['limit'] #5.0.x-
380
+ [200, {}, []]
381
+ }
382
+
383
+ @app = new_rack_app_with_lambda(check_parameter, schema: open_api_3_schema)
384
+ get "/characters?limit=3"
385
+ assert_equal 200, last_response.status
386
+ end
387
+
388
+ it "detects an invalid request for OpenAPI" do
389
+ @app = new_rack_app(schema: open_api_3_schema)
390
+ get "/characters?limit=foo"
391
+
392
+ assert_equal 400, last_response.status
393
+ assert_match(/expected integer, but received String: \\"foo\\"/i, last_response.body)
394
+ end
395
+
396
+ it "ignores errors when ignore_error: true" do
397
+ @app = new_rack_app(schema: open_api_3_schema, ignore_error: true)
398
+ get "/characters?limit=foo"
399
+
400
+ assert_equal 200, last_response.status
401
+ end
402
+
403
+ it "coerce string to integer" do
404
+ check_parameter_string = lambda { |env|
405
+ assert env['committee.params']['integer'].is_a?(Integer)
406
+ [200, {}, []]
407
+ }
408
+
409
+ @app = new_rack_app_with_lambda(check_parameter_string, schema: open_api_3_schema, coerce_path_params: true)
410
+ get "/coerce_path_params/1"
411
+ end
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
+
536
+ it "OpenAPI3 raise not support method" do
537
+ @app = new_rack_app(schema: open_api_3_schema)
538
+
539
+ e = assert_raises(RuntimeError) {
540
+ custom_request('TRACE', "/characters")
541
+ }
542
+
543
+ assert_equal 'Committee OpenAPI3 not support trace method', e.message
544
+ end
545
+
546
+ describe 'check header' do
547
+ [
548
+ { check_header: true, description: 'valid value', value: 1, expected: { status: 200 } },
549
+ { check_header: true, description: 'missing value', value: nil, expected: { status: 400, error: 'missing required parameters: integer' } },
550
+ { check_header: true, description: 'invalid value', value: 'x', expected: { status: 400, error: 'expected integer, but received String: \\"x\\"' } },
551
+
552
+ { check_header: false, description: 'valid value', value: 1, expected: { status: 200 } },
553
+ { check_header: false, description: 'missing value', value: nil, expected: { status: 200 } },
554
+ { check_header: false, description: 'invalid value', value: 'x', expected: { status: 200 } },
555
+ ].each do |h|
556
+ check_header = h[:check_header]
557
+ description = h[:description]
558
+ value = h[:value]
559
+ expected = h[:expected]
560
+ describe "when #{check_header}" do
561
+ %w(get post put patch delete options).each do |method|
562
+ describe method do
563
+ describe description do
564
+ it (expected[:error].nil? ? 'should pass' : 'should fail') do
565
+ @app = new_rack_app(schema: open_api_3_schema, check_header: check_header)
566
+
567
+ header 'integer', value
568
+ send(method, "/header")
569
+
570
+ assert_equal expected[:status], last_response.status
571
+ assert_match(expected[:error], last_response.body) if expected[:error]
572
+ end
573
+ end
574
+ end
575
+ end
576
+ end
577
+ end
578
+ end
579
+
580
+ describe ':accept_request_filter' do
581
+ [
582
+ { description: 'when not specified, includes everything', accept_request_filter: nil, expected: { status: 400 } },
583
+ { description: 'when predicate matches, performs validation', accept_request_filter: -> (request) { request.path.start_with?('/v1/c') }, expected: { status: 400 } },
584
+ { description: 'when predicate does not match, skips validation', accept_request_filter: -> (request) { request.path.start_with?('/v1/x') }, expected: { status: 200 } },
585
+ ].each do |h|
586
+ description = h[:description]
587
+ accept_request_filter = h[:accept_request_filter]
588
+ expected = h[:expected]
589
+ it description do
590
+ @app = new_rack_app(prefix: '/v1', schema: open_api_3_schema, accept_request_filter: accept_request_filter)
591
+
592
+ post 'v1/characters', JSON.generate(string_post_1: 1)
593
+
594
+ assert_equal expected[:status], last_response.status
595
+ end
596
+ end
597
+ end
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
+
609
+ private
610
+
611
+ def new_rack_app(options = {})
612
+ new_rack_app_with_lambda(lambda { |_|
613
+ [200, {}, []]
614
+ }, options)
615
+ end
616
+
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
+
621
+ Rack::Builder.new {
622
+ use Committee::Middleware::RequestValidation, options
623
+ run check_lambda
624
+ }
625
+ end
626
+ end