committee 1.15.0 → 2.0.0.pre
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.
- checksums.yaml +4 -4
- data/bin/committee-stub +10 -36
- data/lib/committee/bin/committee_stub.rb +61 -0
- data/lib/committee/drivers/hyper_schema.rb +151 -0
- data/lib/committee/drivers/open_api_2.rb +297 -0
- data/lib/committee/drivers.rb +57 -0
- data/lib/committee/middleware/base.rb +52 -13
- data/lib/committee/middleware/request_validation.rb +33 -10
- data/lib/committee/middleware/response_validation.rb +2 -1
- data/lib/committee/middleware/stub.rb +24 -8
- data/lib/committee/request_validator.rb +1 -1
- data/lib/committee/response_generator.rb +58 -13
- data/lib/committee/response_validator.rb +32 -8
- data/lib/committee/router.rb +5 -33
- data/lib/committee/{query_params_coercer.rb → string_params_coercer.rb} +11 -6
- data/lib/committee/test/methods.rb +49 -12
- data/lib/committee.rb +15 -1
- data/test/bin/committee_stub_test.rb +45 -0
- data/test/bin_test.rb +20 -0
- data/test/committee_test.rb +49 -0
- data/test/drivers/hyper_schema_test.rb +95 -0
- data/test/drivers/open_api_2_test.rb +255 -0
- data/test/drivers_test.rb +60 -0
- data/test/middleware/base_test.rb +49 -5
- data/test/middleware/request_validation_test.rb +39 -25
- data/test/middleware/response_validation_test.rb +32 -20
- data/test/middleware/stub_test.rb +50 -19
- data/test/request_unpacker_test.rb +10 -0
- data/test/request_validator_test.rb +4 -3
- data/test/response_generator_test.rb +50 -6
- data/test/response_validator_test.rb +29 -4
- data/test/router_test.rb +40 -13
- data/test/{query_params_coercer_test.rb → string_params_coercer_test.rb} +3 -4
- data/test/test/methods_test.rb +44 -5
- data/test/test_helper.rb +59 -1
- metadata +62 -10
| @@ -0,0 +1,49 @@ | |
| 1 | 
            +
            require_relative "test_helper"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Committee do
         | 
| 4 | 
            +
              it "debugs based off env" do
         | 
| 5 | 
            +
                old = ENV["COMMITTEE_DEBUG"]
         | 
| 6 | 
            +
                begin
         | 
| 7 | 
            +
                  ENV["COMMITTEE_DEBUG"] = nil
         | 
| 8 | 
            +
                  refute Committee.debug?
         | 
| 9 | 
            +
                  ENV["COMMITTEE_DEBUG"] = "true"
         | 
| 10 | 
            +
                  assert Committee.debug?
         | 
| 11 | 
            +
                ensure
         | 
| 12 | 
            +
                  ENV["COMMITTEE_DEBUG"] = old
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              it "logs debug messages to stderr" do
         | 
| 17 | 
            +
                old_stderr = $stderr
         | 
| 18 | 
            +
                $stderr = StringIO.new
         | 
| 19 | 
            +
                begin
         | 
| 20 | 
            +
                  stub(Committee).debug? { false }
         | 
| 21 | 
            +
                  Committee.log_debug "blah"
         | 
| 22 | 
            +
                  assert_equal "", $stderr.string
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  stub(Committee).debug? { true }
         | 
| 25 | 
            +
                  Committee.log_debug "blah"
         | 
| 26 | 
            +
                  assert_equal "blah\n", $stderr.string
         | 
| 27 | 
            +
                ensure
         | 
| 28 | 
            +
                  $stderr = old_stderr
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              it "warns on deprecated unless $VERBOSE is nil" do
         | 
| 33 | 
            +
                old_stderr = $stderr
         | 
| 34 | 
            +
                old_verbose = $VERBOSE
         | 
| 35 | 
            +
                $stderr = StringIO.new
         | 
| 36 | 
            +
                begin
         | 
| 37 | 
            +
                  $VERBOSE = nil
         | 
| 38 | 
            +
                  Committee.warn_deprecated "blah"
         | 
| 39 | 
            +
                  assert_equal "", $stderr.string
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  $VERBOSE = true
         | 
| 42 | 
            +
                  Committee.warn_deprecated "blah"
         | 
| 43 | 
            +
                  assert_equal "blah\n", $stderr.string
         | 
| 44 | 
            +
                ensure
         | 
| 45 | 
            +
                  $stderr = old_stderr
         | 
| 46 | 
            +
                  $VERBOSE = old_verbose
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
            end
         | 
| @@ -0,0 +1,95 @@ | |
| 1 | 
            +
            require_relative "../test_helper"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Committee::Drivers::HyperSchema do
         | 
| 4 | 
            +
              before do
         | 
