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
@@ -11,11 +11,11 @@ module Committee
11
11
  @validator_option = validator_option
12
12
  end
13
13
 
14
- def call(request, params, headers)
14
+ def call(request, path_params, query_params, body_params, headers)
15
15
  content_type = ::Committee::SchemaValidator.request_media_type(request)
16
16
  check_content_type(request, content_type) if @validator_option.check_content_type
17
17
 
18
- @operation_object.validate_request_params(params, headers, @validator_option)
18
+ @operation_object.validate_request_params(path_params, query_params, body_params, headers, @validator_option)
19
19
  end
20
20
 
21
21
  private
@@ -24,6 +24,7 @@ module Committee
24
24
  # support post, put, patch only
25
25
  return true unless request.post? || request.put? || request.patch?
26
26
  return true if @operation_object.valid_request_content_type?(content_type)
27
+ return true if @operation_object.optional_body? && empty_request?(request)
27
28
 
28
29
  message = if valid_content_types.size > 1
29
30
  types = valid_content_types.map {|x| %{"#{x}"} }.join(', ')
@@ -37,6 +38,14 @@ module Committee
37
38
  def valid_content_types
38
39
  @operation_object&.request_content_types
39
40
  end
41
+
42
+ def empty_request?(request)
43
+ return true if !request.body
44
+
45
+ data = request.body.read
46
+ request.body.rewind
47
+ data.empty?
48
+ end
40
49
  end
41
50
  end
42
51
  end
@@ -22,8 +22,10 @@ module Committee
22
22
  end
23
23
 
24
24
  def operation_object(request)
25
+ return nil unless includes_request?(request)
26
+
25
27
  path = request.path
26
- path = path.gsub(@prefix_regexp, '') if prefix_request?(request)
28
+ path = path.gsub(@prefix_regexp, '') if @prefix_regexp
27
29
 
28
30
  request_method = request.request_method.downcase
29
31
 
@@ -14,15 +14,10 @@ module Committee
14
14
  def request_validate(request)
15
15
  return unless link_exist?
16
16
 
17
- path_params = validator_option.coerce_path_params ? coerce_path_params : {}
18
-
19
17
  request_unpack(request)
20
-
21
- request.env[validator_option.params_key]&.merge!(path_params) unless path_params.empty?
22
-
23
18
  request_schema_validation(request)
24
19
 
25
- copy_coerced_data_to_query_hash(request)
20
+ copy_coerced_data_to_params(request)
26
21
  end
27
22
 
28
23
  def response_validate(status, headers, response, test_method = false)
@@ -30,8 +25,16 @@ module Committee
30
25
  response.each do |chunk|
31
26
  full_body << chunk
32
27
  end
33
- data = full_body.empty? ? {} : JSON.parse(full_body)
34
28
 
29
+ parse_to_json = !validator_option.parse_response_by_content_type ||
30
+ headers.fetch('Content-Type', nil)&.start_with?('application/json')
31
+ data = if parse_to_json
32
+ full_body.empty? ? {} : JSON.parse(full_body)
33
+ else
34
+ full_body
35
+ end
36
+
37
+ # TODO: refactoring name
35
38
  strict = test_method
36
39
  Committee::SchemaValidator::OpenAPI3::ResponseValidator.
37
40
  new(@operation_object, validator_option).
@@ -42,23 +45,32 @@ module Committee
42
45
  !@operation_object.nil?
43
46
  end
44
47
 
45
- def coerce_form_params(_parameter)
46
- # Empty because request_schema_validation checks and coerces
47
- end
48
-
49
48
  private
50
49
 
51
50
  attr_reader :validator_option
52
51
 
53
52
  def coerce_path_params
54
- @operation_object.coerce_path_parameter(@validator_option)
53
+ return Committee::Utils.indifferent_hash unless validator_option.coerce_path_params
54
+ Committee::RequestUnpacker.indifferent_params(@operation_object.coerce_path_parameter(@validator_option))
55
55
  end
56
56
 
