committee 1.15.0 → 2.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/bin/committee-stub +10 -36
  3. data/lib/committee/bin/committee_stub.rb +61 -0
  4. data/lib/committee/drivers/hyper_schema.rb +151 -0
  5. data/lib/committee/drivers/open_api_2.rb +297 -0
  6. data/lib/committee/drivers.rb +57 -0
  7. data/lib/committee/middleware/base.rb +52 -13
  8. data/lib/committee/middleware/request_validation.rb +33 -10
  9. data/lib/committee/middleware/response_validation.rb +2 -1
  10. data/lib/committee/middleware/stub.rb +24 -8
  11. data/lib/committee/request_validator.rb +1 -1
  12. data/lib/committee/response_generator.rb +58 -13
  13. data/lib/committee/response_validator.rb +32 -8
  14. data/lib/committee/router.rb +5 -33
  15. data/lib/committee/{query_params_coercer.rb → string_params_coercer.rb} +11 -6
  16. data/lib/committee/test/methods.rb +49 -12
  17. data/lib/committee.rb +15 -1
  18. data/test/bin/committee_stub_test.rb +45 -0
  19. data/test/bin_test.rb +20 -0
  20. data/test/committee_test.rb +49 -0
  21. data/test/drivers/hyper_schema_test.rb +95 -0
  22. data/test/drivers/open_api_2_test.rb +255 -0
  23. data/test/drivers_test.rb +60 -0
  24. data/test/middleware/base_test.rb +49 -5
  25. data/test/middleware/request_validation_test.rb +39 -25
  26. data/test/middleware/response_validation_test.rb +32 -20
  27. data/test/middleware/stub_test.rb +50 -19
  28. data/test/request_unpacker_test.rb +10 -0
  29. data/test/request_validator_test.rb +4 -3
  30. data/test/response_generator_test.rb +50 -6
  31. data/test/response_validator_test.rb +29 -4
  32. data/test/router_test.rb +40 -13
  33. data/test/{query_params_coercer_test.rb → string_params_coercer_test.rb} +3 -4
  34. data/test/test/methods_test.rb +44 -5
  35. data/test/test_helper.rb +59 -1
  36. metadata +62 -10
@@ -6,19 +6,12 @@ module Committee::Middleware
6
6
  @error_class = options.fetch(:error_class, Committee::ValidationError)
7
7
  @params_key = options[:params_key] || "committee.params"
8
8
  @raise = options[:raise]
9
+ @schema = get_schema(options[:schema] ||
10
+ raise(ArgumentError, "Committee: need option `schema`"))
9
11
 
10
- schema = options[:schema] || raise("need option `schema`")
11
- if schema.is_a?(String)
12
- warn_string_deprecated
13
- schema = JSON.parse(schema)
14
- end
15
- if schema.is_a?(Hash)
16
- schema = JsonSchema.parse!(schema)
17
- schema.expand_references!
18
- end
19
- @schema = schema
20
-
21
- @router = Committee::Router.new(@schema, options)
12
+ @router = Committee::Router.new(@schema,
13
+ prefix: options[:prefix]
14
+ )
22
15
  end
23
16
 
24
17
  def call(env)
@@ -33,8 +26,54 @@ module Committee::Middleware
33
26
 
34
27
  private
35
28
 
