require "spec_helper"

describe Clearance::PasswordStrategies::BCryptMigrationFromSHA1 do
  describe "#password=" do
    it "encrypts the password into a BCrypt-encrypted encrypted_password" do
      stub_bcrypt_password

      expect(model_instance.encrypted_password).to eq encrypted_password
    end

    it "encrypts with BCrypt" do
      stub_bcrypt_password

      expect(BCrypt::Password).to have_received(:create).
        with(password, anything)
    end

    it "sets the pasword on the subject" do
      stub_bcrypt_password

      expect(model_instance.password).to be_present
    end

    def stub_bcrypt_password
      model_instance.salt = salt
      digestable = "--#{salt}--#{password}--"
      model_instance.encrypted_password = Digest::SHA1.hexdigest(digestable)
      allow(BCrypt::Password).to receive(:create).and_return(encrypted_password)
      model_instance.password = password
    end

    def encrypted_password
      @encrypted_password ||= double("encrypted password")
    end
  end

  describe "#authenticated?" do
    context "with a SHA1-encrypted password" do
      it "is authenticated" do
        model_instance.salt = salt
        model_instance.encrypted_password = sha1_hash
        allow(model_instance).to receive(:save)

        expect(model_instance).to be_authenticated(password)
      end

      it "changes the hash into a BCrypt-encrypted one" do
        model_instance.salt = salt
        model_instance.encrypted_password = sha1_hash
        allow(model_instance).to receive(:save)

        model_instance.authenticated? password

        expect(model_instance.encrypted_password).not_to eq sha1_hash
      end

      it "does not raise a BCrypt error for invalid passwords" do
        model_instance.salt = salt
        model_instance.encrypted_password = sha1_hash

        expect do
          model_instance.authenticated? "bad" + password
        end.not_to raise_error
      end

      it "saves the subject to database" do
        model_instance.salt = salt
        model_instance.encrypted_password = sha1_hash
        allow(model_instance).to receive(:save)

        model_instance.authenticated? password

        expect(model_instance).to have_received(:save)
      end

      def sha1_hash
        Digest::SHA1.hexdigest("--#{salt}--#{password}--")
      end
    end

    context "with a BCrypt-encrypted password" do
      it "is authenticated" do
        model_instance.encrypted_password = bcrypt_hash

        expect(model_instance).to be_authenticated(password)
      end

      it "does not change the hash" do
        model_instance.encrypted_password = bcrypt_hash

        model_instance.authenticated? password

        expect(model_instance.encrypted_password.to_s).to eq bcrypt_hash.to_s
      end

      def bcrypt_hash
        @bcrypt_hash ||= ::BCrypt::Password.create(password)
      end
    end
  end

  def model_instance
    @model_instance ||= fake_model_with_password_strategy(
      Clearance::PasswordStrategies::BCryptMigrationFromSHA1
    )
  end

  def salt
    "salt"
  end

  def password
    "password"
  end
end