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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/committee/drivers/open_api_2/driver.rb +1 -2
  3. data/lib/committee/drivers/open_api_2/parameter_schema_builder.rb +1 -1
  4. data/lib/committee/drivers.rb +22 -10
  5. data/lib/committee/errors.rb +12 -0
  6. data/lib/committee/middleware/base.rb +5 -4
  7. data/lib/committee/middleware/request_validation.rb +4 -18
  8. data/lib/committee/middleware/response_validation.rb +15 -16
  9. data/lib/committee/request_unpacker.rb +46 -60
  10. data/lib/committee/schema_validator/hyper_schema/response_validator.rb +8 -2
  11. data/lib/committee/schema_validator/hyper_schema.rb +41 -27
  12. data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +44 -37
  13. data/lib/committee/schema_validator/open_api_3/request_validator.rb +11 -2
  14. data/lib/committee/schema_validator/open_api_3/router.rb +3 -1
  15. data/lib/committee/schema_validator/open_api_3.rb +52 -26
  16. data/lib/committee/schema_validator/option.rb +14 -3
  17. data/lib/committee/schema_validator.rb +1 -1
  18. data/lib/committee/test/methods.rb +27 -16
  19. data/lib/committee/test/schema_coverage.rb +101 -0
  20. data/lib/committee/utils.rb +28 -0
  21. data/lib/committee/validation_error.rb +3 -2
  22. data/lib/committee/version.rb +5 -0
  23. data/lib/committee.rb +11 -4
  24. data/test/bin/committee_stub_test.rb +5 -1
  25. data/test/committee_test.rb +29 -3
  26. data/test/drivers/open_api_3/driver_test.rb +1 -1
  27. data/test/drivers_test.rb +20 -7
  28. data/test/middleware/base_test.rb +9 -10
  29. data/test/middleware/request_validation_open_api_3_test.rb +175 -18
  30. data/test/middleware/request_validation_test.rb +20 -28
  31. data/test/middleware/response_validation_open_api_3_test.rb +96 -7
  32. data/test/middleware/response_validation_test.rb +21 -26
  33. data/test/middleware/stub_test.rb +4 -0
  34. data/test/request_unpacker_test.rb +51 -110
  35. data/test/schema_validator/hyper_schema/response_validator_test.rb +10 -0
  36. data/test/schema_validator/hyper_schema/router_test.rb +4 -0
  37. data/test/schema_validator/hyper_schema/string_params_coercer_test.rb +1 -1
  38. data/test/schema_validator/open_api_3/operation_wrapper_test.rb +72 -20
  39. data/test/schema_validator/open_api_3/request_validator_test.rb +27 -0
  40. data/test/schema_validator/open_api_3/response_validator_test.rb +26 -5
  41. data/test/test/methods_new_version_test.rb +17 -5
  42. data/test/test/methods_test.rb +155 -31
  43. data/test/test/schema_coverage_test.rb +216 -0
  44. data/test/test_helper.rb +34 -4
  45. metadata +47 -15
@@ -9,8 +9,18 @@ describe Committee::RequestUnpacker do
9
9
  "rack.input" => StringIO.new('{"x":"y"}'),
10
10
  }
11
11
  request = Rack::Request.new(env)
12
- params, _ = Committee::RequestUnpacker.new(request).call
13
- assert_equal({ "x" => "y" }, params)
12
+ unpacker = Committee::RequestUnpacker.new
13
+ assert_equal([{ "x" => "y" }, false], unpacker.unpack_request_params(request))
14
+ end
15
+
16
+ it "unpacks JSON on Content-Type: application/vnd.api+json" do
17
+ env = {
18
+ "CONTENT_TYPE" => "application/vnd.api+json",
19
+ "rack.input" => StringIO.new('{"x":"y"}'),
20
+ }
21
+ request = Rack::Request.new(env)
22
+ unpacker = Committee::RequestUnpacker.new
23
+ assert_equal([{ "x" => "y" }, false], unpacker.unpack_request_params(request))
14
24
  end
15
25
 
16
26
  it "unpacks JSON on no Content-Type" do
@@ -18,8 +28,18 @@ describe Committee::RequestUnpacker do
18
28
  "rack.input" => StringIO.new('{"x":"y"}'),
19
29
  }
20
30
  request = Rack::Request.new(env)