| 5 | 
            +
                @driver = Committee::Drivers::HyperSchema.new
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              it "has a name" do
         | 
| 9 | 
            +
                assert_equal :hyper_schema, @driver.name
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              it "has a schema class" do
         | 
| 13 | 
            +
                assert_equal Committee::Drivers::HyperSchema::Schema, @driver.schema_class
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              it "parses a hyper-schema and builds routes" do
         | 
| 17 | 
            +
                schema = @driver.parse(hyper_schema_data)
         | 
| 18 | 
            +
                assert_kind_of Committee::Drivers::HyperSchema::Schema, schema
         | 
| 19 | 
            +
                assert_equal @driver, schema.driver
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                assert_kind_of Hash, schema.routes
         | 
| 22 | 
            +
                refute schema.routes.empty?
         | 
| 23 | 
            +
                assert(schema.routes.keys.all? { |m|
         | 
| 24 | 
            +
                  ["DELETE", "GET", "PATCH", "POST", "PUT"].include?(m)
         | 
| 25 | 
            +
                })
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                schema.routes.each do |(_, method_routes)|
         | 
| 28 | 
            +
                  method_routes.each do |regex, link|
         | 
| 29 | 
            +
                    assert_kind_of Regexp, regex
         | 
| 30 | 
            +
                    assert_kind_of Committee::Drivers::HyperSchema::Link, link
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              it "defaults to no path parameters" do
         | 
| 36 | 
            +
                assert_equal false, @driver.default_path_params
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              it "defaults to no query parameters" do
         | 
| 40 | 
            +
                assert_equal false, @driver.default_query_params
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
            end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            describe Committee::Drivers::HyperSchema::Link do
         | 
| 45 | 
            +
              before do
         | 
| 46 | 
            +
                @hyper_schema_link = JsonSchema::Schema::Link.new
         | 
| 47 | 
            +
                @hyper_schema_link.enc_type = "application/x-www-form-urlencoded"
         | 
| 48 | 
            +
                @hyper_schema_link.href = "/apps"
         | 
| 49 | 
            +
                @hyper_schema_link.media_type = "application/json"
         | 
| 50 | 
            +
                @hyper_schema_link.method = "GET"
         | 
| 51 | 
            +
                @hyper_schema_link.parent = { "title" => "parent" }
         | 
| 52 | 
            +
                @hyper_schema_link.rel = "instances"
         | 
| 53 | 
            +
                @hyper_schema_link.schema = { "title" => "input" }
         | 
| 54 | 
            +
                @hyper_schema_link.target_schema = { "title" => "target" }
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                @link = Committee::Drivers::HyperSchema::Link.new(@hyper_schema_link)
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              it "proxies #enc_type" do
         | 
| 60 | 
            +
                assert_equal "application/x-www-form-urlencoded", @link.enc_type
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              it "proxies #href" do
         | 
| 64 | 
            +
                assert_equal "/apps", @link.href
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              it "proxies #media_type" do
         | 
| 68 | 
            +
                assert_equal "application/json", @link.media_type
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              it "proxies #method" do
         | 
| 72 | 
            +
                assert_equal "GET", @link.method
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
              it "proxies #rel" do
         | 
| 76 | 
            +
                assert_equal "instances", @link.rel
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              it "proxies #schema" do
         | 
| 80 | 
            +
                assert_equal @hyper_schema_link.schema, @link.schema
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
              it "generates 200 #status_success for non-create" do
         | 
| 84 | 
            +
                assert_equal 200, @link.status_success
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              it "generates 201 #status_success for create" do
         | 
| 88 | 
            +
                @hyper_schema_link.rel = "create"
         | 
| 89 | 
            +
                assert_equal 201, @link.status_success
         | 
| 90 | 
            +
              end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
              it "proxies #target_schema" do
         | 
| 93 | 
            +
                assert_equal @hyper_schema_link.target_schema, @link.target_schema
         | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
            end
         | 
| @@ -0,0 +1,255 @@ | |
| 1 | 
            +
            require_relative "../test_helper"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Committee::Drivers::OpenAPI2 do
         | 
| 4 | 
            +
              before do
         | 
| 5 | 
            +
                @driver = Committee::Drivers::OpenAPI2.new
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              it "has a name" do
         | 
| 9 | 
            +
                assert_equal :open_api_2, @driver.name
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              it "has a schema class" do
         | 
| 13 | 
            +
                assert_equal Committee::Drivers::OpenAPI2::Schema, @driver.schema_class
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              it "parses an OpenAPI 2 spec" do
         | 
| 17 | 
            +
                schema = @driver.parse(open_api_2_data)
         | 
| 18 | 
            +
                assert_kind_of Committee::Drivers::OpenAPI2::Schema, schema
         | 
| 19 | 
            +
                assert_kind_of JsonSchema::Schema, schema.definitions
         | 
