Testing HashWithIndifferentAccess Parameter Building in Grape API
This test suite validates the HashWithIndifferentAccess parameter builder extension in Grape API. It ensures consistent parameter handling regardless of whether string or symbol keys are used, with comprehensive testing of nested parameter structures and route parameter interactions.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
ruby-grape/grape
spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb
# frozen_string_literal: true
describe Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder do
subject { Class.new(Grape::API) }
def app
subject
end
describe 'in an endpoint' do
describe '#params' do
before do
subject.params do
build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder # rubocop:disable RSpec/DescribedClass
end
subject.get do
params.class
end
end
it 'is of type Hash' do
get '/'
expect(last_response.status).to eq(200)
expect(last_response.body).to eq('ActiveSupport::HashWithIndifferentAccess')
end
end
end
describe 'in an api' do
before do
subject.include Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder # rubocop:disable RSpec/DescribedClass
end
describe '#params' do
before do
subject.get do
params.class
end
end
it 'is a Hash' do
get '/'
expect(last_response.status).to eq(200)
expect(last_response.body).to eq('ActiveSupport::HashWithIndifferentAccess')
end
it 'parses sub hash params' do
subject.params do
build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder # rubocop:disable RSpec/DescribedClass
optional :a, type: Hash do
optional :b, type: Hash do
optional :c, type: String
end
optional :d, type: Array
end
end
subject.get '/foo' do
[params[:a]['b'][:c], params['a'][:d]]
end
get '/foo', a: { b: { c: 'bar' }, d: ['foo'] }
expect(last_response.status).to eq(200)
expect(last_response.body).to eq('["bar", ["foo"]]')
end
it 'params are indifferent to symbol or string keys' do
subject.params do
build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder # rubocop:disable RSpec/DescribedClass
optional :a, type: Hash do
optional :b, type: Hash do
optional :c, type: String
end
optional :d, type: Array
end
end
subject.get '/foo' do
[params[:a]['b'][:c], params['a'][:d]]
end
get '/foo', 'a' => { b: { c: 'bar' }, 'd' => ['foo'] }
expect(last_response.status).to eq(200)
expect(last_response.body).to eq('["bar", ["foo"]]')
end
it 'responds to string keys' do
subject.params do
build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder # rubocop:disable RSpec/DescribedClass
requires :a, type: String
end
subject.get '/foo' do
[params[:a], params['a']]
end
get '/foo', a: 'bar'
expect(last_response.status).to eq(200)
expect(last_response.body).to eq('["bar", "bar"]')
end
end
it 'does not overwrite route_param with a regular param if they have same name' do
subject.namespace :route_param do
route_param :foo do
get { params.to_json }
end
end
get '/route_param/bar', foo: 'baz'
expect(last_response.status).to eq(200)
expect(last_response.body).to eq('{"foo":"bar"}')
end
it 'does not overwrite route_param with a defined regular param if they have same name' do
subject.namespace :route_param do
params do
requires :foo, type: String
end
route_param :foo do
get do
[params[:foo], params['foo']]
end
end
end
get '/route_param/bar', foo: 'baz'
expect(last_response.status).to eq(200)
expect(last_response.body).to eq('["bar", "bar"]')
end
end
end