57
57
  def request_schema_validation(request)
58
58
  return unless @operation_object
59
59
 
60
60
  validator = Committee::SchemaValidator::OpenAPI3::RequestValidator.new(@operation_object, validator_option: validator_option)
61
- validator.call(request, request.env[validator_option.params_key], header(request))
61
+ validator.call(request, path_params(request), query_params(request), body_params(request), header(request))
62
+ end
63
+
64
+ def path_params(request)
65
+ request.env[validator_option.path_hash_key]
66
+ end
67
+
68
+ def query_params(request)
69
+ request.env[validator_option.query_hash_key]
70
+ end
71
+
72
+ def body_params(request)
73
+ request.env[validator_option.request_body_hash_key]
62
74
  end
63
75
 
64
76
  def header(request)
@@ -66,22 +78,36 @@ module Committee
66
78
  end
67
79
 
68
80
  def request_unpack(request)
69
- request.env[validator_option.params_key], request.env[validator_option.headers_key] = Committee::RequestUnpacker.new(
70
- request,
71
- allow_form_params: validator_option.allow_form_params,
72
- allow_get_body: validator_option.allow_get_body,
73
- allow_query_params: validator_option.allow_query_params,
74
- coerce_form_params: validator_option.coerce_form_params,
75
- optimistic_json: validator_option.optimistic_json,
76
- schema_validator: self
77
- ).call
81
+ unpacker = Committee::RequestUnpacker.new(
82
+ allow_form_params: validator_option.allow_form_params,
83
+ allow_get_body: validator_option.allow_get_body,
84
+ allow_query_params: validator_option.allow_query_params,
85
+ optimistic_json: validator_option.optimistic_json,
86
+ )
87
+
88
+ request.env[validator_option.headers_key] = unpacker.unpack_headers(request)
89
+
90
+ request_param, is_form_params = unpacker.unpack_request_params(request)
91
+ request.env[validator_option.request_body_hash_key] = request_param
92
+ request.env[validator_option.path_hash_key] = coerce_path_params
93
+
94
+ query_param = unpacker.unpack_query_params(request)
95
+ query_param.merge!(request_param) if request.get? && validator_option.allow_get_body
96
+ request.env[validator_option.query_hash_key] = query_param
78
97
  end
79
98
 
80
- def copy_coerced_data_to_query_hash(request)
81
- return if request.env["rack.request.query_hash"].nil? || request.env["rack.request.query_hash"].empty?
99
+ def copy_coerced_data_to_params(request)
100
+ order = if validator_option.parameter_overwite_by_rails_rule
101
+ # (high priority) path_hash_key -> query_param -> request_body_hash
102
+ [validator_option.request_body_hash_key, validator_option.query_hash_key, validator_option.path_hash_key]
103
+ else
104
+ # (high priority) path_hash_key -> request_body_hash -> query_param
105
+ [validator_option.query_hash_key, validator_option.request_body_hash_key, validator_option.path_hash_key]
106
+ end
82
107
 
83
- request.env["rack.request.query_hash"].keys.each do |k|
84
- request.env["rack.request.query_hash"][k] = request.env[validator_option.params_key][k]
108
+ request.env[validator_option.params_key] = Committee::Utils.indifferent_hash
109
+ order.each do |key|
110
+ request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(request.env[key]))
85
111
  end
86
112
  end
87
113
  end
@@ -15,17 +15,26 @@ module Committee
15
15
  :coerce_query_params,
16
16
  :coerce_recursive,
17
17
  :optimistic_json,
18
- :validate_success_only
18
+ :validate_success_only,
19
+ :parse_response_by_content_type,
20
+ :parameter_overwite_by_rails_rule
19
21
 
20
22
  # Non-boolean options:
21
23
  attr_reader :headers_key,
22
24
  :params_key,
25
+ :query_hash_key,
26
+ :request_body_hash_key,
27
+ :path_hash_key,
23
28
  :prefix
24
29
 
25
30
  def initialize(options, schema, schema_type)
