# frozen_string_literal: true
require 'spec_helper'

RSpec.describe Gitlab::Graphql::CallsGitaly::FieldExtension, :request_store do
  include GraphqlHelpers

  let(:field_args) { {} }
  let(:owner) { fresh_object_type }
  let(:field) do
    ::Types::BaseField.new(name: 'value', type: GraphQL::Types::String, null: true, owner: owner, **field_args)
  end

  def resolve_value
    resolve_field(field, { value: 'foo' }, object_type: owner)
  end

  context 'when the field calls gitaly' do
    before do
      owner.define_method :value do
        Gitlab::SafeRequestStore['gitaly_call_actual'] = 1
        'fresh-from-the-gitaly-mines!'
      end
    end

    context 'when the field has a constant complexity' do
      let(:field_args) { { complexity: 100 } }

      it 'allows the call' do
        expect { resolve_value }.not_to raise_error
      end
    end

    context 'when the field declares that it calls gitaly' do
      let(:field_args) { { calls_gitaly: true } }

      it 'allows the call' do
        expect { resolve_value }.not_to raise_error
      end
    end

    context 'when the field does not have these arguments' do
      let(:field_args) { {} }

      it 'notices, and raises, mentioning the field' do
        expect { resolve_value }.to raise_error(include('Object.value'))
      end
    end
  end

  context 'when it does not call gitaly' do
    let(:field_args) { {} }

    it 'does not raise' do
      value = resolve_value

      expect(value).to eq 'foo'
    end
  end

  context 'when some field calls gitaly while we were waiting' do
    let(:extension) { described_class.new(field: field, options: {}) }

    it 'is acceptable if all are accounted for' do
      object = :anything
      arguments = :any_args

      ::Gitlab::SafeRequestStore['gitaly_call_actual'] = 3
      ::Gitlab::SafeRequestStore['graphql_gitaly_accounted_for'] = 0

      expect do |b|
        extension.resolve(object: object, arguments: arguments, &b)
      end.to yield_with_args(object, arguments, [3, 0])

      ::Gitlab::SafeRequestStore['gitaly_call_actual'] = 13
      ::Gitlab::SafeRequestStore['graphql_gitaly_accounted_for'] = 10

      expect { extension.after_resolve(value: 'foo', memo: [3, 0]) }.not_to raise_error
    end

    it 'is unacceptable if some of the calls are unaccounted for' do
      ::Gitlab::SafeRequestStore['gitaly_call_actual'] = 10
      ::Gitlab::SafeRequestStore['graphql_gitaly_accounted_for'] = 9

      expect { extension.after_resolve(value: 'foo', memo: [0, 0]) }.to raise_error(include('Object.value'))
    end
  end
end
