committee 2.5.1 → 3.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/lib/committee.rb +16 -5
  3. data/lib/committee/bin/committee_stub.rb +4 -0
  4. data/lib/committee/drivers.rb +12 -29
  5. data/lib/committee/drivers/hyper_schema.rb +9 -0
  6. data/lib/committee/drivers/open_api_2.rb +9 -20
  7. data/lib/committee/drivers/open_api_3.rb +70 -0
  8. data/lib/committee/errors.rb +3 -0
  9. data/lib/committee/middleware/base.rb +20 -62
  10. data/lib/committee/middleware/request_validation.rb +5 -62
  11. data/lib/committee/middleware/response_validation.rb +5 -19
  12. data/lib/committee/middleware/stub.rb +5 -1
  13. data/lib/committee/parameter_coercer.rb +1 -0
  14. data/lib/committee/request_unpacker.rb +2 -5
  15. data/lib/committee/schema_validator/hyper_schema.rb +92 -0
  16. data/lib/committee/{request_validator.rb → schema_validator/hyper_schema/request_validator.rb} +1 -1
  17. data/lib/committee/{response_generator.rb → schema_validator/hyper_schema/response_generator.rb} +1 -1
  18. data/lib/committee/{response_validator.rb → schema_validator/hyper_schema/response_validator.rb} +6 -6
  19. data/lib/committee/{router.rb → schema_validator/hyper_schema/router.rb} +10 -4
  20. data/lib/committee/{string_params_coercer.rb → schema_validator/hyper_schema/string_params_coercer.rb} +1 -1
  21. data/lib/committee/schema_validator/open_api_3.rb +67 -0
  22. data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +98 -0
  23. data/lib/committee/schema_validator/open_api_3/request_validator.rb +17 -0
  24. data/lib/committee/schema_validator/open_api_3/response_validator.rb +35 -0
  25. data/lib/committee/schema_validator/open_api_3/router.rb +26 -0
  26. data/lib/committee/schema_validator/option.rb +31 -0
  27. data/lib/committee/test/methods.rb +14 -117
  28. data/test/bin/committee_stub_test.rb +6 -0
  29. data/test/drivers/open_api_2_test.rb +0 -81
  30. data/test/drivers/open_api_3_test.rb +81 -0
  31. data/test/drivers_test.rb +2 -42
  32. data/test/middleware/base_test.rb +42 -21
  33. data/test/middleware/request_validation_open_api_3_test.rb +499 -0
  34. data/test/middleware/request_validation_test.rb +18 -0
  35. data/test/middleware/response_validation_open_api_3_test.rb +96 -0
  36. data/test/middleware/response_validation_test.rb +7 -30
  37. data/test/middleware/stub_test.rb +9 -0
  38. data/test/request_unpacker_test.rb +55 -21
  39. data/test/{request_validator_test.rb → schema_validator/hyper_schema/request_validator_test.rb} +4 -4
  40. data/test/{response_generator_test.rb → schema_validator/hyper_schema/response_generator_test.rb} +11 -11
  41. data/test/{response_validator_test.rb → schema_validator/hyper_schema/response_validator_test.rb} +3 -3
  42. data/test/{router_test.rb → schema_validator/hyper_schema/router_test.rb} +8 -10
  43. data/test/{string_params_coercer_test.rb → schema_validator/hyper_schema/string_params_coercer_test.rb} +3 -3
  44. data/test/schema_validator/open_api_3/operation_wrapper_test.rb +132 -0
  45. data/test/schema_validator/open_api_3/request_validator_test.rb +151 -0
  46. data/test/schema_validator/open_api_3/response_validator_test.rb +55 -0
  47. data/test/test/methods_new_version_test.rb +11 -20
  48. data/test/test/methods_test.rb +51 -55
  49. data/test/test_helper.rb +22 -8
  50. metadata +46 -18
