Hartl test errors - redirect when (not) logged in as wrong user, not allow admin edited - testing
I have a few errors that I can't resolve. All the tests pass. If there are errors, I only need to check the test, right? I only need to check the code if they fail, right?
Below are the errors:
Error:
UsersControllerTest#test_should_redirect_update_when_logged_in_as_wrong_user:
ArgumentError: wrong number of arguments (given 2, expected 1)
test/controllers/users_controller_test.rb:45:in `block in '
bin/rails test test/controllers/users_controller_test.rb:43
Error:
UsersControllerTest#test_should_not_allow_the_admin_attribute_to_be_edited_via_the_web:
ArgumentError: unknown keywords: id, user
test/controllers/users_controller_test.rb:36:in `block in '
bin/rails test test/controllers/users_controller_test.rb:33
Error:
UsersControllerTest#test_should_redirect_index_when_not_logged_in:
URI::InvalidURIError: bad URI(is not URI?): http://www.example.com:80index
test/controllers/users_controller_test.rb:11:in `block in '
bin/rails test test/controllers/users_controller_test.rb:10
Error:
PasswordResetsTest#test_password_resets:
NameError: undefined local variable or method 'expired' for #
test/integration/password_resets_test.rb:62:in `block in '
bin/rails test test/integration/password_resets_test.rb:10
Below are my files:
test/integration/password_resets_test.rb
require 'test_helper'
class PasswordResetsTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
#user = users(:michael)
end
test "password resets" do
get new_password_reset_path
assert_template 'password_resets/new'
# Invalid email
post password_resets_path, params: { password_reset: { email: "" } }
assert_not flash.empty?
assert_template 'password_resets/new'
# Valid email
post password_resets_path,
params: { password_reset: { email: #user.email } }
assert_not_equal #user.reset_digest, #user.reload.reset_digest
assert_equal 1, ActionMailer::Base.deliveries.size
assert_not flash.empty?
assert_redirected_to root_url
# Password reset form
user = assigns(:user)
# Wrong email
get edit_password_reset_path(user.reset_token, email: "")
assert_redirected_to root_url
# Inactive user
user.toggle!(:activated)
get edit_password_reset_path(user.reset_token, email: user.email)
assert_redirected_to root_url
user.toggle!(:activated)
# Right email, wrong token
get edit_password_reset_path('wrong token', email: user.email)
assert_redirected_to root_url
# Right email, right token
get edit_password_reset_path(user.reset_token, email: user.email)
assert_template 'password_resets/edit'
assert_select "input[name=email][type=hidden][value=?]", user.email
# Invalid password & confirmation
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "foobaz",
password_confirmation: "barquux" } }
assert_select 'div#error_explanation'
# Empty password
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "",
password_confirmation: "" } }
assert_select 'div#error_explanation'
# Valid password & confirmation
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "foobaz",
password_confirmation: "foobaz" } }
assert is_logged_in?
assert_not flash.empty?
assert_redirected_to user
#passes w percent now, may need to change
assert_match(/%#{expired}/i, response.body)
end
end
test/controllers/users_controller_test.rb
require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest
def setup
#user = users(:michael)
#other_user = users(:archer)
end
test "should redirect index when not logged in" do
get :index
assert_redirected_to login_url
end
test "should get new" do
get signup_path
assert_response :success
end
test "should redirect edit when not logged in" do
get edit_user_path(#user)
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect update when not logged in" do
patch user_path(#user), params: { user: { name: #user.name,
email: #user.email } }
assert_not flash.empty?
assert_redirected_to login_url
end
test "should not allow the admin attribute to be edited via the web" do
log_in_as(#other_user)
assert_not #other_user.admin?
patch :update, id: #other_user, user: { password: #other_user.password,
password_confirmation: #other_user.password_confirmation,
admin: true }
assert_not #other_user.reload.admin?
end
test "should redirect update when logged in as wrong user" do
log_in_as(#other_user)
patch :update, user_path(#user), user: { name: #user.name, email: #user.email }
assert flash.empty?
assert_redirected_to root_url
end
test "should redirect destroy when not logged in" do
assert_no_difference 'User.count' do
delete user_path(#user)
end
assert_redirected_to login_url
end
test "should redirect destroy when logged in as a non-admin" do
log_in_as(#other_user)
assert_no_difference 'User.count' do
delete user_path(#user)
end
assert_redirected_to root_url
end
end
Below are supporting files:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
# Shows all users, delete for sups app but useful for BRBBaby
def index
#users = User.where(activated: true).paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
redirect_to root_url and return unless :active
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
end
def update
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Before filters
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
class AccountActivationsController < ApplicationController
def edit
user = User.find_by(email: params[:email])
if user && !user.activated? && user.authenticated?(:activation, params[:id])
user.activate
log_in user
flash[:success] = "Account activated!"
redirect_to user
else
flash[:danger] = "Invalid activation link"
redirect_to root_url
end
end
end
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
before_action :check_expiration, only: [:edit, :update]
def new
end
def create
#user = User.find_by(email: params[:password_reset][:email].downcase)
if #user
#user.create_reset_digest
#user.send_password_reset_email
flash[:info] = "Email sent with password reset instructions"
redirect_to root_url
else
flash.now[:danger] = "Email address not found"
render 'new'
end
end
def edit
end
def update
if params[:user][:password].empty? # Case (3)
#user.errors.add(:password, "can't be empty")
render 'edit'
elsif #user.update_attributes(user_params) # Case (4)
log_in #user
#user.update_attribute(:reset_digest, nil)
flash[:success] = "Password has been reset."
redirect_to #user
else
render 'edit' # Case (2)
end
end
private
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
def get_user
#user = User.find_by(email: params[:email])
end
# Confirms a valid user.
def valid_user
unless (#user && #user.activated? &&
#user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
# Checks expiration of reset token.
def check_expiration
if #user.password_reset_expired?
flash[:danger] = "Password reset has expired."
redirect_to new_password_reset_url
end
end
end
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
if user.activated?
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_back_or user
else
message = "Account not activated. "
message += "Check your email for the activation link."
flash[:warning] = message
redirect_to root_url
end
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out if logged_in?
redirect_to root_url
end
end
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
# Remembers a user in a persistent session.
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
# Returns true if the given user is the current user.
def current_user?(user)
user == current_user
end
# Returns the current logged-in user (if any).
def current_user
if (user_id = session[:user_id])
#current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
#current_user = user
end
end
end
# Returns true if the user is logged in, false otherwise.
# FIXES - this was !current_user.nil? and had lots of errors and was fixed with below, but not sure right now how it might affect other parts of app.
def logged_in?
!current_user.nil?
end
# Forgets a persistent session.
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
# Logs out the current user.
def log_out
forget(current_user)
session.delete(:user_id)
#current_user = nil
end
# Redirects to stored location (or to the default).
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete(:forwarding_url)
end
# Stores the URL trying to be accessed.
def store_location
session[:forwarding_url] = request.original_url if request.get?
end
end
module UsersHelper
# Returns the Gravatar for the given user.
def gravatar_for(user, options = { size: 80 })
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
size = options[:size]
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
image_tag(gravatar_url, alt: user.name, class: "gravatar")
end
end
class User < ApplicationRecord
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /[A-Z0-9._%+-]+#(?:[A-Z0-9-]+\.)+[A-Z]{2,}/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Activates an account.
def activate
update_columns(activated: true, activated_at: Time.zone.now)
# The above line should count for the below two lines
# update_attribute(:activated, true)
# update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_columns(reset_digest: User.digest(reset_token), reset_sent_at: Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
require 'test_helper'
class UsersEditTest < ActionDispatch::IntegrationTest
def setup
#user = users(:michael)
end
test "unsuccessful edit" do
log_in_as(#user)
get edit_user_path(#user)
assert_template 'users/edit'
patch user_path(#user), params: { user: { name: "",
email: "foo#invalid",
password: "foo",
password_confirmation: "bar" } }
assert_template 'users/edit'
end
test "successful edit with friendly forwarding" do
get edit_user_path(#user)
log_in_as(#user)
assert_redirected_to edit_user_path(#user)
name = "Foo Bar"
email = "foo#bar.com"
patch user_path(#user), params: { user: { name: name,
email: email,
password: "",
password_confirmation: "" } }
assert_not flash.empty?
assert_redirected_to #user
#user.reload
assert_equal name, #user.name
assert_equal email, #user.email
end
end
require 'test_helper'
class UsersIndexTest < ActionDispatch::IntegrationTest
def setup
#admin = users(:michael)
#non_admin = users(:archer)
end
test "index as admin including pagination and delete links" do
log_in_as(#admin)
get users_path
assert_template 'users/index'
assert_select 'div.pagination'
first_page_of_users = User.paginate(page: 1)
first_page_of_users.each do |user|
assert_select 'a[href=?]', user_path(user), text: user.name
unless user == #admin
assert_select 'a[href=?]', user_path(user), text: 'delete'
end
end
assert_difference 'User.count', -1 do
delete user_path(#non_admin)
end
end
test "index as non-admin" do
log_in_as(#non_admin)
get users_path
assert_select 'a', text: 'delete', count: 0
end
end
require 'test_helper'
class UsersLoginTest < ActionDispatch::IntegrationTest
def setup
#user = users(:michael)
end
test "login with invalid information" do
get login_path
assert_template 'sessions/new'
post login_path, params: { session: { email: "", password: "" } }
assert_template 'sessions/new'
assert_not flash.empty?
get root_path
assert flash.empty?
end
# Validations may have been cheated; confirm platonic result
test "login with valid information followed by logout" do
get login_path
post login_path, params: { session: { email: #user.email,
password: 'password' } }
assert is_logged_in?
assert_redirected_to #user
follow_redirect!
assert_template 'users/show'
assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(#user)
delete logout_path
assert_not is_logged_in?
assert_redirected_to root_url
# Simulate a user clicking logout in a second window.
delete logout_path
follow_redirect!
assert_select "a[href=?]", login_path
assert_select "a[href=?]", logout_path, count: 0
assert_select "a[href=?]", user_path(#user), count: 0
end
test "login with remembering" do
log_in_as(#user, remember_me: '1')
assert_not_nil cookies['remember_token']
end
test "login without remembering" do
# Log in to set the cookie.
log_in_as(#user, remember_me: '0')
# Log in again and verify that the cookie is deleted.
assert_nil cookies['remember_token']
end
end
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
end
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user#invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
assert_select 'div#error_explanation'
assert_select 'div.field_with_errors'
end
test "valid signup information with account activation" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { user: { name: "Example User",
email: "user#example.com",
password: "password",
password_confirmation: "password" } }
end
assert_equal 1, ActionMailer::Base.deliveries.size
user = assigns(:user)
assert_not user.activated?
# Try to log in before activation.
log_in_as(user)
assert_not is_logged_in?
# Invalid activation token
get edit_account_activation_path("invalid token", email: user.email)
assert_not is_logged_in?
# Valid token, wrong email
get edit_account_activation_path(user.activation_token, email: 'wrong')
assert_not is_logged_in?
# Valid activation token
get edit_account_activation_path(user.activation_token, email: user.email)
assert user.reload.activated?
follow_redirect!
assert_template 'users/show'
assert is_logged_in?
end
end
require 'test_helper'
#######
## Amp up password security in the future
## https://www.google.com/search?q=rails+enforce+password+strength
######
class UserTest < ActiveSupport::TestCase
def setup
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
end
test "should be valid" do
assert #user.valid?
end
test "name should be present" do
#user.name = " "
assert_not #user.valid?
end
test "email should be present" do
#user.email = " "
assert_not #user.valid?
end
test "name should not be too long" do
#user.name = "a" * 51
assert_not #user.valid?
end
test "email should not be too long" do
#user.email = "a" * 244 + "#example.com"
assert_not #user.valid?
end
test "email validation should accept valid addresses" do
valid_addresses = %w[user#example.com USER#foo.COM A_US-ER#foo.bar.org
first.last#foo.jp alice+bob#baz.cn]
valid_addresses.each do |valid_address|
#user.email = valid_address
assert #user.valid?, "#{valid_address.inspect} should be valid"
end
end
test "email validation should reject invalid addresses" do
invalid_addresses = %w[user#example,com user_at_foo.org user.name#example.
foo#bar_baz.com foo#bar+baz.com]
invalid_addresses.each do |invalid_address|
#user.email = invalid_address
assert_not #user.valid?, "#{invalid_address.inspect} should be invalid"
end
end
test "email addresses should be unique" do
duplicate_user = #user.dup
duplicate_user.email = #user.email.upcase
#user.save
assert_not duplicate_user.valid?
end
test "password should be present (nonblank)" do
#user.password = #user.password_confirmation = " " * 6
assert_not #user.valid?
end
test "password should have a minimum length" do
#user.password = #user.password_confirmation = "a" * 5
assert_not #user.valid?
end
test "authenticated? should return false for a user with nil digest" do
assert_not #user.authenticated?(:remember, '')
end
end
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require "minitest/reporters"
Minitest::Reporters.use!
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Returns true if a test user is logged in.
def is_logged_in?
!session[:user_id].nil?
end
# Logs in a test user.
def log_in_as(user, options = {})
password = options[:password] || 'password'
remember_me = options[:remember_me] || '1'
if integration_test?
post login_path, params: { session: { email: user.email,
password: password,
remember_me: remember_me } }
else
session[:user_id] = user.id
end
end
private
# Returns true inside an integration test.
def integration_test?
defined?(post_via_redirect)
end
end
Any help would be appreciated. Let me know if there is any info I can provide or if you have questions for me. Thank you for your help. Cheers.
1)
Error: UsersControllerTest#test_should_redirect_update_when_logged_in_as_wrong_user: ArgumentError: wrong number of arguments (given 2, expected 1) test/controllers/users_controller_test.rb:45:in `block in ' bin/rails test test/controllers/users_controller_test.rb:43
For this one you've used patch :update user_path(#user)... when you should just have patch user_path(#user)...
2)
Error: UsersControllerTest#test_should_not_allow_the_admin_attribute_to_be_edited_via_the_web: ArgumentError: unknown keywords: id, user test/controllers/users_controller_test.rb:36:in `block in ' bin/rails test test/controllers/users_controller_test.rb:33
This is because you have used patch :update id: #othr_user ... instead of patch user_path(#other_user) ...
3)
Error: UsersControllerTest#test_should_redirect_index_when_not_logged_in: URI::InvalidURIError: bad URI(is not URI?): http://www.example.com:80index test/controllers/users_controller_test.rb:11:in `block in ' bin/rails test test/controllers/users_controller_test.rb:10
http://www.example.com:80index is indeed a bad URL and should be http://www.example.com:80/index
I'd be guessing that login_url is setup incorrectly in your routes file... have a look at what it is and try to add the missing /?
EDIT: Actually I think there might be something else here
You have get :index where all the other tests use an actual test path eg get users_path.
4)
Error: PasswordResetsTest#test_password_resets: NameError: undefined local variable or method 'expired' for # test/integration/password_resets_test.rb:62:in `block in ' bin/rails test test/integration/password_resets_test.rb:10
This line: assert_match(/%#{expired}/i, response.body) contains the local variable expired but nowhere have you assigned a value to expired... what do you intend for this value to be?
Related
Clearance failure when forbidden password reset
I’m using clearance and love it, but I'm having trouble resetting passwords. I type in my email to reset the password, which works, but then when I try to navigate to the edit password page using the reset token, I get the failure when forbidden flash error “Please double check the URL or try submitting the form again” and it redirects me back. I get the same error in my tests. I think this has something to do with my before_action statements, but I just don’t know how to fix them. I have researched questions like this to no avail. I'm sure it's a stupid question, but I'm new so I really appreciate any help. Please let me know if this isn't enough code. class UsersController < Clearance::UsersController before_action :require_login, only: [:create] # does this need to be in both user controllers? ... def user_params params.require(:user) end end And here is the clearance controller. class Clearance::UsersController < ApplicationController before_action :require_login, only: [:create] require 'will_paginate/array' def new #user = user_from_params render template: 'users/new' end def create #user = user_from_params #user.regenerate_password if #user.save sign_in #user unless current_user UserMailer.welcome_email(#user).deliver! redirect_to users_path else render template: 'users/new' end end def edit #user = User.friendly.find(params[:id]) end def update #user = User.friendly.find(params[:id]) if #user.update(permit_params) redirect_to #user flash[:success] = "This profile has been updated." else render 'edit' end end private def avoid_sign_in redirect_to Clearance.configuration.redirect_url end def url_after_create(user) dashboards_path(user) end def user_from_params user_params = params[:user] || Hash.new is_public = check_public_params(user_params) first_name = user_params.delete(:first_name) last_name = user_params.delete(:last_name) email = user_params.delete(:email) password = user_params.delete(:password) parish = user_params.delete(:parish) division = user_params.delete(:division) admin = user_params.delete(:admin) Clearance.configuration.user_model.new(user_params).tap do |user| user.first_name = first_name user.last_name = last_name user.password = password user.email = email user.is_public = is_public user.parish_id = parish.to_i user.division = division user.admin = admin end end def permit_params params.require(:user).permit(:first_name, :last_name, :email, :password, :is_public, :parish_id, :division, :admin) end end EDIT: relevant portions of routes.rb Rails.application.routes.draw do resources :passwords, controller: "clearance/passwords", only: [:create, :new] resource :session, controller: "clearance/sessions", only: [:create] resources :users, controller: "clearance/users", only: [:create] do resource :password, controller: "clearance/passwords", only: [:create, :edit, :update] end get "/sign_in" => "clearance/sessions#new", as: "sign_in" delete "/sign_out" => "clearance/sessions#destroy", as: "sign_out" get "/sign_up" => "clearance/users#new", as: "sign_up" constraints Clearance::Constraints::SignedOut.new do root to: 'high_voltage/pages#show', id: 'landing' end constraints Clearance::Constraints::SignedIn.new do # root to: 'dashboards#index', as: :signed_in_root root to: 'high_voltage/pages#show', id: 'parish_dashboard', as: :signed_in_root end # constraints Clearance::Constraints::SignedIn.new { |user| user.admin? } do # root to: 'teams#index', as: :admin_root # end resources :users do collection { post :import } end
It turns out there was a conflict between the way I was finding the user instance in the password reset link. Clearance finds users simply by using #user, but since I'm using FriendlyId I needed to change that to #user.id. So instead of... <%= link_to 'Change My Password', edit_user_password_url(#user, token: #user.confirmation_token.html_safe) %> I did <%= link_to 'Change My Password', edit_user_password_url(#user.id, token: #user.confirmation_token.html_safe) %> Thanks, Thoughbot, for this great gem!
testing create action with nested resources
I have a bit of a confusing rSpec issue - depending how I write my code, either the tests that describe the 'failing' specs fail or the tests that describe the 'successful' specs fail. Here are the tests for the create action: describe "POST 'create'" do describe "failure" do before(:each) do #attr = {name: "", type_of_group: ""} #student_attr = [{name: "Joe", gender: "Male"}, {name: "sally twotrees", gender: "Female"}] #create = post :create, student_group: #attr, student: #student_attr end it "should have the right title" do #create response.should have_selector('title', :content => "Create a new group" ) end it "should render the 'new' page" do #create response.should render_template('new') end it "should not create a user" do lambda do post :create, student_group: #attr end.should_not change {#user.student_groups.count} end it "should flash an error message" do #create flash[:error].should =~ /please/i end end describe "success" do before(:each) do #attr = FactoryGirl.attributes_for(:student_group) # #student_attr = {name: "test", gender: "Male"} end it "should create a student_group" do lambda do post :create, student_group: #attr end.should change {#user.student_groups.count}.by(1) end it "should create students" # do # lambda do # post :create, student_group: #attr, student: #student_attr # end.should change {#student_groups.students.count}.by(1) # end it "should flash a success message" do post :create, student_group: #attr flash[:success].should =~ /has been added/i end it "should redirect" do post :create, student_group_id: #group, student_group: #attr response.should be_redirect end end end All of the 'failure' tests fail with this error: Failure/Error: #create = post :create, student_group: #attr, student: #student_attr ActionView::Template::Error: `#student_group[students_attributes]' is not allowed as an instance variable name if I write the code in my controller this way: def create #params = params[:student_group][:students_attributes] #student_group = #user.student_groups.build(params[:student_group]) if #student_group.save ### RE: 'defensive coding' https://stackoverflow.com/questions/14502508/undefined-method-for-nilnilclass-when-pushing-values-to-an-array if #params.present? ### https://stackoverflow.com/questions/11355820/rails-3-2-iterate-through-an-array #params.each do |student| #student_group.students.create(name:"#{student[:name]}", gender: "#{student[:gender]}") end end # new subject path redirect_to class_path(#student_group), flash: { success: "#{#student_group.name} has been added successfully" } else #title = "Create a new group" flash.now[:error] = "Something's gone wrong. Please try again!" render 'new' end end and all of the 'success' tests fail if the controller code is written like this: def create #params = params[:student_group][:students_attributes] #student_group = #user.student_groups.build(params[:student_group]) ### http://railsforum.com/viewtopic.php?pid=40056#p40056 if #params.present? #student = Student.new else #student = #student_group.students.build(#params) end if #student_group.save ### RE: 'defensive coding' https://stackoverflow.com/questions/14502508/undefined-method-for-nilnilclass-when-pushing-values-to-an-array if #params.present? ### https://stackoverflow.com/questions/11355820/rails-3-2-iterate-through-an-array #params.each do |student| #student_group.students.create(name:"#{student[:name]}", gender: "#{student[:gender]}") end end # new subject path redirect_to class_path(#student_group), flash: { success: "#{#student_group.name} has been added successfully" } else #title = "Create a new group" flash.now[:error] = "Something's gone wrong. Please try again!" render 'new' end end the form code is here: https://stackoverflow.com/a/17591802/2128691
from the above code it seems that your controller code is really messed up. In case of nested attributes, u just have to save the parent object. the child objects get saved automatically if they are valid. Also u dont need to assign the params the some instance object. they should be used directly. a simple example of nested attributes can be User has_many :comments accepts_nested_attributes_for :comments Comment belongs_to :user ur controller code should be as def create #user = User.new(params[:user]) if #user.save flash[:notice] = 'success' redirect_to some_path and return end render 'new' end the rspec controller test case can be as it "should create a user with comments if valid data is provided" do post :create, "user"=>{"name"=>"Prasad", "comments_attributes"=>{"0"=>{"comment"=>"first comment"}, "1"=>{"comment"=>"second comment"}}, "commit"=>"Save" user = assigns[:user] #assigns lets u access the instance variable from the controller in the spec user.should be_valid user.comments.count.should == 2 #check that all the child models are saved user.name.should == "Prasad" user.comments.first.comment.should == 'first comment' user.comments.last.comment.should == 'second comment' response.should be_redirect(some_path) #since u redirected in the code end seriously, u need to go through rails guides.
I ended up using this code: def create #student_group = #user.student_groups.new(params[:student_group]) #params = params[:student_group][:students_attributes] #student_group = #user.student_groups.build(params[:student_group]) if #student_group.save ### RE: 'defensive coding' http://stackoverflow.com/questions/14502508/undefined-method-for-nilnilclass-when-pushing-values-to-an-array if #params.present? ### http://stackoverflow.com/questions/11355820/rails-3-2-iterate-through-an-array #params.each do |student| #student_group.students.create(name:"#{student[:name]}", gender: "#{student[:gender]}") end end redirect_to new_student_group_subject_path(#student_group), flash: { success: "#{#student_group.name} has been added successfully. Next, add the subjects for this group" } else ### http://railsforum.com/viewtopic.php?pid=40056#p40056 #student = #student_group.students.build #title = "Create a new group" flash.now[:error] = "Something's gone wrong. Please try again!" render 'new' end end
Using flash to notify user of previous registration
I'm trying to use my Users controller to notify the user when their email has already been used in a registration, but even when the email already exists, I still get the error "Plase validate your input and try again," rather than "You've already registered! Thanks for being enthusiastic!" Is using the controller not the create way of achieving this behavior? In the rails console (assuming "foo#bar.com" is in the database"), when I use user = User.new(name:"Example", email:"foo#bar.com") then User.find_by_email(user.email) it does return the proper User entry, so I'm not sure if I'm on the right track and just executing it incorrectly or what. Any ideas? users_controller.rb: class UsersController < ApplicationController def new #user = User.new(params[:user]) end def create #user = User.new(params[:user]) if #user.save flash[:success] = "Thanks for supporting cofind! We'll be in touch!" redirect_to root_path UserMailer.welcome_email(#user).deliver else if #user.email == User.find_by_email(#user.email) flash[:error] = "You've already registered! Thanks for being enthusiastic!" redirect_to root_path else flash[:error] = "Plase validate your input and try again." redirect_to signup_path end end end end user.rb: class User < ActiveRecord::Base attr_accessible :email, :name before_save { |user| user.email = email.downcase } validates :name, presence: true VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } end
this line if #user.email == User.find_by_email(#user.email) checks the user's email (a string) against a user record (an ActiveRecord object) which will always be false. You should change that to if User.where(email: #user.email).exists?
ActiveRecord::RecordNotFound in UsersController#show
I'm just following Ruby on Rails 3 Tutorials (Mhartl) chapter-7 at the stage of 7.3.2 name and Gravatar. Here I am facing a problem when I open on my browser it's says: ActiveRecord::RecordNotFound in UsersController#show Couldn't find User with id=1 Rails.root: C:/RubyOnRails/MyWorkPlace/sample_app_1 Application Trace | Framework Trace | Full Trace app/controllers/users_controller.rb:5:in `show' Request Parameters: {"id"=>"1"} Show session dump Show env dump Response Headers: None Also I pasted below User_controller.rb and user.rb user.rb: require 'digest' class User < ActiveRecord::Base attr_accessor :pasword attr_accessible :login, :username, :email, :password, :password_confirmation, :remember_me email_regex = /\A[\w+\-.]+#[a-z\-.]+\.[a-z]+\z/i validates :name, :presence => true, :length => { :maximum => 50 } validates :email, :presence => true, :format => { :with => email_regex }, :uniqueness => { :case_sensitive => false } validates :pasword, :presence => true, :confirmation => true, :length => { :within => 6..40 } def self.authenticate(email, submitted_password) user = find_by_email(email) return nil if user.nil? return user if user.has_password?(submitted_password) end before_save :encrypt_password def has_password?(submitted_password) encrypted_password == encrypt(submitted_password) end private def encrypt_password self.salt = make_salt if new_record? self.encrypted_password = encrypt(password) end def encrypt(string) secure_hash("#{salt}--#{string}") end def make_salt secure_hash("#{Time.now.utc}--#{password}") end def secure_hash(string) Digest::SHA2.hexdigest(string) end end users_controller.rb: class UsersController < ApplicationController def show #user = User.find(params[:id]) #title = #user.name end def new #title = "Sign up" end end
Are you sure you created any user with id=1 ? To check, go to rails console and get the user with id 1. If there is no user, then create one.
At firest, I see you have attr_accessor :pasword I think it should be :password Ontopic: There are some actions missing in the restful controller, so it wont be possible to create a user. See http://guides.rubyonrails.org/getting_started.html#rest for more details on RESTful controllers. class UsersController < ApplicationController def show #user = User.find(params[:id]) #title = #user.name end def new #user = User.new #this creates a empty user object to be filled with signup data #title = "Sign up" end def create #user = User.new(params[:user]) #this creates a new user object with the data you entered before. if #user.save #if the data is valid, save it redirect_to user_path(#user) #and go to the #user show action else render :action => :new #edit the invalid user data end end def edit #user = User.find(params[:id]) end def update #user = User.find(params[:id]) if #user.update_attributes(params[:user]) redirect_to user_url(#user) else render edit_user_url(#user) end end def index #users = User.all end def destroy #user = User.find(params[:id] #user.destroy redirect_to :action => :index end end edit: complete restful actions
I had the same problema. In my case, my 'redirect_to' on my detroy action was missin a 's' in 'posts_path'. It was post_path Noob, but worth i had checked up.
The reason you could not find the "user/1" is when you Added microposts to the sample data(db/seeds.rb) by typing users = User.order(:created_at).take(6) 50.times do content = Faker::Lorem.sentence(5) users.each { |user| user.microposts.create!(content: content) } end You forgot the "END" of the previous code, so the full picture of db/seeds.rb is User.create!(name: "Example User", email: "example#railstutorial.org", password: "foobar", password_confirmation: "foobar", admin: true, activated: true, activated_at: Time.zone.now) 99.times do |n| name = Faker::Name.name email = "example-#{n+1}#railstutorial.org" password = "password" User.create!(name: name, email: email, password: password, password_confirmation: password, activated: true, activated_at: Time.zone.now) end users = User.order(:created_at).take(6) 50.times do content = Faker::Lorem.sentence(5) users.each { |user| user.microposts.create!(content: content) } end
Failing test in ruby tutorial (Michael Hartl)
I'm studying Michael Hartl's tutorial. I'm using RSPEC to run the test. So far so good but it seems that I've hit the wall with the following example. Here is the test that fails (it should pass): describe "authenticate method" do it "should return the user on email/password match" do matching_user = User.authenticate(#attr[:email], #attr[:password]) matching_user.should == #user end end Just in case. #user defined as: before(:each) do #user = User.create!(#attr) end #attr defined as: before(:each) do #attr = { :name => "Example user", :email => "user#example.com", :password => "foobar", :password_confirmation => "foobar" } end Entries in user.rb before_save :encrypt_password def has_password?(submitted_password) encrypted_password == encrypt(submitted_password) end def self.authenticate(email, submitted_password) user = find_by_email(email) return nil if user.nil? return user if user.has_password?(submitted_password) end private def encrypt_password self.salt = make_salt unless has_password?(password) self.encrypted_password = encrypt(password) end def encrypt(string) secure_hash("#{salt}--#{string}") end def make_salt secure_hash("#{Time.now.utc}--#{password}") end def secure_hash(string) Digest::SHA2.hexdigest(string) end Error message displayed when the test is failing c:\RailsInstaller\work\apptwit>rspec spec/models/user_spec.rb .................F Failures: 1) User password validations password encryption authenticate method should return the user on email/password Failure/Error: matching_user.should == #user expected: #<User id: 1, name: "Example user", email: "user#example.com", created_at: "2011-12-07 19:08:23 ed_at: "2011-12-07 19:08:23", encrypted_password: "fbdbaf712fa1b6c925c4ab2192e73ac9f9d1bedf67630610d68..."> got: nil (using ==) # ./spec/models/user_spec.rb:204:in `block (5 levels) in <top (required)>' Finished in 221.37 seconds 18 examples, 1 failure Failed examples: rspec ./spec/models/user_spec.rb:202 # User password validations password encryption authenticate method should he user on email/password match I would appreciate any pointers, Thanks a lot.
If matching_user is nil, then you might want to put some puts email and puts user.inspect statements in self.authenticate to debug it. It seems as though it's either not able to find the user by email or your password is incorrect for some reason in the authenticate method.