29
+ # For modern use of the library a schema should be an instance of
30
+ # Committee::Drivers::Schema so that we know that all the computationally
31
+ # difficult parsing is already done by the time we try to handle any
32
+ # request.
33
+ #
34
+ # However, for reasons of backwards compatibility we also allow schema
35
+ # input to be a string, a data hash, or a JsonSchema::Schema. In the former
36
+ # two cases we just parse as if we were sent hyper-schema. In the latter,
37
+ # we have the hyper-schema driver wrap it in a new Committee object.
38
+ def get_schema(schema)
39
+ # These are in a separately conditional ladder so that we only show the
40
+ # user one warning.
41
+ if schema.is_a?(String)
42
+ warn_string_deprecated
43
+ elsif schema.is_a?(Hash)
44
+ warn_hash_deprecated
45
+ end
46
+
47
+ if schema.is_a?(String)
48
+ schema = JSON.parse(schema)
49
+ end
50
+
51
+ if schema.is_a?(Hash) || schema.is_a?(JsonSchema::Schema)
52
+ driver = Committee::Drivers::HyperSchema.new
53
+
54
+ # The driver itself has its own special cases to be able to parse
55
+ # either a hash or JsonSchema::Schema object.
56
+ schema = driver.parse(schema)
57
+ end
58
+
59
+ # Expect the type we want by now. If we don't have it, the user passed
60
+ # something else non-standard in.
61
+ if !schema.is_a?(Committee::Drivers::Schema)
62
+ raise ArgumentError, "Committee: schema expected to be a hash or " \
63
+ "an instance of Committee::Drivers::Schema."
64
+ end
65
+
66
+ schema
67
+ end
68
+
69
+ def warn_hash_deprecated
70
+ Committee.warn_deprecated("Committee: passing a hash to schema " \
71
+ "option is deprecated; please send a driver object instead.")
72
+ end
73
+
36
74
  def warn_string_deprecated
37
- Committee.warn_deprecated("Committee: passing a string to `schema` option is deprecated; please send a deserialized hash instead.")
75
+ Committee.warn_deprecated("Committee: passing a string to schema " \
76
+ "option is deprecated; please send a driver object instead.")
38
77
  end
39
78
  end
40
79
  end
@@ -2,27 +2,48 @@ module Committee::Middleware
2
2
  class RequestValidation < Base
3
3
  def initialize(app, options={})
4
4
  super
5
+
5
6
  @allow_form_params = options.fetch(:allow_form_params, true)
6
7
  @allow_query_params = options.fetch(:allow_query_params, true)
7
8
  @check_content_type = options.fetch(:check_content_type, true)
8
9
  @optimistic_json = options.fetch(:optimistic_json, false)
9
- @coerce_query_params = options.fetch(:coerce_query_params, false)
10
10
  @strict = options[:strict]
11
11
 
12
+ @coerce_path_params = options.fetch(:coerce_path_params,
13
+ @schema.driver.default_path_params)
14
+ @coerce_query_params = options.fetch(:coerce_query_params,
15
+ @schema.driver.default_query_params)
16
+
12
17
  # deprecated
13
18
  @allow_extra = options[:allow_extra]
14
19
  end
15
20
 
16
21
  def handle(request)
17
- link = @router.find_request_link(request)
18
-
19
- if link && @coerce_query_params && !request.GET.nil? && !link.schema.nil?
20
- request.env["rack.request.query_hash"].merge!(
21
- Committee::QueryParamsCoercer.new(
22
- request.GET,
23
- link.schema
24
- ).call
25
- )
22
+ link, param_matches = @router.find_request_link(request)
23
+ path_params = {}
24
+
25
+ if link
26
+ # Attempts to coerce parameters that appear in a link's URL to Ruby
27
+ # types that can be validated with a schema.
28
+ if @coerce_path_params
29
+ path_params = param_matches.merge(
30
+ Committee::StringParamsCoercer.new(
31
+ param_matches,
32
+ link.schema
33
+ ).call
34
+ )
35
+ end
36
+
37
+ # Attempts to coerce parameters that appear in a query string to Ruby
38
+ # types that can be validated with a schema.
39
+ if @coerce_query_params && !request.GET.nil? && !link.schema.nil?
40
+ request.env["rack.request.query_hash"].merge!(
41
+ Committee::StringParamsCoercer.new(
42
+ request.GET,
43
+ link.schema
44
+ ).call
45
+ )
46
+ end
26
47
  end
27
48
 
28
49
  request.env[@params_key] = Committee::RequestUnpacker.new(
@@ -32,6 +53,8 @@ module Committee::Middleware
32
53
  optimistic_json: @optimistic_json
33
54
  ).call
34
55
 
56
+ request.env[@params_key].merge!(path_params)
57
+
35
58
  if link