@@ -4,6 +4,7 @@ describe Committee::Drivers do
4
4
  DRIVERS = [
5
5
  :hyper_schema,
6
6
  :open_api_2,
7
+ :open_api_3,
7
8
  ].freeze
8
9
 
9
10
  it "gets driver with .driver_from_name" do
@@ -19,48 +20,6 @@ describe Committee::Drivers do
19
20
  end
20
21
  assert_equal %{Committee: unknown driver "blueprint".}, e.message
21
22
  end
22
-
23
- describe 'load_from_file(schema_path)' do
24
- it 'load OpenAPI2' do
25
- s = Committee::Drivers.load_from_file(open_api_2_schema_path)
26
- assert_kind_of Committee::Drivers::Schema, s
27
- assert_kind_of Committee::Drivers::OpenAPI2::Schema, s
28
- end
29
-
30
- it 'load Hyper-Schema' do
31
- s = Committee::Drivers.load_from_file(hyper_schema_schema_path)
32
- assert_kind_of Committee::Drivers::Schema, s
33
- assert_kind_of Committee::Drivers::HyperSchema::Schema, s
34
- end
35
- end
36
-
37
- describe 'load_from_json(schema_path)' do
38
- it 'load OpenAPI2' do
39
- s = Committee::Drivers.load_from_json(open_api_2_schema_path)
40
- assert_kind_of Committee::Drivers::Schema, s
41
- assert_kind_of Committee::Drivers::OpenAPI2::Schema, s
42
- end
43
-
44
- it 'load Hyper-Schema' do
45
- s = Committee::Drivers.load_from_json(hyper_schema_schema_path)
46
- assert_kind_of Committee::Drivers::Schema, s
47
- assert_kind_of Committee::Drivers::HyperSchema::Schema, s
48
- end
49
- end
50
-
51
- describe 'load_from_data(schema_path)' do
52
- it 'load OpenAPI2' do
53
- s = Committee::Drivers.load_from_data(open_api_2_data)
54
- assert_kind_of Committee::Drivers::Schema, s
55
- assert_kind_of Committee::Drivers::OpenAPI2::Schema, s
56
- end
57
-
58
- it 'load Hyper-Schema' do
59
- s = Committee::Drivers.load_from_data(hyper_schema_data)
60
- assert_kind_of Committee::Drivers::Schema, s
61
- assert_kind_of Committee::Drivers::HyperSchema::Schema, s
62
- end
63
- end
64
23
  end
65
24
 
66
25
  describe Committee::Drivers::Driver do
@@ -88,6 +47,7 @@ end
88
47
  describe Committee::Drivers::Schema do
89
48
  SCHEMA_METHODS = {
90
49
  :driver => [],
50
+ :build_router => [validator_option: nil, prefix: nil]
91
51
  }
92
52
 
93
53
  it "has a set of abstract methods" do
@@ -19,8 +19,17 @@ describe Committee::Middleware::Base do
19
19
  assert_equal 200, last_response.status
20
20
  end
21
21
 
22
- it "accepts schema string (legacy behavior)" do
23
- mock(Committee).warn_deprecated.with_any_args
22
+ it "accepts just a OpenAPI3 schema object" do
23
+ @app = new_rack_app(open_api_3: open_api_3_schema)
24
+ params = {
25
+ "name" => "cloudnasium"
26
+ }
27
+ header "Content-Type", "application/json"
28
+ post "/apps", JSON.generate(params)
29
+ assert_equal 200, last_response.status
30
+ end
31
+
32
+ it "don't accepts schema string" do
24
33
  @app = new_rack_app(
25
34
  schema: JSON.dump(hyper_schema_data)
26
35
  )
@@ -28,12 +37,16 @@ describe Committee::Middleware::Base do
28
37
  "name" => "cloudnasium"
29
38
  }
30
39
  header "Content-Type", "application/json"
