# frozen_string_literal: true

require 'spec_helper'

describe Projects::Environments::PrometheusApiController do
  set(:project) { create(:project) }
  set(:environment) { create(:environment, project: project) }
  set(:user) { create(:user) }

  before do
    project.add_reporter(user)
    sign_in(user)
  end

  describe 'GET #proxy' do
    let(:prometheus_proxy_service) { instance_double(Prometheus::ProxyService) }

    let(:expected_params) do
      ActionController::Parameters.new(
        environment_params(
          proxy_path: 'query',
          controller: 'projects/environments/prometheus_api',
          action: 'proxy'
        )
      ).permit!
    end

    context 'with valid requests' do
      before do
        allow(Prometheus::ProxyService).to receive(:new)
          .with(environment, 'GET', 'query', expected_params)
          .and_return(prometheus_proxy_service)

        allow(prometheus_proxy_service).to receive(:execute)
          .and_return(service_result)
      end

      context 'with success result' do
        let(:service_result) { { status: :success, body: prometheus_body } }
        let(:prometheus_body) { '{"status":"success"}' }
        let(:prometheus_json_body) { JSON.parse(prometheus_body) }

        it 'returns prometheus response' do
          get :proxy, params: environment_params

          expect(Prometheus::ProxyService).to have_received(:new)
            .with(environment, 'GET', 'query', expected_params)
          expect(response).to have_gitlab_http_status(:ok)
          expect(json_response).to eq(prometheus_json_body)
        end

        context 'with format string' do
          before do
            expected_params[:query] = %{up{environment="#{environment.slug}"}}
          end

          it 'replaces variables with values' do
            get :proxy, params: environment_params.merge(query: 'up{environment="%{ci_environment_slug}"}')

            expect(Prometheus::ProxyService).to have_received(:new)
              .with(environment, 'GET', 'query', expected_params)
          end

          context 'with nil query' do
            let(:params_without_query) do
              params = environment_params
              params.delete(:query)
              params
            end

            before do
              expected_params.delete(:query)
            end

            it 'does not raise error' do
              get :proxy, params: params_without_query

              expect(Prometheus::ProxyService).to have_received(:new)
                .with(environment, 'GET', 'query', expected_params)
            end
          end
        end
      end

      context 'with nil result' do
        let(:service_result) { nil }

        it 'returns 204 no_content' do
          get :proxy, params: environment_params

          expect(json_response['status']).to eq('processing')
          expect(json_response['message']).to eq('Not ready yet. Try again later.')
          expect(response).to have_gitlab_http_status(:no_content)
        end
      end

      context 'with 404 result' do
        let(:service_result) { { http_status: 404, status: :success, body: '{"body": "value"}' } }

        it 'returns body' do
          get :proxy, params: environment_params

          expect(response).to have_gitlab_http_status(:not_found)
          expect(json_response['body']).to eq('value')
        end
      end

      context 'with error result' do
        context 'with http_status' do
          let(:service_result) do
            { http_status: :service_unavailable, status: :error, message: 'error message' }
          end

          it 'sets the http response status code' do
            get :proxy, params: environment_params

            expect(response).to have_gitlab_http_status(:service_unavailable)
            expect(json_response['status']).to eq('error')
            expect(json_response['message']).to eq('error message')
          end
        end

        context 'without http_status' do
          let(:service_result) { { status: :error, message: 'error message' } }

          it 'returns bad_request' do
            get :proxy, params: environment_params

            expect(response).to have_gitlab_http_status(:bad_request)
            expect(json_response['status']).to eq('error')
            expect(json_response['message']).to eq('error message')
          end
        end
      end
    end

    context 'with inappropriate requests' do
      context 'with anonymous user' do
        before do
          sign_out(user)
        end

        it 'redirects to signin page' do
          get :proxy, params: environment_params

          expect(response).to redirect_to(new_user_session_path)
        end
      end

      context 'without correct permissions' do
        before do
          project.team.truncate
        end

        it 'returns 404' do
          get :proxy, params: environment_params

          expect(response).to have_gitlab_http_status(:not_found)
        end
      end
    end

    context 'with invalid environment id' do
      let(:other_environment) { create(:environment) }

      it 'returns 404' do
        get :proxy, params: environment_params(id: other_environment.id)

        expect(response).to have_gitlab_http_status(:not_found)
      end
    end
  end

  private

  def environment_params(params = {})
    {
      id: environment.id.to_s,
      namespace_id: project.namespace.full_path,
      project_id: project.name,
      proxy_path: 'query',
      query: '1'
    }.merge(params)
  end
end