36
59
  validator = Committee::RequestValidator.new(link, check_content_type: @check_content_type)
37
60
  validator.call(request, request.env[@params_key])
@@ -10,7 +10,8 @@ module Committee::Middleware
10
10
  def handle(request)
11
11
  status, headers, response = @app.call(request.env)
12
12
 
13
- if validate?(status) && link = @router.find_request_link(request)
13
+ link, _ = @router.find_request_link(request)
14
+ if validate?(status) && link
14
15
  full_body = ""
15
16
  response.each do |chunk|
16
17
  full_body << chunk
@@ -2,16 +2,27 @@ module Committee::Middleware
2
2
  class Stub < Base
3
3
  def initialize(app, options={})
4
4
  super
5
- @cache = {}
6
- @call = options[:call]
5
+
6
+ # A bug in Committee's cache implementation meant that it wasn't working
7
+ # for a very long time, even for people who thought they were taking
8
+ # advantage of it. I repaired the caching feature, but have disable it by
9
+ # default so that we don't need to introduce any class-level variables
10
+ # that could have memory leaking implications. To enable caching, just
11
+ # pass an empty hash to this option.
12
+ @cache = options[:cache]
13
+
14
+ @call = options[:call]
7
15
  end
8
16
 
9
17
  def handle(request)
10
- if link = @router.find_request_link(request)
18
+ link, _ = @router.find_request_link(request)
19
+ if link
11
20
  headers = { "Content-Type" => "application/json" }
12
- data = cache(link.method, link.href) do
21
+
22
+ data = cache(link) do
13
23
  Committee::ResponseGenerator.new.call(link)
14
24
  end
25
+
15
26
  if @call
16
27
  request.env["committee.response"] = data
17
28
  call_status, call_headers, call_body = @app.call(request.env)
@@ -29,8 +40,8 @@ module Committee::Middleware
29
40
  # will be the same one that we set above)
30
41
  data = request.env["committee.response"]
31
42
  end
32
- status = link.rel == "create" ? 201 : 200
33
- [status, headers, [JSON.pretty_generate(data)]]
43
+
44
+ [link.status_success, headers, [JSON.pretty_generate(data)]]
34
45
  else
35
46
  @app.call(request.env)
36
47
  end
@@ -38,8 +49,13 @@ module Committee::Middleware
38
49
 
39
50
  private
40
51
 
41
- def cache(method, href)
42
- key = "#{method}+#{href}"
52
+ def cache(link)
53
+ return yield unless @cache
54
+
55
+ # Just the object ID is enough to uniquely identify the link, but store
56
+ # the method and href so that we can more easily introspect the cache if
57
+ # necessary.
58
+ key = "#{link.object_id}##{link.method}+#{link.href}"
43
59
  if @cache[key]
44
60
  @cache[key]
45
61
  else
@@ -24,7 +24,7 @@ module Committee
24
24
 
25
25
  def check_content_type!(request, data)
26
26
  content_type = request_media_type(request)
27
- if content_type && !empty_request?(request)
27
+ if content_type && @link.enc_type && !empty_request?(request)
28
28
  unless Rack::Mime.match?(content_type, @link.enc_type)
29
29
  raise Committee::InvalidRequest,
30
30
  %{"Content-Type" request header must be set to "#{@link.enc_type}".}
@@ -1,35 +1,80 @@
1
1
  module Committee
2
2
  class ResponseGenerator
3
3
  def call(link)
4
- data = generate_properties(link.target_schema || link.parent)
4
+ data = generate_properties(link, target_schema(link))
5
5
 
6
- # list is a special case; wrap data in an array
7
- data = [data] if link.rel == "instances"
6
+ # List is a special case; wrap data in an array.
7
+ #
8
+ # This is poor form that's here so as not to introduce breaking behavior.
9
+ # The "instances" value of "rel" is a Heroku-ism and was originally
10
+ # introduced before we understood how to use "targetSchema". It's not
11
+ # meaningful with the context of the hyper-schema specification and
12
+ # should be eventually be removed.
13
+ if legacy_hyper_schema_rel?(link)
14
+ data = [data]
15
+ end
8
16
 
