require 'unit_spec_helper' describe Shoulda::Matchers::ActionController, type: :controller do describe '#permit' do it 'matches when the sent parameter is allowed' do controller_for_resource_with_strong_parameters(action: :create) do params.require(:user).permit(:name) end expect(@controller).to permit(:name).for(:create) end it 'does not match when the sent parameter is not allowed' do controller_for_resource_with_strong_parameters(action: :create) do params.require(:user).permit(:name) end expect(@controller).not_to permit(:admin).for(:create) end it 'matches against multiple attributes' do controller_for_resource_with_strong_parameters(action: :create) do params.require(:user).permit(:name, :age) end expect(@controller).to permit(:name, :age).for(:create) end it 'can be used more than once in the same test' do controller_for_resource_with_strong_parameters(action: :create) do params.require(:user).permit(:name) end expect(@controller).to permit(:name).for(:create) expect(@controller).not_to permit(:admin).for(:create) end end end describe Shoulda::Matchers::ActionController::StrongParametersMatcher, type: :controller do describe '#description' do it 'returns the correct string' do options = { action: :create, method: :post } controller_for_resource_with_strong_parameters(options) do params.permit(:name, :age) end matcher = described_class.new([:name, :age, :height]).for(:create) expect(matcher.description). to eq 'permit POST #create to receive parameters :name, :age, and :height' end context 'when a verb is specified' do it 'returns the correct string' do options = { action: :some_action } controller_for_resource_with_strong_parameters(options) do params.permit(:name, :age) end matcher = described_class.new([:name]). for(:some_action, verb: :put) expect(matcher.description). to eq 'permit PUT #some_action to receive parameters :name' end end end describe '#matches?' do it 'is true for a subset of the allowable attributes' do controller_for_resource_with_strong_parameters(action: :create) do params.require(:user).permit(:name) end matcher = described_class.new([:name]).in_context(self).for(:create) expect(matcher.matches?(@controller)).to be true end it 'is true for all the allowable attributes' do controller_for_resource_with_strong_parameters(action: :create) do params.require(:user).permit(:name, :age) end matcher = described_class.new([:name, :age]).in_context(self).for(:create) expect(matcher.matches?(@controller)).to be true end it 'is false when any attributes are not allowed' do controller_for_resource_with_strong_parameters(action: :create) do params.require(:user).permit(:name) end matcher = described_class.new([:name, :admin]).in_context(self).for(:create) expect(matcher.matches?(@controller)).to be false end it 'is false when permit is not called' do controller_for_resource_with_strong_parameters(action: :create) matcher = described_class.new([:name]).in_context(self).for(:create) expect(matcher.matches?(@controller)).to be false end it 'requires an action' do controller_for_resource_with_strong_parameters matcher = described_class.new([:name]) expect { matcher.matches?(@controller) }. to raise_error(described_class::ActionNotDefinedError) end it 'requires a verb for non-restful action' do controller_for_resource_with_strong_parameters matcher = described_class.new([:name]).for(:authorize) expect { matcher.matches?(@controller) }. to raise_error(described_class::VerbNotDefinedError) end it 'works with routes that require extra params' do options = { controller_name: 'Posts', action: :show, routes: -> { get '/posts/:slug', to: 'posts#show' } } controller_for_resource_with_strong_parameters(options) do params.require(:user).permit(:name) end matcher = described_class.new([:name]). in_context(self). for(:show, verb: :get, params: { slug: 'foo' }) expect(matcher.matches?(@controller)).to be true end it 'works with #update specifically' do controller_for_resource_with_strong_parameters(action: :update) do params.require(:user).permit(:name) end matcher = described_class.new([:name]). in_context(self). for(:update, params: { id: 1 }) expect(matcher.matches?(@controller)).to be true end it 'does not raise an error when #fetch was used instead of #require (issue #495)' do controller_for_resource_with_strong_parameters(action: :create) do params.fetch(:order, {}).permit(:eta, :diner_id) end matcher = described_class.new([:eta, :diner_id]). in_context(self). for(:create) expect(matcher.matches?(@controller)).to be true end it 'tracks multiple calls to #permit' do sets_of_attributes = [ [:eta, :diner_id], [:phone_number, :address_1, :address_2, :city, :state, :zip] ] controller_for_resource_with_strong_parameters(action: :create) do params.require(:order).permit(sets_of_attributes[0]) params.require(:diner).permit(sets_of_attributes[1]) end matcher = described_class.new(sets_of_attributes[0]). in_context(self). for(:create) expect(matcher.matches?(@controller)).to be true matcher = described_class.new(sets_of_attributes[1]). in_context(self). for(:create) expect(matcher.matches?(@controller)).to be true end context 'stubbing params on the controller' do it 'still allows the original params to be set and accessed' do actual_user_params = nil actual_foo_param = nil controller_for_resource_with_strong_parameters(action: :create) do params[:foo] = 'bar' actual_foo_param = params[:foo] actual_user_params = params[:user] params.require(:user).permit(:name) end matcher = described_class.new([:name]). in_context(self). for(:create, params: { user: { some: 'params' } }) matcher.matches?(@controller) expect(actual_user_params).to eq('some' => 'params') expect(actual_foo_param).to eq 'bar' end it 'stubs the params during the controller action' do controller_for_resource_with_strong_parameters(action: :create) do params.require(:user) end matcher = described_class.new([:name]).in_context(self).for(:create) expect { matcher.matches?(@controller) }.not_to raise_error end it 'does not permanently stub params' do controller_for_resource_with_strong_parameters(action: :create) matcher = described_class.new([:name]).in_context(self).for(:create) matcher.matches?(@controller) expect { @controller.params.require(:user) }.to raise_error(::ActionController::ParameterMissing) end it 'prevents permanently stubbing params on error' do stub_controller_with_exception begin matcher = described_class.new([:name]).in_context(self).for(:create) matcher.matches?(@controller) rescue SimulatedError end expect { @controller.params.require(:user) }.to raise_error(::ActionController::ParameterMissing) end end end describe 'failure message' do it 'includes all missing attributes' do controller_for_resource_with_strong_parameters(action: :create) do params.require(:user).permit(:name, :age) end expect { expect(@controller).to permit(:name, :age, :city, :country).for(:create) }.to fail_with_message('Expected controller to permit city and country, but it did not.') end it 'includes all attributes that should not have been allowed but were' do controller_for_resource_with_strong_parameters(action: :create) do params.require(:user).permit(:name, :age) end expect { expect(@controller).not_to permit(:name, :age).for(:create) }.to fail_with_message('Expected controller not to permit name and age, but it did.') end end describe '#for' do context 'when given :create' do it 'POSTs to the controller' do controller = ActionController::Base.new context = build_context matcher = described_class.new([:name]).in_context(context).for(:create) matcher.matches?(controller) expect(context).to have_received(:post).with(:create, {}) end end context 'when given :update' do if rails_gte_4_1? it 'PATCHes to the controller' do controller = ActionController::Base.new context = build_context matcher = described_class.new([:name]).in_context(context).for(:update) matcher.matches?(controller) expect(context).to have_received(:patch).with(:update, {}) end else it 'PUTs to the controller' do controller = ActionController::Base.new context = build_context matcher = described_class.new([:name]).in_context(context).for(:update) matcher.matches?(controller) expect(context).to have_received(:put).with(:update, {}) end end end context 'when given a custom action and verb' do it 'calls the action with the verb' do controller = ActionController::Base.new context = build_context matcher = described_class.new([:name]). in_context(context). for(:hide, verb: :delete) matcher.matches?(controller) expect(context).to have_received(:delete).with(:hide, {}) end end end def stub_controller_with_exception controller_class = define_controller('Examples') do def create raise SimulatedError end end setup_rails_controller_test(controller_class) define_routes do get 'examples', to: 'examples#create' end end def build_context double('context', post: nil, put: nil, patch: nil, delete: nil) end class SimulatedError < StandardError; end end