21
- params, _ = Committee::RequestUnpacker.new(request).call
22
- assert_equal({ "x" => "y" }, params)
31
+ unpacker = Committee::RequestUnpacker.new
32
+ assert_equal([{ "x" => "y" }, false], unpacker.unpack_request_params(request))
33
+ end
34
+
35
+ it "doesn't unpack JSON on application/x-ndjson" do
36
+ env = {
37
+ "CONTENT_TYPE" => "application/x-ndjson",
38
+ "rack.input" => StringIO.new('{"x":"y"}\n{"a":"b"}'),
39
+ }
40
+ request = Rack::Request.new(env)
41
+ unpacker = Committee::RequestUnpacker.new
42
+ assert_equal([{}, false], unpacker.unpack_request_params(request))
23
43
  end
24
44
 
25
45
  it "doesn't unpack JSON under other Content-Types" do
@@ -29,8 +49,8 @@ describe Committee::RequestUnpacker do
29
49
  "rack.input" => StringIO.new('{"x":"y"}'),
30
50
  }
31
51
  request = Rack::Request.new(env)
32
- params, _ = Committee::RequestUnpacker.new(request).call
33
- assert_equal({}, params)
52
+ unpacker = Committee::RequestUnpacker.new
53
+ assert_equal([{}, false], unpacker.unpack_request_params(request))
34
54
  end
35
55
  end
36
56
 
@@ -41,8 +61,8 @@ describe Committee::RequestUnpacker do
41
61
  "rack.input" => StringIO.new('{"x":"y"}'),
42
62
  }
43
63
  request = Rack::Request.new(env)
44
- params, _ = Committee::RequestUnpacker.new(request, optimistic_json: true).call
45
- assert_equal({ "x" => "y" }, params)
64
+ unpacker = Committee::RequestUnpacker.new(optimistic_json: true)
65
+ assert_equal([{ "x" => "y" }, false], unpacker.unpack_request_params(request))
46
66
  end
47
67
  end
48
68
 
@@ -53,8 +73,8 @@ describe Committee::RequestUnpacker do
53
73
  "rack.input" => StringIO.new('x=y&foo=42'),
54
74
  }
55
75
  request = Rack::Request.new(env)
56
- params, _ = Committee::RequestUnpacker.new(request, optimistic_json: true).call
57
- assert_equal({}, params)
76
+ unpacker = Committee::RequestUnpacker.new(optimistic_json: true)
77
+ assert_equal([{}, false], unpacker.unpack_request_params(request))
58
78
  end
59
79
  end
60
80
 
@@ -64,8 +84,8 @@ describe Committee::RequestUnpacker do
64
84
  "rack.input" => StringIO.new(""),
65
85
  }
66
86
  request = Rack::Request.new(env)
67
- params, _ = Committee::RequestUnpacker.new(request).call
68
- assert_equal({}, params)
87
+ unpacker = Committee::RequestUnpacker.new
88
+ assert_equal([{}, false], unpacker.unpack_request_params(request))
69
89
  end
70
90
 
71
91
  it "doesn't unpack form params" do
@@ -75,8 +95,8 @@ describe Committee::RequestUnpacker do
75
95
  "rack.input" => StringIO.new("x=y"),
76
96
  }
77
97
  request = Rack::Request.new(env)
78
- params, _ = Committee::RequestUnpacker.new(request).call
79
- assert_equal({}, params)
98
+ unpacker = Committee::RequestUnpacker.new
99
+ assert_equal([{}, false], unpacker.unpack_request_params(request))
80
100
  end
81
101
  end
82
102
 
@@ -87,87 +107,8 @@ describe Committee::RequestUnpacker do
87
107
  "rack.input" => StringIO.new("x=y"),
88
108
  }
89
109
  request = Rack::Request.new(env)