| 20 | 
            +
                assert_equal @driver, schema.driver
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                assert_kind_of Hash, schema.routes
         | 
| 23 | 
            +
                refute schema.routes.empty?
         | 
| 24 | 
            +
                assert(schema.routes.keys.all? { |m|
         | 
| 25 | 
            +
                  ["DELETE", "GET", "PATCH", "POST", "PUT"].include?(m)
         | 
| 26 | 
            +
                })
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                schema.routes.each do |(_, method_routes)|
         | 
| 29 | 
            +
                  method_routes.each do |regex, link|
         | 
| 30 | 
            +
                    assert_kind_of Regexp, regex
         | 
| 31 | 
            +
                    assert_kind_of Committee::Drivers::OpenAPI2::Link, link
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    # verify that we've correct generated a parameters schema for each link
         | 
| 34 | 
            +
                    if link.target_schema
         | 
| 35 | 
            +
                      assert_kind_of JsonSchema::Schema, link.schema if link.schema
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    if link.target_schema
         | 
| 39 | 
            +
                      assert_kind_of JsonSchema::Schema, link.target_schema
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              it "names capture groups into href regexes" do
         | 
| 46 | 
            +
                schema = @driver.parse(open_api_2_data)
         | 
| 47 | 
            +
                assert_equal %r{^\/api\/pets\/(?<id>[^\/]+)$}.inspect,
         | 
| 48 | 
            +
                  schema.routes["DELETE"][0][0].inspect
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              it "prefers a 200 response first" do
         | 
| 52 | 
            +
                schema_data = schema_data_with_responses({
         | 
| 53 | 
            +
                  '201' => { 'schema' => { 'description' => '201 response' } },
         | 
| 54 | 
            +
                  '200' => { 'schema' => { 'description' => '200 response' } },
         | 
| 55 | 
            +
                })
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                schema = @driver.parse(schema_data)
         | 
| 58 | 
            +
                link = schema.routes['GET'][0][1]
         | 
| 59 | 
            +
                assert_equal 200, link.status_success
         | 
| 60 | 
            +
                assert_equal({ 'description' => '200 response' }, link.target_schema.data)
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              it "prefers a 201 response next" do
         | 
| 64 | 
            +
                schema_data = schema_data_with_responses({
         | 
| 65 | 
            +
                  '302' => { 'schema' => { 'description' => '302 response' } },
         | 
| 66 | 
            +
                  '201' => { 'schema' => { 'description' => '201 response' } },
         | 
| 67 | 
            +
                })
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                schema = @driver.parse(schema_data)
         | 
| 70 | 
            +
                link = schema.routes['GET'][0][1]
         | 
| 71 | 
            +
                assert_equal 201, link.status_success
         | 
| 72 | 
            +
                assert_equal({ 'description' => '201 response' }, link.target_schema.data)
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
              it "prefers any three-digit response next" do
         | 
| 76 | 
            +
                schema_data = schema_data_with_responses({
         | 
| 77 | 
            +
                  'default' => { 'schema' => { 'description' => 'default response' } },
         | 
| 78 | 
            +
                  '302' => { 'schema' => { 'description' => '302 response' } },
         | 
| 79 | 
            +
                })
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                schema = @driver.parse(schema_data)
         | 
| 82 | 
            +
                link = schema.routes['GET'][0][1]
         | 
| 83 | 
            +
                assert_equal 302, link.status_success
         | 
| 84 | 
            +
                assert_equal({ 'description' => '302 response' }, link.target_schema.data)
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              it "falls back to no response" do
         | 
| 88 | 
            +
                schema_data = schema_data_with_responses({})
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                schema = @driver.parse(schema_data)
         | 
| 91 | 
            +
                link = schema.routes['GET'][0][1]
         | 
| 92 | 
            +
                assert_equal nil, link.status_success
         | 
| 93 | 
            +
                assert_equal nil, link.target_schema
         | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
              it "refuses to parse other version of OpenAPI" do
         | 
| 97 | 
            +
                data = open_api_2_data
         | 
| 98 | 
            +
                data['swagger'] = '3.0'
         | 
| 99 | 
            +
                e = assert_raises(ArgumentError) do
         | 
| 100 | 
            +
                  @driver.parse(data)
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
                assert_equal "Committee: driver requires OpenAPI 2.0.", e.message
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
              it "refuses to parse a spec without mandatory fields" do
         | 
| 106 | 
            +
                data = open_api_2_data
         | 
| 107 | 
            +
                data['definitions'] = nil
         | 
| 108 | 
            +
                e = assert_raises(ArgumentError) do
         | 
| 109 | 
            +
                  @driver.parse(data)
         | 
| 110 | 
            +
                end
         | 
| 111 | 
            +
                assert_equal "Committee: no definitions section in spec data.", e.message
         | 
