Rails 5 testing controller unfiltered params - ruby-on-rails

I have recently upgraded my application to Rails 5 and when I am testing my controller I am getting the following error:
ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash.
My controller code looks like this:
def bid
widget_mode = params.include?(:widget)
if !#auction.published?
redirect_to '/' #go to the homepage
elsif #auction.third_party?
redirect_to #auction.third_party_bidding_url
elsif current_user && current_user.clerk? &&
!#auction.listing? &&
(#auction.items_count == 1 || params["section"] == "auction") &&
!widget_mode
redirect_to action: 'clerk', id: #auction.id, params: params.slice(:item, :section)
else
# Make sure the auction is in firebase
exists = #auction.rt_get('updated_at').body.to_i > 0 rescue false
#auction.queue_realtime_update unless exists
end
end
and my test code looks like this:
test "should redirect to powerclerk if multi item auction and params section = auction" do
sign_in users(:clerk)
a = auctions(:kenwood_dr)
assert a.items.count > 1, "Expected auction to have more than one item"
get :bid, params: {id: a.id, item: a.items.first.id, section: "auction"}
assert_redirected_to "/clerk/1?item=1&section=auction"
end
I tried adding:
params.permit(:item, :section, :id, :controller, :action, :widget)
to the beginning of my bid controller method and that didn't make a difference. Any insight would be appreciated.

This error occurs when calling to_h or to_hash on an instance of ActionController::Parameters that doesn't have any permitted keys (documentation).
Since ActionController::Parameters#slice returns an instance of the same, this code does not give you a hash like it would seem: params.slice(:item, :section).
In most cases you can use permit instead of slice on parameters instances. If you ever want to bypass the safe access whitelisting of ActionController::Parameters you can use permit! and use ActionController::Parameters#slice, or if you want to convert to a hash without sanitization you can use to_unsafe_h.

I ended up solving this by switching:
redirect_to action: 'clerk', id: #auction.id, params: params.slice(:item, :section)
to
redirect_to action: 'clerk', id: #auction.id, params: params.permit(:item, :section)

Related

Error while iterating through params

I get an error when I try to iterate through params
When running code below:
def create_score
#quiz = Test.find_by(password: session[:test_password])
#points = 0
#quiz.tasks.each_with_index do |task, index|
#task = Task.find_by(id: task)
#points += #task.score if #task.correct_answers.to_s == send("params[:test][:task#{index}]")
end
#score = Score.new(user_id: 2, name: "Test1", points: #points)
if #score.save
redirect_to root_url
else
redirect_to signup_path
end
end
I get:
undefined method `params[:test][:task0]' ...
at the
#points += #task.score if #task.correct_answers.to_s == send("params[:test][:task#{index}]")
Which means that it has problem with send method
Parameters look like this:
{"utf8"=>"✓",
"authenticity_token"=>"8h7rtv2yWio11DFo6kBKutdZl7RDBBaTrt7e8qel8fR5R5XsoXRhRrBeDQPPoZeuBlZ7N5PmqCxik06Z/gQLZQ==",
"test"=>{"task0"=>["4"], "task1"=>["0"], "task2"=>["10"]},
"commit"=>"Zakończ test",
"locale"=>"pl"}
Which means that there is params[:test][:task0], but still for some reason it fires an error, but I don't really know why. Any ideas why this happens?
You want to index with dynamic key, not call a method dynamically. Aka:
params[:test]["task#{index}"]
Should do. Note that params are have indifferent access for strings and symbols.
To give you more food for thought, here is how you might have done the same with #send:
params[:test].send(:[], "task#{index}")
And here is how to define a method that would have the name you are trying to call:
define_method("params[:test][:task#{index}]") do
puts 'WTF'
end
You're calling your params with a symbol but instead you should use a string.
This means you should use one of the following approaches:
params["test"]["task0"]
or
params[:test.to_s][:task0.to_s]
Hope that helped :)
You should use params object something likeparams[:test][:task]) instead of send("params[:test][:task#{index}]".

Strong parameters in test issue

Ruby 2.1.1p76 on Rails 4.1.1.
Please check out my controller:
def update
begin
current_user.update_settings user_settings_params unless params[:user_setting].blank?
current_user.update_attribute :district_id, params[:user][:district_id] unless params[:user].blank? || params[:user][:district_id].blank?
flash[:success] = "Preferencje zostały zaktualizowane"
redirect_to subscription_index_path
rescue UserLevelException => exception
flash[:alert] = "Sprytnie, za karę zostałeś wylogowany ;)"
session[:user_id] = nil
redirect_to root_path
return
end
end
private
def user_settings_params
params.require(:user_setting).permit(
:inquiry_subject, :inquiry_body,
:offer_subject, :offer_body,
:only_companies_with_email,
{:district_ids => []},
# {:district_ids => params[:user_setting][:district_ids].try(:keys)},
:delivery_address,
)
end
See the commented line? In the form above - user_settings_params will not return :district_ids array of ids, and this is fine, since I can use the line below instead to have them (got it from guides).
The problem I have is when running this test:
test 'should set user level10 districts' do
user = login_user :paid10
post :update, :user_setting => {:district_ids => [districts(:zachodniopomorskie).id, districts(:slaskie).id]}
assert_equal nil, flash[:alert]
assert_equal 'Preferencje zostały zaktualizowane', flash[:success]
db_user_districts = User.find(user.id).settings.districts.all
assert db_user_districts.include? districts(:zachodniopomorskie)
assert db_user_districts.include? districts(:slaskie)
assert_equal 2, db_user_districts.count
end
It passes. When debugging user_settings_param has :district_ids available as if strong parameters were disabled or something. I wanted to submit an issue to rails but most probably I'm doing something wrong and can't figure it out.
I've found it - it was because of quirky way I was creating checkboxes for HABTM
= check_box_tag "user_setting[district_ids][#{district.id}]", district.id, user.settings.district_ids.include?(district.id)
= label_tag "user_setting[district_ids][#{district.id}]", district.name
For no particular reason I've inserted ids into params keys AND values. And because of that those were passed to params object as hash. In test though those were sent as array. So it was the view to blame.
= check_box_tag "user_setting[district_ids][]", district.id, user.settings.district_ids.include?(district.id)
= label_tag "user_setting[district_ids][]", district.name

Rspec testing a controller search method

I'm trying to test the behavior of a custom search method in my controller:
#RecordingsController
def search
# raise params.inspect
#search = params[:search]
searches = []
searches2 = []
for n in 1..5
searches << #search["date(#{n}i)"].to_i
searches2 << #search["date2(#{n}i)"].to_i
end
start_date = date_format(*searches)
end_date = date_format(*searches2)
conditions = []
conditions << "agent like '%#{#search["agent"]}%'" unless #search["agent"].blank?
conditions << "phone like '%#{#search["phone"]}%'" unless #search["phone"].blank?
conditions << "date between '#{start_date}' and '#{end_date}'"
#recordings = Recording.where(conditions.join(" AND ")).order('date ASC')
if #recordings.blank?
redirect_to("/", alert: "No results were found for this search. Please try again.")
else
render "recordings/search"
end
end
using the following layout:
#recordings_controller_spec.rb
describe RecordingsController do
describe "POST #search" do
context "with valid attributes" do
it "assigns a new search to #search" do
search = #recording_search
get :search, #recording_search
assigns(:search).should eq(search)
end
it "populates an array of recordings"
it "renders the :search view"
end
end
end
The furthest I've gotten is trying to build a hash that mimics what my params hash would be for the form
#params hash
params = {"search" => { "date_1i" => "2012", "date_2i" => "1", ... "date2_5i" => "00" } }
where date_#{n}i is the start date [year, month, day, hour, minute], and date2_#{n}i is the end date. I'm trying to follow the answer posted here, mimicking the params hash with just a regular hash. As you can see from my controller, I don't actually pass parameters to my #search method. Should I be? Or is there a way to mock a params hash in an rspec test and determine if my #search, #recordings, and redirect_to/render variables/actions are being performed? I'm already kind of testing the render/redirect in my request spec, but I'd like to fully test this method if I can.
You should be able to generate a controller spec that GETs the search action with a given set of parameters. This will cause those parameters to be available to the params hash. You can then verify how the search is constructed and which results are returned.
describe RecordingsController do
describe '#search' do
it 'should return results' do
get :search, "search" => { "date_1i" => "2012", "date_2i" => "1", ... "date2_5i" => "00" }
response.should be_ok
#recordings.map(&:name).should == ['expected1', 'expected2']
end
end
end
This example executes a search with some search criteria as query parameters, verifies the response is successful (http 200 status), and then extracts the list of recordings returned and tries to map them to a list of friendly recording names (you can use any key on this model) to compare them to an expected list of results.
It'll be easier to write/test this code if you separate the concerns in this controller - you could write a helper that processes the query parameters and builds a search filter, which it then passes to the Recording model in the controller:
class RecordingController
def search
#search_filter = SearchFilter.for_params(params[:search])
#recordings = Recording.where(#search_filter).order('date ASC')
render "recordings/search"
end
end
class SearchFilter
# Return search params as a hash for given request params hash
def self.for_params(params)
...
end
end
This would let you write a unit test for the logic that generates search filters and only verify that the controller is doing the more simple operation of passing information between the search logic and the Recording model collection. I'd also recommend moving your logic about displaying empty results into the view on the results page and out of the controller.

What's the right way to modify the params passed to controller in rails 3.1.0?

In our Rails 3.1.0 app, we need to modify params passed to rfq controller in create and update. For example, we want to record the current user id under input_by_id. What we did was:
#rfq.input_by_id = session[:user_id]
It worked as expected. Also when need_report field is false, then report_language field should be nil. We decide to add the following line in rfq controller to make sure the nil is passed to report_language when need_report is false:
#rfq.report_language = nil unless params[:need_report]
However this addition causes the rspec case failure (in create/update of the controller) because of the data validation failure. However when we fire up the app, it behaves fine without saving the report_language when need_report is false. I am wondering if the line above is not the right way to use params[:need_report] for #rfq updating.
Thanks so much.
UPDATE:
Controller code:
def create
if has_create_right?
#rfq = Rfq.new(params[:rfq], :as => :roles_new )
#rfq.input_by_id = session[:user_id]
#save sales_id selected
if sales? && member? && !team_lead?
#rfq.sales_id = session[:user_id]
end
#view page may carry the hidden report language even if need_report == false
#rfq.report_language = nil unless params[:need_report]
#save into join table rfqs_standards
params[:rfq][:standard_ids].each do |sid|
#rfq.standards << Standard.find(sid.to_i) if !sid.nil? && sid.to_i > 0
end unless params[:rfq][:standard_ids].nil?
#save into join table rfqs_test_items
params[:rfq][:test_item_ids].each do |tid|
#rfq.test_items << TestItem.find(tid.to_i) if !tid.nil? && tid.to_i > 0
end unless params[:rfq][:test_item_ids].nil?
if #rfq.save!
redirect_to URI.escape("/view_handler?index=0&msg=RFQ saved!")
else
flash.now[:error] = "RFQ not saved!"
render 'new'
end
else
redirect_to URI.escape("/view_handler?index=0&msg=No rights!")
end
end
Test case failed after addition of #rfq.report_language = nil unless params[:need_report]
it "should be successful for corp head" do
session[:corp_head] = true
session[:user_id] = 1
s = Factory(:standard)
rfq = Factory.attributes_for(:rfq, :need_report => true, :report_language => 'EN')
rfq[:standard_ids] = [s.id] # attach standard_id's to mimic the POST'ed form data
get 'create', :rfq => rfq
#response.should redirect_to URI.escape("/view_handler?index=0&msg=RFQ saved!")
response.should render_template('new')
end
the problem ist that you are simply not looking at the right value.
get 'create', :rfq => rfq will result in a params-hash like {:rfq => {...}}
so you need to #rfq.report_language = nil unless params[:rfq][:need_report] == 'true'

Rails validations running against original record during update

I'm trying to figure out an inconsistency between what's happening in a functional test and what is happening in my development environment. I have a custom validation method unique_entry that is essentially a specialized version of validates_uniqueness_of. It looks like this:
def unique_entry
matched_entry = Entry.first(:conditions => ['LOWER(field_one) = LOWER(?) AND LOWER(field_two) = LOWER(?)', self.field_one, self.field_two])
errors.add_to_base('Duplicate detected') if matched_entry && (matched_entry.id != self.id)
end
The update action in the controller is very basic:
def update
if #entry.update_attributes(params[:entry])
flash.now[:success] = 'Success'
render :action => 'show'
else
flash.now[:error] = 'Error'
render :action => 'edit'
end
end
This works just fine when I'm creating a new record. When I update a record, however, I get inconsistent behavior. If I test it from a browser in my development environment, it correctly renders the edit action with an error message, but in my functional test, it accepts the update as successful. Here is the test:
test "should not update entry and should render edit view if invalid update" do
put :update, { :id => 1, :field_one => 'new_value', :field_two => 'new_value' } # 'new values' are the same as another existing record to trigger the duplication check
assert_template :edit
assert_not_nil flash[:error]
end
I looked at the test log and discovered that the values unique_entry is using are the record's original values instead of the values it should be attempting to update with. That is, the first line of unique_entry generates an SQL query like this:
SELECT * FROM "entries" WHERE (LOWER(field_one) = LOWER('original_value_of_field_one') AND LOWER(field_two) = LOWER('original_value_of_field_two')) LIMIT 1
What am I missing here? Why do my validations seem to be running against the original record instead of the new values only in the test environment?
In your test, shouldn't there be some reference to :entry, since that is what you are looking for in the controller params[:entry] ?

Resources