90
- params, _ = Committee::RequestUnpacker.new(request, allow_form_params: true).call
91
- assert_equal({ "x" => "y" }, params)
92
- end
93
- end
94
-
95
- it "coerces form params with coerce_form_params and a schema" do
96
- %w[application/x-www-form-urlencoded multipart/form-data].each do |content_type|
97
- env = {
98
- "CONTENT_TYPE" => content_type,
99
- "rack.input" => StringIO.new("x=1"),
100
- }
101
- request = Rack::Request.new(env)
102
-
103
- router = hyper_schema.build_router({})
104
- validator = router.build_schema_validator(request)
105
-
106
- schema = JsonSchema::Schema.new
107
- schema.properties = { "x" => JsonSchema::Schema.new }
108
- schema.properties["x"].type = ["integer"]
109
-
110
- link_class = Struct.new(:schema)
111
- link_object = link_class.new(schema)
112
-
113
- validator.instance_variable_set(:@link, link_object)
114
-
115
- params, _ = Committee::RequestUnpacker.new(
116
- request,
117
- allow_form_params: true,
118
- coerce_form_params: true,
119
- schema_validator: validator,
120
- ).call
121
- assert_equal({ "x" => 1 }, params)
122
- end
123
- end
124
-
125
- it "coerces form params with coerce_form_params and an OpenAPI3 schema" do
126
- %w[application/x-www-form-urlencoded multipart/form-data].each do |content_type|
127
- env = {
128
- "CONTENT_TYPE" => content_type,
129
- "rack.input" => StringIO.new("limit=20"),
130
- "PATH_INFO" => "/characters",
131
- "SCRIPT_NAME" => "",
132
- "REQUEST_METHOD" => "GET",
133
- }
134
- request = Rack::Request.new(env)
135
-
136
- router = open_api_3_schema.build_router({})
137
- validator = router.build_schema_validator(request)
138
-
139
- params, _ = Committee::RequestUnpacker.new(
140
- request,
141
- allow_form_params: true,
142
- coerce_form_params: true,
143
- schema_validator: validator,
144
- ).call
145
- # openapi3 not support coerce in request unpacker
146
- assert_equal({ "limit" => '20' }, params)
147
- end
148
- end
149
-
150
- it "coerces error params with coerce_form_params and a OpenAPI3 schema" do
151
- %w[application/x-www-form-urlencoded multipart/form-data].each do |content_type|
152
- env = {
153
- "CONTENT_TYPE" => content_type,
154
- "rack.input" => StringIO.new("limit=twenty"),
155
- "PATH_INFO" => "/characters",
156
- "SCRIPT_NAME" => "",
157
- "REQUEST_METHOD" => "GET",
158
- }
159
- request = Rack::Request.new(env)
160
-
161
- router = open_api_3_schema.build_router({})
162
- validator = router.build_schema_validator(request)
163
-
164
- params, _ = Committee::RequestUnpacker.new(
165
- request,
166
- allow_form_params: true,
167
- coerce_form_params: true,
168
- schema_validator: validator,
169
- ).call
170
- assert_equal({ "limit" => "twenty" }, params)
110
+ unpacker = Committee::RequestUnpacker.new(allow_form_params: true)
111
+ assert_equal([{ "x" => "y" }, true], unpacker.unpack_request_params(request))
171
112
  end
172
113
  end
173
114
 
@@ -179,8 +120,8 @@ describe Committee::RequestUnpacker do
179
120
  "QUERY_STRING" => "a=b"
180
121
  }
181
122
  request = Rack::Request.new(env)
182
- params, _ = Committee::RequestUnpacker.new(request, allow_form_params: true, allow_query_params: true).call
183
- assert_equal({ "x" => "y", "a" => "b" }, params)
123
+ unpacker = Committee::RequestUnpacker.new(allow_form_params: true, allow_query_params: true)
124
+ assert_equal([ { "x" => "y"}, true], unpacker.unpack_request_params(request))
184
125
  end
185
126
  end
186
127
 
@@ -190,8 +131,8 @@ describe Committee::RequestUnpacker do
190
131
  "QUERY_STRING" => "a=b"
191
132
  }
192
133
  request = Rack::Request.new(env)
193
- params, _ = Committee::RequestUnpacker.new(request, allow_query_params: true).call
194
- assert_equal({ "a" => "b" }, params)
134
+ unpacker = Committee::RequestUnpacker.new(allow_query_params: true)
135
+ assert_equal({ "a" => "b" }, unpacker.unpack_query_params(request))
195
136
  end
196
137
 
197
138
  it "errors if JSON is not an object" do
@@ -201,7 +142,7 @@ describe Committee::RequestUnpacker do
201
142
  }
202
143
  request = Rack::Request.new(env)
203
144
  assert_raises(Committee::BadRequest) do
