Home > Software engineering >  Rspec test failing with a "Expected response to be a <3XX: redirect>, but was a <200:
Rspec test failing with a "Expected response to be a <3XX: redirect>, but was a <200:

Time:10-26

I am trying to validate that I will be redirected to the homepage after a successful password reset. The final line of my Rspec test is throwing the following error:

  1) PasswordRetrievalController check if password updated check if password and repassword do match
     Failure/Error: expect(response).to redirect_to '/'
     
       Expected response to be a <3XX: redirect>, but was a <200: OK>
       Response body: 
     # ./spec/controllers/password_retrieval_controller_spec.rb:89:in `block (3 levels) in <top (required)>'

I have tried many things from looking through other threads related to this error but none seem to solve my issue. This is my Rspec test:

  describe 'check if password updated' do
    it 'check if password and repassword do match' do
      @user = User.new
      @user.email = '[email protected]'
      @user.fullname = 'John Doe'
      @user.name = 'classman'
      @user.save!
      request_params = { reset: { password: 'AAAAAAAAA123!!', password_confirmation: 'AAAAAAAAA123!!', email: '[email protected]' } }
      post :update_password, params: request_params
      expect(response).to redirect_to '/'
    end
  end

And this is my controller function, update password is towards the bottom. As you can see the password must match the retyped password in order to redirect to the homepage:

class PasswordRetrievalController < ApplicationController
  def action_allowed?
    true
  end

  def forgotten
    render template: 'password_retrieval/forgotten'
  end

  # on click of request password button, sends a password reset url with a randomly generated token parameter to an authenticated user email
  def send_password
    if params[:user][:email].nil? || params[:user][:email].strip.empty?
      flash[:error] = 'Please enter an e-mail address.'
      render template: 'password_retrieval/forgotten'
    else
      user = User.find_by(email: params[:user][:email])
      if user
        # formats password reset url to include a queryable token parameter 
        url_format = '/password_edit/validate_password_token?token='
        # generates a random URL-safe base64 token with default length of 16 characters
        token = SecureRandom.urlsafe_base64
        PasswordReset.save_token(user, token)
        url = request.base_url   url_format   token
        # delivers formatted password reset url to a valid user email
        MailerHelper.send_mail_to_user(user, 'Expertiza password reset', 'send_password', url).deliver_now
        ExpertizaLogger.info LoggerMessage.new(controller_name, user.name, 'A link to reset your password has been sent to users e-mail address.', request)
        flash[:success] = 'A link to reset your password has been sent to your e-mail address.'
        redirect_to '/'
      else
        ExpertizaLogger.error LoggerMessage.new(controller_name, params[:user][:email], 'No user is registered with provided email id!', request)
        flash[:error] = 'No account is associated with the e-mail address: "'   params[:user][:email]   '". Please try again.'
        render template: 'password_retrieval/forgotten'
      end
    end
  end

  # The token obtained from the reset url is first checked if it is valid ( if actually generated by the application), then checks if the token is active.
  def validate_password_token
    days_until_token_expiration = 1

    if params[:token].nil?
      require_password_reset_token
    else
      @token = Digest::SHA1.hexdigest(params[:token])
      password_reset = PasswordReset.find_by(token: @token)
      if password_reset
        # URL expires after 1 day
        expired_url = password_reset.updated_at   days_until_token_expiration.day
        if Time.now < expired_url
          # redirect_to action: 'reset_password', email: password_reset.user_email
          @email = password_reset.user_email
          render template: 'password_retrieval/reset_password'
        else
          ExpertizaLogger.error LoggerMessage.new(controller_name, '', 'User tried to access expired link!', request)
          flash[:error] = 'Link expired . Please request to reset password again'
          render template: 'password_retrieval/forgotten'
        end
      else
        ExpertizaLogger.error LoggerMessage.new(controller_name, '', 'User tried to access either expired link or wrong token!', request)
        flash[:error] = 'Link either expired or wrong Token. Please request to reset password again'
        render template: 'password_retrieval/forgotten'
      end
    end
  end

  # avoid users to access this page without a valid token
  def reset_password
    require_password_reset_token
  end

  # called after entering password and repassword, checks for validation and updates the password of the email
  def update_password
    if params[:reset][:password] == params[:reset][:repassword]
      user = User.find_by(email: params[:reset][:email])
      user.password = params[:reset][:password]
      user.password_confirmation = params[:reset][:repassword]
      if user.save
        PasswordReset.delete_all(user_email: user.email)
        ExpertizaLogger.info LoggerMessage.new(controller_name, user.name, 'Password was reset for the user', request)
        flash[:success] = 'Password was successfully reset'
      else
        ExpertizaLogger.error LoggerMessage.new(controller_name, user.name, 'Password reset operation failed for the user while saving record', request)
        flash[:error] = 'Password cannot be updated. Please try again'
      end
      redirect_to '/'
    else
      ExpertizaLogger.error LoggerMessage.new(controller_name, '', 'Password provided by the user did not match', request)
      flash[:error] = 'Password and confirm-password do not match. Try again'
      @email = params[:reset][:email]
      render template: 'password_retrieval/reset_password'
    end
  end

  def require_password_reset_token
    flash[:error] = 'Password reset page can only be accessed with a generated link, sent to your email'
    render template: 'password_retrieval/forgotten'
  end
    
end

Any help would be greatly appreciated.

UPDATE: HTTP error is gone and I am now getting this:

  1) PasswordRetrievalController check if password updated check if password and repassword do match
     Failure/Error: PasswordReset.delete_all(user_email: user.email)
     
     ArgumentError:
       wrong number of arguments (given 1, expected 0)

My new Rspec tests looks like this:

describe 'check if password updated' do
    it 'check if password and repassword do match' do
      @user = User.new
      @user.email = '[email protected]'
      @user.fullname = 'John Doe'
      @user.name = 'classman'
      @user.save!
      local_token = 'some random string'
      @password_retreival = PasswordReset.new
      @password_retreival.token = Digest::SHA1.hexdigest(local_token)
      @password_retreival.user_email = '[email protected]'
      @password_retreival.save!
      request_params = { reset: { password: 'AAAAAAAAA123!!', repassword: 'AAAAAAAAA123!!', email: '[email protected]' } }
      post :update_password, params: request_params
      expect(response).to redirect_to '/'
    end
  end

CodePudding user response:

In your request_params you're calling the confirmation param password_confirmation, but then in your update_password method, you're first checking if params[:reset][:password] == params[:reset][:repassword]. It doesn't seem like you're passing in params[:reset][:repassword] at all, so my guess is that you're failing the if condition and falling through to your error case

  • Related