26
31
  # Non-boolean options
27
- @headers_key = options[:headers_key] || "committee.headers"
28
- @params_key = options[:params_key] || "committee.params"
32
+ @headers_key = options[:headers_key] || "committee.headers"
33
+ @params_key = options[:params_key] || "committee.params"
34
+ @query_hash_key = options[:query_hash_key] || "committee.query_hash"
35
+ @path_hash_key = options[:path_hash_key] || "committee.path_hash"
36
+ @request_body_hash_key = options[:request_body_hash_key] || "committee.request_body_hash"
37
+
29
38
  @prefix = options[:prefix]
30
39
 
31
40
  # Boolean options and have a common value by default
@@ -35,6 +44,8 @@ module Committee
35
44
  @check_header = options.fetch(:check_header, true)
36
45
  @coerce_recursive = options.fetch(:coerce_recursive, true)
37
46
  @optimistic_json = options.fetch(:optimistic_json, false)
47
+ @parse_response_by_content_type = options.fetch(:parse_response_by_content_type, true)
48
+ @parameter_overwite_by_rails_rule = options.fetch(:parameter_overwite_by_rails_rule, true)
38
49
 
39
50
  # Boolean options and have a different value by default
40
51
  @allow_get_body = options.fetch(:allow_get_body, schema.driver.default_allow_get_body)
@@ -4,7 +4,7 @@ module Committee
4
4
  module SchemaValidator
5
5
  class << self
6
6
  def request_media_type(request)
7
- request.content_type.to_s.split(";").first.to_s
7
+ request.media_type.to_s
8
8
  end
9
9
 
10
10
  # @param [String] prefix
@@ -3,27 +3,40 @@
3
3
  module Committee
4
4
  module Test
5
5
  module Methods
6
- def assert_schema_conform
6
+ def assert_schema_conform(expected_status = nil)
7
7
  assert_request_schema_confirm unless old_behavior
8
- assert_response_schema_confirm
8
+ assert_response_schema_confirm(expected_status)
9
9
  end
10
10
 
11
11
  def assert_request_schema_confirm
12
12
  unless schema_validator.link_exist?
13
- request = "`#{request_object.request_method} #{request_object.path_info}` undefined in schema."
13
+ request = "`#{request_object.request_method} #{request_object.path_info}` undefined in schema (prefix: #{committee_options[:prefix].inspect})."
14
14
  raise Committee::InvalidRequest.new(request)
15
15
  end
16
16
 
17
17
  schema_validator.request_validate(request_object)
18
18
  end
19
19
 
20
- def assert_response_schema_confirm
20
+ def assert_response_schema_confirm(expected_status = nil)
21
21
  unless schema_validator.link_exist?
22
- response = "`#{request_object.request_method} #{request_object.path_info}` undefined in schema."
22
+ response = "`#{request_object.request_method} #{request_object.path_info}` undefined in schema (prefix: #{committee_options[:prefix].inspect})."
23
23
  raise Committee::InvalidResponse.new(response)
24
24
  end
25
25
 
26
26
  status, headers, body = response_data
27
+
28
+ if expected_status.nil?
29
+ Committee.need_good_option('Pass expected response status code to check it against the corresponding schema explicitly.')
30
+ elsif expected_status != status
31
+ response = "Expected `#{expected_status}` status code, but it was `#{status}`."
32
+ raise Committee::InvalidResponse.new(response)
33
+ end
34
+
35
+ if schema_coverage
36
+ operation_object = router.operation_object(request_object)
37
+ schema_coverage&.update_response_coverage!(operation_object.original_path, operation_object.http_method, status)
38
+ end
39
+
27
40
  schema_validator.response_validate(status, headers, [body], true) if validate_response?(status)
28
41
  end
29
42
 
@@ -55,18 +68,16 @@ module Committee
55
68
  @schema_validator ||= router.build_schema_validator(request_object)
56
69
  end
57
70
 
