require "spec_helper"

describe Clearance::PasswordsController do
  it { is_expected.to be_a Clearance::BaseController }

  describe "#new" do
    it "renders the password reset form" do
      get :new

      expect(response).to be_success
      expect(response).to render_template(:new)
    end
  end

  describe "#create" do
    context "email corresponds to an existing user" do
      it "generates a password change token" do
        user = create(:user)

        post :create, password: { email: user.email.upcase }

        expect(user.reload.confirmation_token).not_to be_nil
      end

      it "sends the password reset email" do
        ActionMailer::Base.deliveries.clear
        user = create(:user)

        post :create, password: { email: user.email }

        email = ActionMailer::Base.deliveries.last
        expect(email.subject).to match(/change your password/i)
      end
    end

    context "email does not belong to an existing user" do
      it "does not deliver an email" do
        ActionMailer::Base.deliveries.clear
        email = "this_user_does_not_exist@non_existent_domain.com"

        post :create, password: { email: email }

        expect(ActionMailer::Base.deliveries).to be_empty
      end

      it "still responds with success so as not to leak registered users" do
        email = "this_user_does_not_exist@non_existent_domain.com"

        post :create, password: { email: email }

        expect(response).to be_success
        expect(response).to render_template "passwords/create"
      end
    end
  end

  describe "#edit" do
    context "valid id and token are supplied in url" do
      it "redirects to the edit page with token now removed from url" do
        user = create(:user, :with_forgotten_password)

        get :edit, user_id: user, token: user.confirmation_token

        expect(response).to be_redirect
        expect(response).to redirect_to edit_user_password_url(user)
        expect(session[:password_reset_token]).to eq user.confirmation_token
      end
    end

    context "valid id in url and valid token in session" do
      it "renders the password reset form" do
        user = create(:user, :with_forgotten_password)

        request.session[:password_reset_token] = user.confirmation_token
        get :edit, user_id: user

        expect(response).to be_success
        expect(response).to render_template(:edit)
        expect(assigns(:user)).to eq user
      end
    end

    context "blank token is supplied" do
      it "renders the new password reset form with a flash notice" do
        get :edit, user_id: 1, token: ""

        expect(response).to render_template(:new)
        expect(flash.now[:notice]).to match(/double check the URL/i)
      end
    end

    context "invalid token is supplied" do
      it "renders the new password reset form with a flash notice" do
        user = create(:user, :with_forgotten_password)

        get :edit, user_id: 1, token: user.confirmation_token + "a"

        expect(response).to render_template(:new)
        expect(flash.now[:notice]).to match(/double check the URL/i)
      end
    end
  end

  describe "#update" do
    context "valid id, token, and new password provided" do
      it "updates the user's password" do
        user = create(:user, :with_forgotten_password)
        old_encrypted_password = user.encrypted_password

        put :update, update_parameters(user, new_password: "my_new_password")

        expect(user.reload.encrypted_password).not_to eq old_encrypted_password
      end

      it "signs the user in and redirects" do
        user = create(:user, :with_forgotten_password)

        put :update, update_parameters(user, new_password: "my_new_password")

        expect(response).to redirect_to(Clearance.configuration.redirect_url)
        expect(cookies[:remember_token]).to be_present
      end
    end

    context "password update fails" do
      it "does not update the password" do
        user = create(:user, :with_forgotten_password)
        old_encrypted_password = user.encrypted_password

        put :update, update_parameters(user, new_password: "")

        user.reload
        expect(user.encrypted_password).to eq old_encrypted_password
        expect(user.confirmation_token).to be_present
      end

      it "re-renders the password edit form" do
        user = create(:user, :with_forgotten_password)

        put :update, update_parameters(user, new_password: "")

        expect(flash.now[:notice]).to match(/password can't be blank/i)
        expect(response).to render_template(:edit)
        expect(cookies[:remember_token]).to be_nil
      end
    end
  end

  def update_parameters(user, options = {})
    new_password = options.fetch(:new_password)

    {
      user_id: user,
      token: user.confirmation_token,
      password_reset: { password: new_password }
    }
  end
end