31
- post "/apps", JSON.generate(params)
32
- assert_equal 200, last_response.status
40
+
41
+ e = assert_raises(ArgumentError) do
42
+ post "/apps", JSON.generate(params)
43
+ end
44
+
45
+ assert_equal "Committee: schema expected to be an instance " +
46
+ "of Committee::Drivers::Schema.", e.message
33
47
  end
34
48
 
35
- it "accepts schema hash (legacy behavior)" do
36
- mock(Committee).warn_deprecated.with_any_args
49
+ it "don't accepts schema hash" do
37
50
  @app = new_rack_app(
38
51
  schema: hyper_schema_data
39
52
  )
@@ -41,12 +54,16 @@ describe Committee::Middleware::Base do
41
54
  "name" => "cloudnasium"
42
55
  }
43
56
  header "Content-Type", "application/json"
44
- post "/apps", JSON.generate(params)
45
- assert_equal 200, last_response.status
57
+
58
+ e = assert_raises(ArgumentError) do
59
+ post "/apps", JSON.generate(params)
60
+ end
61
+
62
+ assert_equal "Committee: schema expected to be an instance " +
63
+ "of Committee::Drivers::Schema.", e.message
46
64
  end
47
65
 
48
- it "accepts schema JsonSchema::Schema object (legacy behavior)" do
49
- mock(Committee).warn_deprecated.with_any_args
66
+ it "don't accepts schema JsonSchema::Schema object" do
50
67
  @app = new_rack_app(
51
68
  schema: JsonSchema.parse!(hyper_schema_data)
52
69
  )
@@ -54,8 +71,13 @@ describe Committee::Middleware::Base do
54
71
  "name" => "cloudnasium"
55
72
  }
56
73
  header "Content-Type", "application/json"
57
- post "/apps", JSON.generate(params)
58
- assert_equal 200, last_response.status
74
+
75
+ e = assert_raises(ArgumentError) do
76
+ post "/apps", JSON.generate(params)
77
+ end
78
+
79
+ assert_equal "Committee: schema expected to be an instance " +
80
+ "of Committee::Drivers::Schema.", e.message
59
81
  end
60
82
 
61
83
  it "doesn't accept other schema types" do
@@ -65,19 +87,18 @@ describe Committee::Middleware::Base do
65
87
  e = assert_raises(ArgumentError) do
66
88
  post "/apps"
67
89
  end
68
- assert_equal "Committee: schema expected to be an instance of Committee::Drivers::Schema.", e.message
90
+
91
+ assert_equal "Committee: schema expected to be an instance " +
92
+ "of Committee::Drivers::Schema.", e.message
69
93
  end
70
94
 
71
- describe 'initialize option' do
72
- it "schema_path option with hyper-schema" do
73
- b = Committee::Middleware::Base.new(nil, schema_path: hyper_schema_schema_path)
74
- assert_kind_of Committee::Drivers::HyperSchema::Schema, b.instance_variable_get(:@schema)
95
+ it "schema not exist" do
96
+ @app = new_rack_app
97
+ e = assert_raises(ArgumentError) do
98
+ post "/apps"
75
99
  end
76
100
 
77
- it "schema_path option with OpenAPI2" do
78
- b = Committee::Middleware::Base.new(nil, schema_path: open_api_2_schema_path)
79
- assert_kind_of Committee::Drivers::OpenAPI2::Schema, b.instance_variable_get(:@schema)
80
- end
101
+ assert_equal "Committee: need option `schema` or `open_api_3`", e.message
81
102
  end
82
103
 
83
104
  private