204
- Committee::RequestUnpacker.new(request).call
145
+ Committee::RequestUnpacker.new.unpack_request_params(request)
205
146
  end
206
147
  end
207
148
 
@@ -211,8 +152,8 @@ describe Committee::RequestUnpacker do
211
152
  "rack.input" => StringIO.new('{"x":"y"}'),
212
153
  }
213
154
  request = Rack::Request.new(env)
214
- params, _ = Committee::RequestUnpacker.new(request).call
215
- assert_equal({}, params)
155
+ unpacker = Committee::RequestUnpacker.new
156
+ assert_equal([{}, false], unpacker.unpack_request_params(request))
216
157
  end
217
158
 
218
159
  # this is mostly here for line coverage
@@ -221,8 +162,8 @@ describe Committee::RequestUnpacker do
221
162
  "rack.input" => StringIO.new('{"x":[]}'),
222
163
  }
223
164
  request = Rack::Request.new(env)
224
- params, _ = Committee::RequestUnpacker.new(request).call
225
- assert_equal({ "x" => [] }, params)
165
+ unpacker = Committee::RequestUnpacker.new
166
+ assert_equal([{ "x" => [] }, false], unpacker.unpack_request_params(request))
226
167
  end
227
168
 
228
169
  it "unpacks http header" do
@@ -231,8 +172,8 @@ describe Committee::RequestUnpacker do
231
172
  "rack.input" => StringIO.new(""),
232
173
  }
233
174
  request = Rack::Request.new(env)
234
- _, headers = Committee::RequestUnpacker.new(request, { allow_header_params: true }).call
235
- assert_equal({ "FOO-BAR" => "some header value" }, headers)
175
+ unpacker = Committee::RequestUnpacker.new({ allow_header_params: true })
176
+ assert_equal({ "FOO-BAR" => "some header value" }, unpacker.unpack_headers(request))
236
177
  end
237
178
 
238
179
  it "includes request body when`use_get_body` is true" do
@@ -242,8 +183,8 @@ describe Committee::RequestUnpacker do
242
183
  "QUERY_STRING"=>"data=value&x=aaa",
243
184
  }
244
185
  request = Rack::Request.new(env)
245
- params, _ = Committee::RequestUnpacker.new(request, { allow_query_params: true, allow_get_body: true }).call
246
- assert_equal({ 'data' => 'value', 'x' => 1, 'y' => 2 }, params)
186
+ unpacker = Committee::RequestUnpacker.new({ allow_query_params: true, allow_get_body: true })
187
+ assert_equal([{ 'x' => 1, 'y' => 2 }, false], unpacker.unpack_request_params(request))
247
188
  end
248
189
 
249
190
  it "doesn't include request body when `use_get_body` is false" do
@@ -253,7 +194,7 @@ describe Committee::RequestUnpacker do
253
194
  "QUERY_STRING"=>"data=value&x=aaa",
254
195
  }
255
196
  request = Rack::Request.new(env)
256
- params, _ = Committee::RequestUnpacker.new(request, { allow_query_params: true, use_get_body: false }).call
257
- assert_equal({ 'data' => 'value', 'x' => 'aaa' }, params)
197
+ unpacker = Committee::RequestUnpacker.new({ allow_query_params: true, use_get_body: false })
198
+ assert_equal({ 'data' => 'value', 'x' => 'aaa' }, unpacker.unpack_query_params(request))
258
199
  end
259
200
  end
@@ -38,6 +38,11 @@ describe Committee::SchemaValidator::HyperSchema::ResponseValidator do
38
38
  call
39
39
  end
40
40
 
41
+ it "passes through a 304 Not Modified response" do
42
+ @status, @headers, @data = 304, {}, nil
43
+ call
44
+ end
45
+
41
46
  it "passes through a valid list response for for rel instances links" do
42
47
  @link = @list_link
43
48
 
@@ -91,6 +96,11 @@ describe Committee::SchemaValidator::HyperSchema::ResponseValidator do
91
96
  call
92
97
  end
93
98
 
99
+ it "allows no Content-Type for 304 Not Modified" do
100
+ @status, @headers = 304, {}
101
+ call
102
+ end
103
+
94
104
  it "raises errors generated by json_schema" do
95
105
  @data.merge!("name" => "%@!")
96
106
  e = assert_raises(Committee::InvalidResponse) { call }
