idrysdale
idrysdale

Reputation: 1551

Why is rspec not updating the database record with a password reset token?

I'm running into a bit of a problem with RSpec when running a rails test. The test is as follows:

it "allows a user to request a password reset" do
  user = FactoryGirl.create(:user, :name => "John Smith", :email => "[email protected]")
  identity = Identity.create!(email: user.email, password: "please", password_confirmation: "please")
  visit "/"
  click_on "Forgotten password?"
  fill_in "email", :with => "[email protected]"
  click_on "Reset Password"
  ActionMailer::Base.deliveries.last.to.should include(user.email)
  ActionMailer::Base.deliveries.last.body.should include(identity.password_reset_token)
  visit '/password_resets/' + identity.password_reset_token.to_s + '/edit'

    ^ **** ERROR HERE : password_reset_token is nil! ****

  fill_in "identity_password", :with => "newpass123"
  fill_in "identity_password_confirmation", :with => "newpass123"
  click_on "Update Password"
  within page.all('div#loginModal')[0] do
    fill_in "auth_key", :with => "[email protected]"
    fill_in "password", :with => "newpass123"
    click_button "Log in"
  end
  page.should have_content("Hi John")
end

You can see I've outlined the error I'm getting back, which is strange, because when I inspect the email sent, it does in fact generate a password_reset_token.

For information, the code I use to do this is:

class PasswordResetsController < ApplicationController

  def create
    identity = Identity.find_by_email(params[:email])
    identity.send_password_reset if identity
    redirect_to root_url, :notice => "Email sent with password reset instructions."
  end

  def edit
    @identity = Identity.find_by_password_reset_token!(params[:id])
  end

  def update
    @identity = Identity.find_by_password_reset_token!(params[:id])
    if @identity.password_reset_sent_at < 2.hours.ago
      redirect_to new_password_reset_path, :alert => "Password reset has expired."
    elsif @identity.update_attributes(params[:identity])
      redirect_to root_url, :notice => "Password has been reset."
    else
      render :edit
    end
  end

end

and...

class Identity < OmniAuth::Identity::Models::ActiveRecord
  attr_accessible :email, :password, :password_confirmation

  validates_uniqueness_of :email
  validates_format_of :email, :with => /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i

  def send_password_reset
    generate_token(:password_reset_token)
    self.password_reset_sent_at = Time.zone.now
    self.save!
    UserMailer.password_reset(self).deliver
  end

  private

    def generate_token(column)
      begin
        self[column] = SecureRandom.urlsafe_base64
      end while Identity.exists?(column => self[column])
    end
end

Can anyone suggest why this might be happening? Is it something to do with the database record not being saved / accessed or cached somewhere in my test environment?

Any help appreciated.

Upvotes: 0

Views: 510

Answers (2)

zetetic
zetetic

Reputation: 47548

You may be running into timing issues. After the click_on 'Reset Password' step, check that the page comes back with the expected value before moving on to the visit 'passwords_reset...' step.

Upvotes: 0

Paulo Fidalgo
Paulo Fidalgo

Reputation: 22311

Just a guess: Don't you need to update your identity variable, like find it by email, in order to update the object with the new parameters? Before you visit 'password_resets', try to load the identity with

Identity.find_by_email(user.email)

Upvotes: 1

Related Questions