71
+ def schema_coverage
72
+ return nil unless schema.is_a?(Committee::Drivers::OpenAPI3::Schema)
73
+
74
+ coverage = committee_options.fetch(:schema_coverage, nil)
75
+
76
+ coverage.is_a?(SchemaCoverage) ? coverage : nil
77
+ end
78
+
58
79
  def old_behavior
59
- old_assert_behavior = committee_options.fetch(:old_assert_behavior, nil)
60
- if old_assert_behavior.nil?
61
- warn <<-MSG
62
- [DEPRECATION] now assert_schema_conform check response schema only.
63
- but we will change check request and response in future major version.
64
- so if you want to conform response only, please use assert_response_schema_confirm,
65
- or you can suppress this message and keep old behavior by setting old_assert_behavior=true.
66
- MSG
67
- old_assert_behavior = true
68
- end
69
- old_assert_behavior
80
+ committee_options.fetch(:old_assert_behavior, false)
70
81
  end
71
82
  end
72
83
  end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Test
5
+ class SchemaCoverage
6
+ attr_reader :schema
7
+
8
+ class << self
9
+ def merge_report(first, second)
10
+ report = first.dup
11
+ second.each do |k, v|
12
+ if v.is_a?(Hash)
13
+ if report[k].nil?
14
+ report[k] = v
15
+ else
16
+ report[k] = merge_report(report[k], v)
17
+ end
18
+ else
19
+ report[k] ||= v
20
+ end
21
+ end
22
+ report
23
+ end
24
+
25
+ def flatten_report(report)
26
+ responses = []
27
+ report.each do |path_name, path_coverage|
28
+ path_coverage.each do |method, method_coverage|
29
+ responses_coverage = method_coverage['responses']
30
+ responses_coverage.each do |response_status, is_covered|
31
+ responses << {
32
+ path: path_name,
33
+ method: method,
34
+ status: response_status,
35
+ is_covered: is_covered,
36
+ }
37
+ end
38
+ end
39
+ end
40
+ {
41
+ responses: responses,
42
+ }
43
+ end
44
+ end
45
+
46
+ def initialize(schema)
47
+ raise 'Unsupported schema' unless schema.is_a?(Committee::Drivers::OpenAPI3::Schema)
48
+
49
+ @schema = schema
50
+ @covered = {}
51
+ end
52
+
53
+ def update_response_coverage!(path, method, response_status)
54
+ method = method.to_s.downcase
55
+ response_status = response_status.to_s
56
+
57
+ @covered[path] ||= {}
58
+ @covered[path][method] ||= {}
59
+ @covered[path][method]['responses'] ||= {}
60
+ @covered[path][method]['responses'][response_status] = true
61
+ end
62
+
63
+ def report
64
+ report = {}
65
+
66
+ schema.open_api.paths.path.each do |path_name, path_item|
67
+ report[path_name] = {}
68
+ path_item._openapi_all_child_objects.each do |object_name, object|
69
+ next unless object.is_a?(OpenAPIParser::Schemas::Operation)
70
+
71
+ method = object_name.split('/').last&.downcase
72
+ next unless method
73
+
74
+ report[path_name][method] ||= {}
75
+
76
+ # TODO: check coverage on request params/body as well?
77
+
78
+ report[path_name][method]['responses'] ||= {}
79
+ object.responses.response.each do |response_status, _|
80
+ is_covered = @covered.dig(path_name, method, 'responses', response_status) || false
81
+ report[path_name][method]['responses'][response_status] = is_covered
82
+ end
83
+ if object.responses.default
84
+ is_default_covered = (@covered.dig(path_name, method, 'responses') || {}).any? do |status, is_covered|
85
+ is_covered && !object.responses.response.key?(status)
86
+ end
87
+ report[path_name][method]['responses']['default'] = is_default_covered
88
+ end
89
+ end
90
+ end
91
+
92
+ report
93
+ end
94
+
95
+ def report_flatten
96
+ self.class.flatten_report(report)
97
+ end
98
+ end
99
+ end
100
+ end
101
+
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Utils
5
+ # Creates a Hash with indifferent access.
6
+ #
7
+ # (Copied from Sinatra)
8
+ def self.indifferent_hash
9
+ Hash.new { |hash,key| hash[key.to_s] if Symbol === key }
10
+ end
11
+
12
+ def self.deep_copy(from)
13
+ if from.is_a?(Hash)
14
+ h = Committee::Utils.indifferent_hash
15
+ from.each_pair do |k, v|
16
+ h[k] = deep_copy(v)
17
+ end
18
+ return h
19
+ end
20
+
21
+ if from.is_a?(Array)
22
+ return from.map{ |v| deep_copy(v) }
23
+ end
24
+
25
+ return from
26
+ end
27
+ end
28
+ end
@@ -2,12 +2,13 @@
2
2
 