9
17
  data
10
18
  end
11
19
 
12
20
  private
13
21
 
14
- def generate_properties(schema)
22
+ # These are basic types that are part of the JSON schema for which we'll
23
+ # emit zero values when generating a response. For a schema that allows
24
+ # multiple of the types in the list, types are preferred in the order in
25
+ # which they're defined.
26
+ SCALAR_TYPES = {
27
+ "boolean" => false,
28
+ "integer" => 0,
29
+ "number" => 0.0,
30
+ "string" => "",
31
+
32
+ # Prefer null last.
33
+ "null" => nil,
34
+ }.freeze
35
+
36
+ def generate_properties(link, schema)
15
37
  # special example attribute was included; use its value
16
- if !schema.data["example"].nil?
38
+ if schema.data && !schema.data["example"].nil?
17
39
  schema.data["example"]
18
- # null is allowed; use that
19
- elsif schema.type.include?("null")
20
- nil
21
- elsif schema.type.include?("array") && !schema.items.nil?
22
- [generate_properties(schema.items)]
23
- elsif !schema.properties.empty?
40
+ elsif !schema.all_of.empty? || !schema.properties.empty?
24
41
  data = {}
42
+ schema.all_of.each do |subschema|
43
+ data.merge!(generate_properties(link, subschema))
44
+ end
25
45
  schema.properties.map do |key, value|
26
- data[key] = generate_properties(value)
46
+ data[key] = generate_properties(link, value)
27
47
  end
28
48
  data
49
+ elsif schema.type.include?("array") && !schema.items.nil?
50
+ [generate_properties(link, schema.items)]
51
+ elsif schema.type.any? { |t| SCALAR_TYPES.include?(t) }
52
+ SCALAR_TYPES.each do |k, v|
53
+ break(v) if schema.type.include?(k)
54
+ end
29
55
  else
