# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Gitlab::ErrorTracking::ContextPayloadGenerator do
  subject(:generator) { described_class.new }

  let(:extra) do
    {
      some_other_info: 'info',
      issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/-/issues/1'
    }
  end

  let(:exception) { StandardError.new("Dummy exception") }

  before do
    allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('cid')
    allow(I18n).to receive(:locale).and_return('en')
  end

  context 'user metadata' do
    let(:user) { create(:user) }

    it 'appends user metadata to the payload' do
      payload = {}

      Gitlab::ApplicationContext.with_context(user: user) do
        payload = generator.generate(exception, extra)
      end

      expect(payload[:user]).to eql(
        username: user.username
      )
    end
  end

  context 'tags metadata' do
    context 'when the GITLAB_SENTRY_EXTRA_TAGS env is not set' do
      before do
        stub_env('GITLAB_SENTRY_EXTRA_TAGS', nil)
      end

      it 'does not log into AppLogger' do
        expect(Gitlab::AppLogger).not_to receive(:debug)

        generator.generate(exception, extra)
      end

      it 'does not send any extra tags' do
        payload = {}

        Gitlab::ApplicationContext.with_context(feature_category: 'feature_a') do
          payload = generator.generate(exception, extra)
        end

        expect(payload[:tags]).to eql(
          correlation_id: 'cid',
          locale: 'en',
          program: 'test',
          feature_category: 'feature_a'
        )
      end
    end

    context 'when the GITLAB_SENTRY_EXTRA_TAGS env is a JSON hash' do
      before do
        stub_env('GITLAB_SENTRY_EXTRA_TAGS', { foo: 'bar', baz: 'quux' }.to_json)
      end

      it 'includes those tags in all events' do
        payload = {}

        Gitlab::ApplicationContext.with_context(feature_category: 'feature_a') do
          payload = generator.generate(exception, extra)
        end

        expect(payload[:tags]).to eql(
          correlation_id: 'cid',
          locale: 'en',
          program: 'test',
          feature_category: 'feature_a',
          'foo' => 'bar',
          'baz' => 'quux'
        )
      end

      it 'does not log into AppLogger' do
        expect(Gitlab::AppLogger).not_to receive(:debug)

        generator.generate(exception, extra)
      end

      context 'with generated tags' do
        it 'includes all tags' do
          payload = {}

          Gitlab::ApplicationContext.with_context(feature_category: 'feature_a') do
            payload = generator.generate(exception, extra, { 'mytag' => '123' })
          end

          expect(payload[:tags]).to eql(
            correlation_id: 'cid',
            locale: 'en',
            program: 'test',
            feature_category: 'feature_a',
            'foo' => 'bar',
            'baz' => 'quux',
            'mytag' => '123'
          )
        end
      end
    end

    context 'when the GITLAB_SENTRY_EXTRA_TAGS env is not a JSON hash' do
      using RSpec::Parameterized::TableSyntax

      where(:env_var, :error) do
        { foo: 'bar', baz: 'quux' }.inspect | 'JSON::ParserError'
        [].to_json | 'NoMethodError'
        [%w[foo bar]].to_json | 'NoMethodError'
        %w[foo bar].to_json | 'NoMethodError'
        '"string"' | 'NoMethodError'
      end

      with_them do
        before do
          stub_env('GITLAB_SENTRY_EXTRA_TAGS', env_var)
        end

        it 'logs into AppLogger' do
          expect(Gitlab::AppLogger).to receive(:debug).with(a_string_matching(error))

          generator.generate({})
        end

        it 'does not include any extra tags' do
          payload = {}

          Gitlab::ApplicationContext.with_context(feature_category: 'feature_a') do
            payload = generator.generate(exception, extra)
          end

          expect(payload[:tags]).to eql(
            correlation_id: 'cid',
            locale: 'en',
            program: 'test',
            feature_category: 'feature_a'
          )
        end
      end
    end
  end

  context 'extra metadata' do
    it 'appends extra metadata to the payload' do
      payload = generator.generate(exception, extra)

      expect(payload[:extra]).to eql(
        some_other_info: 'info',
        issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/-/issues/1'
      )
    end

    it 'appends exception embedded extra metadata to the payload' do
      allow(exception).to receive(:sentry_extra_data).and_return(
        some_other_info: 'another_info',
        mr_url: 'https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1'
      )

      payload = generator.generate(exception, extra)

      expect(payload[:extra]).to eql(
        some_other_info: 'another_info',
        issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/-/issues/1',
        mr_url: 'https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1'
      )
    end

    it 'filters sensitive extra info' do
      extra[:my_token] = '456'
      allow(exception).to receive(:sentry_extra_data).and_return(
        mr_url: 'https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1',
        another_token: '1234'
      )

      payload = generator.generate(exception, extra)

      expect(payload[:extra]).to eql(
        some_other_info: 'info',
        issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/-/issues/1',
        mr_url: 'https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1',
        my_token: '[FILTERED]',
        another_token: '[FILTERED]'
      )
    end
  end
end