@@ -69,6 +69,8 @@ describe Committee::SchemaValidator::HyperSchema::Router do
69
69
  end
70
70
 
71
71
  def hyper_schema_router(options = {})
72
+ # TODO: delete when 5.0.0 released because default value changed
73
+ options[:parse_response_by_content_type] = true if options[:parse_response_by_content_type] == nil
72
74
  schema = Committee::Drivers::HyperSchema::Driver.new.parse(hyper_schema_data)
73
75
  validator_option = Committee::SchemaValidator::Option.new(options, schema, :hyper_schema)
74
76
 
@@ -76,6 +78,8 @@ describe Committee::SchemaValidator::HyperSchema::Router do
76
78
  end
77
79
 
78
80
  def open_api_2_router(options = {})
81
+ # TODO: delete when 5.0.0 released because default value changed
82
+ options[:parse_response_by_content_type] = true if options[:parse_response_by_content_type] == nil
79
83
  schema = Committee::Drivers::OpenAPI2::Driver.new.parse(open_api_2_data)
80
84
  validator_option = Committee::SchemaValidator::Option.new(options, schema, :hyper_schema)
81
85
 
@@ -11,7 +11,7 @@ describe Committee::SchemaValidator::HyperSchema::StringParamsCoercer do
11
11
  end
12
12
 
13
13
  it "doesn't coerce params not in the schema" do
14
- check_convert("onwer", "admin", "admin")
14
+ check_convert("owner", "admin", "admin")
15
15
  end
16
16
 
17
17
  it "skips values for string param" do
@@ -9,7 +9,12 @@ describe Committee::SchemaValidator::OpenAPI3::OperationWrapper do
9
9
  before do
10
10
  @path = '/validate'
11
11
  @method = 'post'
12
- @validator_option = Committee::SchemaValidator::Option.new({}, open_api_3_schema, :open_api_3)
12
+
13
+ # TODO: delete when 5.0.0 released because default value changed
14
+ options = {}
15
+ options[:parse_response_by_content_type] = true if options[:parse_response_by_content_type] == nil
16
+
17
+ @validator_option = Committee::SchemaValidator::Option.new(options, open_api_3_schema, :open_api_3)
13
18
  end
14
19
 
15
20
  def operation_object
@@ -27,12 +32,15 @@ describe Committee::SchemaValidator::OpenAPI3::OperationWrapper do
27
32
  ]
28
33
 
29
34
  it 'correct data' do
30
- operation_object.validate_request_params(SCHEMA_PROPERTIES_PAIR.to_h, HEADER, @validator_option)
35
+ operation_object.validate_request_params({}, {}, SCHEMA_PROPERTIES_PAIR.to_h, HEADER, @validator_option)
31
36
  assert true
32
37
  end
33
38
 
34
39
  it 'correct object data' do