| 112 | 
            +
              end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
              it "defaults to path parameters" do
         | 
| 115 | 
            +
                assert_equal true, @driver.default_path_params
         | 
| 116 | 
            +
              end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
              it "defaults to query parameters" do
         | 
| 119 | 
            +
                assert_equal true, @driver.default_query_params
         | 
| 120 | 
            +
              end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
              def schema_data_with_responses(response_data)
         | 
| 123 | 
            +
                {
         | 
| 124 | 
            +
                  'swagger' => '2.0',
         | 
| 125 | 
            +
                  'consumes' => ['application/json'],
         | 
| 126 | 
            +
                  'produces' => ['application/json'],
         | 
| 127 | 
            +
                  'paths' => {
         | 
| 128 | 
            +
                    '/foos' => {
         | 
| 129 | 
            +
                      'get' => {
         | 
| 130 | 
            +
                        'responses' => response_data,
         | 
| 131 | 
            +
                      },
         | 
| 132 | 
            +
                    },
         | 
| 133 | 
            +
                  },
         | 
| 134 | 
            +
                  'definitions' => {},
         | 
| 135 | 
            +
                }
         | 
| 136 | 
            +
              end
         | 
| 137 | 
            +
            end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
            describe Committee::Drivers::OpenAPI2::Link do
         | 
| 140 | 
            +
              before do
         | 
| 141 | 
            +
                @link = Committee::Drivers::OpenAPI2::Link.new
         | 
| 142 | 
            +
                @link.enc_type = "application/x-www-form-urlencoded"
         | 
| 143 | 
            +
                @link.href = "/apps"
         | 
| 144 | 
            +
                @link.media_type = "application/json"
         | 
| 145 | 
            +
                @link.method = "GET"
         | 
| 146 | 
            +
                @link.status_success = 200
         | 
| 147 | 
            +
                @link.schema = { "title" => "input" }
         | 
| 148 | 
            +
                @link.target_schema = { "title" => "target" }
         | 
| 149 | 
            +
              end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
              it "uses set #enc_type" do
         | 
| 152 | 
            +
                assert_equal "application/x-www-form-urlencoded", @link.enc_type
         | 
| 153 | 
            +
              end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
              it "uses set #href" do
         | 
| 156 | 
            +
                assert_equal "/apps", @link.href
         | 
| 157 | 
            +
              end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
              it "uses set #media_type" do
         | 
| 160 | 
            +
                assert_equal "application/json", @link.media_type
         | 
| 161 | 
            +
              end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
              it "uses set #method" do
         | 
| 164 | 
            +
                assert_equal "GET", @link.method
         | 
| 165 | 
            +
              end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
              it "proxies #rel" do
         | 
| 168 | 
            +
                e = assert_raises do
         | 
| 169 | 
            +
                  @link.rel
         | 
| 170 | 
            +
                end
         | 
| 171 | 
            +
                assert_equal "Committee: rel not implemented for OpenAPI", e.message
         | 
| 172 | 
            +
              end
         | 
| 173 | 
            +
             | 
| 174 | 
            +
              it "uses set #schema" do
         | 
| 175 | 
            +
                assert_equal({ "title" => "input" }, @link.schema)
         | 
| 176 | 
            +
              end
         | 
| 177 | 
            +
             | 
| 178 | 
            +
              it "uses set #status_success" do
         | 
| 179 | 
            +
                assert_equal 200, @link.status_success
         | 
| 180 | 
            +
              end
         | 
| 181 | 
            +
             | 
| 182 | 
            +
              it "uses set #target_schema" do
         | 
| 183 | 
            +
                assert_equal({ "title" => "target" }, @link.target_schema)
         | 
| 184 | 
            +
              end
         | 
| 185 | 
            +
            end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
            describe Committee::Drivers::OpenAPI2::ParameterSchemaBuilder do
         | 
| 188 | 
            +
              before do
         | 
| 189 | 
            +
              end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
              it "reflects a basic type into a schema" do
         | 
| 192 | 
            +
                data = {
         | 
| 193 | 
            +
                  "parameters" => [
         | 
| 194 | 
            +
                    {
         | 
| 195 | 
            +
                      "name" => "limit",
         | 
| 196 | 
            +
                      "type" => "integer",
         | 
| 197 | 
            +
                    }
         | 
| 198 | 
            +
                  ]
         | 
| 199 | 
            +
                }
         | 
| 200 | 
            +
                schema = call(data)
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                assert_equal ["limit"], schema.properties.keys
         | 
| 203 | 
            +
                assert_equal [], schema.required
         | 
| 204 | 
            +
                assert_equal ["integer"], schema.properties["limit"].type
         | 
| 205 | 
            +
              end
         | 
| 206 | 
            +
             | 
| 207 | 
            +
              it "reflects a required property into a schema" do
         | 