3
3
  module Committee
4
4
  class ValidationError
5
- attr_reader :id, :message, :status
5
+ attr_reader :id, :message, :status, :request
6
6
 
7
- def initialize(status, id, message)
7
+ def initialize(status, id, message, request = nil)
8
8
  @status = status
9
9
  @id = id
10
10
  @message = message
11
+ @request = request
11
12
  end
12
13
 
13
14
  def error_body
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ VERSION = '5.0.0'.freeze
5
+ end
data/lib/committee.rb CHANGED
@@ -6,6 +6,8 @@ require "json_schema"
6
6
  require "rack"
7
7
  require 'openapi_parser'
8
8
 
9
+ require_relative "committee/version"
10
+
9
11
  module Committee
10
12
  def self.debug?
11
13
  ENV["COMMITTEE_DEBUG"]
@@ -15,13 +17,17 @@ module Committee
15
17
  $stderr.puts(message) if debug?
16
18
  end
17
19
 
18
- def self.warn_deprecated(message)
19
- if !$VERBOSE.nil?
20
- $stderr.puts(message)
21
- end
20
+ def self.need_good_option(message)
21
+ warn(message)
22
+ end
23
+
24
+ def self.warn_deprecated_until_6(cond, message)
25
+ raise "remove deprecated!" unless Committee::VERSION.start_with?("5")
26
+ warn("[DEPRECATION] #{message}") if cond
22
27
  end
23
28
  end
24
29
 
30
+ require_relative "committee/utils"
25
31
  require_relative "committee/drivers"
26
32
  require_relative "committee/errors"
27
33
  require_relative "committee/middleware"
@@ -31,3 +37,4 @@ require_relative "committee/validation_error"
31
37
 
32
38
  require_relative "committee/bin/committee_stub"
33
39
  require_relative "committee/test/methods"
40
+ require_relative "committee/test/schema_coverage"
@@ -43,7 +43,11 @@ describe Committee::Bin::CommitteeStub, "app" do
43
43
  end
44
44
 
45
45
  def app
46
- @bin.get_app(hyper_schema, {})
46
+ options = {}
47
+ # TODO: delete when 5.0.0 released because default value changed
48
+ options[:parse_response_by_content_type] = false
49
+
50
+ @bin.get_app(hyper_schema, options)
47
51
  end
48
52
 
49
53
  it "defaults to a 404" do
@@ -31,18 +31,44 @@ describe Committee do
31
31
  end
32
32
  end
33
33
 
34
+ it "warns need_good_option" do
35
+ old_stderr = $stderr
36
+ $stderr = StringIO.new
37
+ begin
38
+ Committee.need_good_option "show"
39
+ assert_equal "show\n", $stderr.string
40
+ ensure
41
+ $stderr = old_stderr
42
+ end
43
+ end
44
+
34
45
  it "warns on deprecated unless $VERBOSE is nil" do
35
46
  old_stderr = $stderr
36
47
  old_verbose = $VERBOSE
37
48
  $stderr = StringIO.new
38
49
  begin
39
50
  $VERBOSE = nil
40
- Committee.warn_deprecated "blah"
51
+ Committee.warn_deprecated_until_6 true, "blah"
41
52
  assert_equal "", $stderr.string
