rspec controller spec for js request - testing

In my controller specs everything works fine (update action, edit action, etc. also via js request), except the create action. For some reason it doesn't change the Task.count, but the http response is 200.
There are no other callbacks and in dev ENV it saves the task in the db. This factory passes in model spec. I also tried to comment out the Notification.create, TaskCreatorJob and Conversation.create.., but didn't help. In my other controller specs the expect { create_action }.to change{Class.count}.by(1) works properly.
What did I miss?
conroller
def create
#task = Task.new(task_params)
#task.assigner_id = current_user.id
if #task.save
Notification.create(recipient_id: #task.executor_id, sender_id: current_user.id, notifiable: #task, action: "assigned")
TaskCreatorJob.perform_later(#task, #task.executor, #task.assigner)
Conversation.create_or_find_conversation(#task.assigner_id, #task.executor_id)
respond_to do |format|
format.js
end
else
respond_to do |format|
format.js
end
end
end
factory
factory :task do
content { Faker::Lorem.sentence }
deadline { Faker::Time.between(DateTime.now + 2, DateTime.now + 3) }
association :executor, factory: :user
association :assigner, factory: :user
end
tasks_controller_spec.rb
.....
before(:each) do
login_user
end
describe "POST create" do
context "with valid attributes" do
let!(:user) { create(:user) }
let!(:profile) { create(:profile, user: #user) }
let!(:profile_2) { create(:profile, user: user) }
let!(:conversation) { create(:conversation, sender: #user, recipient: user) }
subject(:create_action) { xhr :post, :create, user_id: #user.id, task: attributes_for(:task, assigner: #user, executor: user) }
it "saves the new task in the db" do
expect{ create_action }.to change{ Task.count }.by(1)
end
it "responds with success" do
create_action
expect(response).to have_http_status(200)
end
end
end

It seems in controller spec it's not enough to define task params like assigner: #user, so I had to change
subject(:create_action) { xhr :post, :create, user_id: #user.id, task: attributes_for(:task, assigner: #user, executor: user) }
to
subject(:create_action) { xhr :post, :create, user_id: #user.id, task: attributes_for(:task, assigner_id: #user.id, executor_id: user.id) }

Related

Accepts_nested_attributes_for child validation not working

I have a model called Test that accepts_nested_attributes_for SubTest. A Test basically has an id, and there are multiple SubTests associated with an individual Test.
I want to validate that an attribute of the SubTest is numeric. Here are my models so far.
class Test < ActiveRecord::Base
has_many :sub_tests
accepts_nested_attributes_for :sub_tests
end
class SubTest < ActiveRecord::Base
belongs_to :test
validates :measurement, :numericality => true, :allow_nil => true
end
Here is the Test controller
def create
respond_to do |format|
if #test.save
flash.now[:notice] = "Test Created"
format.html { redirect_to(#test) }
format.xml { render :xml => #test, :status => :created, :location => #test }
format.js
else
flash.now[:error] = "[Error] Test Not Created Because: #{#test.errors.full_messages.join(", ")}"
format.html { render :action => "new" }
format.xml { render :xml => #test.errors, :status => :unprocessable_entity }
format.js
end
end
end
I want the create action in the controller to throw an error if the user enters a non-numeric string in the form for the sub_test.
Currently, if I enter non-numeric values for a sub_test.measurement, Rails doesn't create the Test or SubTest objects (desired behavior). But for some reason no error is thrown, and the Test controller triggers the create.js.erb partial.
I'm getting the impression that the validation for the numericality of sub_test.measurement should actually happen in the Test model, but I'm not sure how to write a custom validation method that tests for numericality.
Thanks in advance!
Got it. I needed to specify an _error.js.erb partial in my Test controller.
def create
respond_to do |format|
if #test.save
flash.now[:notice] = "Test Created"
format.html { redirect_to(#test) }
format.xml { render :xml => #test, :status => :created, :location => #test }
format.js
else
flash.now[:error] = "[Error] Test Not Created Because: #{#test.errors.full_messages.join(", ")}"
format.html { render :action => "new" }
format.xml { render :xml => #test.errors, :status => :unprocessable_entity }
format.js { render :partial => 'error' }
end
end
In the error partial I just appended my #messages div (since I'm doing form submissions through ajax.)

Devise and Authentication with CURL !

I would like to be able to authenticate through a curl style !
So I would like to get a Token when I signed in :
curl -X POST 'http://localhost:3000/users/sign_in.json' -d 'user[email]=em...#provider.us&user[password]=pass'
But if I perform this action I only get :
{"email":"em...#provider.us","name":"Name of the user"}
How I could add some specific fields like authentication_token ?
Is what I want to do is right ? Is there any other better way to do
this ?
Thanks !
I created a Controller named SessionController
class Users::SessionsController < Devise::SessionsController
append_view_path 'app/views/devise'
def create
respond_to do |format|
format.html { super }
format.json {
warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")
current_user.ensure_authentication_token!
render :json => {:user => current_user.api_attributes, :auth_token => current_user.authentication_token}.to_json, :status => :ok
}
end
end
def destroy
respond_to do |format|
format.html { super }
format.json {
warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")
current_user.empty_authentication_token!
render :json => {}.to_json, :status => :ok
}
end
end
end
And added this to the root file :
devise_for :users, :controllers => { :sessions => "users/sessions" }
And it works :-)

Include two database results in one API request output using Rails 3

I am developing an API in Rails 3 and I got a user model and a message model.
I want the developer to be able to get all info about the user as well as the last message from the user. How can I do this? How can I "include" the messages into the user output?
How can this code be modified to suit my purpose?
def index
#contacts = current_user.contacts
#users = Array.new
#messages = Array.new
#contacts.each do |contact|
user = User.find(contact.friend_id)
#users << user
message = Message.find_by_user_id(contact.friend_id)
#messages << message
end
respond_to do |format|
format.html { render :text => 'Use either JSON or XML' }
format.json { render :json => #users }
format.xml { render :xml => #users }
end
end
Thankful for all input!
You should take advantage of your model, and include the last message as a method in your User model. You can then use the :methods hash of the render method.
format.json { render :json => #users , :methods => [:last_message]}
EDIT
Your user model should be something like:
def User < ActiveRecord::Base
has_many :messages
attr_accessor :name, :email #Include the fields you wish to show here
def last_message
self.messages.first
end
end
And your controller:
def index
#contacts = current_user.contacts
respond_to do |format|
format.html { render :text => 'Use either JSON or XML' }
format.json { render :json => #contacts, :methods => :last_message }
format.xml { render :xml => #contacts, :methods => :last_message }
end
end

failed to test create action

I am playing with rails 3.0.6 with ruby 1.9.2,
the app is working on browser but not in testing..
1) I created a new rails application by "rails new myapp"
2) Generate a scaffold "rails generate scaffold user username:string hashed_password:string salt:string"
3) after that, i changed the users_controller a bit
# POST /users
# POST /users.xml
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
// change #user to usrs_url
format.html { redirect_to(users_url, :notice => "User #{#user.username} was successfully created.") }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
# PUT /users/1
# PUT /users/1.xml
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
// change #user to usrs_url
format.html { redirect_to(users_url, :notice => "User #{#user.username} was successfully updated.") }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
4) so i try to modify tests as well:
setup do
#input_attributes = {
:username => 'username#goodmail.com',
:password => 'secret',
:password_confirmation => 'secret'
}
#user = users(:one)
end
test "should create user" do
assert_difference('User.count') do
post :create, :user => #input_attributes
end
assert_redirected_to users_path
end
test "should update user" do
put :update, :id => #user.to_param, :user => #input_attributes
assert_redirected_to users_path
end
But the create and update tests failed
Can anyone let me know what had i done wrong?
Thanks
Loaded suite C:/Ruby192/lib/ruby/1.9.1/rake/rake_test_loader
Started
F.....F
Finished in 5.628874 seconds.
1) Failure:
test_should_create_user(UsersControllerTest) [test/functional/users_controller_test.rb:26]:
"User.count" didn't change by 1.
<3> expected but was
<2>.
2) Failure:
test_should_update_user(UsersControllerTest) [test/functional/users_controller_test.rb:45]:
Expected block to return true value.
7 tests, 9 assertions, 2 failures, 0 errors, 0 skips
require 'digest/sha2'
class User < ActiveRecord::Base
validates :username, :presence => true, :uniqueness => true
validates_format_of :username, :with => /\A^[^\r\n# ][^\r\n# ]+#[^\r\n# ]+[.][^\r\n#. ]+$\Z/i
#password is a fake field
validates :password, :confirmation => true
validate :password_must_be_present
attr_accessor :password_confirmation
attr_reader :password
def password=(password)
if password.present?
generate_salt
self.hashed_password = self.class.encrypt_password(password, salt)
end
end
class << self
def encrypt_password(password, salt)
Digest::SHA2.hexdigest(password + "shrimpy" + salt)
end
def authenticate(username, password)
if user = find_by_username(username)
if user.hashed_password == encrypt_password(password, user.salt)
user
end
end
end
end
private
def password_must_be_present
errors.add(:password, "Missing password") unless hashed_password.present?
end
def generate_salt
self.salt = self.object_id.to_s + rand.to_s
end
end
assert_redirected_to user_path is singular. You probably don't even have a singular user resource route defined. What you want there is probably assert_redirected_to users_path with a plural users.

Howto assign an ordinary user as superuser or admin

I was following the tutorial at http://www.logansbailey.com/ and modified it to enable an unregistered person to be able to register with a username, email and password.
I already enabled a logged in user to modify his/her email and password but not the username.
What I want to add is:
1) to enable a logged in user to be able to see/reach his/her username and email,
2) to enable a user with admin_flag set (I handled this in the sql table and created the user) to be able to see/modify all user records.
I modifyed the app/cotrollers/user_controller.rb like this:
class UsersController < ApplicationController
before_filter :is_user, :only => [:index, :show, :edit, :update, :destroy]
def index
#users = User.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #users }
end
end
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #user }
end
end
def new
#user = User.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #user }
end
end
def edit
end
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
flash[:notice] = 'Registration successful.'
format.html { redirect_to(:controller => 'home', :action => 'tutorial') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #user.update_attributes(params[:user])
flash[:notice] = 'Your profile was successfully updated.'
format.html { redirect_to(:controller => 'home', :action => 'index') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to(users_url) }
format.xml { head :ok }
end
end
def is_user
if User.exists?(params[:id])
#user = User.find(params[:id]);
if current_user.admin_flag == true
flash[:notice] = 'Welcome Admin'
end
if !current_user || current_user.id != #user.id
flash[:notice] = 'You do not have access to that page'
redirect_to(:controller => 'home', :action => 'index')
end
else
flash[:notice] = 'You do not have access to that page'
redirect_to(:controller => 'home', :action => 'index')
end
end
end
The file app/models/user.rb is:
class User < ActiveRecord::Base
acts_as_authentic
end
And I can confirm that the admin_flag set user is get correctly since the file app/views/layouts/application.html.erb containing:
<div id="admin">
<% if current_user %>
<% if current_user.admin_flag == true %> |
<%= link_to "Users", users_path %>
<% end %>
<% end %>
</div>
correctly displays the 'Users' link when I log in as the admin.
Now the problem is that I can't get the show all users, edit other users etc.. functionality. As the admin, I can show and modify the admin user just like all the other ordinary users, meaning I can't modify the username, too.
What may be wrong here?
When you added a boolean attribute admin to user in the right way, the Rails should add question-mark method admin? in User model. It's not important but for convenience.
On every method you want to protect from unwanted actions use before_filter:
class UsersController < ApplicationController
before_filter :admin_user, :only => :destroy
before_filter :correct_user, :only => [:edit, :update]
def destroy
end
...
private
def admin_user
redirect_to(root_path) unless current_user.admin?
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user) || current_user.admin?
end
end
In views more convenient to use current_user.admin?
<div id="admin">
<% if current_user.admin? %>
<%= link_to "Users", users_path %>
<% end %>
</div>