@@ -0,0 +1,499 @@
1
+ require_relative "../test_helper"
2
+
3
+ describe Committee::Middleware::RequestValidation do
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ @app
8
+ end
9
+
10
+ ARRAY_PROPERTY = [
11
+ {
12
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
13
+ "per_page" => 1,
14
+ "nested_coercer_object" => {
15
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
16
+ "threshold" => 1.5
17
+ },
18
+ "nested_no_coercer_object" => {
19
+ "per_page" => 1,
20
+ "threshold" => 1.5
21
+ },
22
+ "nested_coercer_array" => [
23
+ {
24
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
25
+ "threshold" => 1.5
26
+ }
27
+ ],
28
+ "nested_no_coercer_array" => [
29
+ {
30
+ "per_page" => 1,
31
+ "threshold" => 1.5
32
+ }
33
+ ],
34
+ "integer_array" => [
35
+ 1, 2, 3
36
+ ],
37
+ "datetime_array" => [
38
+ "2016-04-01T16:00:00.000+09:00",
39
+ "2016-04-01T17:00:00.000+09:00",
40
+ "2016-04-01T18:00:00.000+09:00"
41
+ ]
42
+ },
43
+ {
44
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
45
+ "per_page" => 1,
46
+ "threshold" => 1.5
47
+ },
48
+ {
49
+ "threshold" => 1.5,
50
+ "per_page" => 1
51
+ }
52
+ ]
53
+
54
+
55
+ it "OpenAPI3 pass through a valid request" do
56
+ @app = new_rack_app(open_api_3: open_api_3_schema)
57
+ params = {
58
+ "string_post_1" => "cloudnasium"
59
+ }
60
+ header "Content-Type", "application/json"
61
+ post "/characters", JSON.generate(params)
62
+
63
+ assert_equal 200, last_response.status
64
+ end
65
+
66
+ it "not parameter requset" do
67
+ check_parameter_string = lambda { |_|
68
+ [200, {integer: 1}, []]
69
+ }
70
+
71
+ @app = new_rack_app_with_lambda(check_parameter_string, open_api_3: open_api_3_schema)
72
+
73
+ put "/validate_no_parameter", {no_schema: 'no'}
74
+ end
75
+
76
+ it "passes given a datetime and with coerce_date_times enabled on GET endpoint" do
77
+ params = { "datetime_string" => "2016-04-01T16:00:00.000+09:00" }
78
+
79
+ check_parameter = lambda { |env|
80
+ assert_equal DateTime, env['rack.request.query_hash']["datetime_string"].class
81
+ [200, {}, []]
82
+ }
83
+
84
+ @app = new_rack_app_with_lambda(check_parameter, open_api_3: open_api_3_schema, coerce_date_times: true)
85
+
86
+ get "/string_params_coercer", params
87
+ assert_equal 200, last_response.status
88
+ end
89
+
90
+ it "passes given a datetime and with coerce_date_times enabled on POST endpoint" do
91
+ params = {
92
+ "nested_array" => [
93
+ {
94
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
95
+ "nested_coercer_object" => {
96
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
97
+ },
98
+ "nested_no_coercer_object" => {
99
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
100
+ },
101
+ "nested_coercer_array" => [
102
+ {
103
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
104
+ }
105
+ ],
106
+ "nested_no_coercer_array" => [
107
+ {
108
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
109
+ }
110
+ ]
111
+ },
112
+ {
113
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
114
+ }
115
+ ]
116
+ }
117
+
118
+ check_parameter = lambda { |env|
119
+ nested_array = env['committee.params']["nested_array"]
120
+ first_data = nested_array[0]
121
+ assert_kind_of DateTime, first_data["update_time"]
122
+
123
+ second_data = nested_array[1]
124
+ assert_kind_of DateTime, second_data["update_time"]
125
+
126
+ assert_kind_of DateTime, first_data["nested_coercer_object"]["update_time"]
127
+
128
+ assert_kind_of String, first_data["nested_no_coercer_object"]["update_time"]
129
+
130
+ assert_kind_of DateTime, first_data["nested_coercer_array"].first["update_time"]
131
+ assert_kind_of String, first_data["nested_no_coercer_array"].first["update_time"]
132
+ [200, {}, []]
133
+ }
134
+
135
+ @app = new_rack_app_with_lambda(check_parameter, open_api_3: open_api_3_schema, coerce_date_times: true, coerce_recursive: true)
136
+
137
+ post "/string_params_coercer", params
138
+
139
+ assert_equal 200, last_response.status
140
+ end
141
+
142
+ # TODO: support date-time object format check
143
+ =begin
144
+ it "passes given an invalid datetime string with coerce_date_times enabled" do
145
+ @app = new_rack_app(open_api_3: open_api_3_schema, coerce_date_times: true)
146
+ params = {
147
+ "datetime_string" => "invalid_datetime_format"
148
+ }
149
+ get "/string_params_coercer", JSON.generate(params)
150
+
151
+ assert_equal 400, last_response.status
152
+ assert_match(/invalid request/i, last_response.body)
153
+ end
154
+ =end
155
+
156
+ it "passes a nested object with recursive option" do
157
+ params = {
158
+ "nested_array" => [
159
+ {
160
+ "update_time" => "2016-04-01T16:00:00.000+09:00",
161
+ "per_page" => "1",
162
+ }
163
+ ],
164
+ }
165
+
166
+ check_parameter = lambda { |env|
167
+ hash = env['rack.request.query_hash']
168
+ assert_equal DateTime, hash['nested_array'].first['update_time'].class
169
+ assert_equal 1, hash['nested_array'].first['per_page']
170
+
171
+ [200, {}, []]
172
+ }
173
+
174
+ @app = new_rack_app_with_lambda(check_parameter,
175
+ coerce_query_params: true,
176
+ coerce_recursive: true,
177
+ coerce_date_times: true,
178
+ open_api_3: open_api_3_schema)
179
+
180
+ get "/string_params_coercer", params
181
+
182
+ assert_equal 200, last_response.status
183
+ end
184
+
185
+ =begin
186
+ it "passes a nested object with coerce_recursive default(true)" do
187
+ key_name = "update_time"
188
+ params = {
189
+ key_name => "2016-04-01T16:00:00.000+09:00",
190
+ "query" => "cloudnasium",
191
+ "array_property" => ARRAY_PROPERTY
192
+ }
193
+
194
+ @app = new_rack_app(coerce_query_params: true, coerce_date_times: true, schema: hyper_schema)
195
+
196
+ get "/search/apps", params
197
+ assert_equal 200, last_response.status # schema expect integer but we don't convert string to integer, so 400
198
+ end
199
+
200
+ it "passes given a nested datetime and with coerce_recursive=true and coerce_date_times=true on POST endpoint" do
201
+ key_name = "update_time"
202
+ params = {
203
+ key_name => "2016-04-01T16:00:00.000+09:00",
204
+ "array_property" => ARRAY_PROPERTY
205
+ }
206
+
207
+ check_parameter = lambda { |env|
208
+ hash = env['committee.params']
209
+ assert_equal DateTime, hash['array_property'].first['update_time'].class
210
+ assert_equal 1, hash['array_property'].first['per_page']
211
+ assert_equal DateTime, hash['array_property'].first['nested_coercer_object']['update_time'].class
212
+ assert_equal 1, hash['array_property'].first['nested_no_coercer_object']['per_page']
213
+ assert_equal 2, hash['array_property'].first['integer_array'][1]
214
+ assert_equal DateTime, hash['array_property'].first['datetime_array'][0].class
215
+ assert_equal DateTime, env['committee.params'][key_name].class
216
+ [200, {}, []]
217
+ }
218
+
219
+ @app = new_rack_app_with_lambda(check_parameter, coerce_date_times: true, coerce_recursive: true, schema: hyper_schema)
220
+
221
+ header "Content-Type", "application/json"
222
+ post "/apps", JSON.generate(params)
223
+ assert_equal 200, last_response.status
224
+ end
225
+
226
+ it "passes given a nested datetime and with coerce_recursive=true and coerce_date_times=true on POST endpoint" do
227
+ key_name = "update_time"
228
+ params = {
229
+ key_name => "2016-04-01T16:00:00.000+09:00",
230
+ "array_property" => ARRAY_PROPERTY
231
+ }
232
+
233
+ check_parameter = lambda { |env|
234
+ hash = env['committee.params']
235
+ assert_equal String, hash['array_property'].first['update_time'].class
236
+ assert_equal 1, hash['array_property'].first['per_page']
237
+ assert_equal String, hash['array_property'].first['nested_coercer_object']['update_time'].class
238
+ assert_equal 1, hash['array_property'].first['nested_no_coercer_object']['per_page']
239
+ assert_equal 2, hash['array_property'].first['integer_array'][1]
240
+ assert_equal String, hash['array_property'].first['datetime_array'][0].class
241
+ assert_equal String, env['committee.params'][key_name].class
242
+ [200, {}, []]
243
+ }
244
+
245
+ @app = new_rack_app_with_lambda(check_parameter, schema: hyper_schema)
246
+
247
+ header "Content-Type", "application/json"
248
+ post "/apps", JSON.generate(params)
249
+ assert_equal 200, last_response.status
250
+ end
251
+
252
+ it "passes given a nested datetime and with coerce_recursive=false and coerce_date_times=true on POST endpoint" do
253
+ key_name = "update_time"
254
+ params = {
255
+ key_name => "2016-04-01T16:00:00.000+09:00",
256
+ "array_property" => ARRAY_PROPERTY
257
+ }
258
+
259
+ check_parameter = lambda { |env|
260
+ hash = env['committee.params']
261
+ assert_equal String, hash['array_property'].first['update_time'].class
262
+ assert_equal 1, hash['array_property'].first['per_page']
263
+ assert_equal String, hash['array_property'].first['nested_coercer_object']['update_time'].class
264
+ assert_equal 1, hash['array_property'].first['nested_no_coercer_object']['per_page']
265
+ assert_equal 2, hash['array_property'].first['integer_array'][1]
266
+ assert_equal String, hash['array_property'].first['datetime_array'][0].class
267
+ assert_equal DateTime, env['committee.params'][key_name].class
268
+ [200, {}, []]
269
+ }
270
+
271
+ @app = new_rack_app_with_lambda(check_parameter, coerce_date_times: true, coerce_recursive: false, schema: hyper_schema)
272
+
273
+ header "Content-Type", "application/json"
274
+ post "/apps", JSON.generate(params)
275
+ assert_equal 200, last_response.status
276
+ end
277
+
278
+
279
+ it "OpenAPI3 passes given a nested datetime and with coerce_recursive=false and coerce_date_times=false on POST endpoint" do
280
+ key_name = "update_time"
281
+ params = {
282
+ key_name => "2016-04-01T16:00:00.000+09:00",
283
+ "array_property" => ARRAY_PROPERTY
284
+ }
285
+
286
+ check_parameter = lambda { |env|
287
+ hash = env['committee.params']
288
+ assert_equal String, hash['array_property'].first['update_time'].class
289
+ assert_equal 1, hash['array_property'].first['per_page']
290
+ assert_equal String, hash['array_property'].first['nested_coercer_object']['update_time'].class
291
+ assert_equal 1, hash['array_property'].first['nested_no_coercer_object']['per_page']
292
+ assert_equal 2, hash['array_property'].first['integer_array'][1]
293
+ assert_equal String, hash['array_property'].first['datetime_array'][0].class
294
+ assert_equal String, env['committee.params'][key_name].class
295
+ [200, {}, []]
296
+ }
297
+
298
+ @app = new_rack_app_with_lambda(check_parameter, open_api_3: open_api_3_schema)
299
+
300
+ header "Content-Type", "application/json"
301
+ post "/apps", JSON.generate(params)
302
+ assert_equal 200, last_response.status
303
+ end
304
+ =end
305
+
306
+ it "OpenAPI3 detects an invalid request" do
307
+ @app = new_rack_app(open_api_3: open_api_3_schema, strict: true)
308
+ header "Content-Type", "application/json"
309
+ params = {
310
+ "string_post_1" => 1
311
+ }
312
+ post "/characters", JSON.generate(params)
313
+ assert_equal 400, last_response.status
314
+ # FIXME: when ruby 2.3 dropped, fix because ruby 2.3 return Fixnum, ruby 2.4 or later return Integer
315
+ assert_match(/1 class is #{1.class}/i, last_response.body)
316
+ end
317
+
318
+ it "rescues JSON errors" do
319
+ @app = new_rack_app(open_api_3: open_api_3_schema)
320
+ header "Content-Type", "application/json"
321
+ post "/apps", "{x:y}"
322
+ assert_equal 400, last_response.status
323
+ assert_match(/valid json/i, last_response.body)
324
+ end
325
+
326
+ it "take a prefix" do
327
+ @app = new_rack_app(prefix: "/v1", open_api_3: open_api_3_schema)
328
+ params = {
329
+ "string_post_1" => "cloudnasium"
330
+ }
331
+ header "Content-Type", "application/json"
332
+ post "/v1/characters", JSON.generate(params)
333
+ assert_equal 200, last_response.status
334
+ end
335
+
336
+ it "ignores paths outside the prefix" do
337
+ @app = new_rack_app(prefix: "/v1", open_api_3: open_api_3_schema)
338
+ header "Content-Type", "text/html"
339
+ get "/hello"
340
+ assert_equal 200, last_response.status
341
+ end
342
+
343
+ it "OpenAPI3 pass not exist href" do
344
+ @app = new_rack_app(open_api_3: open_api_3_schema)
345
+ get "/unknown"
346
+ assert_equal 200, last_response.status
347
+ end
348
+
349
+ it "OpenAPI3 pass not exist href in strict mode" do
350
+ @app = new_rack_app(open_api_3: open_api_3_schema, strict: true)
351
+ get "/unknown"
352
+ assert_equal 404, last_response.status
353
+ end
354
+
355
+ it "optionally raises an error" do
356
+ @app = new_rack_app(raise: true, open_api_3: open_api_3_schema)
357
+ header "Content-Type", "application/json"
358
+ assert_raises(Committee::InvalidRequest) do
359
+ post "/characters", "{x:y}"
360
+ end
361
+ end
362
+
363
+ # TODO: support check_content_type
364
+ it "OpenAPI not support check_content_type" do
365
+ @app = new_rack_app(open_api_3: open_api_3_schema, check_content_type: true)
366
+
367
+ e = assert_raises(RuntimeError) {
368
+ post "/characters", {}
369
+ }
370
+
371
+ assert_equal 'OpenAPI3 not support @check_content_type option', e.message
372
+ end
373
+ =begin
374
+ it "optionally content_type check" do
375
+ @app = new_rack_app(check_content_type: true, open_api_3: open_api_3_schema)
376
+ params = {
377
+ "string_post_1" => "cloudnasium"
378
+ }
379
+ header "Content-Type", "text/html"
380
+ post "/characters", JSON.generate(params)
381
+ assert_equal 400, last_response.status
382
+ end
383
+ =end
384
+
385
+ it "optionally skip content_type check" do
386
+ @app = new_rack_app(check_content_type: false, open_api_3: open_api_3_schema)
387
+ params = {
388
+ "string_post_1" => "cloudnasium"
389
+ }
390
+ header "Content-Type", "text/html"
391
+ post "/characters", JSON.generate(params)
392
+ assert_equal 200, last_response.status
393
+ end
394
+
395
+ it "optionally coerces query params" do
396
+ @app = new_rack_app(coerce_query_params: true, open_api_3: open_api_3_schema)
397
+ header "Content-Type", "application/json"
398
+ get "/string_params_coercer", {"integer_1" => "1"}
399
+ assert_equal 200, last_response.status
400
+ end
401
+
402
+ =begin
403
+ it "still raises an error if query param coercion is not possible" do
404
+ @app = new_rack_app(coerce_query_params: true, schema: hyper_schema)
405
+ header "Content-Type", "application/json"
406
+ get "/search/apps", {"per_page" => "foo", "query" => "cloudnasium"}
407
+ assert_equal 400, last_response.status
408
+ assert_match(/invalid request/i, last_response.body)
409
+ end
410
+
411
+ it "passes through a valid request for OpenAPI" do
412
+ check_parameter = lambda { |env|
413
+ assert_equal 3, env['rack.request.query_hash']['limit']
414
+ [200, {}, []]
415
+ }
416
+
417
+ @app = new_rack_app_with_lambda(check_parameter, schema: open_api_2_schema)
418
+ get "/api/pets?limit=3", nil, { "HTTP_AUTH_TOKEN" => "xxx" }
419
+ assert_equal 200, last_response.status
420
+ end
421
+
422
+ it "detects an invalid request for OpenAPI" do
423
+ @app = new_rack_app(schema: open_api_2_schema)
424
+ get "/api/pets?limit=foo", nil, { "HTTP_AUTH_TOKEN" => "xxx" }
425
+ assert_equal 400, last_response.status
426
+ assert_match(/invalid request/i, last_response.body)
427
+ end
428
+
429
+ it "passes through a valid request for OpenAPI including path parameters" do
430
+ @app = new_rack_app(schema: open_api_2_schema)
431
+ # not that ID is expect to be an integer
432
+ get "/api/pets/123"
433
+ assert_equal 200, last_response.status
434
+ end
435
+
436
+ it "detects an invalid request for OpenAPI including path parameters" do
437
+ @app = new_rack_app(schema: open_api_2_schema)
438
+ # not that ID is expect to be an integer
439
+ get "/api/pets/not-integer"
440
+ assert_equal 400, last_response.status
441
+ assert_match(/invalid request/i, last_response.body)
442
+ end
443
+ =end
444
+
445
+ describe "coerce_path_params" do
446
+ it "coerce string to integer" do
447
+ check_parameter_string = lambda { |env|
448
+ assert env['committee.params']['integer'].is_a?(Integer)
449
+ [200, {}, []]
450
+ }
451
+
452
+ @app = new_rack_app_with_lambda(check_parameter_string, open_api_3: open_api_3_schema, coerce_path_params: true)
453
+ get "/coerce_path_params/1"
454
+ end
455
+
456
+ # TODO: support parameter validation
457
+ it "path parameter validation" do
458
+ @app = new_rack_app(open_api_3: open_api_3_schema, coerce_path_params: false)
459
+ get "/coerce_path_params/1"
460
+
461
+ assert true
462
+ end
463
+ =begin
464
+ it "path parameter validation" do
465
+ @app = new_rack_app_with_lambda(check_parameter_string, open_api_3: open_api_3_schema, coerce_path_params: false)
466
+ e = assert_raises(RuntimeError) {
467
+ get "/coerce_path_params/1"
468
+ }
469
+
470
+ assert_equal 'OpenAPI3 not support @coerce_query_params option', e.message
471
+ end
472
+ =end
473
+ end
474
+
475
+ it "OpenAPI3 raise not support method" do
476
+ @app = new_rack_app(open_api_3: open_api_3_schema)
477
+
478
+ e = assert_raises(RuntimeError) {
479
+ head "/characters", {}
480
+ }
481
+
482
+ assert_equal 'Committee OpenAPI3 not support head method', e.message
483
+ end
484
+
485
+ private
486
+
487
+ def new_rack_app(options = {})
488
+ new_rack_app_with_lambda(lambda { |_|
489
+ [200, {}, []]
490
+ }, options)
491
+ end
492
+
493
+ def new_rack_app_with_lambda(check_lambda, options = {})
494
+ Rack::Builder.new {
495
+ use Committee::Middleware::RequestValidation, options
496
+ run check_lambda
497
+ }
498
+ end
499
+ end