diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 81384c026d..11eb9f51e3 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,18 +1,11 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2024-07-23 11:24:53 UTC using RuboCop version 1.64.1. +# on 2024-10-06 16:00:59 UTC using RuboCop version 1.66.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 1 -# Configuration parameters: AllowedMethods. -# AllowedMethods: enums -Lint/ConstantDefinitionInBlock: - Exclude: - - 'spec/grape/validations/validators/except_values_spec.rb' - # Offense count: 1 # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: @@ -29,17 +22,6 @@ Naming/VariableNumber: - 'spec/grape/exceptions/validation_errors_spec.rb' - 'spec/grape/validations_spec.rb' -# Offense count: 1 -# Configuration parameters: IgnoredMetadata. -RSpec/DescribeClass: - Exclude: - - '**/spec/features/**/*' - - '**/spec/requests/**/*' - - '**/spec/routing/**/*' - - '**/spec/system/**/*' - - '**/spec/views/**/*' - - 'spec/grape/named_api_spec.rb' - # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. @@ -68,7 +50,7 @@ RSpec/ExpectActual: # Offense count: 1 RSpec/ExpectInHook: Exclude: - - 'spec/grape/validations/validators/values_spec.rb' + - 'spec/grape/validations/validators/values_validator_spec.rb' # Offense count: 6 # Configuration parameters: Max, AllowedIdentifiers, AllowedPatterns. @@ -78,7 +60,7 @@ RSpec/IndexedLet: - 'spec/grape/presenters/presenter_spec.rb' - 'spec/shared/versioning_examples.rb' -# Offense count: 39 +# Offense count: 38 # Configuration parameters: AssignmentOnly. RSpec/InstanceVariable: Exclude: @@ -87,12 +69,6 @@ RSpec/InstanceVariable: - 'spec/grape/middleware/base_spec.rb' - 'spec/grape/middleware/versioner/accept_version_header_spec.rb' - 'spec/grape/middleware/versioner/header_spec.rb' - - 'spec/grape/validations/validators/except_values_spec.rb' - -# Offense count: 6 -RSpec/LeakyConstantDeclaration: - Exclude: - - 'spec/grape/validations/validators/except_values_spec.rb' # Offense count: 1 RSpec/MessageChain: @@ -109,14 +85,14 @@ RSpec/RepeatedDescription: Exclude: - 'spec/grape/api_spec.rb' - 'spec/grape/endpoint_spec.rb' - - 'spec/grape/validations/validators/allow_blank_spec.rb' - - 'spec/grape/validations/validators/values_spec.rb' + - 'spec/grape/validations/validators/allow_blank_validator_spec.rb' + - 'spec/grape/validations/validators/values_validator_spec.rb' # Offense count: 6 RSpec/RepeatedExample: Exclude: - 'spec/grape/middleware/versioner/accept_version_header_spec.rb' - - 'spec/grape/validations/validators/allow_blank_spec.rb' + - 'spec/grape/validations/validators/allow_blank_validator_spec.rb' # Offense count: 10 RSpec/RepeatedExampleGroupDescription: @@ -124,7 +100,7 @@ RSpec/RepeatedExampleGroupDescription: - 'spec/grape/api_spec.rb' - 'spec/grape/endpoint_spec.rb' - 'spec/grape/util/inheritable_setting_spec.rb' - - 'spec/grape/validations/validators/values_spec.rb' + - 'spec/grape/validations/validators/values_validator_spec.rb' # Offense count: 4 RSpec/StubbedMock: @@ -133,7 +109,7 @@ RSpec/StubbedMock: - 'spec/grape/dsl/routing_spec.rb' - 'spec/grape/middleware/formatter_spec.rb' -# Offense count: 121 +# Offense count: 118 RSpec/SubjectStub: Exclude: - 'spec/grape/api_spec.rb' @@ -150,7 +126,7 @@ RSpec/SubjectStub: - 'spec/grape/middleware/globals_spec.rb' - 'spec/grape/middleware/stack_spec.rb' -# Offense count: 23 +# Offense count: 22 # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. RSpec/VerifiedDoubles: Exclude: diff --git a/CHANGELOG.md b/CHANGELOG.md index 344f349341..e5d85dfec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ #### Fixes +* [#2504](https://github.com/ruby-grape/grape/pull/2504): Fix leaky modules in specs - [@ericproulx](https://github.com/ericproulx). * Your contribution here. ### 2.2.0 (2024-09-14) diff --git a/spec/grape/api/deeply_included_options_spec.rb b/spec/grape/api/deeply_included_options_spec.rb index 08084811b8..940e11560c 100644 --- a/spec/grape/api/deeply_included_options_spec.rb +++ b/spec/grape/api/deeply_included_options_spec.rb @@ -1,21 +1,17 @@ # frozen_string_literal: true -module DeeplyIncludedOptionsSpec - module Defaults - extend ActiveSupport::Concern - included do - format :json +describe Grape::API do + let(:app) do + main_api = api + Class.new(Grape::API) do + mount main_api end end - module Admin - module Defaults - extend ActiveSupport::Concern - include DeeplyIncludedOptionsSpec::Defaults - end - - class Users < Grape::API - include DeeplyIncludedOptionsSpec::Admin::Defaults + let(:api) do + deeply_included_options = options + Class.new(Grape::API) do + include deeply_included_options resource :users do get do @@ -25,16 +21,21 @@ class Users < Grape::API end end - class Main < Grape::API - mount DeeplyIncludedOptionsSpec::Admin::Users + let(:options) do + deep_included_options_default = default + Module.new do + extend ActiveSupport::Concern + include deep_included_options_default + end end -end - -describe Grape::API do - subject { DeeplyIncludedOptionsSpec::Main } - def app - subject + let(:default) do + Module.new do + extend ActiveSupport::Concern + included do + format :json + end + end end it 'works for unspecified format' do diff --git a/spec/grape/dsl/callbacks_spec.rb b/spec/grape/dsl/callbacks_spec.rb index bec2d823fa..7fc444fe20 100644 --- a/spec/grape/dsl/callbacks_spec.rb +++ b/spec/grape/dsl/callbacks_spec.rb @@ -1,45 +1,41 @@ # frozen_string_literal: true -module Grape - module DSL - module CallbacksSpec - class Dummy - include Grape::DSL::Callbacks - end - end +describe Grape::DSL::Callbacks do + subject { dummy_class } - describe Callbacks do - subject { Class.new(CallbacksSpec::Dummy) } + let(:dummy_class) do + Class.new do + include Grape::DSL::Callbacks + end + end - let(:proc) { -> {} } + let(:proc) { -> {} } - describe '.before' do - it 'adds a block to "before"' do - expect(subject).to receive(:namespace_stackable).with(:befores, proc) - subject.before(&proc) - end - end + describe '.before' do + it 'adds a block to "before"' do + expect(subject).to receive(:namespace_stackable).with(:befores, proc) + subject.before(&proc) + end + end - describe '.before_validation' do - it 'adds a block to "before_validation"' do - expect(subject).to receive(:namespace_stackable).with(:before_validations, proc) - subject.before_validation(&proc) - end - end + describe '.before_validation' do + it 'adds a block to "before_validation"' do + expect(subject).to receive(:namespace_stackable).with(:before_validations, proc) + subject.before_validation(&proc) + end + end - describe '.after_validation' do - it 'adds a block to "after_validation"' do - expect(subject).to receive(:namespace_stackable).with(:after_validations, proc) - subject.after_validation(&proc) - end - end + describe '.after_validation' do + it 'adds a block to "after_validation"' do + expect(subject).to receive(:namespace_stackable).with(:after_validations, proc) + subject.after_validation(&proc) + end + end - describe '.after' do - it 'adds a block to "after"' do - expect(subject).to receive(:namespace_stackable).with(:afters, proc) - subject.after(&proc) - end - end + describe '.after' do + it 'adds a block to "after"' do + expect(subject).to receive(:namespace_stackable).with(:afters, proc) + subject.after(&proc) end end end diff --git a/spec/grape/dsl/headers_spec.rb b/spec/grape/dsl/headers_spec.rb index 154fbf82b2..1502176bd9 100644 --- a/spec/grape/dsl/headers_spec.rb +++ b/spec/grape/dsl/headers_spec.rb @@ -1,62 +1,59 @@ # frozen_string_literal: true -module Grape - module DSL - module HeadersSpec - class Dummy - include Grape::DSL::Headers - end +describe Grape::DSL::Headers do + subject { dummy_class.new } + + let(:dummy_class) do + Class.new do + include Grape::DSL::Headers end - describe Headers do - subject { HeadersSpec::Dummy.new } + end + + let(:header_data) do + { 'first key' => 'First Value', + 'second key' => 'Second Value' } + end + + context 'when headers are set' do + describe '#header' do + before do + header_data.each { |k, v| subject.header(k, v) } + end - let(:header_data) do - { 'first key' => 'First Value', - 'second key' => 'Second Value' } + describe 'get' do + it 'returns a specifc value' do + expect(subject.header['first key']).to eq 'First Value' + expect(subject.header['second key']).to eq 'Second Value' + end + + it 'returns all set headers' do + expect(subject.header).to eq header_data + expect(subject.headers).to eq header_data + end end - context 'when headers are set' do - describe '#header' do - before do - header_data.each { |k, v| subject.header(k, v) } - end - - describe 'get' do - it 'returns a specifc value' do - expect(subject.header['first key']).to eq 'First Value' - expect(subject.header['second key']).to eq 'Second Value' - end - - it 'returns all set headers' do - expect(subject.header).to eq header_data - expect(subject.headers).to eq header_data - end - end - - describe 'set' do - it 'returns value' do - expect(subject.header('third key', 'Third Value')) - expect(subject.header['third key']).to eq 'Third Value' - end - end - - describe 'delete' do - it 'deletes a header key-value pair' do - expect(subject.header('first key')).to eq header_data['first key'] - expect(subject.header).not_to have_key('first key') - end - end + describe 'set' do + it 'returns value' do + expect(subject.header('third key', 'Third Value')) + expect(subject.header['third key']).to eq 'Third Value' end end - context 'when no headers are set' do - describe '#header' do - it 'returns nil' do - expect(subject.header['first key']).to be_nil - expect(subject.header('first key')).to be_nil - end + describe 'delete' do + it 'deletes a header key-value pair' do + expect(subject.header('first key')).to eq header_data['first key'] + expect(subject.header).not_to have_key('first key') end end end end + + context 'when no headers are set' do + describe '#header' do + it 'returns nil' do + expect(subject.header['first key']).to be_nil + expect(subject.header('first key')).to be_nil + end + end + end end diff --git a/spec/grape/dsl/helpers_spec.rb b/spec/grape/dsl/helpers_spec.rb index ce122e953c..1c4d539ee2 100644 --- a/spec/grape/dsl/helpers_spec.rb +++ b/spec/grape/dsl/helpers_spec.rb @@ -1,100 +1,103 @@ # frozen_string_literal: true -module Grape - module DSL - module HelpersSpec - class Dummy - include Grape::DSL::Helpers - - def self.mods - namespace_stackable(:helpers) - end +describe Grape::DSL::Helpers do + subject { dummy_class } - def self.first_mod - mods.first - end - end - end + let(:dummy_class) do + Class.new do + include Grape::DSL::Helpers - module BooleanParam - extend Grape::API::Helpers + def self.mods + namespace_stackable(:helpers) + end - params :requires_toggle_prm do - requires :toggle_prm, type: Boolean + def self.first_mod + mods.first end end + end - class Base < Grape::API - helpers BooleanParam + let(:proc) do + lambda do |*| + def test + :test + end end + end - class Child < Base; end + describe '.helpers' do + it 'adds a module with the given block' do + expect(subject).to receive(:namespace_stackable).with(:helpers, kind_of(Grape::DSL::Helpers::BaseHelper)).and_call_original + expect(subject).to receive(:namespace_stackable).with(:helpers).and_call_original + subject.helpers(&proc) - describe Helpers do - subject { Class.new(HelpersSpec::Dummy) } + expect(subject.first_mod.instance_methods).to include(:test) + end - let(:proc) do - lambda do |*| - def test - :test - end - end - end + it 'uses provided modules' do + mod = Module.new - describe '.helpers' do - it 'adds a module with the given block' do - expect(subject).to receive(:namespace_stackable).with(:helpers, kind_of(Grape::DSL::Helpers::BaseHelper)).and_call_original - expect(subject).to receive(:namespace_stackable).with(:helpers).and_call_original - subject.helpers(&proc) + expect(subject).to receive(:namespace_stackable).with(:helpers, kind_of(Grape::DSL::Helpers::BaseHelper)).and_call_original.twice + expect(subject).to receive(:namespace_stackable).with(:helpers).and_call_original + subject.helpers(mod, &proc) - expect(subject.first_mod.instance_methods).to include(:test) - end - - it 'uses provided modules' do - mod = Module.new + expect(subject.first_mod).to eq mod + end - expect(subject).to receive(:namespace_stackable).with(:helpers, kind_of(Grape::DSL::Helpers::BaseHelper)).and_call_original.twice - expect(subject).to receive(:namespace_stackable).with(:helpers).and_call_original - subject.helpers(mod, &proc) + it 'uses many provided modules' do + mod = Module.new + mod2 = Module.new + mod3 = Module.new - expect(subject.first_mod).to eq mod - end + expect(subject).to receive(:namespace_stackable).with(:helpers, kind_of(Grape::DSL::Helpers::BaseHelper)).and_call_original.exactly(4).times + expect(subject).to receive(:namespace_stackable).with(:helpers).and_call_original.exactly(3).times - it 'uses many provided modules' do - mod = Module.new - mod2 = Module.new - mod3 = Module.new + subject.helpers(mod, mod2, mod3, &proc) - expect(subject).to receive(:namespace_stackable).with(:helpers, kind_of(Grape::DSL::Helpers::BaseHelper)).and_call_original.exactly(4).times - expect(subject).to receive(:namespace_stackable).with(:helpers).and_call_original.exactly(3).times + expect(subject.mods).to include(mod) + expect(subject.mods).to include(mod2) + expect(subject.mods).to include(mod3) + end - subject.helpers(mod, mod2, mod3, &proc) + context 'with an external file' do + let(:boolean_helper) do + Module.new do + extend Grape::API::Helpers - expect(subject.mods).to include(mod) - expect(subject.mods).to include(mod2) - expect(subject.mods).to include(mod3) + params :requires_toggle_prm do + requires :toggle_prm, type: Boolean + end end + end - context 'with an external file' do - it 'sets Boolean as a Grape::API::Boolean' do - subject.helpers BooleanParam - expect(subject.first_mod::Boolean).to eq Grape::API::Boolean + it 'sets Boolean as a Grape::API::Boolean' do + subject.helpers boolean_helper + expect(subject.first_mod::Boolean).to eq Grape::API::Boolean + end + end + + context 'in child classes' do + let(:base_class) do + Class.new(Grape::API) do + helpers do + params :requires_toggle_prm do + requires :toggle_prm, type: Integer + end end end + end - context 'in child classes' do - it 'is available' do - klass = Child - expect do - klass.instance_eval do - params do - use :requires_toggle_prm - end - end - end.not_to raise_exception + let(:api_class) do + Class.new(base_class) do + params do + use :requires_toggle_prm end end end + + it 'is available' do + expect { api_class }.not_to raise_exception + end end end end diff --git a/spec/grape/dsl/inside_route_spec.rb b/spec/grape/dsl/inside_route_spec.rb index 038e203140..ea8d6d9875 100644 --- a/spec/grape/dsl/inside_route_spec.rb +++ b/spec/grape/dsl/inside_route_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -describe Grape::Endpoint do +describe Grape::DSL::InsideRoute do subject { dummy_class.new } let(:dummy_class) do diff --git a/spec/grape/dsl/middleware_spec.rb b/spec/grape/dsl/middleware_spec.rb index 8413eaaaf4..1b363697a4 100644 --- a/spec/grape/dsl/middleware_spec.rb +++ b/spec/grape/dsl/middleware_spec.rb @@ -1,60 +1,56 @@ # frozen_string_literal: true -module Grape - module DSL - module MiddlewareSpec - class Dummy - include Grape::DSL::Middleware - end - end +describe Grape::DSL::Middleware do + subject { dummy_class } - describe Middleware do - subject { Class.new(MiddlewareSpec::Dummy) } + let(:dummy_class) do + Class.new do + include Grape::DSL::Middleware + end + end - let(:proc) { -> {} } - let(:foo_middleware) { Class.new } - let(:bar_middleware) { Class.new } + let(:proc) { -> {} } + let(:foo_middleware) { Class.new } + let(:bar_middleware) { Class.new } - describe '.use' do - it 'adds a middleware with the right operation' do - expect(subject).to receive(:namespace_stackable).with(:middleware, [:use, foo_middleware, :arg1, proc]) + describe '.use' do + it 'adds a middleware with the right operation' do + expect(subject).to receive(:namespace_stackable).with(:middleware, [:use, foo_middleware, :arg1, proc]) - subject.use foo_middleware, :arg1, &proc - end - end + subject.use foo_middleware, :arg1, &proc + end + end - describe '.insert' do - it 'adds a middleware with the right operation' do - expect(subject).to receive(:namespace_stackable).with(:middleware, [:insert, 0, :arg1, proc]) + describe '.insert' do + it 'adds a middleware with the right operation' do + expect(subject).to receive(:namespace_stackable).with(:middleware, [:insert, 0, :arg1, proc]) - subject.insert 0, :arg1, &proc - end - end + subject.insert 0, :arg1, &proc + end + end - describe '.insert_before' do - it 'adds a middleware with the right operation' do - expect(subject).to receive(:namespace_stackable).with(:middleware, [:insert_before, foo_middleware, :arg1, proc]) + describe '.insert_before' do + it 'adds a middleware with the right operation' do + expect(subject).to receive(:namespace_stackable).with(:middleware, [:insert_before, foo_middleware, :arg1, proc]) - subject.insert_before foo_middleware, :arg1, &proc - end - end + subject.insert_before foo_middleware, :arg1, &proc + end + end - describe '.insert_after' do - it 'adds a middleware with the right operation' do - expect(subject).to receive(:namespace_stackable).with(:middleware, [:insert_after, foo_middleware, :arg1, proc]) + describe '.insert_after' do + it 'adds a middleware with the right operation' do + expect(subject).to receive(:namespace_stackable).with(:middleware, [:insert_after, foo_middleware, :arg1, proc]) - subject.insert_after foo_middleware, :arg1, &proc - end - end + subject.insert_after foo_middleware, :arg1, &proc + end + end - describe '.middleware' do - it 'returns the middleware stack' do - subject.use foo_middleware, :arg1, &proc - subject.insert_before bar_middleware, :arg1, :arg2 + describe '.middleware' do + it 'returns the middleware stack' do + subject.use foo_middleware, :arg1, &proc + subject.insert_before bar_middleware, :arg1, :arg2 - expect(subject.middleware).to eq [[:use, foo_middleware, :arg1, proc], [:insert_before, bar_middleware, :arg1, :arg2]] - end - end + expect(subject.middleware).to eq [[:use, foo_middleware, :arg1, proc], [:insert_before, bar_middleware, :arg1, :arg2]] end end end diff --git a/spec/grape/dsl/parameters_spec.rb b/spec/grape/dsl/parameters_spec.rb index 97aae0a586..28ff1cce0b 100644 --- a/spec/grape/dsl/parameters_spec.rb +++ b/spec/grape/dsl/parameters_spec.rb @@ -1,289 +1,285 @@ # frozen_string_literal: true -module Grape - module DSL - module ParametersSpec - class Dummy - include Grape::DSL::Parameters - attr_accessor :api, :element, :parent - - def initialize - @validate_attributes = [] - end - - def validate_attributes(*args) - @validate_attributes.push(*args) - end +describe Grape::DSL::Parameters do + subject { dummy_class.new } - def validate_attributes_reader - @validate_attributes - end + let(:dummy_class) do + Class.new do + include Grape::DSL::Parameters + attr_accessor :api, :element, :parent - def push_declared_params(args, **_opts) - @push_declared_params = args - end + def initialize + @validate_attributes = [] + end - def push_declared_params_reader - @push_declared_params - end + def validate_attributes(*args) + @validate_attributes.push(*args) + end - def validates(*args) - @validates = *args - end + def validate_attributes_reader + @validate_attributes + end - def validates_reader - @validates - end + def push_declared_params(args, **_opts) + @push_declared_params = args + end - def new_scope(args, _, &block) - nested_scope = self.class.new - nested_scope.new_group_scope(args, &block) - nested_scope - end + def push_declared_params_reader + @push_declared_params + end - def new_group_scope(args) - prev_group = @group - @group = args.clone.first - yield - @group = prev_group - end + def validates(*args) + @validates = *args + end - def extract_message_option(attrs) - return nil unless attrs.is_a?(Array) + def validates_reader + @validates + end - opts = attrs.last.is_a?(Hash) ? attrs.pop : {} - opts.key?(:message) && !opts[:message].nil? ? opts.delete(:message) : nil - end + def new_scope(args, _, &block) + nested_scope = self.class.new + nested_scope.new_group_scope(args, &block) + nested_scope end - end - describe Parameters do - subject { ParametersSpec::Dummy.new } + def new_group_scope(args) + prev_group = @group + @group = args.clone.first + yield + @group = prev_group + end - describe '#use' do - before do - allow_message_expectations_on_nil - allow(subject.api).to receive(:namespace_stackable).with(:named_params) - end + def extract_message_option(attrs) + return nil unless attrs.is_a?(Array) - let(:options) { { option: 'value' } } - let(:named_params) { { params_group: proc {} } } + opts = attrs.last.is_a?(Hash) ? attrs.pop : {} + opts.key?(:message) && !opts[:message].nil? ? opts.delete(:message) : nil + end + end + end - it 'calls processes associated with named params' do - allow(subject.api).to receive(:namespace_stackable_with_hash).and_return(named_params) - expect(subject).to receive(:instance_exec).with(options).and_yield - subject.use :params_group, options - end + describe '#use' do + before do + allow_message_expectations_on_nil + allow(subject.api).to receive(:namespace_stackable).with(:named_params) + end - it 'raises error when non-existent named param is called' do - allow(subject.api).to receive(:namespace_stackable_with_hash).and_return({}) - expect { subject.use :params_group }.to raise_error('Params :params_group not found!') - end - end + let(:options) { { option: 'value' } } + let(:named_params) { { params_group: proc {} } } - describe '#use_scope' do - it 'is alias to #use' do - expect(subject.method(:use_scope)).to eq subject.method(:use) - end - end + it 'calls processes associated with named params' do + allow(subject.api).to receive(:namespace_stackable_with_hash).and_return(named_params) + expect(subject).to receive(:instance_exec).with(options).and_yield + subject.use :params_group, options + end - describe '#includes' do - it 'is alias to #use' do - expect(subject.method(:includes)).to eq subject.method(:use) - end - end + it 'raises error when non-existent named param is called' do + allow(subject.api).to receive(:namespace_stackable_with_hash).and_return({}) + expect { subject.use :params_group }.to raise_error('Params :params_group not found!') + end + end - describe '#requires' do - it 'adds a required parameter' do - subject.requires :id, type: Integer, desc: 'Identity.' + describe '#use_scope' do + it 'is alias to #use' do + expect(subject.method(:use_scope)).to eq subject.method(:use) + end + end - expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.', presence: { value: true, message: nil } }]) - expect(subject.push_declared_params_reader).to eq([:id]) - end - end + describe '#includes' do + it 'is alias to #use' do + expect(subject.method(:includes)).to eq subject.method(:use) + end + end - describe '#optional' do - it 'adds an optional parameter' do - subject.optional :id, type: Integer, desc: 'Identity.' + describe '#requires' do + it 'adds a required parameter' do + subject.requires :id, type: Integer, desc: 'Identity.' - expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.' }]) - expect(subject.push_declared_params_reader).to eq([:id]) - end - end + expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.', presence: { value: true, message: nil } }]) + expect(subject.push_declared_params_reader).to eq([:id]) + end + end - describe '#with' do - it 'creates a scope with group attributes' do - subject.with(type: Integer) { subject.optional :id, desc: 'Identity.' } + describe '#optional' do + it 'adds an optional parameter' do + subject.optional :id, type: Integer, desc: 'Identity.' - expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.' }]) - expect(subject.push_declared_params_reader).to eq([:id]) - end + expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.' }]) + expect(subject.push_declared_params_reader).to eq([:id]) + end + end - it 'merges the group attributes' do - subject.with(documentation: { in: 'body' }) { subject.optional :vault, documentation: { default: 33 } } + describe '#with' do + it 'creates a scope with group attributes' do + subject.with(type: Integer) { subject.optional :id, desc: 'Identity.' } - expect(subject.validate_attributes_reader).to eq([[:vault], { documentation: { in: 'body', default: 33 } }]) - expect(subject.push_declared_params_reader).to eq([:vault]) - end + expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.' }]) + expect(subject.push_declared_params_reader).to eq([:id]) + end - it 'overrides the group attribute when values not mergable' do - subject.with(type: Integer, documentation: { in: 'body', default: 33 }) do - subject.optional :vault - subject.optional :allowed_vaults, type: [Integer], documentation: { default: [31, 32, 33], is_array: true } - end + it 'merges the group attributes' do + subject.with(documentation: { in: 'body' }) { subject.optional :vault, documentation: { default: 33 } } - expect(subject.validate_attributes_reader).to eq( - [ - [:vault], { type: Integer, documentation: { in: 'body', default: 33 } }, - [:allowed_vaults], { type: [Integer], documentation: { in: 'body', default: [31, 32, 33], is_array: true } } - ] - ) - end + expect(subject.validate_attributes_reader).to eq([[:vault], { documentation: { in: 'body', default: 33 } }]) + expect(subject.push_declared_params_reader).to eq([:vault]) + end - it 'allows a primitive type attribite to overwrite a complex type group attribute' do - subject.with(documentation: { x: { nullable: true } }) do - subject.optional :vault, type: Integer, documentation: { x: nil } - end + it 'overrides the group attribute when values not mergable' do + subject.with(type: Integer, documentation: { in: 'body', default: 33 }) do + subject.optional :vault + subject.optional :allowed_vaults, type: [Integer], documentation: { default: [31, 32, 33], is_array: true } + end - expect(subject.validate_attributes_reader).to eq( - [ - [:vault], { type: Integer, documentation: { x: nil } } - ] - ) - end + expect(subject.validate_attributes_reader).to eq( + [ + [:vault], { type: Integer, documentation: { in: 'body', default: 33 } }, + [:allowed_vaults], { type: [Integer], documentation: { in: 'body', default: [31, 32, 33], is_array: true } } + ] + ) + end - it 'does not nest primitives inside existing complex types erroneously' do - subject.with(type: Hash, documentation: { default: { vault: '33' } }) do - subject.optional :info - subject.optional :role, type: String, documentation: { default: 'resident' } - end + it 'allows a primitive type attribite to overwrite a complex type group attribute' do + subject.with(documentation: { x: { nullable: true } }) do + subject.optional :vault, type: Integer, documentation: { x: nil } + end - expect(subject.validate_attributes_reader).to eq( - [ - [:info], { type: Hash, documentation: { default: { vault: '33' } } }, - [:role], { type: String, documentation: { default: 'resident' } } - ] - ) - end + expect(subject.validate_attributes_reader).to eq( + [ + [:vault], { type: Integer, documentation: { x: nil } } + ] + ) + end - it 'merges deeply nested attributes' do - subject.with(documentation: { details: { in: 'body', hidden: false } }) do - subject.optional :vault, documentation: { details: { desc: 'The vault number' } } - end + it 'does not nest primitives inside existing complex types erroneously' do + subject.with(type: Hash, documentation: { default: { vault: '33' } }) do + subject.optional :info + subject.optional :role, type: String, documentation: { default: 'resident' } + end - expect(subject.validate_attributes_reader).to eq( - [ - [:vault], { documentation: { details: { in: 'body', hidden: false, desc: 'The vault number' } } } - ] - ) - end + expect(subject.validate_attributes_reader).to eq( + [ + [:info], { type: Hash, documentation: { default: { vault: '33' } } }, + [:role], { type: String, documentation: { default: 'resident' } } + ] + ) + end - it "supports nested 'with' calls" do - subject.with(type: Integer, documentation: { in: 'body' }) do - subject.optional :pipboy_id - subject.with(documentation: { default: 33 }) do - subject.optional :vault - subject.with(type: String) do - subject.with(documentation: { default: 'resident' }) do - subject.optional :role - end - end - subject.optional :age, documentation: { default: 42 } - end - end + it 'merges deeply nested attributes' do + subject.with(documentation: { details: { in: 'body', hidden: false } }) do + subject.optional :vault, documentation: { details: { desc: 'The vault number' } } + end - expect(subject.validate_attributes_reader).to eq( - [ - [:pipboy_id], { type: Integer, documentation: { in: 'body' } }, - [:vault], { type: Integer, documentation: { in: 'body', default: 33 } }, - [:role], { type: String, documentation: { in: 'body', default: 'resident' } }, - [:age], { type: Integer, documentation: { in: 'body', default: 42 } } - ] - ) - end + expect(subject.validate_attributes_reader).to eq( + [ + [:vault], { documentation: { details: { in: 'body', hidden: false, desc: 'The vault number' } } } + ] + ) + end - it "supports Hash parameter inside the 'with' calls" do - subject.with(documentation: { in: 'body' }) do - subject.optional :info, type: Hash, documentation: { x: { nullable: true }, desc: 'The info' } do - subject.optional :vault, type: Integer, documentation: { default: 33, desc: 'The vault number' } + it "supports nested 'with' calls" do + subject.with(type: Integer, documentation: { in: 'body' }) do + subject.optional :pipboy_id + subject.with(documentation: { default: 33 }) do + subject.optional :vault + subject.with(type: String) do + subject.with(documentation: { default: 'resident' }) do + subject.optional :role end end - - expect(subject.validate_attributes_reader).to eq( - [ - [:info], { type: Hash, documentation: { in: 'body', desc: 'The info', x: { nullable: true } } }, - [:vault], { type: Integer, documentation: { in: 'body', default: 33, desc: 'The vault number' } } - ] - ) + subject.optional :age, documentation: { default: 42 } end end - describe '#mutually_exclusive' do - it 'adds an mutally exclusive parameter validation' do - subject.mutually_exclusive :media, :audio + expect(subject.validate_attributes_reader).to eq( + [ + [:pipboy_id], { type: Integer, documentation: { in: 'body' } }, + [:vault], { type: Integer, documentation: { in: 'body', default: 33 } }, + [:role], { type: String, documentation: { in: 'body', default: 'resident' } }, + [:age], { type: Integer, documentation: { in: 'body', default: 42 } } + ] + ) + end - expect(subject.validates_reader).to eq([%i[media audio], { mutual_exclusion: { value: true, message: nil } }]) + it "supports Hash parameter inside the 'with' calls" do + subject.with(documentation: { in: 'body' }) do + subject.optional :info, type: Hash, documentation: { x: { nullable: true }, desc: 'The info' } do + subject.optional :vault, type: Integer, documentation: { default: 33, desc: 'The vault number' } end end - describe '#exactly_one_of' do - it 'adds an exactly of one parameter validation' do - subject.exactly_one_of :media, :audio + expect(subject.validate_attributes_reader).to eq( + [ + [:info], { type: Hash, documentation: { in: 'body', desc: 'The info', x: { nullable: true } } }, + [:vault], { type: Integer, documentation: { in: 'body', default: 33, desc: 'The vault number' } } + ] + ) + end + end - expect(subject.validates_reader).to eq([%i[media audio], { exactly_one_of: { value: true, message: nil } }]) - end - end + describe '#mutually_exclusive' do + it 'adds an mutally exclusive parameter validation' do + subject.mutually_exclusive :media, :audio - describe '#at_least_one_of' do - it 'adds an at least one of parameter validation' do - subject.at_least_one_of :media, :audio + expect(subject.validates_reader).to eq([%i[media audio], { mutual_exclusion: { value: true, message: nil } }]) + end + end - expect(subject.validates_reader).to eq([%i[media audio], { at_least_one_of: { value: true, message: nil } }]) - end - end + describe '#exactly_one_of' do + it 'adds an exactly of one parameter validation' do + subject.exactly_one_of :media, :audio - describe '#all_or_none_of' do - it 'adds an all or none of parameter validation' do - subject.all_or_none_of :media, :audio + expect(subject.validates_reader).to eq([%i[media audio], { exactly_one_of: { value: true, message: nil } }]) + end + end - expect(subject.validates_reader).to eq([%i[media audio], { all_or_none_of: { value: true, message: nil } }]) - end - end + describe '#at_least_one_of' do + it 'adds an at least one of parameter validation' do + subject.at_least_one_of :media, :audio - describe '#group' do - it 'is alias to #requires' do - expect(subject.method(:group)).to eq subject.method(:requires) - end - end + expect(subject.validates_reader).to eq([%i[media audio], { at_least_one_of: { value: true, message: nil } }]) + end + end - describe '#params' do - it 'inherits params from parent' do - parent_params = { foo: 'bar' } - subject.parent = Object.new - allow(subject.parent).to receive(:params).and_return(parent_params) - expect(subject.params({})).to eq parent_params - end + describe '#all_or_none_of' do + it 'adds an all or none of parameter validation' do + subject.all_or_none_of :media, :audio - describe 'when params argument is an array of hashes' do - it 'returns values of each hash for @element key' do - subject.element = :foo - expect(subject.params([{ foo: 'bar' }, { foo: 'baz' }])).to eq(%w[bar baz]) - end - end + expect(subject.validates_reader).to eq([%i[media audio], { all_or_none_of: { value: true, message: nil } }]) + end + end - describe 'when params argument is a hash' do - it 'returns value for @element key' do - subject.element = :foo - expect(subject.params(foo: 'bar')).to eq('bar') - end - end + describe '#group' do + it 'is alias to #requires' do + expect(subject.method(:group)).to eq subject.method(:requires) + end + end - describe 'when params argument is not a array or a hash' do - it 'returns empty hash' do - subject.element = Object.new - expect(subject.params(Object.new)).to eq({}) - end - end + describe '#params' do + it 'inherits params from parent' do + parent_params = { foo: 'bar' } + subject.parent = Object.new + allow(subject.parent).to receive(:params).and_return(parent_params) + expect(subject.params({})).to eq parent_params + end + + describe 'when params argument is an array of hashes' do + it 'returns values of each hash for @element key' do + subject.element = :foo + expect(subject.params([{ foo: 'bar' }, { foo: 'baz' }])).to eq(%w[bar baz]) + end + end + + describe 'when params argument is a hash' do + it 'returns value for @element key' do + subject.element = :foo + expect(subject.params(foo: 'bar')).to eq('bar') + end + end + + describe 'when params argument is not a array or a hash' do + it 'returns empty hash' do + subject.element = Object.new + expect(subject.params(Object.new)).to eq({}) end end end diff --git a/spec/grape/dsl/request_response_spec.rb b/spec/grape/dsl/request_response_spec.rb index 832ceafcf6..24bedc65b3 100644 --- a/spec/grape/dsl/request_response_spec.rb +++ b/spec/grape/dsl/request_response_spec.rb @@ -1,225 +1,221 @@ # frozen_string_literal: true -module Grape - module DSL - module RequestResponseSpec - class Dummy - include Grape::DSL::RequestResponse +describe Grape::DSL::RequestResponse do + subject { dummy_class } - def self.set(key, value) - settings[key.to_sym] = value - end + let(:dummy_class) do + Class.new do + include Grape::DSL::RequestResponse - def self.imbue(key, value) - settings.imbue(key, value) - end + def self.set(key, value) + settings[key.to_sym] = value + end + + def self.imbue(key, value) + settings.imbue(key, value) end end + end - describe RequestResponse do - subject { Class.new(RequestResponseSpec::Dummy) } + let(:c_type) { 'application/json' } + let(:format) { 'txt' } - let(:c_type) { 'application/json' } - let(:format) { 'txt' } + describe '.default_format' do + it 'sets the default format' do + expect(subject).to receive(:namespace_inheritable).with(:default_format, :format) + subject.default_format :format + end - describe '.default_format' do - it 'sets the default format' do - expect(subject).to receive(:namespace_inheritable).with(:default_format, :format) - subject.default_format :format - end + it 'returns the format without paramter' do + subject.default_format :format - it 'returns the format without paramter' do - subject.default_format :format + expect(subject.default_format).to eq :format + end + end - expect(subject.default_format).to eq :format - end - end + describe '.format' do + it 'sets a new format' do + expect(subject).to receive(:namespace_inheritable).with(:format, format.to_sym) + expect(subject).to receive(:namespace_inheritable).with(:default_error_formatter, Grape::ErrorFormatter::Txt) - describe '.format' do - it 'sets a new format' do - expect(subject).to receive(:namespace_inheritable).with(:format, format.to_sym) - expect(subject).to receive(:namespace_inheritable).with(:default_error_formatter, Grape::ErrorFormatter::Txt) + subject.format format + end + end - subject.format format - end - end + describe '.formatter' do + it 'sets the formatter for a content type' do + expect(subject).to receive(:namespace_stackable).with(:formatters, c_type.to_sym => :formatter) + subject.formatter c_type, :formatter + end + end - describe '.formatter' do - it 'sets the formatter for a content type' do - expect(subject).to receive(:namespace_stackable).with(:formatters, c_type.to_sym => :formatter) - subject.formatter c_type, :formatter - end - end + describe '.parser' do + it 'sets a parser for a content type' do + expect(subject).to receive(:namespace_stackable).with(:parsers, c_type.to_sym => :parser) + subject.parser c_type, :parser + end + end - describe '.parser' do - it 'sets a parser for a content type' do - expect(subject).to receive(:namespace_stackable).with(:parsers, c_type.to_sym => :parser) - subject.parser c_type, :parser - end - end + describe '.default_error_formatter' do + it 'sets a new error formatter' do + expect(subject).to receive(:namespace_inheritable).with(:default_error_formatter, Grape::ErrorFormatter::Json) + subject.default_error_formatter :json + end + end - describe '.default_error_formatter' do - it 'sets a new error formatter' do - expect(subject).to receive(:namespace_inheritable).with(:default_error_formatter, Grape::ErrorFormatter::Json) - subject.default_error_formatter :json - end - end + describe '.error_formatter' do + it 'sets a error_formatter' do + format = 'txt' + expect(subject).to receive(:namespace_stackable).with(:error_formatters, format.to_sym => :error_formatter) + subject.error_formatter format, :error_formatter + end - describe '.error_formatter' do - it 'sets a error_formatter' do - format = 'txt' - expect(subject).to receive(:namespace_stackable).with(:error_formatters, format.to_sym => :error_formatter) - subject.error_formatter format, :error_formatter - end + it 'understands syntactic sugar' do + expect(subject).to receive(:namespace_stackable).with(:error_formatters, format.to_sym => :error_formatter) + subject.error_formatter format, with: :error_formatter + end + end - it 'understands syntactic sugar' do - expect(subject).to receive(:namespace_stackable).with(:error_formatters, format.to_sym => :error_formatter) - subject.error_formatter format, with: :error_formatter - end - end + describe '.content_type' do + it 'sets a content type for a format' do + expect(subject).to receive(:namespace_stackable).with(:content_types, format.to_sym => c_type) + subject.content_type format, c_type + end + end - describe '.content_type' do - it 'sets a content type for a format' do - expect(subject).to receive(:namespace_stackable).with(:content_types, format.to_sym => c_type) - subject.content_type format, c_type - end - end + describe '.content_types' do + it 'returns all content types' do + expect(subject.content_types).to eq(xml: 'application/xml', + serializable_hash: 'application/json', + json: 'application/json', + txt: 'text/plain', + binary: 'application/octet-stream') + end + end - describe '.content_types' do - it 'returns all content types' do - expect(subject.content_types).to eq(xml: 'application/xml', - serializable_hash: 'application/json', - json: 'application/json', - txt: 'text/plain', - binary: 'application/octet-stream') - end - end + describe '.default_error_status' do + it 'sets a default error status' do + expect(subject).to receive(:namespace_inheritable).with(:default_error_status, 500) + subject.default_error_status 500 + end + end - describe '.default_error_status' do - it 'sets a default error status' do - expect(subject).to receive(:namespace_inheritable).with(:default_error_status, 500) - subject.default_error_status 500 - end + describe '.rescue_from' do + describe ':all' do + it 'sets rescue all to true' do + expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) + expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, nil) + subject.rescue_from :all end - describe '.rescue_from' do - describe ':all' do - it 'sets rescue all to true' do - expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) - expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, nil) - subject.rescue_from :all - end - - it 'sets given proc as rescue handler' do - rescue_handler_proc = proc {} - expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) - expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, rescue_handler_proc) - subject.rescue_from :all, rescue_handler_proc - end + it 'sets given proc as rescue handler' do + rescue_handler_proc = proc {} + expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) + expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, rescue_handler_proc) + subject.rescue_from :all, rescue_handler_proc + end - it 'sets given block as rescue handler' do - rescue_handler_proc = proc {} - expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) - expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, rescue_handler_proc) - subject.rescue_from :all, &rescue_handler_proc - end + it 'sets given block as rescue handler' do + rescue_handler_proc = proc {} + expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) + expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, rescue_handler_proc) + subject.rescue_from :all, &rescue_handler_proc + end - it 'sets a rescue handler declared through :with option' do - with_block = -> { 'hello' } - expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) - expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, an_instance_of(Proc)) - subject.rescue_from :all, with: with_block - end + it 'sets a rescue handler declared through :with option' do + with_block = -> { 'hello' } + expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) + expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, an_instance_of(Proc)) + subject.rescue_from :all, with: with_block + end - it 'abort if :with option value is not Symbol, String or Proc' do - expect { subject.rescue_from :all, with: 1234 }.to raise_error(ArgumentError, "with: #{integer_class_name}, expected Symbol, String or Proc") - end + it 'abort if :with option value is not Symbol, String or Proc' do + expect { subject.rescue_from :all, with: 1234 }.to raise_error(ArgumentError, "with: #{integer_class_name}, expected Symbol, String or Proc") + end - it 'abort if both :with option and block are passed' do - expect do - subject.rescue_from :all, with: -> { 'hello' } do - error!('bye') - end - end.to raise_error(ArgumentError, 'both :with option and block cannot be passed') - end - end - - describe ':grape_exceptions' do - it 'sets rescue all to true' do - expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) - expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true) - expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, nil) - subject.rescue_from :grape_exceptions + it 'abort if both :with option and block are passed' do + expect do + subject.rescue_from :all, with: -> { 'hello' } do + error!('bye') end + end.to raise_error(ArgumentError, 'both :with option and block cannot be passed') + end + end - it 'sets given proc as rescue handler' do - rescue_handler_proc = proc {} - expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) - expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true) - expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, rescue_handler_proc) - subject.rescue_from :grape_exceptions, rescue_handler_proc - end + describe ':grape_exceptions' do + it 'sets rescue all to true' do + expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) + expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true) + expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, nil) + subject.rescue_from :grape_exceptions + end - it 'sets given block as rescue handler' do - rescue_handler_proc = proc {} - expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) - expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true) - expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, rescue_handler_proc) - subject.rescue_from :grape_exceptions, &rescue_handler_proc - end + it 'sets given proc as rescue handler' do + rescue_handler_proc = proc {} + expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) + expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true) + expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, rescue_handler_proc) + subject.rescue_from :grape_exceptions, rescue_handler_proc + end - it 'sets a rescue handler declared through :with option' do - with_block = -> { 'hello' } - expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) - expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true) - expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, an_instance_of(Proc)) - subject.rescue_from :grape_exceptions, with: with_block - end - end + it 'sets given block as rescue handler' do + rescue_handler_proc = proc {} + expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) + expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true) + expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, rescue_handler_proc) + subject.rescue_from :grape_exceptions, &rescue_handler_proc + end - describe 'list of exceptions is passed' do - it 'sets hash of exceptions as rescue handlers' do - expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, { StandardError => nil }) - expect(subject).to receive(:namespace_stackable).with(:rescue_options, {}) - subject.rescue_from StandardError - end + it 'sets a rescue handler declared through :with option' do + with_block = -> { 'hello' } + expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) + expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true) + expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, an_instance_of(Proc)) + subject.rescue_from :grape_exceptions, with: with_block + end + end - it 'rescues only base handlers if rescue_subclasses: false option is passed' do - expect(subject).to receive(:namespace_reverse_stackable).with(:base_only_rescue_handlers, { StandardError => nil }) - expect(subject).to receive(:namespace_stackable).with(:rescue_options, { rescue_subclasses: false }) - subject.rescue_from StandardError, rescue_subclasses: false - end + describe 'list of exceptions is passed' do + it 'sets hash of exceptions as rescue handlers' do + expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, { StandardError => nil }) + expect(subject).to receive(:namespace_stackable).with(:rescue_options, {}) + subject.rescue_from StandardError + end - it 'sets given proc as rescue handler for each key in hash' do - rescue_handler_proc = proc {} - expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, { StandardError => rescue_handler_proc }) - expect(subject).to receive(:namespace_stackable).with(:rescue_options, {}) - subject.rescue_from StandardError, rescue_handler_proc - end + it 'rescues only base handlers if rescue_subclasses: false option is passed' do + expect(subject).to receive(:namespace_reverse_stackable).with(:base_only_rescue_handlers, { StandardError => nil }) + expect(subject).to receive(:namespace_stackable).with(:rescue_options, { rescue_subclasses: false }) + subject.rescue_from StandardError, rescue_subclasses: false + end - it 'sets given block as rescue handler for each key in hash' do - rescue_handler_proc = proc {} - expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, { StandardError => rescue_handler_proc }) - expect(subject).to receive(:namespace_stackable).with(:rescue_options, {}) - subject.rescue_from StandardError, &rescue_handler_proc - end + it 'sets given proc as rescue handler for each key in hash' do + rescue_handler_proc = proc {} + expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, { StandardError => rescue_handler_proc }) + expect(subject).to receive(:namespace_stackable).with(:rescue_options, {}) + subject.rescue_from StandardError, rescue_handler_proc + end - it 'sets a rescue handler declared through :with option for each key in hash' do - with_block = -> { 'hello' } - expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, { StandardError => an_instance_of(Proc) }) - expect(subject).to receive(:namespace_stackable).with(:rescue_options, {}) - subject.rescue_from StandardError, with: with_block - end - end + it 'sets given block as rescue handler for each key in hash' do + rescue_handler_proc = proc {} + expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, { StandardError => rescue_handler_proc }) + expect(subject).to receive(:namespace_stackable).with(:rescue_options, {}) + subject.rescue_from StandardError, &rescue_handler_proc end - describe '.represent' do - it 'sets a presenter for a class' do - presenter = Class.new - expect(subject).to receive(:namespace_stackable).with(:representations, ThisClass: presenter) - subject.represent :ThisClass, with: presenter - end + it 'sets a rescue handler declared through :with option for each key in hash' do + with_block = -> { 'hello' } + expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, { StandardError => an_instance_of(Proc) }) + expect(subject).to receive(:namespace_stackable).with(:rescue_options, {}) + subject.rescue_from StandardError, with: with_block end end end + + describe '.represent' do + it 'sets a presenter for a class' do + presenter = Class.new + expect(subject).to receive(:namespace_stackable).with(:representations, ThisClass: presenter) + subject.represent :ThisClass, with: presenter + end + end end diff --git a/spec/grape/dsl/routing_spec.rb b/spec/grape/dsl/routing_spec.rb index e763428f61..617980bb29 100644 --- a/spec/grape/dsl/routing_spec.rb +++ b/spec/grape/dsl/routing_spec.rb @@ -1,275 +1,271 @@ # frozen_string_literal: true -module Grape - module DSL - module RoutingSpec - class Dummy - include Grape::DSL::Routing - end +describe Grape::DSL::Routing do + subject { dummy_class } + + let(:dummy_class) do + Class.new do + include Grape::DSL::Routing end + end - describe Routing do - subject { Class.new(RoutingSpec::Dummy) } + let(:proc) { -> {} } + let(:options) { { a: :b } } + let(:path) { '/dummy' } - let(:proc) { -> {} } - let(:options) { { a: :b } } - let(:path) { '/dummy' } + describe '.version' do + it 'sets a version for route' do + version = 'v1' + expect(subject).to receive(:namespace_inheritable).with(:version, [version]) + expect(subject).to receive(:namespace_inheritable).with(:version_options, { using: :path }) + expect(subject.version(version)).to eq(version) + end + end - describe '.version' do - it 'sets a version for route' do - version = 'v1' - expect(subject).to receive(:namespace_inheritable).with(:version, [version]) - expect(subject).to receive(:namespace_inheritable).with(:version_options, { using: :path }) - expect(subject.version(version)).to eq(version) - end - end + describe '.prefix' do + it 'sets a prefix for route' do + prefix = '/api' + expect(subject).to receive(:namespace_inheritable).with(:root_prefix, prefix) + subject.prefix prefix + end + end - describe '.prefix' do - it 'sets a prefix for route' do - prefix = '/api' - expect(subject).to receive(:namespace_inheritable).with(:root_prefix, prefix) - subject.prefix prefix - end - end + describe '.scope' do + it 'create a scope without affecting the URL' do + expect(subject).to receive(:within_namespace) + subject.scope {} + end + end - describe '.scope' do - it 'create a scope without affecting the URL' do - expect(subject).to receive(:within_namespace) - subject.scope {} - end - end + describe '.do_not_route_head!' do + it 'sets do not route head option' do + expect(subject).to receive(:namespace_inheritable).with(:do_not_route_head, true) + subject.do_not_route_head! + end + end - describe '.do_not_route_head!' do - it 'sets do not route head option' do - expect(subject).to receive(:namespace_inheritable).with(:do_not_route_head, true) - subject.do_not_route_head! - end - end + describe '.do_not_route_options!' do + it 'sets do not route options option' do + expect(subject).to receive(:namespace_inheritable).with(:do_not_route_options, true) + subject.do_not_route_options! + end + end - describe '.do_not_route_options!' do - it 'sets do not route options option' do - expect(subject).to receive(:namespace_inheritable).with(:do_not_route_options, true) - subject.do_not_route_options! - end + describe '.mount' do + it 'mounts on a nested path' do + subject = Class.new(Grape::API) + app1 = Class.new(Grape::API) + app2 = Class.new(Grape::API) + app2.get '/nice' do + 'play' end - describe '.mount' do - it 'mounts on a nested path' do - subject = Class.new(Grape::API) - app1 = Class.new(Grape::API) - app2 = Class.new(Grape::API) - app2.get '/nice' do - 'play' - end - - subject.mount app1 => '/app1' - app1.mount app2 => '/app2' - - expect(subject.inheritable_setting.to_hash[:namespace]).to eq({}) - expect(subject.inheritable_setting.to_hash[:namespace_inheritable]).to eq({}) - expect(app1.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app1']) - - expect(app2.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app1', '/app2']) - end - - it 'mounts multiple routes at once' do - base_app = Class.new(Grape::API) - app1 = Class.new(Grape::API) - app2 = Class.new(Grape::API) - base_app.mount(app1 => '/app1', app2 => '/app2') - - expect(app1.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app1']) - expect(app2.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app2']) - end - end + subject.mount app1 => '/app1' + app1.mount app2 => '/app2' - describe '.route' do - before do - allow(subject).to receive(:endpoints).and_return([]) - allow(subject).to receive(:route_end) - allow(subject).to receive(:reset_validations!) - end - - it 'marks end of the route' do - expect(subject).to receive(:route_end) - subject.route(:any) - end - - it 'resets validations' do - expect(subject).to receive(:reset_validations!) - subject.route(:any) - end - - it 'defines a new endpoint' do - expect { subject.route(:any) } - .to change { subject.endpoints.count }.from(0).to(1) - end - - it 'does not duplicate identical endpoints' do - subject.route(:any) - expect { subject.route(:any) } - .not_to change(subject.endpoints, :count) - end - - it 'generates correct endpoint options' do - allow(subject).to receive(:route_setting).with(:description).and_return(fiz: 'baz') - allow(subject).to receive(:namespace_stackable_with_hash).and_return(nuz: 'naz') - - expect(Grape::Endpoint).to receive(:new) do |_inheritable_setting, endpoint_options| - expect(endpoint_options[:method]).to eq :get - expect(endpoint_options[:path]).to eq '/foo' - expect(endpoint_options[:for]).to eq subject - expect(endpoint_options[:route_options]).to eq(foo: 'bar', fiz: 'baz', params: { nuz: 'naz' }) - end.and_yield - - subject.route(:get, '/foo', { foo: 'bar' }, &proc {}) - end - end + expect(subject.inheritable_setting.to_hash[:namespace]).to eq({}) + expect(subject.inheritable_setting.to_hash[:namespace_inheritable]).to eq({}) + expect(app1.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app1']) - describe '.get' do - it 'delegates to .route' do - expect(subject).to receive(:route).with(Rack::GET, path, options) - subject.get path, options, &proc - end - end + expect(app2.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app1', '/app2']) + end - describe '.post' do - it 'delegates to .route' do - expect(subject).to receive(:route).with(Rack::POST, path, options) - subject.post path, options, &proc - end - end + it 'mounts multiple routes at once' do + base_app = Class.new(Grape::API) + app1 = Class.new(Grape::API) + app2 = Class.new(Grape::API) + base_app.mount(app1 => '/app1', app2 => '/app2') - describe '.put' do - it 'delegates to .route' do - expect(subject).to receive(:route).with(Rack::PUT, path, options) - subject.put path, options, &proc - end - end + expect(app1.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app1']) + expect(app2.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app2']) + end + end - describe '.head' do - it 'delegates to .route' do - expect(subject).to receive(:route).with(Rack::HEAD, path, options) - subject.head path, options, &proc - end - end + describe '.route' do + before do + allow(subject).to receive(:endpoints).and_return([]) + allow(subject).to receive(:route_end) + allow(subject).to receive(:reset_validations!) + end - describe '.delete' do - it 'delegates to .route' do - expect(subject).to receive(:route).with(Rack::DELETE, path, options) - subject.delete path, options, &proc - end - end + it 'marks end of the route' do + expect(subject).to receive(:route_end) + subject.route(:any) + end - describe '.options' do - it 'delegates to .route' do - expect(subject).to receive(:route).with(Rack::OPTIONS, path, options) - subject.options path, options, &proc - end - end + it 'resets validations' do + expect(subject).to receive(:reset_validations!) + subject.route(:any) + end - describe '.patch' do - it 'delegates to .route' do - expect(subject).to receive(:route).with(Rack::PATCH, path, options) - subject.patch path, options, &proc - end - end + it 'defines a new endpoint' do + expect { subject.route(:any) } + .to change { subject.endpoints.count }.from(0).to(1) + end - describe '.namespace' do - let(:new_namespace) { Object.new } + it 'does not duplicate identical endpoints' do + subject.route(:any) + expect { subject.route(:any) } + .not_to change(subject.endpoints, :count) + end - it 'creates a new namespace with given name and options' do - expect(subject).to receive(:within_namespace).and_yield - expect(subject).to receive(:nest).and_yield - expect(Namespace).to receive(:new).with(:foo, foo: 'bar').and_return(new_namespace) - expect(subject).to receive(:namespace_stackable).with(:namespace, new_namespace) + it 'generates correct endpoint options' do + allow(subject).to receive(:route_setting).with(:description).and_return(fiz: 'baz') + allow(subject).to receive(:namespace_stackable_with_hash).and_return(nuz: 'naz') - subject.namespace :foo, foo: 'bar', &proc {} - end + expect(Grape::Endpoint).to receive(:new) do |_inheritable_setting, endpoint_options| + expect(endpoint_options[:method]).to eq :get + expect(endpoint_options[:path]).to eq '/foo' + expect(endpoint_options[:for]).to eq subject + expect(endpoint_options[:route_options]).to eq(foo: 'bar', fiz: 'baz', params: { nuz: 'naz' }) + end.and_yield - it 'calls #joined_space_path on Namespace' do - result_of_namspace_stackable = Object.new - allow(subject).to receive(:namespace_stackable).and_return(result_of_namspace_stackable) - expect(Namespace).to receive(:joined_space_path).with(result_of_namspace_stackable) - subject.namespace - end - end + subject.route(:get, '/foo', { foo: 'bar' }, &proc {}) + end + end - describe '.group' do - it 'is alias to #namespace' do - expect(subject.method(:group)).to eq subject.method(:namespace) - end - end + describe '.get' do + it 'delegates to .route' do + expect(subject).to receive(:route).with(Rack::GET, path, options) + subject.get path, options, &proc + end + end - describe '.resource' do - it 'is alias to #namespace' do - expect(subject.method(:resource)).to eq subject.method(:namespace) - end - end + describe '.post' do + it 'delegates to .route' do + expect(subject).to receive(:route).with(Rack::POST, path, options) + subject.post path, options, &proc + end + end - describe '.resources' do - it 'is alias to #namespace' do - expect(subject.method(:resources)).to eq subject.method(:namespace) - end - end + describe '.put' do + it 'delegates to .route' do + expect(subject).to receive(:route).with(Rack::PUT, path, options) + subject.put path, options, &proc + end + end - describe '.segment' do - it 'is alias to #namespace' do - expect(subject.method(:segment)).to eq subject.method(:namespace) - end - end + describe '.head' do + it 'delegates to .route' do + expect(subject).to receive(:route).with(Rack::HEAD, path, options) + subject.head path, options, &proc + end + end + + describe '.delete' do + it 'delegates to .route' do + expect(subject).to receive(:route).with(Rack::DELETE, path, options) + subject.delete path, options, &proc + end + end + + describe '.options' do + it 'delegates to .route' do + expect(subject).to receive(:route).with(Rack::OPTIONS, path, options) + subject.options path, options, &proc + end + end + + describe '.patch' do + it 'delegates to .route' do + expect(subject).to receive(:route).with(Rack::PATCH, path, options) + subject.patch path, options, &proc + end + end + + describe '.namespace' do + let(:new_namespace) { Object.new } + + it 'creates a new namespace with given name and options' do + expect(subject).to receive(:within_namespace).and_yield + expect(subject).to receive(:nest).and_yield + expect(Grape::Namespace).to receive(:new).with(:foo, foo: 'bar').and_return(new_namespace) + expect(subject).to receive(:namespace_stackable).with(:namespace, new_namespace) + + subject.namespace :foo, foo: 'bar', &proc {} + end + + it 'calls #joined_space_path on Namespace' do + result_of_namspace_stackable = Object.new + allow(subject).to receive(:namespace_stackable).and_return(result_of_namspace_stackable) + expect(Grape::Namespace).to receive(:joined_space_path).with(result_of_namspace_stackable) + subject.namespace + end + end + + describe '.group' do + it 'is alias to #namespace' do + expect(subject.method(:group)).to eq subject.method(:namespace) + end + end - describe '.routes' do - let(:routes) { Object.new } - - it 'returns value received from #prepare_routes' do - expect(subject).to receive(:prepare_routes).and_return(routes) - expect(subject.routes).to eq routes - end - - context 'when #routes was already called once' do - before do - allow(subject).to receive(:prepare_routes).and_return(routes) - subject.routes - end - - it 'does not call prepare_routes again' do - expect(subject).not_to receive(:prepare_routes) - expect(subject.routes).to eq routes - end - end + describe '.resource' do + it 'is alias to #namespace' do + expect(subject.method(:resource)).to eq subject.method(:namespace) + end + end + + describe '.resources' do + it 'is alias to #namespace' do + expect(subject.method(:resources)).to eq subject.method(:namespace) + end + end + + describe '.segment' do + it 'is alias to #namespace' do + expect(subject.method(:segment)).to eq subject.method(:namespace) + end + end + + describe '.routes' do + let(:routes) { Object.new } + + it 'returns value received from #prepare_routes' do + expect(subject).to receive(:prepare_routes).and_return(routes) + expect(subject.routes).to eq routes + end + + context 'when #routes was already called once' do + before do + allow(subject).to receive(:prepare_routes).and_return(routes) + subject.routes end - describe '.route_param' do - let!(:options) { { requirements: regex } } - let(:regex) { /(.*)/ } - - it 'calls #namespace with given params' do - expect(subject).to receive(:namespace).with(':foo', {}).and_yield - subject.route_param('foo', {}, &proc {}) - end - - it 'nests requirements option under param name' do - expect(subject).to receive(:namespace) do |_param, options| - expect(options[:requirements][:foo]).to eq regex - end - subject.route_param('foo', options, &proc {}) - end - - it 'does not modify options parameter' do - allow(subject).to receive(:namespace) - expect { subject.route_param('foo', options, &proc {}) } - .not_to(change { options }) - end + it 'does not call prepare_routes again' do + expect(subject).not_to receive(:prepare_routes) + expect(subject.routes).to eq routes end + end + end + + describe '.route_param' do + let!(:options) { { requirements: regex } } + let(:regex) { /(.*)/ } + + it 'calls #namespace with given params' do + expect(subject).to receive(:namespace).with(':foo', {}).and_yield + subject.route_param('foo', {}, &proc {}) + end - describe '.versions' do - it 'returns last defined version' do - subject.version 'v1' - subject.version 'v2' - expect(subject.version).to eq('v2') - end + it 'nests requirements option under param name' do + expect(subject).to receive(:namespace) do |_param, options| + expect(options[:requirements][:foo]).to eq regex end + subject.route_param('foo', options, &proc {}) + end + + it 'does not modify options parameter' do + allow(subject).to receive(:namespace) + expect { subject.route_param('foo', options, &proc {}) } + .not_to(change { options }) + end + end + + describe '.versions' do + it 'returns last defined version' do + subject.version 'v1' + subject.version 'v2' + expect(subject.version).to eq('v2') end end end diff --git a/spec/grape/dsl/settings_spec.rb b/spec/grape/dsl/settings_spec.rb index 5de6febf1c..e4b20fa36a 100644 --- a/spec/grape/dsl/settings_spec.rb +++ b/spec/grape/dsl/settings_spec.rb @@ -1,261 +1,257 @@ # frozen_string_literal: true -module Grape - module DSL - module SettingsSpec - class Dummy - include Grape::DSL::Settings +describe Grape::DSL::Settings do + subject { dummy_class.new } - def reset_validations!; end - end + let(:dummy_class) do + Class.new do + include Grape::DSL::Settings + + def reset_validations!; end end + end - describe Settings do - subject { SettingsSpec::Dummy.new } + describe '#unset' do + it 'deletes a key from settings' do + subject.namespace_setting :dummy, 1 + expect(subject.inheritable_setting.namespace.keys).to include(:dummy) - describe '#unset' do - it 'deletes a key from settings' do - subject.namespace_setting :dummy, 1 - expect(subject.inheritable_setting.namespace.keys).to include(:dummy) + subject.unset :namespace, :dummy + expect(subject.inheritable_setting.namespace.keys).not_to include(:dummy) + end + end - subject.unset :namespace, :dummy - expect(subject.inheritable_setting.namespace.keys).not_to include(:dummy) - end - end + describe '#get_or_set' do + it 'sets a values' do + subject.get_or_set :namespace, :dummy, 1 + expect(subject.namespace_setting(:dummy)).to eq 1 + end - describe '#get_or_set' do - it 'sets a values' do - subject.get_or_set :namespace, :dummy, 1 - expect(subject.namespace_setting(:dummy)).to eq 1 - end + it 'returns a value when nil is new value is provided' do + subject.get_or_set :namespace, :dummy, 1 + expect(subject.get_or_set(:namespace, :dummy, nil)).to eq 1 + end + end - it 'returns a value when nil is new value is provided' do - subject.get_or_set :namespace, :dummy, 1 - expect(subject.get_or_set(:namespace, :dummy, nil)).to eq 1 - end - end + describe '#global_setting' do + it 'delegates to get_or_set' do + expect(subject).to receive(:get_or_set).with(:global, :dummy, 1) + subject.global_setting(:dummy, 1) + end + end - describe '#global_setting' do - it 'delegates to get_or_set' do - expect(subject).to receive(:get_or_set).with(:global, :dummy, 1) - subject.global_setting(:dummy, 1) - end - end + describe '#unset_global_setting' do + it 'delegates to unset' do + expect(subject).to receive(:unset).with(:global, :dummy) + subject.unset_global_setting(:dummy) + end + end - describe '#unset_global_setting' do - it 'delegates to unset' do - expect(subject).to receive(:unset).with(:global, :dummy) - subject.unset_global_setting(:dummy) - end - end + describe '#route_setting' do + it 'delegates to get_or_set' do + expect(subject).to receive(:get_or_set).with(:route, :dummy, 1) + subject.route_setting(:dummy, 1) + end - describe '#route_setting' do - it 'delegates to get_or_set' do - expect(subject).to receive(:get_or_set).with(:route, :dummy, 1) - subject.route_setting(:dummy, 1) - end + it 'sets a value until the next route' do + subject.route_setting :some_thing, :foo_bar + expect(subject.route_setting(:some_thing)).to eq :foo_bar - it 'sets a value until the next route' do - subject.route_setting :some_thing, :foo_bar - expect(subject.route_setting(:some_thing)).to eq :foo_bar + subject.route_end - subject.route_end + expect(subject.route_setting(:some_thing)).to be_nil + end + end - expect(subject.route_setting(:some_thing)).to be_nil - end - end + describe '#unset_route_setting' do + it 'delegates to unset' do + expect(subject).to receive(:unset).with(:route, :dummy) + subject.unset_route_setting(:dummy) + end + end - describe '#unset_route_setting' do - it 'delegates to unset' do - expect(subject).to receive(:unset).with(:route, :dummy) - subject.unset_route_setting(:dummy) - end - end + describe '#namespace_setting' do + it 'delegates to get_or_set' do + expect(subject).to receive(:get_or_set).with(:namespace, :dummy, 1) + subject.namespace_setting(:dummy, 1) + end - describe '#namespace_setting' do - it 'delegates to get_or_set' do - expect(subject).to receive(:get_or_set).with(:namespace, :dummy, 1) - subject.namespace_setting(:dummy, 1) - end + it 'sets a value until the end of a namespace' do + subject.namespace_start - it 'sets a value until the end of a namespace' do - subject.namespace_start + subject.namespace_setting :some_thing, :foo_bar + expect(subject.namespace_setting(:some_thing)).to eq :foo_bar - subject.namespace_setting :some_thing, :foo_bar - expect(subject.namespace_setting(:some_thing)).to eq :foo_bar + subject.namespace_end - subject.namespace_end + expect(subject.namespace_setting(:some_thing)).to be_nil + end - expect(subject.namespace_setting(:some_thing)).to be_nil - end + it 'resets values after leaving nested namespaces' do + subject.namespace_start - it 'resets values after leaving nested namespaces' do - subject.namespace_start + subject.namespace_setting :some_thing, :foo_bar + expect(subject.namespace_setting(:some_thing)).to eq :foo_bar - subject.namespace_setting :some_thing, :foo_bar - expect(subject.namespace_setting(:some_thing)).to eq :foo_bar + subject.namespace_start - subject.namespace_start + expect(subject.namespace_setting(:some_thing)).to be_nil - expect(subject.namespace_setting(:some_thing)).to be_nil + subject.namespace_end + expect(subject.namespace_setting(:some_thing)).to eq :foo_bar - subject.namespace_end - expect(subject.namespace_setting(:some_thing)).to eq :foo_bar + subject.namespace_end - subject.namespace_end + expect(subject.namespace_setting(:some_thing)).to be_nil + end + end - expect(subject.namespace_setting(:some_thing)).to be_nil - end - end + describe '#unset_namespace_setting' do + it 'delegates to unset' do + expect(subject).to receive(:unset).with(:namespace, :dummy) + subject.unset_namespace_setting(:dummy) + end + end - describe '#unset_namespace_setting' do - it 'delegates to unset' do - expect(subject).to receive(:unset).with(:namespace, :dummy) - subject.unset_namespace_setting(:dummy) - end - end + describe '#namespace_inheritable' do + it 'delegates to get_or_set' do + expect(subject).to receive(:get_or_set).with(:namespace_inheritable, :dummy, 1) + subject.namespace_inheritable(:dummy, 1) + end - describe '#namespace_inheritable' do - it 'delegates to get_or_set' do - expect(subject).to receive(:get_or_set).with(:namespace_inheritable, :dummy, 1) - subject.namespace_inheritable(:dummy, 1) - end + it 'inherits values from surrounding namespace' do + subject.namespace_start - it 'inherits values from surrounding namespace' do - subject.namespace_start + subject.namespace_inheritable(:some_thing, :foo_bar) + expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar - subject.namespace_inheritable(:some_thing, :foo_bar) - expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar + subject.namespace_start - subject.namespace_start + expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar - expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar + subject.namespace_inheritable(:some_thing, :foo_bar_2) - subject.namespace_inheritable(:some_thing, :foo_bar_2) + expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar_2 - expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar_2 + subject.namespace_end + expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar + subject.namespace_end + end + end - subject.namespace_end - expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar - subject.namespace_end - end - end + describe '#unset_namespace_inheritable' do + it 'delegates to unset' do + expect(subject).to receive(:unset).with(:namespace_inheritable, :dummy) + subject.unset_namespace_inheritable(:dummy) + end + end - describe '#unset_namespace_inheritable' do - it 'delegates to unset' do - expect(subject).to receive(:unset).with(:namespace_inheritable, :dummy) - subject.unset_namespace_inheritable(:dummy) - end - end + describe '#namespace_stackable' do + it 'delegates to get_or_set' do + expect(subject).to receive(:get_or_set).with(:namespace_stackable, :dummy, 1) + subject.namespace_stackable(:dummy, 1) + end - describe '#namespace_stackable' do - it 'delegates to get_or_set' do - expect(subject).to receive(:get_or_set).with(:namespace_stackable, :dummy, 1) - subject.namespace_stackable(:dummy, 1) - end + it 'stacks values from surrounding namespace' do + subject.namespace_start - it 'stacks values from surrounding namespace' do - subject.namespace_start + subject.namespace_stackable(:some_thing, :foo_bar) + expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar] - subject.namespace_stackable(:some_thing, :foo_bar) - expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar] + subject.namespace_start - subject.namespace_start + expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar] - expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar] + subject.namespace_stackable(:some_thing, :foo_bar_2) - subject.namespace_stackable(:some_thing, :foo_bar_2) + expect(subject.namespace_stackable(:some_thing)).to eq %i[foo_bar foo_bar_2] - expect(subject.namespace_stackable(:some_thing)).to eq %i[foo_bar foo_bar_2] + subject.namespace_end + expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar] + subject.namespace_end + end + end - subject.namespace_end - expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar] - subject.namespace_end - end - end + describe '#unset_namespace_stackable' do + it 'delegates to unset' do + expect(subject).to receive(:unset).with(:namespace_stackable, :dummy) + subject.unset_namespace_stackable(:dummy) + end + end - describe '#unset_namespace_stackable' do - it 'delegates to unset' do - expect(subject).to receive(:unset).with(:namespace_stackable, :dummy) - subject.unset_namespace_stackable(:dummy) - end - end + describe '#api_class_setting' do + it 'delegates to get_or_set' do + expect(subject).to receive(:get_or_set).with(:api_class, :dummy, 1) + subject.api_class_setting(:dummy, 1) + end + end + + describe '#unset_api_class_setting' do + it 'delegates to unset' do + expect(subject).to receive(:unset).with(:api_class, :dummy) + subject.unset_api_class_setting(:dummy) + end + end + + describe '#within_namespace' do + it 'calls start and end for a namespace' do + expect(subject).to receive :namespace_start + expect(subject).to receive :namespace_end - describe '#api_class_setting' do - it 'delegates to get_or_set' do - expect(subject).to receive(:get_or_set).with(:api_class, :dummy, 1) - subject.api_class_setting(:dummy, 1) - end + subject.within_namespace do end + end - describe '#unset_api_class_setting' do - it 'delegates to unset' do - expect(subject).to receive(:unset).with(:api_class, :dummy) - subject.unset_api_class_setting(:dummy) - end + it 'returns the last result' do + result = subject.within_namespace do + 1 end - describe '#within_namespace' do - it 'calls start and end for a namespace' do - expect(subject).to receive :namespace_start - expect(subject).to receive :namespace_end + expect(result).to eq 1 + end + end - subject.within_namespace do - end - end + describe 'complex scenario' do + it 'plays well' do + obj1 = dummy_class.new + obj2 = dummy_class.new + obj3 = dummy_class.new - it 'returns the last result' do - result = subject.within_namespace do - 1 - end + obj1_copy = nil + obj2_copy = nil + obj3_copy = nil - expect(result).to eq 1 - end + obj1.within_namespace do + obj1.namespace_stackable(:some_thing, :obj1) + expect(obj1.namespace_stackable(:some_thing)).to eq [:obj1] + obj1_copy = obj1.inheritable_setting.point_in_time_copy end - describe 'complex scenario' do - it 'plays well' do - obj1 = SettingsSpec::Dummy.new - obj2 = SettingsSpec::Dummy.new - obj3 = SettingsSpec::Dummy.new - - obj1_copy = nil - obj2_copy = nil - obj3_copy = nil - - obj1.within_namespace do - obj1.namespace_stackable(:some_thing, :obj1) - expect(obj1.namespace_stackable(:some_thing)).to eq [:obj1] - obj1_copy = obj1.inheritable_setting.point_in_time_copy - end - - expect(obj1.namespace_stackable(:some_thing)).to eq [] - expect(obj1_copy.namespace_stackable[:some_thing]).to eq [:obj1] + expect(obj1.namespace_stackable(:some_thing)).to eq [] + expect(obj1_copy.namespace_stackable[:some_thing]).to eq [:obj1] - obj2.within_namespace do - obj2.namespace_stackable(:some_thing, :obj2) - expect(obj2.namespace_stackable(:some_thing)).to eq [:obj2] - obj2_copy = obj2.inheritable_setting.point_in_time_copy - end + obj2.within_namespace do + obj2.namespace_stackable(:some_thing, :obj2) + expect(obj2.namespace_stackable(:some_thing)).to eq [:obj2] + obj2_copy = obj2.inheritable_setting.point_in_time_copy + end - expect(obj2.namespace_stackable(:some_thing)).to eq [] - expect(obj2_copy.namespace_stackable[:some_thing]).to eq [:obj2] + expect(obj2.namespace_stackable(:some_thing)).to eq [] + expect(obj2_copy.namespace_stackable[:some_thing]).to eq [:obj2] - obj3.within_namespace do - obj3.namespace_stackable(:some_thing, :obj3) - expect(obj3.namespace_stackable(:some_thing)).to eq [:obj3] - obj3_copy = obj3.inheritable_setting.point_in_time_copy - end + obj3.within_namespace do + obj3.namespace_stackable(:some_thing, :obj3) + expect(obj3.namespace_stackable(:some_thing)).to eq [:obj3] + obj3_copy = obj3.inheritable_setting.point_in_time_copy + end - expect(obj3.namespace_stackable(:some_thing)).to eq [] - expect(obj3_copy.namespace_stackable[:some_thing]).to eq [:obj3] + expect(obj3.namespace_stackable(:some_thing)).to eq [] + expect(obj3_copy.namespace_stackable[:some_thing]).to eq [:obj3] - obj1.top_level_setting.inherit_from obj2_copy.point_in_time_copy - obj2.top_level_setting.inherit_from obj3_copy.point_in_time_copy + obj1.top_level_setting.inherit_from obj2_copy.point_in_time_copy + obj2.top_level_setting.inherit_from obj3_copy.point_in_time_copy - expect(obj1_copy.namespace_stackable[:some_thing]).to eq %i[obj3 obj2 obj1] - end - end + expect(obj1_copy.namespace_stackable[:some_thing]).to eq %i[obj3 obj2 obj1] end end end diff --git a/spec/grape/named_api_spec.rb b/spec/grape/named_api_spec.rb index e3aac6cb57..77e73d8750 100644 --- a/spec/grape/named_api_spec.rb +++ b/spec/grape/named_api_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -describe 'A named API' do +describe Grape::API do subject(:api_name) { NamedAPI.endpoints.last.options[:for].to_s } let(:api) do @@ -11,9 +11,11 @@ end end - before { stub_const('NamedAPI', api) } + let(:name) { 'NamedAPI' } + + before { stub_const(name, api) } it 'can access the name of the API' do - expect(api_name).to eq 'NamedAPI' + expect(api_name).to eq name end end diff --git a/spec/grape/path_spec.rb b/spec/grape/path_spec.rb index afad438df4..2a6dd2fce7 100644 --- a/spec/grape/path_spec.rb +++ b/spec/grape/path_spec.rb @@ -1,243 +1,241 @@ # frozen_string_literal: true -module Grape - describe Path do - describe '#initialize' do - it 'remembers the path' do - path = described_class.new('/:id', anything, anything) - expect(path.raw_path).to eql('/:id') - end +describe Grape::Path do + describe '#initialize' do + it 'remembers the path' do + path = described_class.new('/:id', anything, anything) + expect(path.raw_path).to eql('/:id') + end - it 'remembers the namespace' do - path = described_class.new(anything, '/users', anything) - expect(path.namespace).to eql('/users') - end + it 'remembers the namespace' do + path = described_class.new(anything, '/users', anything) + expect(path.namespace).to eql('/users') + end - it 'remebers the settings' do - path = described_class.new(anything, anything, foo: 'bar') - expect(path.settings).to eql(foo: 'bar') - end + it 'remebers the settings' do + path = described_class.new(anything, anything, foo: 'bar') + expect(path.settings).to eql(foo: 'bar') end + end - describe '#mount_path' do - it 'is nil when no mount path setting exists' do - path = described_class.new(anything, anything, {}) - expect(path.mount_path).to be_nil - end + describe '#mount_path' do + it 'is nil when no mount path setting exists' do + path = described_class.new(anything, anything, {}) + expect(path.mount_path).to be_nil + end - it 'is nil when the mount path is nil' do - path = described_class.new(anything, anything, mount_path: nil) - expect(path.mount_path).to be_nil - end + it 'is nil when the mount path is nil' do + path = described_class.new(anything, anything, mount_path: nil) + expect(path.mount_path).to be_nil + end - it 'splits the mount path' do - path = described_class.new(anything, anything, mount_path: %w[foo bar]) - expect(path.mount_path).to eql(%w[foo bar]) - end + it 'splits the mount path' do + path = described_class.new(anything, anything, mount_path: %w[foo bar]) + expect(path.mount_path).to eql(%w[foo bar]) end + end - describe '#root_prefix' do - it 'is nil when no root prefix setting exists' do - path = described_class.new(anything, anything, {}) - expect(path.root_prefix).to be_nil - end + describe '#root_prefix' do + it 'is nil when no root prefix setting exists' do + path = described_class.new(anything, anything, {}) + expect(path.root_prefix).to be_nil + end - it 'is nil when the mount path is nil' do - path = described_class.new(anything, anything, root_prefix: nil) - expect(path.root_prefix).to be_nil - end + it 'is nil when the mount path is nil' do + path = described_class.new(anything, anything, root_prefix: nil) + expect(path.root_prefix).to be_nil + end - it 'splits the mount path' do - path = described_class.new(anything, anything, root_prefix: 'hello/world') - expect(path.root_prefix).to eql('hello/world') - end + it 'splits the mount path' do + path = described_class.new(anything, anything, root_prefix: 'hello/world') + expect(path.root_prefix).to eql('hello/world') end + end - describe '#uses_path_versioning?' do - it 'is false when the version setting is nil' do - path = described_class.new(anything, anything, version: nil) - expect(path.uses_path_versioning?).to be false - end + describe '#uses_path_versioning?' do + it 'is false when the version setting is nil' do + path = described_class.new(anything, anything, version: nil) + expect(path.uses_path_versioning?).to be false + end - it 'is false when the version option is header' do - path = described_class.new( - anything, - anything, - version: 'v1', - version_options: { using: :header } - ) + it 'is false when the version option is header' do + path = described_class.new( + anything, + anything, + version: 'v1', + version_options: { using: :header } + ) - expect(path.uses_path_versioning?).to be false - end + expect(path.uses_path_versioning?).to be false + end - it 'is true when the version option is path' do - path = described_class.new( - anything, - anything, - version: 'v1', - version_options: { using: :path } - ) + it 'is true when the version option is path' do + path = described_class.new( + anything, + anything, + version: 'v1', + version_options: { using: :path } + ) - expect(path.uses_path_versioning?).to be true - end + expect(path.uses_path_versioning?).to be true end + end - describe '#namespace?' do - it 'is false when the namespace is nil' do - path = described_class.new(anything, nil, anything) - expect(path).not_to be_namespace - end + describe '#namespace?' do + it 'is false when the namespace is nil' do + path = described_class.new(anything, nil, anything) + expect(path).not_to be_namespace + end - it 'is false when the namespace starts with whitespace' do - path = described_class.new(anything, ' /foo', anything) - expect(path).not_to be_namespace - end + it 'is false when the namespace starts with whitespace' do + path = described_class.new(anything, ' /foo', anything) + expect(path).not_to be_namespace + end - it 'is false when the namespace is the root path' do - path = described_class.new(anything, '/', anything) - expect(path.namespace?).to be false - end + it 'is false when the namespace is the root path' do + path = described_class.new(anything, '/', anything) + expect(path.namespace?).to be false + end - it 'is true otherwise' do - path = described_class.new(anything, '/world', anything) - expect(path.namespace?).to be true - end + it 'is true otherwise' do + path = described_class.new(anything, '/world', anything) + expect(path.namespace?).to be true end + end - describe '#path?' do - it 'is false when the path is nil' do - path = described_class.new(nil, anything, anything) - expect(path).not_to be_path - end + describe '#path?' do + it 'is false when the path is nil' do + path = described_class.new(nil, anything, anything) + expect(path).not_to be_path + end - it 'is false when the path starts with whitespace' do - path = described_class.new(' /foo', anything, anything) - expect(path).not_to be_path - end + it 'is false when the path starts with whitespace' do + path = described_class.new(' /foo', anything, anything) + expect(path).not_to be_path + end - it 'is false when the path is the root path' do - path = described_class.new('/', anything, anything) - expect(path.path?).to be false - end + it 'is false when the path is the root path' do + path = described_class.new('/', anything, anything) + expect(path.path?).to be false + end - it 'is true otherwise' do - path = described_class.new('/hello', anything, anything) - expect(path.path?).to be true - end + it 'is true otherwise' do + path = described_class.new('/hello', anything, anything) + expect(path.path?).to be true end + end - describe '#path' do - context 'mount_path' do - it 'is not included when it is nil' do - path = described_class.new(nil, nil, mount_path: '/foo/bar') - expect(path.path).to eql '/foo/bar' - end + describe '#path' do + context 'mount_path' do + it 'is not included when it is nil' do + path = described_class.new(nil, nil, mount_path: '/foo/bar') + expect(path.path).to eql '/foo/bar' + end - it 'is included when it is not nil' do - path = described_class.new(nil, nil, {}) - expect(path.path).to eql('/') - end + it 'is included when it is not nil' do + path = described_class.new(nil, nil, {}) + expect(path.path).to eql('/') end + end - context 'root_prefix' do - it 'is not included when it is nil' do - path = described_class.new(nil, nil, {}) - expect(path.path).to eql('/') - end - - it 'is included after the mount path' do - path = described_class.new( - nil, - nil, - mount_path: '/foo', - root_prefix: '/hello' - ) - - expect(path.path).to eql('/foo/hello') - end + context 'root_prefix' do + it 'is not included when it is nil' do + path = described_class.new(nil, nil, {}) + expect(path.path).to eql('/') end - it 'uses the namespace after the mount path and root prefix' do + it 'is included after the mount path' do path = described_class.new( nil, - 'namespace', + nil, mount_path: '/foo', root_prefix: '/hello' ) - expect(path.path).to eql('/foo/hello/namespace') + expect(path.path).to eql('/foo/hello') end + end - it 'uses the raw path after the namespace' do - path = described_class.new( - 'raw_path', - 'namespace', - mount_path: '/foo', - root_prefix: '/hello' - ) + it 'uses the namespace after the mount path and root prefix' do + path = described_class.new( + nil, + 'namespace', + mount_path: '/foo', + root_prefix: '/hello' + ) - expect(path.path).to eql('/foo/hello/namespace/raw_path') - end + expect(path.path).to eql('/foo/hello/namespace') end - describe '#suffix' do - context 'when using a specific format' do - it 'accepts specified format' do - path = described_class.new(nil, nil, {}) - allow(path).to receive_messages(uses_specific_format?: true, settings: { format: :json }) + it 'uses the raw path after the namespace' do + path = described_class.new( + 'raw_path', + 'namespace', + mount_path: '/foo', + root_prefix: '/hello' + ) - expect(path.suffix).to eql('(.json)') - end - end + expect(path.path).to eql('/foo/hello/namespace/raw_path') + end + end - context 'when path versioning is used' do - it "includes a '/'" do - path = described_class.new(nil, nil, {}) - allow(path).to receive_messages(uses_specific_format?: false, uses_path_versioning?: true) + describe '#suffix' do + context 'when using a specific format' do + it 'accepts specified format' do + path = described_class.new(nil, nil, {}) + allow(path).to receive_messages(uses_specific_format?: true, settings: { format: :json }) - expect(path.suffix).to eql('(/.:format)') - end + expect(path.suffix).to eql('(.json)') end + end - context 'when path versioning is not used' do - it "does not include a '/' when the path has a namespace" do - path = described_class.new(nil, 'namespace', {}) - allow(path).to receive_messages(uses_specific_format?: false, uses_path_versioning?: true) + context 'when path versioning is used' do + it "includes a '/'" do + path = described_class.new(nil, nil, {}) + allow(path).to receive_messages(uses_specific_format?: false, uses_path_versioning?: true) - expect(path.suffix).to eql('(.:format)') - end + expect(path.suffix).to eql('(/.:format)') + end + end - it "does not include a '/' when the path has a path" do - path = described_class.new('/path', nil, {}) - allow(path).to receive_messages(uses_specific_format?: false, uses_path_versioning?: true) + context 'when path versioning is not used' do + it "does not include a '/' when the path has a namespace" do + path = described_class.new(nil, 'namespace', {}) + allow(path).to receive_messages(uses_specific_format?: false, uses_path_versioning?: true) - expect(path.suffix).to eql('(.:format)') - end + expect(path.suffix).to eql('(.:format)') + end - it "includes a '/' otherwise" do - path = described_class.new(nil, nil, {}) - allow(path).to receive_messages(uses_specific_format?: false, uses_path_versioning?: true) + it "does not include a '/' when the path has a path" do + path = described_class.new('/path', nil, {}) + allow(path).to receive_messages(uses_specific_format?: false, uses_path_versioning?: true) - expect(path.suffix).to eql('(/.:format)') - end + expect(path.suffix).to eql('(.:format)') end - end - describe '#path_with_suffix' do - it 'combines the path and suffix' do + it "includes a '/' otherwise" do path = described_class.new(nil, nil, {}) - allow(path).to receive_messages(path: '/the/path', suffix: 'suffix') + allow(path).to receive_messages(uses_specific_format?: false, uses_path_versioning?: true) - expect(path.path_with_suffix).to eql('/the/pathsuffix') + expect(path.suffix).to eql('(/.:format)') end + end + end - context 'when using a specific format' do - it 'might have a suffix with specified format' do - path = described_class.new(nil, nil, {}) - allow(path).to receive_messages(path: '/the/path', uses_specific_format?: true, settings: { format: :json }) + describe '#path_with_suffix' do + it 'combines the path and suffix' do + path = described_class.new(nil, nil, {}) + allow(path).to receive_messages(path: '/the/path', suffix: 'suffix') + + expect(path.path_with_suffix).to eql('/the/pathsuffix') + end + + context 'when using a specific format' do + it 'might have a suffix with specified format' do + path = described_class.new(nil, nil, {}) + allow(path).to receive_messages(path: '/the/path', uses_specific_format?: true, settings: { format: :json }) - expect(path.path_with_suffix).to eql('/the/path(.json)') - end + expect(path.path_with_suffix).to eql('/the/path(.json)') end end end diff --git a/spec/grape/presenters/presenter_spec.rb b/spec/grape/presenters/presenter_spec.rb index da155190b3..f673b8fa0a 100644 --- a/spec/grape/presenters/presenter_spec.rb +++ b/spec/grape/presenters/presenter_spec.rb @@ -1,69 +1,65 @@ # frozen_string_literal: true -module Grape - module Presenters - module PresenterSpec - class Dummy - include Grape::DSL::InsideRoute +describe Grape::Presenters::Presenter do + subject { dummy_class.new } - attr_reader :env, :request, :new_settings + let(:dummy_class) do + Class.new do + include Grape::DSL::InsideRoute - def initialize - @env = {} - @header = {} - @new_settings = { namespace_inheritable: {}, namespace_stackable: {} } - end + attr_reader :env, :request, :new_settings + + def initialize + @env = {} + @header = {} + @new_settings = { namespace_inheritable: {}, namespace_stackable: {} } end end + end - describe Presenter do - subject { PresenterSpec::Dummy.new } - - describe 'represent' do - let(:object_mock) do - Object.new - end + describe 'represent' do + let(:object_mock) do + Object.new + end - it 'represent object' do - expect(described_class.represent(object_mock)).to eq object_mock - end - end + it 'represent object' do + expect(described_class.represent(object_mock)).to eq object_mock + end + end - describe 'present' do - let(:hash_mock) do - { key: :value } - end + describe 'present' do + let(:hash_mock) do + { key: :value } + end - describe 'instance' do - before do - subject.present hash_mock, with: described_class - end + describe 'instance' do + before do + subject.present hash_mock, with: described_class + end - it 'presents dummy hash' do - expect(subject.body).to eq hash_mock - end - end + it 'presents dummy hash' do + expect(subject.body).to eq hash_mock + end + end - describe 'multiple presenter' do - let(:hash_mock1) do - { key1: :value1 } - end + describe 'multiple presenter' do + let(:hash_mock1) do + { key1: :value1 } + end - let(:hash_mock2) do - { key2: :value2 } - end + let(:hash_mock2) do + { key2: :value2 } + end - describe 'instance' do - before do - subject.present hash_mock1, with: described_class - subject.present hash_mock2, with: described_class - end + describe 'instance' do + before do + subject.present hash_mock1, with: described_class + subject.present hash_mock2, with: described_class + end - it 'presents both dummy presenter' do - expect(subject.body[:key1]).to eq hash_mock1[:key1] - expect(subject.body[:key2]).to eq hash_mock2[:key2] - end - end + it 'presents both dummy presenter' do + expect(subject.body[:key1]).to eq hash_mock1[:key1] + expect(subject.body[:key2]).to eq hash_mock2[:key2] end end end diff --git a/spec/grape/util/inheritable_setting_spec.rb b/spec/grape/util/inheritable_setting_spec.rb index c9ad93bd9e..03a79368cc 100644 --- a/spec/grape/util/inheritable_setting_spec.rb +++ b/spec/grape/util/inheritable_setting_spec.rb @@ -1,239 +1,236 @@ # frozen_string_literal: true -module Grape - module Util - describe InheritableSetting do - before do - described_class.reset_global! - subject.inherit_from parent - end - - let(:parent) do - described_class.new.tap do |settings| - settings.global[:global_thing] = :global_foo_bar - settings.namespace[:namespace_thing] = :namespace_foo_bar - settings.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar - settings.namespace_stackable[:namespace_stackable_thing] = :namespace_stackable_foo_bar - settings.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = :namespace_reverse_stackable_foo_bar - settings.route[:route_thing] = :route_foo_bar - end - end - - let(:other_parent) do - described_class.new.tap do |settings| - settings.namespace[:namespace_thing] = :namespace_foo_bar_other - settings.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar_other - settings.namespace_stackable[:namespace_stackable_thing] = :namespace_stackable_foo_bar_other - settings.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = :namespace_reverse_stackable_foo_bar_other - settings.route[:route_thing] = :route_foo_bar_other - end - end - - describe '#global' do - it 'sets a global value' do - subject.global[:some_thing] = :foo_bar - expect(subject.global[:some_thing]).to eq :foo_bar - subject.global[:some_thing] = :foo_bar_next - expect(subject.global[:some_thing]).to eq :foo_bar_next - end - - it 'sets the global inherited values' do - expect(subject.global[:global_thing]).to eq :global_foo_bar - end - - it 'overrides global values' do - subject.global[:global_thing] = :global_new_foo_bar - expect(parent.global[:global_thing]).to eq :global_new_foo_bar - end - - it 'handles different parents' do - subject.global[:global_thing] = :global_new_foo_bar - - subject.inherit_from other_parent - - expect(parent.global[:global_thing]).to eq :global_new_foo_bar - expect(other_parent.global[:global_thing]).to eq :global_new_foo_bar - end - end - - describe '#api_class' do - it 'is specific to the class' do - subject.api_class[:some_thing] = :foo_bar - parent.api_class[:some_thing] = :some_thing - - expect(subject.api_class[:some_thing]).to eq :foo_bar - expect(parent.api_class[:some_thing]).to eq :some_thing - end - end - - describe '#namespace' do - it 'sets a value until the end of a namespace' do - subject.namespace[:some_thing] = :foo_bar - expect(subject.namespace[:some_thing]).to eq :foo_bar - end - - it 'uses new values when a new namespace starts' do - subject.namespace[:namespace_thing] = :new_namespace_foo_bar - expect(subject.namespace[:namespace_thing]).to eq :new_namespace_foo_bar - - expect(parent.namespace[:namespace_thing]).to eq :namespace_foo_bar - end - end - - describe '#namespace_inheritable' do - it 'works with inheritable values' do - expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar - end - - it 'handles different parents' do - expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar - - subject.inherit_from other_parent +describe Grape::Util::InheritableSetting do + before do + described_class.reset_global! + subject.inherit_from parent + end + + let(:parent) do + described_class.new.tap do |settings| + settings.global[:global_thing] = :global_foo_bar + settings.namespace[:namespace_thing] = :namespace_foo_bar + settings.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar + settings.namespace_stackable[:namespace_stackable_thing] = :namespace_stackable_foo_bar + settings.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = :namespace_reverse_stackable_foo_bar + settings.route[:route_thing] = :route_foo_bar + end + end + + let(:other_parent) do + described_class.new.tap do |settings| + settings.namespace[:namespace_thing] = :namespace_foo_bar_other + settings.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar_other + settings.namespace_stackable[:namespace_stackable_thing] = :namespace_stackable_foo_bar_other + settings.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = :namespace_reverse_stackable_foo_bar_other + settings.route[:route_thing] = :route_foo_bar_other + end + end + + describe '#global' do + it 'sets a global value' do + subject.global[:some_thing] = :foo_bar + expect(subject.global[:some_thing]).to eq :foo_bar + subject.global[:some_thing] = :foo_bar_next + expect(subject.global[:some_thing]).to eq :foo_bar_next + end + + it 'sets the global inherited values' do + expect(subject.global[:global_thing]).to eq :global_foo_bar + end + + it 'overrides global values' do + subject.global[:global_thing] = :global_new_foo_bar + expect(parent.global[:global_thing]).to eq :global_new_foo_bar + end + + it 'handles different parents' do + subject.global[:global_thing] = :global_new_foo_bar + + subject.inherit_from other_parent + + expect(parent.global[:global_thing]).to eq :global_new_foo_bar + expect(other_parent.global[:global_thing]).to eq :global_new_foo_bar + end + end + + describe '#api_class' do + it 'is specific to the class' do + subject.api_class[:some_thing] = :foo_bar + parent.api_class[:some_thing] = :some_thing + + expect(subject.api_class[:some_thing]).to eq :foo_bar + expect(parent.api_class[:some_thing]).to eq :some_thing + end + end + + describe '#namespace' do + it 'sets a value until the end of a namespace' do + subject.namespace[:some_thing] = :foo_bar + expect(subject.namespace[:some_thing]).to eq :foo_bar + end + + it 'uses new values when a new namespace starts' do + subject.namespace[:namespace_thing] = :new_namespace_foo_bar + expect(subject.namespace[:namespace_thing]).to eq :new_namespace_foo_bar + + expect(parent.namespace[:namespace_thing]).to eq :namespace_foo_bar + end + end + + describe '#namespace_inheritable' do + it 'works with inheritable values' do + expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar + end - expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar_other - - subject.inherit_from parent + it 'handles different parents' do + expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar - expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar - - subject.inherit_from other_parent + subject.inherit_from other_parent - subject.namespace_inheritable[:namespace_inheritable_thing] = :my_thing - - expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_thing + expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar_other - subject.inherit_from parent + subject.inherit_from parent - expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_thing - end - end + expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar - describe '#namespace_stackable' do - it 'works with stackable values' do - expect(subject.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar] + subject.inherit_from other_parent - subject.inherit_from other_parent + subject.namespace_inheritable[:namespace_inheritable_thing] = :my_thing - expect(subject.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar_other] - end - end + expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_thing - describe '#namespace_reverse_stackable' do - it 'works with reverse stackable values' do - expect(subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar] + subject.inherit_from parent - subject.inherit_from other_parent + expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_thing + end + end + + describe '#namespace_stackable' do + it 'works with stackable values' do + expect(subject.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar] + + subject.inherit_from other_parent + + expect(subject.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar_other] + end + end + + describe '#namespace_reverse_stackable' do + it 'works with reverse stackable values' do + expect(subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar] + + subject.inherit_from other_parent + + expect(subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar_other] + end + end + + describe '#route' do + it 'sets a value until the next route' do + subject.route[:some_thing] = :foo_bar + expect(subject.route[:some_thing]).to eq :foo_bar + + subject.route_end + + expect(subject.route[:some_thing]).to be_nil + end - expect(subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar_other] - end - end + it 'works with route values' do + expect(subject.route[:route_thing]).to eq :route_foo_bar + end + end - describe '#route' do - it 'sets a value until the next route' do - subject.route[:some_thing] = :foo_bar - expect(subject.route[:some_thing]).to eq :foo_bar + describe '#api_class' do + it 'is specific to the class' do + subject.api_class[:some_thing] = :foo_bar + expect(subject.api_class[:some_thing]).to eq :foo_bar + end + end - subject.route_end + describe '#inherit_from' do + it 'notifies clones' do + new_settings = subject.point_in_time_copy + expect(new_settings).to receive(:inherit_from).with(other_parent) - expect(subject.route[:some_thing]).to be_nil - end + subject.inherit_from other_parent + end + end - it 'works with route values' do - expect(subject.route[:route_thing]).to eq :route_foo_bar - end - end + describe '#point_in_time_copy' do + let!(:cloned_obj) { subject.point_in_time_copy } - describe '#api_class' do - it 'is specific to the class' do - subject.api_class[:some_thing] = :foo_bar - expect(subject.api_class[:some_thing]).to eq :foo_bar - end - end + it 'resets point_in_time_copies' do + expect(cloned_obj.point_in_time_copies).to be_empty + end - describe '#inherit_from' do - it 'notifies clones' do - new_settings = subject.point_in_time_copy - expect(new_settings).to receive(:inherit_from).with(other_parent) + it 'decouples namespace values' do + subject.namespace[:namespace_thing] = :namespace_foo_bar - subject.inherit_from other_parent - end - end + cloned_obj.namespace[:namespace_thing] = :new_namespace_foo_bar + expect(subject.namespace[:namespace_thing]).to eq :namespace_foo_bar + end - describe '#point_in_time_copy' do - let!(:cloned_obj) { subject.point_in_time_copy } + it 'decouples namespace inheritable values' do + expect(cloned_obj.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar - it 'resets point_in_time_copies' do - expect(cloned_obj.point_in_time_copies).to be_empty - end + subject.namespace_inheritable[:namespace_inheritable_thing] = :my_thing + expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_thing - it 'decouples namespace values' do - subject.namespace[:namespace_thing] = :namespace_foo_bar + expect(cloned_obj.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar - cloned_obj.namespace[:namespace_thing] = :new_namespace_foo_bar - expect(subject.namespace[:namespace_thing]).to eq :namespace_foo_bar - end + cloned_obj.namespace_inheritable[:namespace_inheritable_thing] = :my_cloned_thing + expect(cloned_obj.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_cloned_thing + expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_thing + end - it 'decouples namespace inheritable values' do - expect(cloned_obj.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar + it 'decouples namespace stackable values' do + expect(cloned_obj.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar] - subject.namespace_inheritable[:namespace_inheritable_thing] = :my_thing - expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_thing + subject.namespace_stackable[:namespace_stackable_thing] = :other_thing + expect(subject.namespace_stackable[:namespace_stackable_thing]).to eq %i[namespace_stackable_foo_bar other_thing] + expect(cloned_obj.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar] + end - expect(cloned_obj.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar + it 'decouples namespace reverse stackable values' do + expect(cloned_obj.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar] - cloned_obj.namespace_inheritable[:namespace_inheritable_thing] = :my_cloned_thing - expect(cloned_obj.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_cloned_thing - expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_thing - end + subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = :other_thing + expect(subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq %i[other_thing namespace_reverse_stackable_foo_bar] + expect(cloned_obj.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar] + end - it 'decouples namespace stackable values' do - expect(cloned_obj.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar] + it 'decouples route values' do + expect(cloned_obj.route[:route_thing]).to eq :route_foo_bar - subject.namespace_stackable[:namespace_stackable_thing] = :other_thing - expect(subject.namespace_stackable[:namespace_stackable_thing]).to eq %i[namespace_stackable_foo_bar other_thing] - expect(cloned_obj.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar] - end + subject.route[:route_thing] = :new_route_foo_bar + expect(cloned_obj.route[:route_thing]).to eq :route_foo_bar + end - it 'decouples namespace reverse stackable values' do - expect(cloned_obj.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar] + it 'adds itself to original as clone' do + expect(subject.point_in_time_copies).to include(cloned_obj) + end + end - subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = :other_thing - expect(subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq %i[other_thing namespace_reverse_stackable_foo_bar] - expect(cloned_obj.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar] - end - - it 'decouples route values' do - expect(cloned_obj.route[:route_thing]).to eq :route_foo_bar - - subject.route[:route_thing] = :new_route_foo_bar - expect(cloned_obj.route[:route_thing]).to eq :route_foo_bar - end - - it 'adds itself to original as clone' do - expect(subject.point_in_time_copies).to include(cloned_obj) - end - end - - describe '#to_hash' do - it 'return all settings as a hash' do - subject.global[:global_thing] = :global_foo_bar - subject.namespace[:namespace_thing] = :namespace_foo_bar - subject.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar - subject.namespace_stackable[:namespace_stackable_thing] = [:namespace_stackable_foo_bar] - subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = [:namespace_reverse_stackable_foo_bar] - subject.route[:route_thing] = :route_foo_bar - - expect(subject.to_hash).to include(global: { global_thing: :global_foo_bar }) - expect(subject.to_hash).to include(namespace: { namespace_thing: :namespace_foo_bar }) - expect(subject.to_hash).to include(namespace_inheritable: { - namespace_inheritable_thing: :namespace_inheritable_foo_bar - }) - expect(subject.to_hash).to include(namespace_stackable: { namespace_stackable_thing: [:namespace_stackable_foo_bar, [:namespace_stackable_foo_bar]] }) - expect(subject.to_hash).to include(namespace_reverse_stackable: - { namespace_reverse_stackable_thing: [[:namespace_reverse_stackable_foo_bar], :namespace_reverse_stackable_foo_bar] }) - expect(subject.to_hash).to include(route: { route_thing: :route_foo_bar }) - end - end + describe '#to_hash' do + it 'return all settings as a hash' do + subject.global[:global_thing] = :global_foo_bar + subject.namespace[:namespace_thing] = :namespace_foo_bar + subject.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar + subject.namespace_stackable[:namespace_stackable_thing] = [:namespace_stackable_foo_bar] + subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = [:namespace_reverse_stackable_foo_bar] + subject.route[:route_thing] = :route_foo_bar + expect(subject.to_hash).to match( + global: { global_thing: :global_foo_bar }, + namespace: { namespace_thing: :namespace_foo_bar }, + namespace_inheritable: { + namespace_inheritable_thing: :namespace_inheritable_foo_bar + }, + namespace_stackable: { namespace_stackable_thing: [:namespace_stackable_foo_bar, [:namespace_stackable_foo_bar]] }, + namespace_reverse_stackable: + { namespace_reverse_stackable_thing: [[:namespace_reverse_stackable_foo_bar], :namespace_reverse_stackable_foo_bar] }, + route: { route_thing: :route_foo_bar } + ) end end end diff --git a/spec/grape/util/inheritable_values_spec.rb b/spec/grape/util/inheritable_values_spec.rb index fed4a62c22..1f424e59f2 100644 --- a/spec/grape/util/inheritable_values_spec.rb +++ b/spec/grape/util/inheritable_values_spec.rb @@ -1,78 +1,74 @@ # frozen_string_literal: true -module Grape - module Util - describe InheritableValues do - subject { described_class.new(parent) } +describe Grape::Util::InheritableValues do + subject { described_class.new(parent) } - let(:parent) { described_class.new } + let(:parent) { described_class.new } - describe '#delete' do - it 'deletes a key' do - subject[:some_thing] = :new_foo_bar - subject.delete :some_thing - expect(subject[:some_thing]).to be_nil - end + describe '#delete' do + it 'deletes a key' do + subject[:some_thing] = :new_foo_bar + subject.delete :some_thing + expect(subject[:some_thing]).to be_nil + end - it 'does not delete parent values' do - parent[:some_thing] = :foo - subject[:some_thing] = :new_foo_bar - subject.delete :some_thing - expect(subject[:some_thing]).to eq :foo - end - end + it 'does not delete parent values' do + parent[:some_thing] = :foo + subject[:some_thing] = :new_foo_bar + subject.delete :some_thing + expect(subject[:some_thing]).to eq :foo + end + end - describe '#[]' do - it 'returns a value' do - subject[:some_thing] = :foo - expect(subject[:some_thing]).to eq :foo - end + describe '#[]' do + it 'returns a value' do + subject[:some_thing] = :foo + expect(subject[:some_thing]).to eq :foo + end - it 'returns parent value when no value is set' do - parent[:some_thing] = :foo - expect(subject[:some_thing]).to eq :foo - end + it 'returns parent value when no value is set' do + parent[:some_thing] = :foo + expect(subject[:some_thing]).to eq :foo + end - it 'overwrites parent value with the current one' do - parent[:some_thing] = :foo - subject[:some_thing] = :foo_bar - expect(subject[:some_thing]).to eq :foo_bar - end + it 'overwrites parent value with the current one' do + parent[:some_thing] = :foo + subject[:some_thing] = :foo_bar + expect(subject[:some_thing]).to eq :foo_bar + end - it 'parent values are not changed' do - parent[:some_thing] = :foo - subject[:some_thing] = :foo_bar - expect(parent[:some_thing]).to eq :foo - end - end + it 'parent values are not changed' do + parent[:some_thing] = :foo + subject[:some_thing] = :foo_bar + expect(parent[:some_thing]).to eq :foo + end + end - describe '#[]=' do - it 'sets a value' do - subject[:some_thing] = :foo - expect(subject[:some_thing]).to eq :foo - end - end + describe '#[]=' do + it 'sets a value' do + subject[:some_thing] = :foo + expect(subject[:some_thing]).to eq :foo + end + end - describe '#to_hash' do - it 'returns a Hash representation' do - parent[:some_thing] = :foo - subject[:some_thing_more] = :foo_bar - expect(subject.to_hash).to eq(some_thing: :foo, some_thing_more: :foo_bar) - end - end + describe '#to_hash' do + it 'returns a Hash representation' do + parent[:some_thing] = :foo + subject[:some_thing_more] = :foo_bar + expect(subject.to_hash).to eq(some_thing: :foo, some_thing_more: :foo_bar) + end + end - describe '#clone' do - let(:obj_cloned) { subject.clone } + describe '#clone' do + let(:obj_cloned) { subject.clone } - context 'complex (i.e. not primitive) data types (ex. entity classes, please see bug #891)' do - let(:description) { { entity: double } } + context 'complex (i.e. not primitive) data types (ex. entity classes, please see bug #891)' do + let(:description) { { entity: double } } - before { subject[:description] = description } + before { subject[:description] = description } - it 'copies values; does not duplicate them' do - expect(obj_cloned[:description]).to eq description - end - end + it 'copies values; does not duplicate them' do + expect(obj_cloned[:description]).to eq description end end end diff --git a/spec/grape/util/reverse_stackable_values_spec.rb b/spec/grape/util/reverse_stackable_values_spec.rb index e2f5b282f2..4037f57788 100644 --- a/spec/grape/util/reverse_stackable_values_spec.rb +++ b/spec/grape/util/reverse_stackable_values_spec.rb @@ -1,133 +1,129 @@ # frozen_string_literal: true -module Grape - module Util - describe ReverseStackableValues do - subject { described_class.new(parent) } +describe Grape::Util::ReverseStackableValues do + subject { described_class.new(parent) } - let(:parent) { described_class.new } + let(:parent) { described_class.new } - describe '#keys' do - it 'returns all keys' do - subject[:some_thing] = :foo_bar - subject[:some_thing_else] = :foo_bar - expect(subject.keys).to eq %i[some_thing some_thing_else].sort - end + describe '#keys' do + it 'returns all keys' do + subject[:some_thing] = :foo_bar + subject[:some_thing_else] = :foo_bar + expect(subject.keys).to eq %i[some_thing some_thing_else].sort + end - it 'returns merged keys with parent' do - parent[:some_thing] = :foo - parent[:some_thing_else] = :foo + it 'returns merged keys with parent' do + parent[:some_thing] = :foo + parent[:some_thing_else] = :foo - subject[:some_thing] = :foo_bar - subject[:some_thing_more] = :foo_bar + subject[:some_thing] = :foo_bar + subject[:some_thing_more] = :foo_bar - expect(subject.keys).to eq %i[some_thing some_thing_else some_thing_more].sort - end - end + expect(subject.keys).to eq %i[some_thing some_thing_else some_thing_more].sort + end + end - describe '#delete' do - it 'deletes a key' do - subject[:some_thing] = :new_foo_bar - subject.delete :some_thing - expect(subject[:some_thing]).to eq [] - end - - it 'does not delete parent values' do - parent[:some_thing] = :foo - subject[:some_thing] = :new_foo_bar - subject.delete :some_thing - expect(subject[:some_thing]).to eq [:foo] - end - end + describe '#delete' do + it 'deletes a key' do + subject[:some_thing] = :new_foo_bar + subject.delete :some_thing + expect(subject[:some_thing]).to eq [] + end - describe '#[]' do - it 'returns an array of values' do - subject[:some_thing] = :foo - expect(subject[:some_thing]).to eq [:foo] - end - - it 'returns parent value when no value is set' do - parent[:some_thing] = :foo - expect(subject[:some_thing]).to eq [:foo] - end - - it 'combines parent and actual values (actual first)' do - parent[:some_thing] = :foo - subject[:some_thing] = :foo_bar - expect(subject[:some_thing]).to eq %i[foo_bar foo] - end - - it 'parent values are not changed' do - parent[:some_thing] = :foo - subject[:some_thing] = :foo_bar - expect(parent[:some_thing]).to eq [:foo] - end - end + it 'does not delete parent values' do + parent[:some_thing] = :foo + subject[:some_thing] = :new_foo_bar + subject.delete :some_thing + expect(subject[:some_thing]).to eq [:foo] + end + end - describe '#[]=' do - it 'sets a value' do - subject[:some_thing] = :foo - expect(subject[:some_thing]).to eq [:foo] - end + describe '#[]' do + it 'returns an array of values' do + subject[:some_thing] = :foo + expect(subject[:some_thing]).to eq [:foo] + end - it 'pushes further values' do - subject[:some_thing] = :foo - subject[:some_thing] = :bar - expect(subject[:some_thing]).to eq %i[foo bar] - end + it 'returns parent value when no value is set' do + parent[:some_thing] = :foo + expect(subject[:some_thing]).to eq [:foo] + end - it 'can handle array values' do - subject[:some_thing] = :foo - subject[:some_thing] = %i[bar more] - expect(subject[:some_thing]).to eq [:foo, %i[bar more]] + it 'combines parent and actual values (actual first)' do + parent[:some_thing] = :foo + subject[:some_thing] = :foo_bar + expect(subject[:some_thing]).to eq %i[foo_bar foo] + end - parent[:some_thing_else] = %i[foo bar] - subject[:some_thing_else] = %i[some bar foo] + it 'parent values are not changed' do + parent[:some_thing] = :foo + subject[:some_thing] = :foo_bar + expect(parent[:some_thing]).to eq [:foo] + end + end - expect(subject[:some_thing_else]).to eq [%i[some bar foo], %i[foo bar]] - end - end + describe '#[]=' do + it 'sets a value' do + subject[:some_thing] = :foo + expect(subject[:some_thing]).to eq [:foo] + end - describe '#to_hash' do - it 'returns a Hash representation' do - parent[:some_thing] = :foo - subject[:some_thing] = %i[bar more] - subject[:some_thing_more] = :foo_bar - expect(subject.to_hash).to eq( - some_thing: [%i[bar more], :foo], - some_thing_more: [:foo_bar] - ) - end - end + it 'pushes further values' do + subject[:some_thing] = :foo + subject[:some_thing] = :bar + expect(subject[:some_thing]).to eq %i[foo bar] + end - describe '#clone' do - let(:obj_cloned) { subject.clone } + it 'can handle array values' do + subject[:some_thing] = :foo + subject[:some_thing] = %i[bar more] + expect(subject[:some_thing]).to eq [:foo, %i[bar more]] - it 'copies all values' do - parent = described_class.new - child = described_class.new parent - grandchild = described_class.new child + parent[:some_thing_else] = %i[foo bar] + subject[:some_thing_else] = %i[some bar foo] - parent[:some_thing] = :foo - child[:some_thing] = %i[bar more] - grandchild[:some_thing] = :grand_foo_bar - grandchild[:some_thing_more] = :foo_bar + expect(subject[:some_thing_else]).to eq [%i[some bar foo], %i[foo bar]] + end + end - expect(grandchild.clone.to_hash).to eq( - some_thing: [:grand_foo_bar, %i[bar more], :foo], - some_thing_more: [:foo_bar] - ) - end + describe '#to_hash' do + it 'returns a Hash representation' do + parent[:some_thing] = :foo + subject[:some_thing] = %i[bar more] + subject[:some_thing_more] = :foo_bar + expect(subject.to_hash).to eq( + some_thing: [%i[bar more], :foo], + some_thing_more: [:foo_bar] + ) + end + end + + describe '#clone' do + let(:obj_cloned) { subject.clone } + + it 'copies all values' do + parent = described_class.new + child = described_class.new parent + grandchild = described_class.new child + + parent[:some_thing] = :foo + child[:some_thing] = %i[bar more] + grandchild[:some_thing] = :grand_foo_bar + grandchild[:some_thing_more] = :foo_bar + + expect(grandchild.clone.to_hash).to eq( + some_thing: [:grand_foo_bar, %i[bar more], :foo], + some_thing_more: [:foo_bar] + ) + end - context 'complex (i.e. not primitive) data types (ex. middleware, please see bug #930)' do - let(:middleware) { double } + context 'complex (i.e. not primitive) data types (ex. middleware, please see bug #930)' do + let(:middleware) { double } - before { subject[:middleware] = middleware } + before { subject[:middleware] = middleware } - it 'copies values; does not duplicate them' do - expect(obj_cloned[:middleware]).to eq [middleware] - end - end + it 'copies values; does not duplicate them' do + expect(obj_cloned[:middleware]).to eq [middleware] end end end diff --git a/spec/grape/util/stackable_values_spec.rb b/spec/grape/util/stackable_values_spec.rb index e857c7f90a..0bcd9c3d9f 100644 --- a/spec/grape/util/stackable_values_spec.rb +++ b/spec/grape/util/stackable_values_spec.rb @@ -1,127 +1,123 @@ # frozen_string_literal: true -module Grape - module Util - describe StackableValues do - subject { described_class.new(parent) } +describe Grape::Util::StackableValues do + subject { described_class.new(parent) } - let(:parent) { described_class.new } + let(:parent) { described_class.new } - describe '#keys' do - it 'returns all keys' do - subject[:some_thing] = :foo_bar - subject[:some_thing_else] = :foo_bar - expect(subject.keys).to eq %i[some_thing some_thing_else].sort - end + describe '#keys' do + it 'returns all keys' do + subject[:some_thing] = :foo_bar + subject[:some_thing_else] = :foo_bar + expect(subject.keys).to eq %i[some_thing some_thing_else].sort + end - it 'returns merged keys with parent' do - parent[:some_thing] = :foo - parent[:some_thing_else] = :foo + it 'returns merged keys with parent' do + parent[:some_thing] = :foo + parent[:some_thing_else] = :foo - subject[:some_thing] = :foo_bar - subject[:some_thing_more] = :foo_bar + subject[:some_thing] = :foo_bar + subject[:some_thing_more] = :foo_bar - expect(subject.keys).to eq %i[some_thing some_thing_else some_thing_more].sort - end - end + expect(subject.keys).to eq %i[some_thing some_thing_else some_thing_more].sort + end + end - describe '#delete' do - it 'deletes a key' do - subject[:some_thing] = :new_foo_bar - subject.delete :some_thing - expect(subject[:some_thing]).to eq [] - end - - it 'does not delete parent values' do - parent[:some_thing] = :foo - subject[:some_thing] = :new_foo_bar - subject.delete :some_thing - expect(subject[:some_thing]).to eq [:foo] - end - end + describe '#delete' do + it 'deletes a key' do + subject[:some_thing] = :new_foo_bar + subject.delete :some_thing + expect(subject[:some_thing]).to eq [] + end - describe '#[]' do - it 'returns an array of values' do - subject[:some_thing] = :foo - expect(subject[:some_thing]).to eq [:foo] - end - - it 'returns parent value when no value is set' do - parent[:some_thing] = :foo - expect(subject[:some_thing]).to eq [:foo] - end - - it 'combines parent and actual values' do - parent[:some_thing] = :foo - subject[:some_thing] = :foo_bar - expect(subject[:some_thing]).to eq %i[foo foo_bar] - end - - it 'parent values are not changed' do - parent[:some_thing] = :foo - subject[:some_thing] = :foo_bar - expect(parent[:some_thing]).to eq [:foo] - end - end + it 'does not delete parent values' do + parent[:some_thing] = :foo + subject[:some_thing] = :new_foo_bar + subject.delete :some_thing + expect(subject[:some_thing]).to eq [:foo] + end + end - describe '#[]=' do - it 'sets a value' do - subject[:some_thing] = :foo - expect(subject[:some_thing]).to eq [:foo] - end + describe '#[]' do + it 'returns an array of values' do + subject[:some_thing] = :foo + expect(subject[:some_thing]).to eq [:foo] + end - it 'pushes further values' do - subject[:some_thing] = :foo - subject[:some_thing] = :bar - expect(subject[:some_thing]).to eq %i[foo bar] - end + it 'returns parent value when no value is set' do + parent[:some_thing] = :foo + expect(subject[:some_thing]).to eq [:foo] + end - it 'can handle array values' do - subject[:some_thing] = :foo - subject[:some_thing] = %i[bar more] - expect(subject[:some_thing]).to eq [:foo, %i[bar more]] + it 'combines parent and actual values' do + parent[:some_thing] = :foo + subject[:some_thing] = :foo_bar + expect(subject[:some_thing]).to eq %i[foo foo_bar] + end - parent[:some_thing_else] = %i[foo bar] - subject[:some_thing_else] = %i[some bar foo] + it 'parent values are not changed' do + parent[:some_thing] = :foo + subject[:some_thing] = :foo_bar + expect(parent[:some_thing]).to eq [:foo] + end + end - expect(subject[:some_thing_else]).to eq [%i[foo bar], %i[some bar foo]] - end - end + describe '#[]=' do + it 'sets a value' do + subject[:some_thing] = :foo + expect(subject[:some_thing]).to eq [:foo] + end - describe '#to_hash' do - it 'returns a Hash representation' do - parent[:some_thing] = :foo - subject[:some_thing] = %i[bar more] - subject[:some_thing_more] = :foo_bar - expect(subject.to_hash).to eq(some_thing: [:foo, %i[bar more]], some_thing_more: [:foo_bar]) - end - end + it 'pushes further values' do + subject[:some_thing] = :foo + subject[:some_thing] = :bar + expect(subject[:some_thing]).to eq %i[foo bar] + end - describe '#clone' do - let(:obj_cloned) { subject.clone } + it 'can handle array values' do + subject[:some_thing] = :foo + subject[:some_thing] = %i[bar more] + expect(subject[:some_thing]).to eq [:foo, %i[bar more]] - it 'copies all values' do - parent = described_class.new - child = described_class.new parent - grandchild = described_class.new child + parent[:some_thing_else] = %i[foo bar] + subject[:some_thing_else] = %i[some bar foo] - parent[:some_thing] = :foo - child[:some_thing] = %i[bar more] - grandchild[:some_thing] = :grand_foo_bar - grandchild[:some_thing_more] = :foo_bar + expect(subject[:some_thing_else]).to eq [%i[foo bar], %i[some bar foo]] + end + end - expect(grandchild.clone.to_hash).to eq(some_thing: [:foo, %i[bar more], :grand_foo_bar], some_thing_more: [:foo_bar]) - end + describe '#to_hash' do + it 'returns a Hash representation' do + parent[:some_thing] = :foo + subject[:some_thing] = %i[bar more] + subject[:some_thing_more] = :foo_bar + expect(subject.to_hash).to eq(some_thing: [:foo, %i[bar more]], some_thing_more: [:foo_bar]) + end + end + + describe '#clone' do + let(:obj_cloned) { subject.clone } + + it 'copies all values' do + parent = described_class.new + child = described_class.new parent + grandchild = described_class.new child + + parent[:some_thing] = :foo + child[:some_thing] = %i[bar more] + grandchild[:some_thing] = :grand_foo_bar + grandchild[:some_thing_more] = :foo_bar + + expect(grandchild.clone.to_hash).to eq(some_thing: [:foo, %i[bar more], :grand_foo_bar], some_thing_more: [:foo_bar]) + end - context 'complex (i.e. not primitive) data types (ex. middleware, please see bug #930)' do - let(:middleware) { double } + context 'complex (i.e. not primitive) data types (ex. middleware, please see bug #930)' do + let(:middleware) { double } - before { subject[:middleware] = middleware } + before { subject[:middleware] = middleware } - it 'copies values; does not duplicate them' do - expect(obj_cloned[:middleware]).to eq [middleware] - end - end + it 'copies values; does not duplicate them' do + expect(obj_cloned[:middleware]).to eq [middleware] end end end diff --git a/spec/grape/util/strict_hash_configuration_spec.rb b/spec/grape/util/strict_hash_configuration_spec.rb index 7f059eee14..3009868603 100644 --- a/spec/grape/util/strict_hash_configuration_spec.rb +++ b/spec/grape/util/strict_hash_configuration_spec.rb @@ -1,38 +1,34 @@ # frozen_string_literal: true -module Grape - module Util - describe 'StrictHashConfiguration' do - subject do - Class.new do - include Grape::Util::StrictHashConfiguration.module(:config1, :config2, config3: [:config4], config5: [config6: %i[config7 config8]]) - end - end +describe Grape::Util::StrictHashConfiguration do + subject do + Class.new do + include Grape::Util::StrictHashConfiguration.module(:config1, :config2, config3: [:config4], config5: [config6: %i[config7 config8]]) + end + end - it 'set nested configs' do - subject.configure do - config1 'alpha' - config2 'beta' + it 'set nested configs' do + subject.configure do + config1 'alpha' + config2 'beta' - config3 do - config4 'gamma' - end + config3 do + config4 'gamma' + end - local_var = 8 + local_var = 8 - config5 do - config6 do - config7 7 - config8 local_var - end - end + config5 do + config6 do + config7 7 + config8 local_var end - - expect(subject.settings).to eq(config1: 'alpha', - config2: 'beta', - config3: { config4: 'gamma' }, - config5: { config6: { config7: 7, config8: 8 } }) end end + + expect(subject.settings).to eq(config1: 'alpha', + config2: 'beta', + config3: { config4: 'gamma' }, + config5: { config6: { config7: 7, config8: 8 } }) end end diff --git a/spec/grape/validations/validators/all_or_none_spec.rb b/spec/grape/validations/validators/all_or_none_validator_spec.rb similarity index 100% rename from spec/grape/validations/validators/all_or_none_spec.rb rename to spec/grape/validations/validators/all_or_none_validator_spec.rb diff --git a/spec/grape/validations/validators/allow_blank_spec.rb b/spec/grape/validations/validators/allow_blank_validator_spec.rb similarity index 100% rename from spec/grape/validations/validators/allow_blank_spec.rb rename to spec/grape/validations/validators/allow_blank_validator_spec.rb diff --git a/spec/grape/validations/validators/at_least_one_of_spec.rb b/spec/grape/validations/validators/at_least_one_of_validator_spec.rb similarity index 100% rename from spec/grape/validations/validators/at_least_one_of_spec.rb rename to spec/grape/validations/validators/at_least_one_of_validator_spec.rb diff --git a/spec/grape/validations/validators/coerce_spec.rb b/spec/grape/validations/validators/coerce_validator_spec.rb similarity index 100% rename from spec/grape/validations/validators/coerce_spec.rb rename to spec/grape/validations/validators/coerce_validator_spec.rb diff --git a/spec/grape/validations/validators/default_spec.rb b/spec/grape/validations/validators/default_validator_spec.rb similarity index 100% rename from spec/grape/validations/validators/default_spec.rb rename to spec/grape/validations/validators/default_validator_spec.rb diff --git a/spec/grape/validations/validators/exactly_one_of_spec.rb b/spec/grape/validations/validators/exactly_one_of_validator_spec.rb similarity index 100% rename from spec/grape/validations/validators/exactly_one_of_spec.rb rename to spec/grape/validations/validators/exactly_one_of_validator_spec.rb diff --git a/spec/grape/validations/validators/except_values_spec.rb b/spec/grape/validations/validators/except_values_spec.rb deleted file mode 100644 index 63c62dd1df..0000000000 --- a/spec/grape/validations/validators/except_values_spec.rb +++ /dev/null @@ -1,192 +0,0 @@ -# frozen_string_literal: true - -describe Grape::Validations::Validators::ExceptValuesValidator do - module ValidationsSpec - class ExceptValuesModel - DEFAULT_EXCEPTS = %w[invalid-type1 invalid-type2 invalid-type3].freeze - class << self - attr_accessor :excepts - - def excepts - @excepts ||= [] - [DEFAULT_EXCEPTS + @excepts].flatten.uniq - end - end - end - - TEST_CASES = { - req_except: { - requires: { except_values: ExceptValuesModel.excepts }, - tests: [ - { value: 'invalid-type1', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: 'invalid-type3', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json } - ] - }, - req_except_hash: { - requires: { except_values: { value: ExceptValuesModel.excepts } }, - tests: [ - { value: 'invalid-type1', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: 'invalid-type3', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json } - ] - }, - req_except_custom_message: { - requires: { except_values: { value: ExceptValuesModel.excepts, message: 'is not allowed' } }, - tests: [ - { value: 'invalid-type1', rc: 400, body: { error: 'type is not allowed' }.to_json }, - { value: 'invalid-type3', rc: 400, body: { error: 'type is not allowed' }.to_json }, - { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json } - ] - }, - req_except_no_value: { - requires: { except_values: { message: 'is not allowed' } }, - tests: [ - { value: 'invalid-type1', rc: 200, body: { type: 'invalid-type1' }.to_json } - ] - }, - req_except_empty: { - requires: { except_values: [] }, - tests: [ - { value: 'invalid-type1', rc: 200, body: { type: 'invalid-type1' }.to_json } - ] - }, - req_except_lambda: { - requires: { except_values: -> { ExceptValuesModel.excepts } }, - add_excepts: ['invalid-type4'], - tests: [ - { value: 'invalid-type1', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: 'invalid-type4', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json } - ] - }, - req_except_lambda_custom_message: { - requires: { except_values: { value: -> { ExceptValuesModel.excepts }, message: 'is not allowed' } }, - add_excepts: ['invalid-type4'], - tests: [ - { value: 'invalid-type1', rc: 400, body: { error: 'type is not allowed' }.to_json }, - { value: 'invalid-type4', rc: 400, body: { error: 'type is not allowed' }.to_json }, - { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json } - ] - }, - opt_except_default: { - optional: { except_values: ExceptValuesModel.excepts, default: 'valid-type2' }, - tests: [ - { value: 'invalid-type1', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: 'invalid-type3', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json }, - { rc: 200, body: { type: 'valid-type2' }.to_json } - ] - }, - opt_except_lambda_default: { - optional: { except_values: -> { ExceptValuesModel.excepts }, default: 'valid-type2' }, - tests: [ - { value: 'invalid-type1', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: 'invalid-type3', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json }, - { rc: 200, body: { type: 'valid-type2' }.to_json } - ] - }, - req_except_type_coerce: { - requires: { type: Integer, except_values: [10, 11] }, - tests: [ - { value: 'invalid-type1', rc: 400, body: { error: 'type is invalid' }.to_json }, - { value: 11, rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: '11', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: '3', rc: 200, body: { type: 3 }.to_json }, - { value: 3, rc: 200, body: { type: 3 }.to_json } - ] - }, - opt_except_type_coerce_default: { - optional: { type: Integer, except_values: [10, 11], default: 12 }, - tests: [ - { value: 'invalid-type1', rc: 400, body: { error: 'type is invalid' }.to_json }, - { value: 10, rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: '3', rc: 200, body: { type: 3 }.to_json }, - { value: 3, rc: 200, body: { type: 3 }.to_json }, - { rc: 200, body: { type: 12 }.to_json } - ] - }, - opt_except_array_type_coerce_default: { - optional: { type: Array[Integer], except_values: [10, 11], default: 12 }, - tests: [ - { value: 'invalid-type1', rc: 400, body: { error: 'type is invalid' }.to_json }, - { value: 10, rc: 400, body: { error: 'type is invalid' }.to_json }, - { value: [10], rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: ['3'], rc: 200, body: { type: [3] }.to_json }, - { value: [3], rc: 200, body: { type: [3] }.to_json }, - { rc: 200, body: { type: 12 }.to_json } - ] - }, - req_except_range: { - optional: { type: Integer, except_values: 10..12 }, - tests: [ - { value: 11, rc: 400, body: { error: 'type has a value not allowed' }.to_json }, - { value: 13, rc: 200, body: { type: 13 }.to_json } - ] - } - }.freeze - - module ExceptValidatorSpec - class API < Grape::API - default_format :json - - TEST_CASES.each_with_index do |(k, v), _i| - params do - requires :type, v[:requires] if v.key? :requires - optional :type, v[:optional] if v.key? :optional - end - get k do - { type: params[:type] } - end - end - end - end - end - - it 'raises IncompatibleOptionValues on a default value in exclude' do - subject = Class.new(Grape::API) - expect do - subject.params do - optional :type, except_values: ValidationsSpec::ExceptValuesModel.excepts, - default: ValidationsSpec::ExceptValuesModel.excepts.sample - end - end.to raise_error Grape::Exceptions::IncompatibleOptionValues - end - - it 'raises IncompatibleOptionValues when a default array has excluded values' do - subject = Class.new(Grape::API) - expect do - subject.params do - optional :type, type: Array[Integer], - except_values: 10..12, - default: [8, 9, 10] - end - end.to raise_error Grape::Exceptions::IncompatibleOptionValues - end - - it 'raises IncompatibleOptionValues when type is incompatible with values array' do - subject = Class.new(Grape::API) - expect do - subject.params { optional :type, except_values: %w[valid-type1 valid-type2 valid-type3], type: Symbol } - end.to raise_error Grape::Exceptions::IncompatibleOptionValues - end - - def app - ValidationsSpec::ExceptValidatorSpec::API - end - - ValidationsSpec::TEST_CASES.each_with_index do |(k, v), i| - v[:tests].each do |t| - it "#{i}: #{k} - #{t[:value]}" do - ValidationsSpec::ExceptValuesModel.excepts = v[:add_excepts] if v.key? :add_excepts - body = {} - body[:type] = t[:value] if t.key? :value - get k.to_s, **body - expect(last_response.status).to eq t[:rc] - expect(last_response.body).to eq t[:body] - ValidationsSpec::ExceptValuesModel.excepts = nil - end - end - end -end diff --git a/spec/grape/validations/validators/except_values_validator_spec.rb b/spec/grape/validations/validators/except_values_validator_spec.rb new file mode 100644 index 0000000000..d72d0092d4 --- /dev/null +++ b/spec/grape/validations/validators/except_values_validator_spec.rb @@ -0,0 +1,194 @@ +# frozen_string_literal: true + +describe Grape::Validations::Validators::ExceptValuesValidator do + describe 'IncompatibleOptionValues' do + subject { api } + + context 'when a default value is set' do + let(:api) do + ev = except_values + dv = default_value + Class.new(Grape::API) do + params do + optional :type, except_values: ev, default: dv + end + end + end + + context 'when default value is in exclude' do + let(:except_values) { 1..10 } + let(:default_value) { except_values.to_a.sample } + + it 'raises IncompatibleOptionValues' do + expect { subject }.to raise_error Grape::Exceptions::IncompatibleOptionValues + end + end + + context 'when default array has excluded values' do + let(:except_values) { 1..10 } + let(:default_value) { [8, 9, 10] } + + it 'raises IncompatibleOptionValues' do + expect { subject }.to raise_error Grape::Exceptions::IncompatibleOptionValues + end + end + end + + context 'when type is incompatible' do + let(:api) do + Class.new(Grape::API) do + params do + optional :type, except_values: 1..10, type: Symbol + end + end + end + + it 'raises IncompatibleOptionValues' do + expect { subject }.to raise_error Grape::Exceptions::IncompatibleOptionValues + end + end + end + + { + req_except: { + requires: { except_values: %w[invalid-type1 invalid-type2 invalid-type3] }, + tests: [ + { value: 'invalid-type1', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: 'invalid-type3', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json } + ] + }, + req_except_hash: { + requires: { except_values: { value: %w[invalid-type1 invalid-type2 invalid-type3] } }, + tests: [ + { value: 'invalid-type1', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: 'invalid-type3', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json } + ] + }, + req_except_custom_message: { + requires: { except_values: { value: %w[invalid-type1 invalid-type2 invalid-type3], message: 'is not allowed' } }, + tests: [ + { value: 'invalid-type1', rc: 400, body: { error: 'type is not allowed' }.to_json }, + { value: 'invalid-type3', rc: 400, body: { error: 'type is not allowed' }.to_json }, + { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json } + ] + }, + req_except_no_value: { + requires: { except_values: { message: 'is not allowed' } }, + tests: [ + { value: 'invalid-type1', rc: 200, body: { type: 'invalid-type1' }.to_json } + ] + }, + req_except_empty: { + requires: { except_values: [] }, + tests: [ + { value: 'invalid-type1', rc: 200, body: { type: 'invalid-type1' }.to_json } + ] + }, + req_except_lambda: { + requires: { except_values: -> { %w[invalid-type1 invalid-type2 invalid-type3 invalid-type4] } }, + tests: [ + { value: 'invalid-type1', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: 'invalid-type4', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json } + ] + }, + req_except_lambda_custom_message: { + requires: { except_values: { value: -> { %w[invalid-type1 invalid-type2 invalid-type3 invalid-type4] }, message: 'is not allowed' } }, + tests: [ + { value: 'invalid-type1', rc: 400, body: { error: 'type is not allowed' }.to_json }, + { value: 'invalid-type4', rc: 400, body: { error: 'type is not allowed' }.to_json }, + { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json } + ] + }, + opt_except_default: { + optional: { except_values: %w[invalid-type1 invalid-type2 invalid-type3], default: 'valid-type2' }, + tests: [ + { value: 'invalid-type1', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: 'invalid-type3', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json }, + { rc: 200, body: { type: 'valid-type2' }.to_json } + ] + }, + opt_except_lambda_default: { + optional: { except_values: -> { %w[invalid-type1 invalid-type2 invalid-type3] }, default: 'valid-type2' }, + tests: [ + { value: 'invalid-type1', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: 'invalid-type3', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: 'valid-type', rc: 200, body: { type: 'valid-type' }.to_json }, + { rc: 200, body: { type: 'valid-type2' }.to_json } + ] + }, + req_except_type_coerce: { + requires: { type: Integer, except_values: [10, 11] }, + tests: [ + { value: 'invalid-type1', rc: 400, body: { error: 'type is invalid' }.to_json }, + { value: 11, rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: '11', rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: '3', rc: 200, body: { type: 3 }.to_json }, + { value: 3, rc: 200, body: { type: 3 }.to_json } + ] + }, + opt_except_type_coerce_default: { + optional: { type: Integer, except_values: [10, 11], default: 12 }, + tests: [ + { value: 'invalid-type1', rc: 400, body: { error: 'type is invalid' }.to_json }, + { value: 10, rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: '3', rc: 200, body: { type: 3 }.to_json }, + { value: 3, rc: 200, body: { type: 3 }.to_json }, + { rc: 200, body: { type: 12 }.to_json } + ] + }, + opt_except_array_type_coerce_default: { + optional: { type: Array[Integer], except_values: [10, 11], default: 12 }, + tests: [ + { value: 'invalid-type1', rc: 400, body: { error: 'type is invalid' }.to_json }, + { value: 10, rc: 400, body: { error: 'type is invalid' }.to_json }, + { value: [10], rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: ['3'], rc: 200, body: { type: [3] }.to_json }, + { value: [3], rc: 200, body: { type: [3] }.to_json }, + { rc: 200, body: { type: 12 }.to_json } + ] + }, + req_except_range: { + optional: { type: Integer, except_values: 10..12 }, + tests: [ + { value: 11, rc: 400, body: { error: 'type has a value not allowed' }.to_json }, + { value: 13, rc: 200, body: { type: 13 }.to_json } + ] + } + }.each do |path, param_def| + param_def[:tests].each do |t| + describe "when #{path}" do + let(:app) do + Class.new(Grape::API) do + default_format :json + params do + requires :type, param_def[:requires] if param_def.key? :requires + optional :type, param_def[:optional] if param_def.key? :optional + end + get path do + { type: params[:type] } + end + end + end + + let(:body) do + {}.tap do |body| + body[:type] = t[:value] if t.key? :value + end + end + + before do + get path.to_s, **body + end + + it "returns body #{t[:body]} with status #{t[:rc]}" do + expect(last_response.status).to eq t[:rc] + expect(last_response.body).to eq t[:body] + end + end + end + end +end diff --git a/spec/grape/validations/validators/length_spec.rb b/spec/grape/validations/validators/length_validator_spec.rb similarity index 100% rename from spec/grape/validations/validators/length_spec.rb rename to spec/grape/validations/validators/length_validator_spec.rb diff --git a/spec/grape/validations/validators/mutual_exclusion_spec.rb b/spec/grape/validations/validators/mutual_exclusion_validator_spec.rb similarity index 100% rename from spec/grape/validations/validators/mutual_exclusion_spec.rb rename to spec/grape/validations/validators/mutual_exclusion_validator_spec.rb diff --git a/spec/grape/validations/validators/presence_spec.rb b/spec/grape/validations/validators/presence_validator_spec.rb similarity index 100% rename from spec/grape/validations/validators/presence_spec.rb rename to spec/grape/validations/validators/presence_validator_spec.rb diff --git a/spec/grape/validations/validators/regexp_spec.rb b/spec/grape/validations/validators/regexp_validator_spec.rb similarity index 100% rename from spec/grape/validations/validators/regexp_spec.rb rename to spec/grape/validations/validators/regexp_validator_spec.rb diff --git a/spec/grape/validations/validators/same_as_spec.rb b/spec/grape/validations/validators/same_as_validator_spec.rb similarity index 100% rename from spec/grape/validations/validators/same_as_spec.rb rename to spec/grape/validations/validators/same_as_validator_spec.rb diff --git a/spec/grape/validations/validators/values_spec.rb b/spec/grape/validations/validators/values_validator_spec.rb similarity index 100% rename from spec/grape/validations/validators/values_spec.rb rename to spec/grape/validations/validators/values_validator_spec.rb