# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/

require 'rails_helper'
require 'lib/auth/backend/backend_examples'

RSpec.describe Auth::Backend::Internal do

  let(:user)     { create(:user) }
  let(:password) { 'secure' }
  let(:auth)     { Auth.new(user.login, password) }
  let(:instance) { described_class.new({ adapter: described_class.name }, auth) }

  describe '#valid?' do
    it_behaves_like 'Auth backend'

    context 'when password is given' do
      let(:user) { create(:user, password: password) }

      it 'authenticates' do
        expect(instance.valid?).to be true
      end
    end

    context 'when very long password is given' do
      let(:password) { Faker::Lorem.characters(number: 1_111) }
      let(:user)     do
        # temporary override constant to create a test user with a very long password
        initial = PasswordPolicy::MaxLength::MAX_LENGTH
        stub_const 'PasswordPolicy::MaxLength::MAX_LENGTH', 99_999
        user = create(:user, password: password)
        stub_const 'PasswordPolicy::MaxLength::MAX_LENGTH', initial
        user
      end

      it 'does not try to verify it' do
        allow(PasswordHash).to receive(:verified?)

        instance.valid?

        expect(PasswordHash).not_to have_received(:verified?)
      end

      it 'returns false even though password is matching' do
        expect(instance).not_to be_valid
      end
    end

    context 'when given password matches stored hash' do

      let(:password) { user.password }
      let(:user) { create(:user, password: 'secure') }

      it "doesn't authenticate" do
        expect(instance.valid?).to be false
      end
    end

    context 'when given password is blank' do
      let(:password) { '' }
      let(:user) { create(:user, password: 'secure') }

      it "doesn't authenticate" do
        expect(instance.valid?).to be false
      end
    end

    context 'with legacy SHA2 passwords' do
      let(:user) { create(:user, password: PasswordHash.sha2(password)) }

      it 'is password hash crypted' do
        expect(PasswordHash.crypted?(user.password)).to be true
      end

      it 'is password hash legacy' do
        expect(PasswordHash.legacy?(user.password, password)).to be true
      end

      it 'valid authentication' do
        expect(instance.valid?).to be true
      end

      it 'is password hash not legacy after validation' do
        instance.valid?
        expect(PasswordHash.legacy?(user.reload.password, password)).to be false
      end

      it 'is password hash crypted after validation' do
        instance.valid?
        expect(PasswordHash.crypted?(user.password)).to be true
      end
    end

    context 'when affecting Auth#increase_login_failed_attempts' do

      context 'when authentication fails' do
        let(:password) { 'wrong' }
        let(:user)     { create(:user, password: 'secure') }

        it 'sets Auth#increase_login_failed_attempts flag to true' do
          expect { instance.valid? }.to change(auth, :increase_login_failed_attempts).from(false).to(true)
        end
      end

      context 'when authentication succeeds' do
        let(:user) { create(:user, password: password) }

        it "doesn't change Auth#increase_login_failed_attempts flag" do
          expect { instance.valid? }.not_to change(auth, :increase_login_failed_attempts)
        end
      end
    end
  end
end