# frozen_string_literal: true

require 'fast_spec_helper'
require 'rspec-parameterized'

RSpec.describe Gitlab::Cluster::LifecycleEvents do
  # we create a new instance to ensure that we do not touch existing hooks
  let(:replica) { Class.new(described_class) }

  context 'hooks execution' do
    using RSpec::Parameterized::TableSyntax

    where(:method, :hook_names) do
      :do_worker_start             | %i[worker_start_hooks]
      :do_before_fork              | %i[before_fork_hooks]
      :do_before_graceful_shutdown | %i[master_blackout_period master_graceful_shutdown]
      :do_before_master_restart    | %i[master_restart_hooks]
    end

    before do
      # disable blackout period to speed-up tests
      stub_config(shutdown: { blackout_seconds: 0 })
    end

    with_them do
      subject { replica.public_send(method) }

      it 'executes all hooks' do
        hook_names.each do |hook_name|
          hook = double
          replica.instance_variable_set(:"@#{hook_name}", [hook])

          # ensure that proper hooks are called
          expect(hook).to receive(:call)
          expect(replica).to receive(:call).with(hook_name, anything).and_call_original
        end

        subject
      end
    end
  end

  describe '#call' do
    let(:name) { :my_hooks }

    subject { replica.send(:call, name, hooks) }

    context 'when many hooks raise exception' do
      let(:hooks) do
        [
          -> { raise 'Exception A' },
          -> { raise 'Exception B' }
        ]
      end

      context 'USE_FATAL_LIFECYCLE_EVENTS is set to default' do
        it 'only first hook is executed and is fatal' do
          expect(hooks[0]).to receive(:call).and_call_original
          expect(hooks[1]).not_to receive(:call)

          expect(Gitlab::ErrorTracking).to receive(:track_exception).and_call_original
          expect(replica).to receive(:warn).with('ERROR: The hook my_hooks failed with exception (RuntimeError) "Exception A".')

          expect { subject }.to raise_error(described_class::FatalError, 'Exception A')
        end
      end

      context 'when USE_FATAL_LIFECYCLE_EVENTS is disabled' do
        before do
          stub_const('Gitlab::Cluster::LifecycleEvents::USE_FATAL_LIFECYCLE_EVENTS', false)
        end

        it 'many hooks are executed and all exceptions are logged' do
          expect(hooks[0]).to receive(:call).and_call_original
          expect(hooks[1]).to receive(:call).and_call_original

          expect(Gitlab::ErrorTracking).to receive(:track_exception).twice.and_call_original
          expect(replica).to receive(:warn).twice.and_call_original

          expect { subject }.not_to raise_error
        end
      end
    end
  end
end