42
53
 
43
54
  $VERBOSE = true
44
- Committee.warn_deprecated "blah"
45
- assert_equal "blah\n", $stderr.string
55
+ Committee.warn_deprecated_until_6 true, "blah"
56
+ assert_equal "[DEPRECATION] blah\n", $stderr.string
57
+ ensure
58
+ $stderr = old_stderr
59
+ $VERBOSE = old_verbose
60
+ end
61
+ end
62
+
63
+
64
+ it "doesn't warns on deprecated if cond is false" do
65
+ old_stderr = $stderr
66
+ old_verbose = $VERBOSE
67
+ $stderr = StringIO.new
68
+ begin
69
+ $VERBOSE = true
70
+ Committee.warn_deprecated_until_6 false, "blah"
71
+ assert_equal "", $stderr.string
46
72
  ensure
47
73
  $stderr = old_stderr
48
74
  $VERBOSE = old_verbose
@@ -16,7 +16,7 @@ describe Committee::Drivers::OpenAPI3::Driver do
16
16
  end
17
17
 
18
18
  it "override methods" do
19
- parser = OpenAPIParser.parse(open_api_3_data)
19
+ parser = OpenAPIParser.parse(open_api_3_data, strict_reference_validation: false)
20
20
  schema = @driver.parse(parser)
21
21
 
22
22
  assert_kind_of Committee::Drivers::OpenAPI3::Schema, schema
data/test/drivers_test.rb CHANGED
@@ -37,20 +37,33 @@ describe Committee::Drivers do
37
37
  end
38
38
 
39
39
  it 'loads OpenAPI 3' do
40
- s = Committee::Drivers.load_from_file(open_api_3_schema_path)
40
+ s = Committee::Drivers.load_from_file(open_api_3_schema_path, parser_options:{strict_reference_validation: true})
41
41
  assert_kind_of Committee::Drivers::Schema, s
42
42
  assert_kind_of Committee::Drivers::OpenAPI3::Schema, s
43
43
  end
44
44
 
45
45
  it 'load OpenAPI 3 (patch version 3.0.1)' do
46
- s = Committee::Drivers.load_from_file(open_api_3_0_1_schema_path)
46
+ s = Committee::Drivers.load_from_file(open_api_3_0_1_schema_path, parser_options:{strict_reference_validation: true})
47
47
  assert_kind_of Committee::Drivers::Schema, s
48
48
  assert_kind_of Committee::Drivers::OpenAPI3::Schema, s
49
49
  end
50
50
 
51
+ it 'fails to load OpenAPI 3 with invalid reference' do
52
+ parser_options = { strict_reference_validation: true }
53
+ assert_raises(OpenAPIParser::MissingReferenceError) do
54
+ Committee::Drivers.load_from_file(open_api_3_invalid_reference_path, parser_options: parser_options)
55
+ end
56
+ end
57
+
58
+ # This test can be removed when the test above (raising on invalid reference) becomes default behavior?
59
+ it 'allows loading OpenAPI 3 with invalid reference as existing behavior' do
60
+ s = Committee::Drivers.load_from_file(open_api_3_invalid_reference_path, parser_options:{strict_reference_validation: false})
61
+ assert_kind_of Committee::Drivers::OpenAPI3::Schema, s
62
+ end
63
+
51
64
  it 'errors on an unsupported file extension' do
52
65
  e = assert_raises(StandardError) do
53
- Committee::Drivers.load_from_file('test.xml')
66
+ Committee::Drivers.load_from_file('test.xml', parser_options:{strict_reference_validation: true})
54
67
  end
55
68
  assert_equal "Committee only supports the following file extensions: '.json', '.yaml', '.yml'", e.message
56
69
  end
@@ -58,13 +71,13 @@ describe Committee::Drivers do
58
71
 
59
72
  describe 'load_from_json(schema_path)' do
60
73
  it 'loads OpenAPI2' do