35
- operation_object.validate_request_params({
40
+ operation_object.validate_request_params(
41
+ {},
42
+ {},
43
+ {
36
44
  "object_1" =>
37
45
  {
38
46
  "string_1" => nil,
@@ -49,38 +57,39 @@ describe Committee::SchemaValidator::OpenAPI3::OperationWrapper do
49
57
 
50
58
  it 'invalid params' do
51
59
  e = assert_raises(Committee::InvalidRequest) {
52
- operation_object.validate_request_params({"string" => 1}, HEADER, @validator_option)
60
+ operation_object.validate_request_params({}, {}, {"string" => 1}, HEADER, @validator_option)
53
61
  }
54
62
 
55
- # FIXME: when ruby 2.3 dropped, fix because ruby 2.3 return Fixnum, ruby 2.4 or later return Integer
56
- assert_match(/expected string, but received #{1.class}: 1/i, e.message)
63
+ assert_match(/expected string, but received Integer: 1/i, e.message)
64
+ assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
57
65
  end
58
66
 
59
67
  it 'support put method' do
60
68
  @method = "put"
61
- operation_object.validate_request_params({"string" => "str"}, HEADER, @validator_option)
69
+ operation_object.validate_request_params({}, {}, {"string" => "str"}, HEADER, @validator_option)
62
70
 
63
71
  e = assert_raises(Committee::InvalidRequest) {
64
- operation_object.validate_request_params({"string" => 1}, HEADER, @validator_option)
72
+ operation_object.validate_request_params({}, {}, {"string" => 1}, HEADER, @validator_option)
65
73
  }
66
74
 
67
- # FIXME: when ruby 2.3 dropped, fix because ruby 2.3 return Fixnum, ruby 2.4 or later return Integer
68
- assert_match(/expected string, but received #{1.class}: 1/i, e.message)
75
+ assert_match(/expected string, but received Integer: 1/i, e.message)
76
+ assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
69
77
  end
70
78
 
71
79
  it 'support patch method' do
72
80
  @method = "patch"
73
- operation_object.validate_request_params({"integer" => 1}, HEADER, @validator_option)
81
+ operation_object.validate_request_params({}, {}, {"integer" => 1}, HEADER, @validator_option)
74
82
 
75
83
  e = assert_raises(Committee::InvalidRequest) {
76
- operation_object.validate_request_params({"integer" => "str"}, HEADER, @validator_option)
84
+ operation_object.validate_request_params({}, {}, {"integer" => "str"}, HEADER, @validator_option)
77
85
  }
78
86
 
79
- assert_match(/expected integer, but received String: str/i, e.message)
87
+ assert_match(/expected integer, but received String: "str"/i, e.message)
88
+ assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
80
89
  end
81
90
 
82
91
  it 'unknown param' do
83
- operation_object.validate_request_params({"unknown" => 1}, HEADER, @validator_option)
92
+ operation_object.validate_request_params({}, {}, {"unknown" => 1}, HEADER, @validator_option)
84
93
  end
85
94
 
86
95
  describe 'support get method' do
@@ -90,13 +99,17 @@ describe Committee::SchemaValidator::OpenAPI3::OperationWrapper do
90
99
 
91
100
  it 'correct' do
92
101
  operation_object.validate_request_params(
102
+ {},
93
103
  {"query_string" => "query", "query_integer_list" => [1, 2]},
104
+ {},
94
105
  HEADER,
95
106
  @validator_option
96
107
  )
97
108
 
98
109
  operation_object.validate_request_params(
110
+ {},
99
111
  {"query_string" => "query", "query_integer_list" => [1, 2], "optional_integer" => 1},
112
+ {},
100
113
  HEADER,
101
114
  @validator_option
102
115
  )
@@ -106,23 +119,26 @@ describe Committee::SchemaValidator::OpenAPI3::OperationWrapper do
106
119
 
107
120
  it 'not exist required' do
108
121
  e = assert_raises(Committee::InvalidRequest) {
109
- operation_object.validate_request_params({"query_integer_list" => [1, 2]}, HEADER, @validator_option)
122
+ operation_object.validate_request_params({}, {"query_integer_list" => [1, 2]}, {}, HEADER, @validator_option)
110
123
  }
111
124
 
112
125
  assert_match(/missing required parameters: query_string/i, e.message)
126
+ assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
113
127
  end
114
128
 
115
129
  it 'invalid type' do
116
130
  e = assert_raises(Committee::InvalidRequest) {
117
131
  operation_object.validate_request_params(
132
+ {},
118
133
  {"query_string" => 1, "query_integer_list" => [1, 2], "optional_integer" => 1},
134
+ {},
119
135
  HEADER,
120
136
  @validator_option
121
137
  )
122
138
  }
123
139
 
124
- # FIXME: when ruby 2.3 dropped, fix because ruby 2.3 return Fixnum, ruby 2.4 or later return Integer
125
- assert_match(/expected string, but received #{1.class}: 1/i, e.message)
140
+ assert_match(/expected string, but received Integer: 1/i, e.message)
141
+ assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
126
142
  end
127
143
  end
128
144
 
@@ -133,20 +149,56 @@ describe Committee::SchemaValidator::OpenAPI3::OperationWrapper do
133
149
  end
134
150
 
135
151
  it 'correct' do
136
- operation_object.validate_request_params({"limit" => "1"}, HEADER, @validator_option)
152
+ operation_object.validate_request_params({}, {"limit" => "1"}, {}, HEADER, @validator_option)
137
153
 
138
154
  assert true
139
155
  end
140
156
 
141
157
  it 'invalid type' do
142
158
  e = assert_raises(Committee::InvalidRequest) {
143
- operation_object.validate_request_params({"limit" => "a"}, HEADER, @validator_option)
159
+ operation_object.validate_request_params({}, {"limit" => "a"}, {}, HEADER, @validator_option)
144
160
  }
145
161
 
146
- assert_match(/expected integer, but received String: a/i, e.message)
162
+ assert_match(/expected integer, but received String: "a"/i, e.message)
163
+ assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
147
164
  end
148
165
  end
149
166
 
167
+ describe 'support head method' do
168
+ before do
169
+ @path = '/characters'
170
+ @method = 'head'
171
+ end
172
+
173
+ it 'correct' do
174
+ operation_object.validate_request_params({}, {"limit" => "1"}, {}, HEADER, @validator_option)
175
+
176
+ assert true
177
+ end
178
+
179
+ it 'invalid type' do
180
+ e = assert_raises(Committee::InvalidRequest) {
181
+ operation_object.validate_request_params({}, {"limit" => "a"}, {}, HEADER, @validator_option)
182
+ }
183
+
184
+ assert_match(/expected integer, but received String: "a"/i, e.message)
185
+ assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
186
+ end
187
+ end
188
+
189
+ it 'support options method' do
190
+ @method = "options"
191
+ operation_object.validate_request_params({}, {}, {"integer" => 1}, HEADER, @validator_option)
192
+
193
+ e = assert_raises(Committee::InvalidRequest) {
194
+ operation_object.validate_request_params({}, {}, {"integer" => "str"}, HEADER, @validator_option)
195
+ }
196
+
197
+ assert_match(/expected integer, but received String: "str"/i, e.message)
198
+ assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
199
+ end
200
+
201
+
150
202
  describe '#content_types' do
151
203
  it 'returns supported content types' do
152
204
  @path = '/validate_content_types'
@@ -71,7 +71,34 @@ describe Committee::SchemaValidator::OpenAPI3::RequestValidator do
71
71
  assert_equal 200, last_response.status
72
72
  end
73
73
 
74
+ it "skips content_type check with an empty body" do
75
+ @app = new_rack_app(check_content_type: true, schema: open_api_3_schema)
76
+ header "Content-Type", "application/x-www-form-urlencoded"
77
+ patch "/validate_empty_optional_body"
78
+ assert_equal 200, last_response.status
79
+ end
80
+
81
+ it "does not mix up parameters and requestBody" do
82
+ @app = new_rack_app(check_content_type: true, schema: open_api_3_schema)
83
+ params = {
84
+ "last_name" => "Skywalker"
85
+ }
86
+ header "Content-Type", "application/json"
87
+ post "/additional_properties?first_name=Luke", JSON.generate(params)
88
+ assert_equal 200, last_response.status
89
+ end
90
+
91
+ it "error because content_type check with body" do
92
+ @app = new_rack_app(check_content_type: true, schema: open_api_3_schema)
93
+ header "Content-Type", "application/x-www-form-urlencoded"
94
+ patch "/validate_empty_optional_body", "{}"
95
+ assert_equal 400, last_response.status
96
+ end
97
+
74
98
  def new_rack_app(options = {})
99
+ # TODO: delete when 5.0.0 released because default value changed
100
+ options[:parse_response_by_content_type] = true if options[:parse_response_by_content_type] == nil
101
+
75
102
  Rack::Builder.new {
76
103
  use Committee::Middleware::RequestValidation, options
77
104
  run lambda { |_|
@@ -12,7 +12,12 @@ describe Committee::SchemaValidator::OpenAPI3::ResponseValidator do
12
12
 
13
13
  @path = '/validate'
14
14
  @method = 'post'
15
- @validator_option = Committee::SchemaValidator::Option.new({}, open_api_3_schema, :open_api_3)
15
+
16
+ # TODO: delete when 5.0.0 released because default value changed
17
+ options = {}
18
+ options[:parse_response_by_content_type] = true if options[:parse_response_by_content_type] == nil
19
+
20
+ @validator_option = Committee::SchemaValidator::Option.new(options, open_api_3_schema, :open_api_3)
16
21
  end
17
22
 
18
23
  it "passes through a valid response" do
@@ -29,11 +34,21 @@ describe Committee::SchemaValidator::OpenAPI3::ResponseValidator do
29
34
  call_response_validator
30
35
  end
31
36
 
32
- it "passes through a valid response with no registered Content-Type with strict = true" do
37
+ it "raises InvalidResponse when a valid response with no registered body with strict option" do
33
38
  @headers = { "Content-Type" => "application/xml" }
34
- assert_raises(Committee::InvalidResponse) {
39
+ e = assert_raises(Committee::InvalidResponse) {
35
40
  call_response_validator(true)
36
41
  }
42
+ assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
43
+ end
44
+
45
+ it "raises InvalidResponse when a invalid status code with strict option" do
46
+ @status = 201
47
+ e = assert_raises(Committee::InvalidResponse) {
48
+ call_response_validator(true)
49
+ }
50
+
51
+ assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
37
52
  end
38
53
 
39
54
  it "passes through a valid response with no Content-Type" do
@@ -41,11 +56,12 @@ describe Committee::SchemaValidator::OpenAPI3::ResponseValidator do
41
56
  call_response_validator
42
57
  end
43
58
 
44
- it "passes through a valid response with no Content-Type with strict option" do
59
+ it "raises InvalidResponse when a valid response with no Content-Type headers with strict option" do
45
60
  @headers = {}
46
- assert_raises(Committee::InvalidResponse) {
61
+ e = assert_raises(Committee::InvalidResponse) {
47
62
  call_response_validator(true)
48
63
  }
64
+ assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
49
65
  end
50
66
 
51
67
  it "passes through a valid list response" do
@@ -60,6 +76,11 @@ describe Committee::SchemaValidator::OpenAPI3::ResponseValidator do
60
76
  call_response_validator
61
77
  end
62
78
 
79
+ it "passes through a 304 Not Modified response" do
80
+ @status, @headers, @data = 304, {}, nil
81
+ call_response_validator
82
+ end
83
+
63
84
  private
64
85
 
65
86
  def call_response_validator(strict = false)
@@ -30,13 +30,16 @@ describe Committee::Test::Methods do
30
30
  @committee_schema = nil
31
31
 
32
32
  @committee_options = {schema: hyper_schema}
33
+
34
+ # TODO: delete when 5.0.0 released because default value changed
35
+ @committee_options[:parse_response_by_content_type] = false
33
36
  end
34
37
 
35
38
  describe "#assert_schema_conform" do
36
39
  it "passes through a valid response" do
37
40
  @app = new_rack_app(JSON.generate([ValidApp]))
38
41
  get "/apps"
39
- assert_schema_conform
42
+ assert_schema_conform(200)
40
43
  end
41
44
 
42
45
  it "passes with prefix" do
@@ -44,30 +47,39 @@ describe Committee::Test::Methods do
44
47
 
45
48
  @app = new_rack_app(JSON.generate([ValidApp]))
46
49
  get "/v1/apps"
47
- assert_schema_conform
50
+ assert_schema_conform(200)
48
51
  end
49
52
 
50
53
  it "detects an invalid response Content-Type" do
51
54
  @app = new_rack_app(JSON.generate([ValidApp]), 200, {})
52
55
  get "/apps"
53
56
  e = assert_raises(Committee::InvalidResponse) do
54
- assert_schema_conform
57
+ assert_schema_conform(200)
55
58
  end
56
59
  assert_match(/response header must be set to/i, e.message)
57
60
  end
58
61
 
62
+ it "it detects unexpected response code" do
63
+ @app = new_rack_app(JSON.generate([ValidApp]), 400)
64
+ get "/apps"
65
+ e = assert_raises(Committee::InvalidResponse) do
66
+ assert_schema_conform(200)
67
+ end
68
+ assert_match(/Expected `200` status code, but it was `400`/i, e.message)
69
+ end
70
+
59
71
  it "detects an invalid response Content-Type but ignore because it's not success status code" do
60
72
  @committee_options.merge!(validate_success_only: true)
61
73
  @app = new_rack_app(JSON.generate([ValidApp]), 400, {})
62
74
  get "/apps"
63
- assert_schema_conform
75
+ assert_schema_conform(400)
64
76
  end
65
77
 
66
78
  it "detects an invalid response Content-Type and check all status code" do
67
79
  @app = new_rack_app(JSON.generate([ValidApp]), 400, {})
68
80
  get "/apps"
69
81
  e = assert_raises(Committee::InvalidResponse) do
70
- assert_schema_conform
82
+ assert_schema_conform(400)
71
83
  end
72
84
  assert_match(/response header must be set to/i, e.message)
73
85
  end