| 208 | 
            +
                data = {
         | 
| 209 | 
            +
                  "parameters" => [
         | 
| 210 | 
            +
                    {
         | 
| 211 | 
            +
                      "name" => "limit",
         | 
| 212 | 
            +
                      "required" => true,
         | 
| 213 | 
            +
                    }
         | 
| 214 | 
            +
                  ]
         | 
| 215 | 
            +
                }
         | 
| 216 | 
            +
                schema = call(data)
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                assert_equal ["limit"], schema.required
         | 
| 219 | 
            +
              end
         | 
| 220 | 
            +
             | 
| 221 | 
            +
              it "reflects an array with an items schema into a schema" do
         | 
| 222 | 
            +
                data = {
         | 
| 223 | 
            +
                  "parameters" => [
         | 
| 224 | 
            +
                    {
         | 
| 225 | 
            +
                      "name" => "tags",
         | 
| 226 | 
            +
                      "type" => "array",
         | 
| 227 | 
            +
                      "items" => {
         | 
| 228 | 
            +
                        "type" => "string"
         | 
| 229 | 
            +
                      }
         | 
| 230 | 
            +
                    }
         | 
| 231 | 
            +
                  ]
         | 
| 232 | 
            +
                }
         | 
| 233 | 
            +
                schema = call(data)
         | 
| 234 | 
            +
             | 
| 235 | 
            +
                assert_equal ["array"], schema.properties["tags"].type
         | 
| 236 | 
            +
                assert_equal({ "type" => "string" }, schema.properties["tags"].items)
         | 
| 237 | 
            +
              end
         | 
| 238 | 
            +
             | 
| 239 | 
            +
              it "requires that certain fields are present" do
         | 
| 240 | 
            +
                data = {
         | 
| 241 | 
            +
                  "parameters" => [
         | 
| 242 | 
            +
                    {
         | 
| 243 | 
            +
                    }
         | 
| 244 | 
            +
                  ]
         | 
| 245 | 
            +
                }
         | 
| 246 | 
            +
                e = assert_raises ArgumentError do
         | 
| 247 | 
            +
                  call(data)
         | 
| 248 | 
            +
                end
         | 
| 249 | 
            +
                assert_equal "Committee: no name section in link data.", e.message
         | 
| 250 | 
            +
              end
         | 
| 251 | 
            +
             | 
| 252 | 
            +
              def call(data)
         | 
| 253 | 
            +
                Committee::Drivers::OpenAPI2::ParameterSchemaBuilder.new(data).call
         | 
| 254 | 
            +
              end
         | 
| 255 | 
            +
            end
         | 
| @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            require_relative "test_helper"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Committee::Drivers do
         | 
| 4 | 
            +
              DRIVERS = [
         | 
| 5 | 
            +
                :hyper_schema,
         | 
| 6 | 
            +
                :open_api_2,
         | 
| 7 | 
            +
              ].freeze
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              it "gets driver with .driver_from_name" do
         | 
| 10 | 
            +
                DRIVERS.each do |name|
         | 
| 11 | 
            +
                  driver = Committee::Drivers.driver_from_name(name)
         | 
| 12 | 
            +
                  assert_kind_of Committee::Drivers::Driver, driver
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              it "raises an ArgumentError for an unknown driver with .driver_from_name" do
         | 
| 17 | 
            +
                e = assert_raises(ArgumentError) do
         | 
| 18 | 
            +
                  Committee::Drivers.driver_from_name(:blueprint)
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
                assert_equal %{Committee: unknown driver "blueprint".}, e.message
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            describe Committee::Drivers::Driver do
         | 
| 25 | 
            +
              DRIVER_METHODS = {
         | 
| 26 | 
            +
                :default_path_params  => [],
         | 
| 27 | 
            +
                :default_query_params => [],
         | 
| 28 | 
            +
                :name                 => [],
         | 
| 29 | 
            +
                :parse                => [nil],
         | 
| 30 | 
            +
                :schema_class         => [],
         | 
| 31 | 
            +
              }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              it "has a set of abstract methods" do
         | 
| 34 | 
            +
                driver = Committee::Drivers::Driver.new
         | 
| 35 | 
            +
                DRIVER_METHODS.each do |name, args|
         | 
| 36 | 
            +
                  e = assert_raises do
         | 
| 37 | 
            +
                    driver.send(name, *args)
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                  assert_equal "needs implementation", e.message,
         | 
| 40 | 
            +
                    "Incorrect error message while sending #{name}: #{e.message}"
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
            end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            describe Committee::Drivers::Schema do
         | 
| 46 | 
            +
              SCHEMA_METHODS = {
         | 
| 47 | 
            +
                :driver => [],
         | 
| 48 | 
            +
              }
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              it "has a set of abstract methods" do
         | 
