testing PUT update is not working on RSPEC ( Ruby on Rails) - ruby-on-rails-3

So I'm testing one of my controller in RSPEC and I'm trying to test the whole update shenanigans. This is what i have in the RSPEC
describe 'PUT update' do
before :each do
#user = Factory(:user,name: "Lawrence Smith")
end
context "valid attributes" do
it "located the requested #user" do
put :update, :id => #user, user: Factory.attributes_for(:user)
assigns(:user).should eq(#user)
end
it "changes #user's attributes" do
put :update, :id=> #user.id, user: Factory.attributes_for(:user, name: "Larry Smith")
#user.reload
#user.name.should eq("Larry Smith")
end
end
And this is what i have in the controller
def update
#update and save
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
sign_in #user
format.json{render :json => #user ,:status => :created, :location => #user }
else
format.json { render :json => #user.errors, :status => :unprocessable_entity }
end
end
end
So apparently the the #user.reload doesn't really refresh the ActiveRecord, as my errors showed that the name has not been changed at all. Any idea what is wrong?

I don't know how you can pass object in get or post parameters. Anyways try like this
describe 'PUT update' do
before :each do
#user = Factory(:user,name: "Lawrence Smith")
end
context "valid attributes" do
it "located the requested #user" do
put :update, :id => #user, user: Factory.attributes_for(:user)
assigns(:user).should eq(#user)
end
it "changes #user's attributes" do
put :update, :id=> #user.id, :user_name => Factory.attributes_for(:user, name: "Larry Smith").name
#user.reload
#user.name.should eq("Larry Smith")
end
end
and this in controller
def update
#update and save
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(:name => params[:user_name])
sign_in #user
format.json{render :json => #user ,:status => :created, :location => #user }
else
format.json { render :json => #user.errors, :status => :unprocessable_entity }
end
end
I think you are getting what I explained here.
Thanks

Related

Failing RSpec test with strong parameters

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

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

How to set custom flash with respond_to in Rails

In respond_to you can set flash[:notice] like this
respond_to do |format|
format.html { redirect_to photo_path(photo), :notice => 'The photos was saved') }
format.xml { render :xml => photo, :status => :created}
end
I am trying to set flash[:success] with :success => "yay" but it doesn't work.
Am I doing something wrong?
You should use redirect_to differently :
redirect_to photo_path(photo), :flash => { :success => "Yeepee!" }
The only flashes you can use directly are
:notice
:alert
:error
Hope that helps
From Rails 4, you can directly use :success in redirect_to.
Just add this line:
# in app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
[...]
add_flash_types :error, :success, :info
[...]
Without this line, in respond_to, :notice produces flash, but :success doesn't work.
Hat tip to Milan Mondal's post for this!

Creating a Friend While On Their Profile Page

I want a User(x) to be able to add another User(y) as a friend while User(x) is on User(y's) Profile Page. I set up a has_many_through and everything works except that I can only add a friend from the User Index View. Thank you in advance...The code is below:
Also:
I wanted to place the "friend" link on the view/profile/show.html.erb. When I added #users = User.all to the existing profiles_controller.rb I received the error - undefined method friendships' for nil:NilClass. When I replaced #user = User.find(params[:id]) with #users = User.all I received the error - NoMethodError in Profiles#show... undefined methodinverse_friends' for nil:NilClass
The Code that works in UserIndexView but not ProfileShowView:
% for user in #users %>
<div class="user">
<p>
<strong><%=h user.email %> <%= user.id %></strong>
<%= link_to "Add Friend", friendships_path(:friend_id => user), :method => :post%>
<div class="clear"></div>
</p>
</div>
<% end %>
The following error occurs:
NoMethodError in Profiles#show
Showing /Users/mgoff1/LOAP_1.2.2/app/views/profiles/show.html.erb where line #13 raised:
undefined method `each' for nil:NilClass
Extracted source (around line #13):
10:
11:
12:
13: <% for user in #users %>
14: <div class="user">
15: <p>
16: <strong><%=h user.email %> <%= user.id %></strong>
. . .
app/views/profiles/show.html.erb: 13:in`_app_views_profiles_show_html_erb___2905846706508390660_2152968520'
app/controllers/profiles_controller.rb:19:in `show'
The code to the rest is below.
friendship.rb
class Friendship < ActiveRecord::Base
attr_accessible :create, :destroy, :friend_id, :user_id
belongs_to :user
belongs_to :friend, :class_name => "User"
end
user.rb
class User < ActiveRecord::Base
has_many :friendships
has_many :friends, :through => :friendships
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
has_many :inverse_friends, :through => :inverse_friendships, :source => :user
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :profile_attributes
# attr_accessible :title, :body
has_one :profile
accepts_nested_attributes_for :profile
before_save do | user |
user.profile = Profile.new unless user.profile
end
end
friendships_controller.rb
class FriendshipsController < ApplicationController
def create
#friendship = current_user.friendships.build(:friend_id => params[:friend_id])
if #friendship.save
flash[:notice] = "Added friend."
redirect_to current_user.profile
else
flash[:error] = "Unable to add friend."
redirect_to root_url
end
end
def destroy
#friendship = current_user.friendships.find(params[:id])
#friendship.destroy
flash[:notice] = "Removed friendship."
redirect_to current_user.profile
end
end
users_controller.rb
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def index
#users = User.all
end
end
profiles_controller.rb
class ProfilesController < ApplicationController
# GET /profiles
# GET /profiles.json
def index
#profiles = Profile.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #profiles }
end
end
# GET /profiles/1
# GET /profiles/1.json
def show
#user = User.find(params[:id])
#profile = Profile.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #profile }
end
end
# GET /profiles/new
# GET /profiles/new.json
def new
#profile = Profile.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #profile }
end
end
# GET /profiles/1/edit
def edit
#user = User.find(params[:id])
#profile = Profile.find(params[:id])
end
# POST /profiles
# POST /profiles.json
def create
#profile = Profile.new(params[:profile])
respond_to do |format|
if #profile.save
format.html { redirect_to #profile, notice: 'Profile was successfully created.' }
format.json { render json: #profile, status: :created, location: #profile }
else
format.html { render action: "new" }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# PUT /profiles/1
# PUT /profiles/1.json
def update
#profile = Profile.find(params[:id])
respond_to do |format|
if #profile.update_attributes(params[:profile])
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.json
def destroy
#profile = Profile.find(params[:id])
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url }
format.json { head :no_content }
end
end
end
routes.rb
BaseApp::Application.routes.draw do
resources :friendships
resources :profiles
#get "users/show"
devise_for :users, :controllers => { :registrations => "registrations" }
resources :users
match '/show', to: 'profile#show'
match '/signup', to: 'users#new'
root to: 'static_pages#home'
match '/', to: 'static_pages#home'
. . .
You aren't setting #users in ProfilesController#show.
for object in collection just calls collection.each do |object|, which is why you're getting undefined method 'each' for NilClass (and also why it's generally discouraged to use that syntax, as it creates confusing errors like this one).
profiles_controller.rb
def show
#users = User.all
#...
end
Anytime you try to call methods with no actual object you'll get the 'method undefined'.
It means that the method IS defined - but you have a 'nil' and are trying to call it on that and that method doesn't exists for the 'nil' object.
Please check your actual users table. You'll need users to work with. Please verify that you have some.
If necessary you can create users (at the script/rails console) with
User.new(:name=>'fred', :password =>'pword', :password_confirmation => 'pword' )
You can also place this in your db/seeds.db file so you can run rake db:seed the first time you set the application up on a new machine.

No Route matches on Form with nested atributes but not default action

I have a form to update an item called post, but i need a different method than update because there are 2 ways to update the post, i have tried this in a lot of ways but i only get this error
No route matches "/topics/1/posts/35/completed"
the controller:
def completed
#post.download_remote_image
respond_to do |format|
if #post.save
format.html { redirect_to topic_path(#topic), :notice => t('.post_created') }
else
format.html { render :action => :edit }
end
end
end
the view part:
= form_for [#topic, #post], :url => completed_topic_post_path(#topic, #post) do |f|
the routes:
resources :topics do
resources :posts do
get 'complete', :as => :complete
post 'completed', :as => :completed
end
end
Thanks !!
:url => completed_topic_post_path(#topic, #post) needs to be :url => topic_post_completed_path(#topic, #post)