61
- s = Committee::Drivers.load_from_json(open_api_2_schema_path)
74
+ s = Committee::Drivers.load_from_json(open_api_2_schema_path, parser_options:{strict_reference_validation: true})
62
75
  assert_kind_of Committee::Drivers::Schema, s
63
76
  assert_kind_of Committee::Drivers::OpenAPI2::Schema, s
64
77
  end
65
78
 
66
79
  it 'loads Hyper-Schema' do
67
- s = Committee::Drivers.load_from_json(hyper_schema_schema_path)
80
+ s = Committee::Drivers.load_from_json(hyper_schema_schema_path, parser_options:{strict_reference_validation: true})
68
81
  assert_kind_of Committee::Drivers::Schema, s
69
82
  assert_kind_of Committee::Drivers::HyperSchema::Schema, s
70
83
  end
@@ -72,7 +85,7 @@ describe Committee::Drivers do
72
85
 
73
86
  describe 'load_from_yaml(schema_path)' do
74
87
  it 'loads OpenAPI3' do
75
- s = Committee::Drivers.load_from_yaml(open_api_3_schema_path)
88
+ s = Committee::Drivers.load_from_yaml(open_api_3_schema_path, parser_options:{strict_reference_validation: true})
76
89
  assert_kind_of Committee::Drivers::Schema, s
77
90
  assert_kind_of Committee::Drivers::OpenAPI3::Schema, s
78
91
  end
@@ -80,7 +93,7 @@ describe Committee::Drivers do
80
93
 
81
94
  describe 'load_from_data(schema_path)' do
82
95
  it 'loads OpenAPI3' do
83
- s = Committee::Drivers.load_from_data(open_api_3_data)
96
+ s = Committee::Drivers.load_from_data(open_api_3_data, parser_options:{strict_reference_validation: false})
84
97
  assert_kind_of Committee::Drivers::Schema, s
85
98
  assert_kind_of Committee::Drivers::OpenAPI3::Schema, s
86
99
  end
@@ -102,18 +102,14 @@ describe Committee::Middleware::Base do
102
102
  end
103
103
 
104
104
  describe 'initialize option' do
105
- it "schema_path option with hyper-schema" do
106
- b = Committee::Middleware::Base.new(nil, schema_path: hyper_schema_schema_path)
107
- assert_kind_of Committee::Drivers::HyperSchema::Schema, b.instance_variable_get(:@schema)
105
+ it "accepts OpenAPI3 parser config of strict_reference_validation and raises" do
106
+ assert_raises(OpenAPIParser::MissingReferenceError) do
107
+ Committee::Middleware::Base.new(nil, schema_path: open_api_3_invalid_reference_path, strict_reference_validation: true)
108
+ end
108
109
  end
109
110
 
110
- it "schema_path option with OpenAPI2" do
111
- b = Committee::Middleware::Base.new(nil, schema_path: open_api_2_schema_path)
112
- assert_kind_of Committee::Drivers::OpenAPI2::Schema, b.instance_variable_get(:@schema)
113
- end
114
-
115
- it "schema_path option with OpenAPI3" do
116
- b = Committee::Middleware::Base.new(nil, schema_path: open_api_3_schema_path)
111
+ it "does not raise by default even with invalid reference OpenAPI3 specification" do
112
+ b = Committee::Middleware::Base.new(nil, schema_path: open_api_3_invalid_reference_path, strict_reference_validation: false)
117
113
  assert_kind_of Committee::Drivers::OpenAPI3::Schema, b.instance_variable_get(:@schema)
118
114
  end
119
115
  end
@@ -121,6 +117,9 @@ describe Committee::Middleware::Base do
121
117
  private
122
118
 
123
119
  def new_rack_app(options = {})
120
+ # TODO: delete when 5.0.0 released because default value changed
121
+ options[:parse_response_by_content_type] = true if options[:parse_response_by_content_type] == nil
122
+
124
123
  Rack::Builder.new {
125
124
  use Committee::Middleware::RequestValidation, options
126
125
  run lambda { |_|