30
- raise(%{At "#{schema.pointer}": no "example" attribute and "null" } +
56
+ raise(%{At "#{link.method} #{link.href}" "#{schema.pointer}": no } +
57
+ %{"example" attribute and "null" } +
31
58
  %{is not allowed; don't know how to generate property.})
32
59
  end
33
60
  end
61
+
62
+ def legacy_hyper_schema_rel?(link)
63
+ link.is_a?(Committee::Drivers::HyperSchema::Link) &&
64
+ link.rel == "instances" &&
65
+ !link.target_schema
66
+ end
67
+
68
+ # Gets the target schema of a link. This is normally just the standard
69
+ # response schema, but we allow some legacy behavior for hyper-schema links
70
+ # tagged with rel=instances to instead use the schema of their parent
71
+ # resource.
72
+ def target_schema(link)
73
+ if link.target_schema
74
+ link.target_schema
75
+ elsif legacy_hyper_schema_rel?(link)
76
+ link.parent
77
+ end
78
+ end
34
79
  end
35
80
  end
@@ -6,10 +6,7 @@ module Committee
6
6
  @link = link
7
7
  @validate_errors = options[:validate_errors]
8
8
 
9
- # we should eventually move off of validating against parent schema too
10
- # ... this is a Herokuism and not in the specification
11
- schema = link.target_schema || link.parent
12
- @validator = JsonSchema::Validator.new(schema)
9
+ @validator = JsonSchema::Validator.new(target_schema(link))
13
10
  end
14
11
 
15
12
  def self.validate?(status, options = {})
@@ -24,7 +21,14 @@ module Committee
24
21
  check_content_type!(response)
25
22
  end
26
23
 
27
- if @link.rel == "instances" && !@link.target_schema
24
+ # List is a special case; expect data in an array.
25
+ #
26
+ # This is poor form that's here so as not to introduce breaking behavior.
27
+ # The "instances" value of "rel" is a Heroku-ism and was originally
28
+ # introduced before we understood how to use "targetSchema". It's not
29
+ # meaningful with the context of the hyper-schema specification and
30
+ # should be eventually be removed.
31
+ if legacy_hyper_schema_rel?(@link)
28
32
  if !data.is_a?(Array)
29
33
  raise InvalidResponse, "List endpoints must return an array of objects."
30
34
  end
@@ -50,9 +54,29 @@ module Committee
50
54
  end
51
55
 
52
56
  def check_content_type!(response)
53
- unless Rack::Mime.match?(response_media_type(response), @link.media_type)
54
- raise Committee::InvalidResponse,
55
- %{"Content-Type" response header must be set to "#{@link.media_type}".}
57
+ if @link.media_type
58
+ unless Rack::Mime.match?(response_media_type(response), @link.media_type)
59
+ raise Committee::InvalidResponse,
60
+ %{"Content-Type" response header must be set to "#{@link.media_type}".}
61
+ end
62
+ end
63
+ end
64
+
65
+ def legacy_hyper_schema_rel?(link)
66
+ link.is_a?(Committee::Drivers::HyperSchema::Link) &&
67
+ link.rel == "instances" &&
68
+ !link.target_schema
69
+ end
70
+
71
+ # Gets the target schema of a link. This is normally just the standard
72
+ # response schema, but we allow some legacy behavior for hyper-schema links
73
+ # tagged with rel=instances to instead use the schema of their parent
74
+ # resource.
75
+ def target_schema(link)
76
+ if link.target_schema
77
+ link.target_schema
78
+ elsif legacy_hyper_schema_rel?(link)
79
+ link.parent
56
80
  end
57
81
  end
58
82
  end
@@ -1,9 +1,9 @@
1
1
  module Committee
2
2
  class Router
3
3
  def initialize(schema, options = {})
4
- @routes = build_routes(schema)
5
4
  @prefix = options[:prefix]
6
5
  @prefix_regexp = /\A#{Regexp.escape(@prefix)}/.freeze if @prefix
6
+ @schema = schema
7
7
  end
8
8
 
9
9
  def includes?(path)
@@ -16,10 +16,11 @@ module Committee
16
16
 
17
17
  def find_link(method, path)
18
18
  path = path.gsub(@prefix_regexp, "") if @prefix
19
- if method_routes = @routes[method]
19
+ if method_routes = @schema.routes[method]
20
20
  method_routes.each do |pattern, link|
21
- if path =~ pattern
22
- return link
21
+ if matches = pattern.match(path)
22
+ hash = Hash[matches.names.zip(matches.captures)]
23
+ return link, hash
23
24
  end
24
25
  end
25
26
  end
@@ -29,34 +30,5 @@ module Committee
29
30
  def find_request_link(request)
30
31
  find_link(request.request_method, request.path_info)
31
32
  end
32
-
33
- private
34
-
35
- def build_routes(schema)
36
- routes = {}
37
-
38
- schema.links.each do |link|
39
- method, href = parse_link(link)
40
- next unless method
41
- routes[method] ||= []
42
- routes[method] << [%r{^#{href}$}, link]
43
- end
44
-
45
- # recursively iterate through all `properties` subschemas to build a
46
- # complete routing table
47
- schema.properties.each do |_, subschema|
48
- routes.merge!(build_routes(subschema)) { |_, r1, r2| r1 + r2 }
49
- end
50
-
51
- routes
52
- end
53
-
54
- def parse_link(link)
55
- return nil, nil if !link.method || !link.href
56
- method = link.method.to_s.upcase
57
- # /apps/{id} --> /apps/([^/]+)
58
- href = link.href.gsub(/\{(.*?)\}/, "[^/]+")
59
- [method, href]
60
- end
61
33
  end
62
34
  end
@@ -1,10 +1,15 @@
1
- # Attempts to coerce params given in the query hash (which are all strings) into
2
- # the types specified by the schema.
3
- # Currently supported types: null, integer, number and boolean.
4
- # +call+ returns a hash of all params which could be coerced - coercion errors
5
- # are simply ignored and expected to be handled later by schema validation.
6
1
  module Committee
7
- class QueryParamsCoercer
2
+ # StringParamsCoercer takes parameters that are specified over a medium that
3
+ # can only accept strings (for example in a URL path or in query parameters)
4
+ # and attempts to coerce them into known types based of a link's schema
5
+ # definition.
6
+ #
7
+ # Currently supported types: null, integer, number and boolean.
8
+ #
9
+ # +call+ returns a hash of all params which could be coerced - coercion
10
+ # errors are simply ignored and expected to be handled later by schema
11
+ # validation.
12
+ class StringParamsCoercer
8
13
  def initialize(query_hash, schema)
9
14
  @query_hash = query_hash
10
15
  @schema = schema
@@ -1,19 +1,42 @@
1
1
  module Committee::Test
2
2
  module Methods
3
3
  def assert_schema_conform
4
- if (data = schema_contents).is_a?(String)
5
- warn_string_deprecated
6
- data = JSON.parse(data)
7
- end
4
+ @committee_schema ||= begin
5
+ # The preferred option. The user has already parsed a schema elsewhere
6
+ # and we therefore don't have to worry about any performance
7
+ # implications of having to do it for every single test suite.
8
+ if committee_schema
9
+ committee_schema
10
+ else
11
+ schema = schema_contents
12
+
13
+ if schema.is_a?(String)
14
+ warn_string_deprecated
15
+ elsif schema.is_a?(Hash)
16
+ warn_hash_deprecated
17
+ end
18
+
19
+ if schema.is_a?(String)
20
+ schema = JSON.parse(schema)
21
+ end
8
22
 
9
- @schema ||= begin
10
- schema = JsonSchema.parse!(data)
11
- schema.expand_references!
12
- schema
23
+ if schema.is_a?(Hash) || schema.is_a?(JsonSchema::Schema)
24
+ driver = Committee::Drivers::HyperSchema.new
25
+
26
+ # The driver itself has its own special cases to be able to parse
27
+ # either a hash or JsonSchema::Schema object.
28
+ schema = driver.parse(schema)
29
+ end
30
+
31
+ schema
32
+ end
13
33
  end
14
- @router ||= Committee::Router.new(@schema, prefix: schema_url_prefix)
15
34
 
16
- unless link = @router.find_request_link(last_request)
35
+ @committee_router ||= Committee::Router.new(@committee_schema,
36
+ prefix: schema_url_prefix)
37
+
38
+ link, _ = @committee_router.find_request_link(last_request)
39
+ unless link
17
40
  response = "`#{last_request.request_method} #{last_request.path_info}` undefined in schema."
18
41
  raise Committee::InvalidResponse.new(response)
19
42
  end
@@ -28,6 +51,12 @@ module Committee::Test
28
51
  Committee.warn_deprecated("Committee: use of #assert_schema_content_type is deprecated; use #assert_schema_conform instead.")
29
52
  end
30
53
 
54
+ # Can be overridden with a different driver name for other API definition
55
+ # formats.
56
+ def committee_schema
57
+ nil
58
+ end
59
+
31
60
  # can be overridden alternatively to #schema_path in case the schema is
32
61
  # easier to access as a string
33
62
  # blob
@@ -36,15 +65,23 @@ module Committee::Test
36
65
  end
37
66
 
38
67
  def schema_path
39
- raise "Please override #schema_contents or #schema_path."
68
+ raise "Please override #commitee_schema."
40
69
  end
41
70
 
42
71
  def schema_url_prefix
43
72
  nil
44
73
  end
45
74
 
75
+ def warn_hash_deprecated
76
+ Committee.warn_deprecated("Committee: returning a hash from " \
77
+ "#schema_contents and using #schema_path is deprecated; please " \
78
+ "override #committee_schema instead.")
79
+ end
80
+
46
81
  def warn_string_deprecated
47
- Committee.warn_deprecated("Committee: returning a string from `#schema_contents` is deprecated; please return a deserialized hash instead.")
82
+ Committee.warn_deprecated("Committee: returning a string from " \
83
+ "#schema_contents is deprecated; please override #committee_schema " \
84
+ "instead.")
48
85
  end
49
86
 
50
87
  def validate_response?(status)
data/lib/committee.rb CHANGED
@@ -3,7 +3,7 @@ require "json_schema"
3
3
  require "rack"
4
4
 
5
5
  require_relative "committee/errors"
6
- require_relative "committee/query_params_coercer"
6
+ require_relative "committee/string_params_coercer"
7
7
  require_relative "committee/request_unpacker"
8
8
  require_relative "committee/request_validator"
9
9
  require_relative "committee/response_generator"
@@ -11,14 +11,28 @@ require_relative "committee/response_validator"
11
11
  require_relative "committee/router"
12
12
  require_relative "committee/validation_error"
13
13
 
14
+ require_relative "committee/drivers"
15
+ require_relative "committee/drivers/hyper_schema"
16
+ require_relative "committee/drivers/open_api_2"
17
+
14
18
  require_relative "committee/middleware/base"
15
19
  require_relative "committee/middleware/request_validation"
16
20
  require_relative "committee/middleware/response_validation"
17
21
  require_relative "committee/middleware/stub"
18
22
 
23
+ require_relative "committee/bin/committee_stub"
24
+
19
25
  require_relative "committee/test/methods"
20
26
 
21
27
  module Committee
28
+ def self.debug?
29
+ ENV["COMMITTEE_DEBUG"]
30
+ end
31
+
32
+ def self.log_debug(message)
33
+ $stderr.puts(message) if debug?
34
+ end
35
+
22
36
  def self.warn_deprecated(message)
23
37
  if !$VERBOSE.nil?
24
38
  $stderr.puts(message)
@@ -0,0 +1,45 @@
1
+ require_relative "../test_helper"
2
+
3
+ describe Committee::Bin::CommitteeStub do
4
+ before do
5
+ @bin = Committee::Bin::CommitteeStub.new
6
+ end
7
+
8
+ it "produces a Rack app" do
9
+ app = @bin.get_app(hyper_schema, {})
10
+ assert_kind_of Rack::Builder, app
11
+ end
12
+
13
+ it "parses command line options" do
14
+ options, parser = @bin.get_options_parser
15
+
16
+ parser.parse!(["--help"])
17
+ assert_equal true, options[:help]
18
+
19
+ parser.parse!([
20
+ "--driver", "open_api_2",
21
+ "--tolerant", "true",
22
+ "--port", "1234"
23
+ ])
24
+ assert_equal :open_api_2, options[:driver]
25
+ assert_equal true, options[:tolerant]
26
+ assert_equal "1234", options[:port]
27
+ end
28
+ end
29
+
30
+ describe Committee::Bin::CommitteeStub, "app" do
31
+ include Rack::Test::Methods
32
+
33
+ before do
34
+ @bin = Committee::Bin::CommitteeStub.new
35
+ end
36
+
37
+ def app
38
+ @bin.get_app(hyper_schema, {})
39
+ end
40
+
41
+ it "defaults to a 404" do
42
+ get "/foos"
43
+ assert_equal 404, last_response.status
44
+ end
45
+ end
data/test/bin_test.rb ADDED
@@ -0,0 +1,20 @@
1
+ require_relative "test_helper"
2
+
3
+ #
4
+ # The purpose of this sets of tests is just to include our Ruby executables
5
+ # where possible so that we can get very basic sanity checks on their syntax
6
+ # (which is something that of course Ruby can't do by default).
7
+ #
8
+ # We can do this without actually executing them because they're gated by `if
9
+ # $0 == __FILE__` statements.
10
+ #
11
+
12
+ describe "executables in bin/" do
13
+ before do
14
+ @bin_dir = File.expand_path("../../bin", __FILE__)
15
+ end
16
+
17
+ it "has roughly valid Ruby structure for committee-stub" do
18
+ load File.join(@bin_dir, "committee-stub")
19
+ end
20
+ end