| 51 | 
            +
                schema = Committee::Drivers::Schema.new
         | 
| 52 | 
            +
                SCHEMA_METHODS.each do |name, args|
         | 
| 53 | 
            +
                  e = assert_raises do
         | 
| 54 | 
            +
                    schema.send(name, *args)
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                  assert_equal "needs implementation", e.message,
         | 
| 57 | 
            +
                    "Incorrect error message while sending #{name}: #{e.message}"
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
            end
         | 
| @@ -7,8 +7,10 @@ describe Committee::Middleware::Base do | |
| 7 7 | 
             
                @app
         | 
| 8 8 | 
             
              end
         | 
| 9 9 |  | 
| 10 | 
            -
              it "accepts schema  | 
| 11 | 
            -
                @app = new_rack_app( | 
| 10 | 
            +
              it "accepts just a schema object" do
         | 
| 11 | 
            +
                @app = new_rack_app(
         | 
| 12 | 
            +
                  schema: hyper_schema
         | 
| 13 | 
            +
                )
         | 
| 12 14 | 
             
                params = {
         | 
| 13 15 | 
             
                  "name" => "cloudnasium"
         | 
| 14 16 | 
             
                }
         | 
| @@ -17,9 +19,11 @@ describe Committee::Middleware::Base do | |
| 17 19 | 
             
                assert_equal 200, last_response.status
         | 
| 18 20 | 
             
              end
         | 
| 19 21 |  | 
| 20 | 
            -
              it "accepts schema  | 
| 21 | 
            -
                 | 
| 22 | 
            -
                @app = new_rack_app( | 
| 22 | 
            +
              it "accepts schema string (legacy behavior)" do
         | 
| 23 | 
            +
                mock(Committee).warn_deprecated.with_any_args
         | 
| 24 | 
            +
                @app = new_rack_app(
         | 
| 25 | 
            +
                  schema: JSON.dump(hyper_schema_data)
         | 
| 26 | 
            +
                )
         | 
| 23 27 | 
             
                params = {
         | 
| 24 28 | 
             
                  "name" => "cloudnasium"
         | 
| 25 29 | 
             
                }
         | 
| @@ -28,6 +32,46 @@ describe Committee::Middleware::Base do | |
| 28 32 | 
             
                assert_equal 200, last_response.status
         | 
| 29 33 | 
             
              end
         | 
| 30 34 |  | 
| 35 | 
            +
              it "accepts schema hash (legacy behavior)" do
         | 
| 36 | 
            +
                mock(Committee).warn_deprecated.with_any_args
         | 
| 37 | 
            +
                @app = new_rack_app(
         | 
| 38 | 
            +
                  schema: hyper_schema_data
         | 
| 39 | 
            +
                )
         | 
| 40 | 
            +
                params = {
         | 
| 41 | 
            +
                  "name" => "cloudnasium"
         | 
| 42 | 
            +
                }
         | 
| 43 | 
            +
                header "Content-Type", "application/json"
         | 
| 44 | 
            +
                post "/apps", JSON.generate(params)
         | 
| 45 | 
            +
                assert_equal 200, last_response.status
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              it "accepts schema JsonSchema::Schema object (legacy behavior)" do
         | 
| 49 | 
            +
                # Note we don't warn here because this is a recent deprecation and passing
         | 
| 50 | 
            +
                # a schema object will not be a huge performance hit. We should probably
         | 
| 51 | 
            +
                # start warning on the next version.
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                @app = new_rack_app(
         | 
| 54 | 
            +
                  schema: JsonSchema.parse!(hyper_schema_data)
         | 
| 55 | 
            +
                )
         | 
| 56 | 
            +
                params = {
         | 
| 57 | 
            +
                  "name" => "cloudnasium"
         | 
| 58 | 
            +
                }
         | 
| 59 | 
            +
                header "Content-Type", "application/json"
         | 
| 60 | 
            +
                post "/apps", JSON.generate(params)
         | 
| 61 | 
            +
                assert_equal 200, last_response.status
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              it "doesn't accept other schema types" do
         | 
| 65 | 
            +
                @app = new_rack_app(
         | 
| 66 | 
            +
                  schema: 7,
         | 
| 67 | 
            +
                )
         | 
| 68 | 
            +
                e = assert_raises(ArgumentError) do
         | 
| 69 | 
            +
                  post "/apps"
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
                assert_equal "Committee: schema expected to be a hash or an instance " +
         | 
| 72 | 
            +
                  "of Committee::Drivers::Schema.", e.message
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
             | 
| 31 75 | 
             
              private
         | 
| 32 76 |  | 
| 33 77 | 
             
              def new_rack_app(options = {})
         | 
| @@ -8,7 +8,7 @@ describe Committee::Middleware::RequestValidation do | |
| 8 8 | 
             
              end
         | 
| 9 9 |  | 
| 10 10 | 
             
              it "passes through a valid request" do
         | 
| 11 | 
            -
                @app = new_rack_app
         | 
| 11 | 
            +
                @app = new_rack_app(schema: hyper_schema)
         | 
| 12 12 | 
             
                params = {
         | 
| 13 13 | 
             
                  "name" => "cloudnasium"
         | 
| 14 14 | 
             
                }
         | 
| @@ -18,7 +18,7 @@ describe Committee::Middleware::RequestValidation do | |
| 18 18 | 
             
              end
         | 
| 19 19 |  | 
| 20 20 | 
             
              it "detects an invalid request" do
         | 
| 21 | 
            -
                @app = new_rack_app
         | 
| 21 | 
            +
                @app = new_rack_app(schema: hyper_schema)
         | 
| 22 22 | 
             
                header "Content-Type", "application/json"
         | 
| 23 23 | 
             
                params = {
         | 
| 24 24 | 
             
                  "name" => 1
         | 
| @@ -29,7 +29,7 @@ describe Committee::Middleware::RequestValidation do | |
| 29 29 | 
             
              end
         | 
| 30 30 |  | 
| 31 31 | 
             
              it "rescues JSON errors" do
         | 
| 32 | 
            -
                @app = new_rack_app
         | 
| 32 | 
            +
                @app = new_rack_app(schema: hyper_schema)
         | 
| 33 33 | 
             
                header "Content-Type", "application/json"
         | 
| 34 34 | 
             
                post "/apps", "{x:y}"
         | 
| 35 35 | 
             
                assert_equal 400, last_response.status
         | 
| @@ -37,7 +37,7 @@ describe Committee::Middleware::RequestValidation do | |
| 37 37 | 
             
              end
         | 
| 38 38 |  | 
| 39 39 | 
             
              it "takes a prefix" do
         | 
| 40 | 
            -
                @app = new_rack_app(prefix: "/v1")
         | 
| 40 | 
            +
                @app = new_rack_app(prefix: "/v1", schema: hyper_schema)
         | 
| 41 41 | 
             
                params = {
         | 
| 42 42 | 
             
                  "name" => "cloudnasium"
         | 
| 43 43 | 
             
                }
         | 
| @@ -47,37 +47,26 @@ describe Committee::Middleware::RequestValidation do | |
| 47 47 | 
             
              end
         | 
| 48 48 |  | 
| 49 49 | 
             
              it "ignores paths outside the prefix" do
         | 
| 50 | 
            -
                @app = new_rack_app(prefix: "/v1")
         | 
| 50 | 
            +
                @app = new_rack_app(prefix: "/v1", schema: hyper_schema)
         | 
| 51 51 | 
             
                header "Content-Type", "text/html"
         | 
| 52 52 | 
             
                get "/hello"
         | 
| 53 53 | 
             
                assert_equal 200, last_response.status
         | 
| 54 54 | 
             
              end
         | 
| 55 55 |  | 
| 56 | 
            -
              it "warns when sending a deprecated string" do
         | 
| 57 | 
            -
                mock(Committee).warn_deprecated.with_any_args
         | 
| 58 | 
            -
                @app = new_rack_app(schema: File.read("./test/data/schema.json"))
         | 
| 59 | 
            -
                params = {
         | 
| 60 | 
            -
                  "name" => "cloudnasium"
         | 
| 61 | 
            -
                }
         | 
| 62 | 
            -
                header "Content-Type", "application/json"
         | 
| 63 | 
            -
                post "/apps", JSON.generate(params)
         | 
| 64 | 
            -
                assert_equal 200, last_response.status
         | 
| 65 | 
            -
              end
         | 
| 66 | 
            -
             | 
| 67 56 | 
             
              it "routes to paths not in schema" do
         | 
| 68 | 
            -
                @app = new_rack_app
         | 
| 57 | 
            +
                @app = new_rack_app(schema: hyper_schema)
         | 
| 69 58 | 
             
                get "/not-a-resource"
         | 
| 70 59 | 
             
                assert_equal 200, last_response.status
         | 
| 71 60 | 
             
              end
         | 
| 72 61 |  | 
| 73 62 | 
             
              it "doesn't route to paths not in schema when in strict mode" do
         | 
| 74 | 
            -
                @app = new_rack_app(strict: true)
         | 
| 63 | 
            +
                @app = new_rack_app(schema: hyper_schema, strict: true)
         | 
| 75 64 | 
             
                get "/not-a-resource"
         | 
| 76 65 | 
             
                assert_equal 404, last_response.status
         | 
| 77 66 | 
             
              end
         | 
| 78 67 |  | 
| 79 68 | 
             
              it "optionally raises an error" do
         | 
| 80 | 
            -
                @app = new_rack_app(raise: true)
         | 
| 69 | 
            +
                @app = new_rack_app(raise: true, schema: hyper_schema)
         | 
| 81 70 | 
             
                header "Content-Type", "application/json"
         | 
| 82 71 | 
             
                assert_raises(Committee::InvalidRequest) do
         | 
| 83 72 | 
             
                  post "/apps", "{x:y}"
         | 
| @@ -85,7 +74,7 @@ describe Committee::Middleware::RequestValidation do | |
| 85 74 | 
             
              end
         | 
| 86 75 |  | 
| 87 76 | 
             
              it "optionally skip content_type check" do
         | 
| 88 | 
            -
                @app = new_rack_app(check_content_type: false)
         | 
| 77 | 
            +
                @app = new_rack_app(check_content_type: false, schema: hyper_schema)
         | 
| 89 78 | 
             
                params = {
         | 
| 90 79 | 
             
                  "name" => "cloudnasium"
         | 
| 91 80 | 
             
                }
         | 
| @@ -95,26 +84,51 @@ describe Committee::Middleware::RequestValidation do | |
| 95 84 | 
             
              end
         | 
| 96 85 |  | 
| 97 86 | 
             
              it "optionally coerces query params" do
         | 
| 98 | 
            -
                @app = new_rack_app(coerce_query_params: true)
         | 
| 87 | 
            +
                @app = new_rack_app(coerce_query_params: true, schema: hyper_schema)
         | 
| 99 88 | 
             
                header "Content-Type", "application/json"
         | 
| 100 89 | 
             
                get "/search/apps", {"per_page" => "10", "query" => "cloudnasium"}
         | 
| 101 90 | 
             
                assert_equal 200, last_response.status
         | 
| 102 91 | 
             
              end
         | 
| 103 92 |  | 
| 104 93 | 
             
              it "still raises an error if query param coercion is not possible" do
         | 
| 105 | 
            -
                @app = new_rack_app(coerce_query_params: true)
         | 
| 94 | 
            +
                @app = new_rack_app(coerce_query_params: true, schema: hyper_schema)
         | 
| 106 95 | 
             
                header "Content-Type", "application/json"
         | 
| 107 96 | 
             
                get "/search/apps", {"per_page" => "foo", "query" => "cloudnasium"}
         | 
| 108 97 | 
             
                assert_equal 400, last_response.status
         | 
| 109 98 | 
             
                assert_match /invalid request/i, last_response.body
         | 
| 110 99 | 
             
              end
         | 
| 111 100 |  | 
| 101 | 
            +
              it "passes through a valid request for OpenAPI" do
         | 
| 102 | 
            +
                @app = new_rack_app(schema: open_api_2_schema)
         | 
| 103 | 
            +
                get "/api/pets?limit=3"
         | 
| 104 | 
            +
                assert_equal 200, last_response.status
         | 
| 105 | 
            +
              end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
              it "detects an invalid request for OpenAPI" do
         | 
| 108 | 
            +
                @app = new_rack_app(schema: open_api_2_schema)
         | 
| 109 | 
            +
                get "/api/pets?limit=foo"
         | 
| 110 | 
            +
                assert_equal 400, last_response.status
         | 
| 111 | 
            +
                assert_match /invalid request/i, last_response.body
         | 
| 112 | 
            +
              end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
              it "passes through a valid request for OpenAPI including path parameters" do
         | 
| 115 | 
            +
                @app = new_rack_app(schema: open_api_2_schema)
         | 
| 116 | 
            +
                # not that ID is expect to be an integer
         | 
| 117 | 
            +
                get "/api/pets/123"
         | 
| 118 | 
            +
                assert_equal 200, last_response.status
         | 
| 119 | 
            +
              end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
              it "detects an invalid request for OpenAPI including path parameters" do
         | 
| 122 | 
            +
                @app = new_rack_app(schema: open_api_2_schema)
         | 
| 123 | 
            +
                # not that ID is expect to be an integer
         | 
| 124 | 
            +
                get "/api/pets/not-integer"
         | 
| 125 | 
            +
                assert_equal 400, last_response.status
         | 
| 126 | 
            +
                assert_match /invalid request/i, last_response.body
         | 
| 127 | 
            +
              end
         | 
| 128 | 
            +
             | 
| 112 129 | 
             
              private
         | 
| 113 130 |  | 
| 114 131 | 
             
              def new_rack_app(options = {})
         | 
| 115 | 
            -
                options = {
         | 
| 116 | 
            -
                  schema: JSON.parse(File.read("./test/data/schema.json"))
         | 
| 117 | 
            -
                }.merge(options)
         | 
| 118 132 | 
             
                Rack::Builder.new {
         | 
| 119 133 | 
             
                  use Committee::Middleware::RequestValidation, options
         | 
| 120 134 | 
             
                  run lambda { |_|
         |