Failing RSpec test with strong parameters - ruby-on-rails-5

I am migrating my Rails 4 app (still using protected attributes gem) to Rails 5.1.4. In the course of this action, I need to rewrite a lot of code to replace protected attributes with strong parameters.
I am currently stuck on one specific controller where my RSpec tests fail, and I don't know how to implement the controller and test logic such that things are correct and tests pass.
The app has an admin backend where users can add (and thus upload) photos to an album. The respective Admin::PhotosController handles the photos of an album.
Here's the relevant exerpt from my app:
def create
# #organizer_account is set by an before_filter
#album = #organizer_account.albums.find_by_id(params[:album_id])
#photo = #album.photos.new(photo_params)
#photo.organizer_account_id = #organizer_account.id
authorize! :create, #photo
respond_to do |format|
if #photo.save
format.html {
render :json => [#photo.to_jq_file].to_json, :content_type => 'text/html', :layout => false
}
format.json {
files = [ #photo.to_jq_file ]
render :json => {:files => [#photo.to_jq_file] }, :status => :created, :location => admin_album_photo_path(#album, #photo)
}
else
format.html {
render action: "new"
}
format.json {
render json: #photo.errors, status: :unprocessable_entity
}
end
end
end
I have defined the following strong parameters:
private
def photo_params
params.require(:photo).permit(:id, :album_id, :organizer_account_id, :file)
end
The failing RSpec test is as follows:
require 'spec_helper'
describe Admin::PhotosController, :type => :controller do
render_views
describe "post 'create'" do
describe "with valid parameters" do
before(:each) do
#organizer_account = FactoryBot.create(:organizer_account)
#user = FactoryBot.create(:user)
#user.organizer_account_id = #organizer_account.id
#user.add_role :admin, #organizer_account
#user.save
sign_in #user
#album = #organizer_account.albums.create(:title => "Album 1")
#photo_attrs = FactoryBot.attributes_for(:photo)
request.env["HTTP_REFERER"] = new_admin_album_path
controller.request.host = #organizer_account.subdomain + ".lvh.me"
end
it "should create a new photo record", :focus => true do
lambda {
post :create, params: {:photo => #photo_attrs, :album_id => #album.id }
}.should change(#organizer_account.albums.find_by_id(#album.id).photos, :count).by(1)
end
end
end
end
I strongly assume that the issue is in parameters are a) passed
post :create, params: {:photo => #photo_attrs, :album_id => #album.id }
and then processed
#photo = #album.photos.new(photo_params)
While the params hash passed by the test has all the required entries
params: {"photo"=><ActionController::Parameters {"file"=>[#<ActionDispatch::Http::UploadedFile:0x00000010dd7560 #tempfile=#<Tempfile:C:/Users/PATRIC~1/AppData/Local/Temp/RackMultipart20180520-11424-avge07.gif>, #original_filename="image6.gif", #content_type="image/gif", #headers="Content-Disposition: form-data; name=\"photo[file][]\"; filename=\"image6.gif\"\r\nContent-Type: image/gif\r\nContent-Length: 46844\r\n">]} permitted: false>, "album_id"=>"1561", "controller"=>"admin/photos", "action"=>"create"}
the photo_params is empty:
photo_params: {}
Update #1: Definition of factory for photo
FactoryBot.define do
factory :photo, :class => Photo do
file Rack::Test::UploadedFile.new(Rails.root + 'spec/fixtures/photos/apfelkuchen.jpg', "image/jpg")
end
end
Update #2: Photo model with file attachment and image processing config
class Photo < ActiveRecord::Base
require 'rmagick'
include Magick
belongs_to :album
belongs_to :organizer_account
before_destroy { |photo| photo.file.destroy }
validates :album_id, :presence => true
validates :organizer_account_id, :presence => true
has_attached_file :file,
# The following tyles and convert options lead to breaking RSpec tests. If commented, RSpec tests pass.
:styles => {
:mini => "50x50#",
:thumb => "160x160#",
:large => "1200x1200>"
},
:convert_options => {
:mini => "-quality 75 -strip",
:thumb => "-quality 75 -strip"
}
validates :file, :presence => true
end

Related

CanCan AccessDenied Error thrown for Update and Destroy despite ability set

I am trying to get some controller tests passing but when they hit the update and delete action, CanCan keeps throwing the Access Denied error despite being set in the abilities. These errors only seem to occur for members, as admins work fine.
Abilities.rb
def initialize(user)
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :member
can :manage, PaymentMethod, :user_id => user.id
end
end
User_Factory
FactoryGirl.define do
factory :user do
sequence(:first_name) { |n| "John_#{n}" }
sequence(:last_name) { |n| "Rambo_#{n}" }
sequence(:email) { |n| "john_rambo_#{n}#example.com" }
sequence(:username) { |n| "john_rambo_#{n}" }
date_of_birth "03/12/1982"
password 'password'
password_confirmation 'password'
picture_url File.open('spec/support/pictures/test.png')
address
factory :member do
sequence(:last_name) { |n| "Member_#{n}" }
roles :member
end
end
end
Controller_Spec.rb
describe "PUT /api/users/:user_id/payment_method/:id" do
before(:each) do
#user = FactoryGirl.create(:member)
sign_in_user #user
#payment_method = FactoryGirl.create(:credit_card, {:user_id => #user.id})
end
it "updates a users payment method" do
attr_to_change = {
brand: "mastercard",
user_id: #user.id,
id: #payment_method.id
}
put :update, attr_to_change
response.status.should == 200
JSON.parse(response.body)["payment_method"]["brand"]
.should == "mastercard"
end
end
describe "DELETE /api/users/:user_id/payment_methods/:id" do
before(:each) do
#user = FactoryGirl.create(:member)
sign_in_user #user
#payment_method = FactoryGirl.create(:credit_card, {:user_id => #user.id})
end
it "destroys a users payment method" do
delete :destroy, {:user_id => #user, :id => #payment_method.id}
response.status.should == 200
end
end
Controller
class Api::PaymentMethodsController < Api::ApiController
before_filter :clean_params, only: [:update, :create]
def index
#user = User.find(params["user_id"])
render json: #user.payment_methods
end
def update
pm_id = params.delete("id")
params.delete("user_id")
#payment_method = PaymentMethod.find(pm_id)
if #payment_method.update_attributes(params)
return render status: 200, json: #payment_method, root: :payment_method
else
return render status: 422, json: {success: false, errors: #payment_method.errors.full_messages.map{|error|{error: error}}}
end
end
def create
#payment_method = PaymentMethod.create_payment_method(params)
if #payment_method
render json: #payment_method, root: :payment_method
else
return render status: 422, json: {success: false, errors: #payment_method.errors.full_messages.map{|error|{error: error}}}
end
end
def destroy
#payment_method = PaymentMethod.find(params["id"])
if #payment_method.destroy
return render status: 200, json: {:message => "PaymentMethod Destroyed"}
else
return render status: 422, json: {success: false, errors: #payment_method.errors.full_messages.map{|error|{error: error}}}
end
end
def clean_params
["controller", "action"].each do |delete_me|
params.delete(delete_me)
end
params
end
end
ApiController
class Api::ApiController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource
rescue_from CanCan::AccessDenied do |exception|
return render :status => 401, :json => {:success => false, :errors => [exception.message]}
end
end
Result of calling the delete action in the test:
delete :destroy, {:user_id => #user, :id => #payment_method.id}
#<ActionController::TestResponse:0x007fb999cf0080
#blank=false,
#block=nil,
#body=
["{\"success\":false,\"errors\":[\"You are not authorized to access this page.\"]}"],
#cache_control={},
#charset="utf-8",
#content_type=application/json,
#etag=nil,
#header={"Content-Type"=>"application/json; charset=utf-8"},
#length=0,
#request=
The other actions seem to work but for Update and Destroy, I keep getting that AccessDenied error. Any idea what I could be doing wrong?
Your ApiController appears to be namespaced, you'll need to change the before_filter to the following:
before_filter :authenticate_api_user!
Then, you need to adjust Cancan to use the current_api_user instead of current_user:
def current_ability
#current_ability ||= ::Ability.new(current_api_user)
end
These links will help:
http://rubydoc.info/github/plataformatec/devise/master/Devise/Controllers/Helpers
https://github.com/ryanb/cancan/issues/656
http://mikepackdev.com/blog_posts/12-managing-devise-s-current-user-current-admin-and-current-troll-with-cancan

Got error while copying existing record in new one in rails3?

Hi i am creating an action to copy an existing record in a new one in rails3 which is correctly populates the values from previous record to new one but at the time i submit the form it gives the error. Here is my code sample
class SalaryStructuresController < ApplicationController
def new
#salary_structure = SalaryStructure.new
#salary_structure.salary_structure_line_items.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #salary_structure }
end
end
def create
#salary_structure = SalaryStructure.new(params[:salary_structure])
#salary_structure.company_id = current_company.id
#salary_structure.created_by = current_user.id
respond_to do |format|
if #salary_structure.valid?
#salary_structure.save_with_variable_payheads
flash[:success]= "Salary structure successfully created."
format.html { redirect_to(#salary_structure) }
format.xml { render :xml => #salary_structure, :status => :created, :location => #salary_structure }
else
format.html { render :action => "new" }
format.xml { render :xml => #salary_structure.errors, :status => :unprocessable_entity }
end
end
end
#action to clone the salary structure
def copy
#payheads = current_company.payheads
#users = current_company.users
#source_salary_structure = SalaryStructure.find(params[:id])
#salary_structure = SalaryStructure.new(#source_salary_structure.attributes)
#source_salary_structure.salary_structure_line_items.each do |line_item|
salary_item = SalaryStructureLineItem.new(line_item.attributes)
#salary_structure.salary_structure_line_items << salary_item
end
render :action => "new"
end
end
My model:
class SalaryStructure < ActiveRecord::Base
has_many :salary_structure_line_items
belongs_to :user
# has_many :payheads
accepts_nested_attributes_for :salary_structure_line_items, :reject_if => lambda {|p| p[:payhead_id].blank? && p[:amount].blank? }, :allow_destroy => true
#validation
validates_presence_of :effective_from_date, :for_employee
validates_presence_of :salary_structure_line_items
validates_associated :salary_structure_line_items
attr_accessible :id, :effective_from_date, :salary_structure_line_items_attributes, :amount, :total, :pay_head_type, :for_employee, :pay_head, :created_by, :updated_at, :created_at, :company_id,
:salary_structure_line_items_attributes
end
When i submit the form (press save) i got the error on salary_structure_id:
ActiveRecord::RecordNotFound in SalaryStructuresController#create
Couldn't find SalaryStructureLineItem with ID=11 for SalaryStructure with ID=
Even in parameters salary_structure_id is present :
"commit"=>"Save",
"salary_structure"=>{"salary_structure_line_items_attributes"=>{"0"=>{"amount"=>"3000.0",
"_destroy"=>"",
"salary_structure_id"=>"4",
"id"=>"11",
"payhead_id"=>"1"},
"1"=>{"amount"=>"500.0",
"_destroy"=>"",
"salary_structure_id"=>"4",
"id"=>"12",
"payhead_id"=>"2"}
i am unable to trace where i am missing something, please help me.
I have created clone file in very easy way here i have created a new action in my controller
def copy_salary_structure
#users = current_company.users.without_salary_structure
#payheads = current_company.payheads.where(:optional => false)
#old_salary_structure = SalaryStructure.find_by_id(params[:id])
#salary_structure = SalaryStructure.new
#salary_structure.company_id = #old_salary_structure.company_id
#salary_structure.created_by = current_user.id
#old_salary_structure.salary_structure_line_items.each do |line_item|
salary_structure_line_item = SalaryStructureLineItem.new(
:payhead_id => line_item.payhead_id,
:amount => line_item.amount
)
#salary_structure.salary_structure_line_items << salary_structure_line_item
end
end
and i have create a view form with same name from which i can review the record and then save it easily

Rspec factory_girl ActiveMerchant undefined method `credit_card='

I'm attempting to test the creation of an Reservation model that involves processing a payment with ActiveMerchant upon creation.
The initial setup for payment processing involved following the ActiveMerchant Railscasts. Payments are working fine within the app. (http://railscasts.com/episodes/145-integrating-active-merchant)
I've tried creating the credit_card object within the Reservation factory and within it's own ":valid_credit_card" factory...
The basic test is just attempting to verify the Reservation can be created.
Test results:
1) Reservation should have a valid factory
Failure/Error: #current_reservation = Factory.create(:reservation)
NoMethodError:
undefined method `credit_card=' for #<Reservation:0xb5f6173c>
# ./spec/models/reservation_spec.rb:11
The Reservation belongs_to a user and has_many rooms through reservation_sets
Factory.define :reservation do |f|
f.association :user
f.rooms { |a| [a.association(:room)] }
f.arrival Time.now + 2.weeks
f.nights 2
f.phone "555-123-1234"
f.credit_card :valid_credit_card
end
Factory.define :valid_credit_card, :class => ActiveMerchant::Billing::CreditCard do |f|
expiration_date = Time.zone.now + 1.year
f.type "visa"
f.number "4111111111111111"
f.verification_value "333"
f.month expiration_date.strftime("%m")
f.year expiration_date.strftime("%y")
f.first_name "Bob"
f.last_name "Smith"
end
And the spec/models/reservation_spec.rb. Using #credit_card Factory.build causes errors about "saving" the credit_card.
If I remove the line f.credit_card :valid_credit_card I get the NoMethodError for :month
even though :month is listed in attr_accessor. The creation of a reservation within the app does work.
1) Reservation should have a valid factory
Failure/Error: #current_reservation = Factory.create(:reservation)
NoMethodError:
undefined method `month' for nil:NilClass
describe Reservation do
before :each do
#smith = Factory.create(:user)
#room = Factory.create(:room)
##credit_card = Factory.build(:valid_credit_card)
end
it "should have a valid factory" do
#current_reservation = Factory.create(:reservation)
#current_reservation.should be_valid
end
end
What am I overlooking / doing incorrectly...?
Reservation model excerpts
class Reservation < ActiveRecord::Base
# relationships
belongs_to :user
has_many :reservation_sets,
:dependent => :destroy
has_many :rooms,
:through => :reservation_sets
has_many :transactions,
:class_name => 'ReservationTransaction',
:dependent => :destroy
attr_accessor :card_number, :card_verification, :card_expires_on, :card_type, :ip_address, :rtype, :month, :year
# other standard validations
validate :validate_card, :on => :create
# other reservation methods...
# gets paid upon reservation creation
def pay_deposit
# Generate active merchant object
ReservationTransaction.gateway =
ActiveMerchant::Billing::AuthorizeNetGateway.new({
:login => rooms[0].user.gateway_login,
:password => rooms[0].user.gateway_password
})
response = ReservationTransaction.gateway.purchase(deposit_price_in_cents, credit_card, purchase_options)
t = transactions.create!(:action => "purchase", :amount => deposit_price_in_cents, :response => response)
if response.success?
update_attribute(:reserved_at, Time.now)
# update state
payment_captured!
else
transaction_declined!
errors.add :base, response.message
end
t.card_number = credit_card.display_number
t.save!
response.success?
end
def validate_card
unless credit_card.valid?
credit_card.errors.full_messages.each do |message|
errors.add :base, message #_to_base message
end
end
end
def credit_card
#credit_card ||= ActiveMerchant::Billing::CreditCard.new(
:type => card_type,
:number => card_number,
:verification_value => card_verification,
:month => card_expires_on.month,
:year => card_expires_on.year,
:first_name => first_name,
:last_name => last_name
)
end
And the create action from the Reservation Controller
def create
#reservation = Reservation.new(params[:reservation])
#reservation.arrival = session[:arrival]
#reservation.nights = session[:nights]
#reservation.number_kids = session[:number_kids]
#reservation.number_adults = session[:number_adults]
session[:creating_reservation] = 1
#reservation.user_id = #reservation.rooms[0].user_id
session[:owner] = #reservation.user_id
#rooms = Room.all
#reservation.ip_address = request.remote_ip
# get room owner...
#owner = User.find(#reservation.user_id)
respond_to do |format|
if #reservation.save
if #reservation.pay_deposit
#set cc...
#reservation.transactions[0].card_number = #reservation.send(:credit_card).display_number
ReservationMailer.reservation_created(#reservation).deliver
ReservationMailer.reservation_notice(#reservation).deliver
session[:arrival] = nil
session[:reservation_id] = #reservation.id
if #owner
thanks_path = "#{#owner.permalink}/reservations/#{#reservation.id}"
else
thanks_path = #reservation
end
format.html { redirect_to #reservation, :notice => 'Reservation was successfully created.' }
format.json { render :json => #reservation, :status => :created, :location => #reservation }
# also trigger email sending or wherever that is
# receipt email and order notification
#
else
# set flash or show message problem w/ transaction
format.html { render :action => "new" }
end
else
format.html { render :action => "new" }
format.json { render :json => #reservation.errors, :status => :unprocessable_entity }
end
end
end
It looks like you are trying to assign credit_card a value, but you don't really have a class accessor. So where you are trying to call f.credit_card :valid_credit_card isn't going to work.
I would remove the f.credit_card :valid_credit_card from your factory and look into using rspec stubs, then you could do something like the following in your rspec test:
mock_cc = ActiveMerchant::Billing::CreditCard.new(
:type => card_type,
:number => card_number,
:verification_value => card_verification,
:month => card_expires_on.month,
:year => card_expires_on.year,
:first_name => first_name,
:last_name => last_name
)
Reservation.stub(:credit_card).and_return(mock_cc)
This would make it so when your model called credit_card it would return a mocked object.

NoHandlerError with Rails3 and Paperclip

So this is my first question on StackOverflow.
I'm trying to implement Paperclip on Rails 3.2.3, and after clicking "submit" to create a profile with an uploaded image, I get:
Paperclip::AdapterRegistry::NoHandlerError in UsersController#update
No handler found for "Screen Shot 2012-09-01 at 11.03.43 AM.png"
My server log reads,
Paperclip::AdapterRegistry::NoHandlerError (No handler found for "Screen Shot 2012-09-01 at 11.03.43 AM.png"):
app/controllers/users_controller.rb:65:in block in update'
app/controllers/users_controller.rb:64:inupdate'
In my User model, I have
attr_accessible :avatar
has_attached_file :avatar,
:styles => {
:large => "500x500>",
:medium => "213x213>", # profile image
:thumb => "50x50>",
:smaller => "30x30>" },
:processors => [:cropper],
# tells paperclip how to crop the image
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml", # TODO
:path => ":attachment/:id/:style/:basename.:extension",
:bucket => 'eventsbucket'
The error persists whether I include the S3 info or not. In my migration I have,
class AddAvatarColumnsToUsers < ActiveRecord::Migration
def self.up
add_attachment :users, :avatar
end
def self.down
remove_attachment :users, :avatar
end
end
Lastly, in my Users controller update action, I have
def update
respond_to do |format|
if #user.update_attributes(params[:user])
sign_in #user
format.html { redirect_to #user, notice: 'Profile Successfully Updated' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
In my Gemfile I have gem "paperclip", "~> 3.1.4" (UPDATE: I've since pulled Paperclip straight from thoughtbot and problem persists). I've run bundle install. I've run db:migrate. I've read this StackOverflow entry, but the error persists whether I include "multipart => true" or not. When I tried the Emerson Lackey Tutorial, it worked up to the point where he tried to display the output of the "5.times..." command.
I'm interested in both getting Paperclip to work and understanding just what a "NoHandlerError" is and how to avoid it in the future.
Did you try the railscasts episode # 134 ?
http://railscasts.com/episodes/134-paperclip?view=asciicast

Rails Devise 2.0 own Log-out action

I use Devise and I want to do my logout action.
What I want to do is, that when I log out, I want to create a own JSON object to return. At this time, after I logt out, I get all my root articles.
How can I write my own destory action like I have found the create action?
class SessionsController < Devise::SessionsController
def create
resource = warden.authenticate!(:scope => resource_name, :recall => :failure)
return sign_in_and_redirect(resource_name, resource)
end
def sign_in_and_redirect(resource_or_scope, resource=nil)
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource ||= resource_or_scope
sign_in(scope, resource) unless warden.user(scope) == resource
return render :json => {:success => true, :redirect => stored_location_for(scope) || after_sign_in_path_for(resource)}
end
def failure
return render:json => {:success => false, :errors => ["Login failed."]}
end
end
And my Routes in routes.rb
devise_for :users, :controllers => {:session => "sessions"} do
get "/users/sing_out" => "devise/sessions#destroy"
end
this is the destroy method of the sessions-controller.
you should be able to customize it to your needs. i think that it would be wiser to add another action and implementing your custom behavior there, as this will be less likely to cause unexpected errors with upgrading devise in the future.
# DELETE /resource/sign_out
def destroy
redirect_path = after_sign_out_path_for(resource_name)
signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
set_flash_message :notice, :signed_out if signed_out
# We actually need to hardcode this as Rails default responder doesn't
# support returning empty response on GET request
respond_to do |format|
format.any(*navigational_formats) { redirect_to redirect_path }
format.all do
method = "to_#{request_format}"
text = {}.respond_to?(method) ? {}.send(method) : ""
render :text => text, :status => :ok
end
end
end