# frozen_string_literal: true

require 'spec_helper'

describe Clusters::Applications::PrometheusConfigService do
  include Gitlab::Routing.url_helpers

  set(:project) { create(:project) }
  set(:production) { create(:environment, project: project) }
  set(:cluster) { create(:cluster, :provided_by_user, projects: [project]) }

  let(:application) do
    create(:clusters_applications_prometheus, :installed, cluster: cluster)
  end

  subject { described_class.new(project, cluster, application).execute(input) }

  describe '#execute' do
    let(:input) do
      YAML.load_file(Rails.root.join('vendor/prometheus/values.yaml'))
    end

    context 'with alerts' do
      let!(:alert) do
        create(:prometheus_alert, project: project, environment: production)
      end

      it 'enables alertmanager' do
        expect(subject.dig('alertmanager', 'enabled')).to eq(true)
      end

      describe 'alertmanagerFiles' do
        let(:alertmanager) do
          subject.dig('alertmanagerFiles', 'alertmanager.yml')
        end

        it 'contains receivers and route' do
          expect(alertmanager.keys).to contain_exactly('receivers', 'route')
        end

        describe 'receivers' do
          let(:receiver) { alertmanager.dig('receivers', 0) }
          let(:webhook_config) { receiver.dig('webhook_configs', 0) }

          let(:notify_url) do
            notify_project_prometheus_alerts_url(project, format: :json)
          end

          it 'sets receiver' do
            expect(receiver['name']).to eq('gitlab')
          end

          it 'sets webhook_config' do
            expect(webhook_config).to eq(
              'url' => notify_url,
              'send_resolved' => true,
              'http_config' => {
                'bearer_token' => application.alert_manager_token
              }
            )
          end
        end

        describe 'route' do
          let(:route) { alertmanager.fetch('route') }

          it 'sets route' do
            expect(route).to eq(
              'receiver' => 'gitlab',
              'group_wait' => '30s',
              'group_interval' => '5m',
              'repeat_interval' => '4h'
            )
          end
        end
      end

      describe 'serverFiles' do
        let(:groups) { subject.dig('serverFiles', 'alerts', 'groups') }

        it 'sets the alerts' do
          rules = groups.dig(0, 'rules')
          expect(rules.size).to eq(1)

          expect(rules.first['alert']).to eq(alert.title)
        end

        context 'with parameterized queries' do
          let!(:alert) do
            create(:prometheus_alert,
                   project: project,
                   environment: production,
                   prometheus_metric: metric)
          end

          let(:metric) do
            create(:prometheus_metric, query: query, project: project)
          end

          let(:query) { '%{ci_environment_slug}' }

          it 'substitutes query variables' do
            expect(Gitlab::Prometheus::QueryVariables)
              .to receive(:call)
              .with(production)
              .and_call_original

            expr = groups.dig(0, 'rules', 0, 'expr')
            expect(expr).to include(production.name)
          end
        end

        context 'with multiple environments' do
          let(:staging) { create(:environment, project: project) }

          before do
            create(:prometheus_alert, project: project, environment: production)
            create(:prometheus_alert, project: project, environment: staging)
          end

          it 'sets alerts for multiple environment' do
            env_names = groups.map { |group| group['name'] }
            expect(env_names).to contain_exactly(
              "#{production.name}.rules",
              "#{staging.name}.rules"
            )
          end

          it 'substitutes query variables once per environment' do
            expect(Gitlab::Prometheus::QueryVariables)
              .to receive(:call)
              .with(production)

            expect(Gitlab::Prometheus::QueryVariables)
              .to receive(:call)
              .with(staging)

            subject
          end
        end
      end
    end

    context 'without alerts' do
      it 'disables alertmanager' do
        expect(subject.dig('alertmanager', 'enabled')).to eq(false)
      end

      it 'removes alertmanagerFiles' do
        expect(subject).not_to include('alertmanagerFiles')
      end

      it 'removes alerts' do
        expect(subject.dig('serverFiles', 'alerts')).to eq({})
      end
    end
  end
end
