summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/graphql
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/graphql')
-rw-r--r--spec/lib/gitlab/graphql/calls_gitaly/field_extension_spec.rb87
-rw-r--r--spec/lib/gitlab/graphql/calls_gitaly/instrumentation_spec.rb23
-rw-r--r--spec/lib/gitlab/graphql/docs/renderer_spec.rb234
-rw-r--r--spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb2
-rw-r--r--spec/lib/gitlab/graphql/pagination/keyset/order_info_spec.rb12
-rw-r--r--spec/lib/gitlab/graphql/pagination/keyset/query_builder_spec.rb38
-rw-r--r--spec/lib/gitlab/graphql/present/field_extension_spec.rb143
-rw-r--r--spec/lib/gitlab/graphql/query_analyzers/logger_analyzer_spec.rb56
8 files changed, 461 insertions, 134 deletions
diff --git a/spec/lib/gitlab/graphql/calls_gitaly/field_extension_spec.rb b/spec/lib/gitlab/graphql/calls_gitaly/field_extension_spec.rb
new file mode 100644
index 00000000000..1d8849f7e38
--- /dev/null
+++ b/spec/lib/gitlab/graphql/calls_gitaly/field_extension_spec.rb
@@ -0,0 +1,87 @@
+# 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::STRING_TYPE, 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
diff --git a/spec/lib/gitlab/graphql/calls_gitaly/instrumentation_spec.rb b/spec/lib/gitlab/graphql/calls_gitaly/instrumentation_spec.rb
deleted file mode 100644
index f16767f7d14..00000000000
--- a/spec/lib/gitlab/graphql/calls_gitaly/instrumentation_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-require 'spec_helper'
-
-RSpec.describe Gitlab::Graphql::CallsGitaly::Instrumentation do
- subject { described_class.new }
-
- describe '#calls_gitaly_check' do
- let(:gitaly_field) { Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true) }
- let(:no_gitaly_field) { Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: false) }
-
- context 'if there are no Gitaly calls' do
- it 'does not raise an error if calls_gitaly is false' do
- expect { subject.send(:calls_gitaly_check, no_gitaly_field, 0) }.not_to raise_error
- end
- end
-
- context 'if there is at least 1 Gitaly call' do
- it 'raises an error if calls_gitaly: is false or not defined' do
- expect { subject.send(:calls_gitaly_check, no_gitaly_field, 1) }.to raise_error(/specify a constant complexity or add `calls_gitaly: true`/)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/graphql/docs/renderer_spec.rb b/spec/lib/gitlab/graphql/docs/renderer_spec.rb
index 064e0c6828b..5afed8c3390 100644
--- a/spec/lib/gitlab/graphql/docs/renderer_spec.rb
+++ b/spec/lib/gitlab/graphql/docs/renderer_spec.rb
@@ -5,27 +5,50 @@ require 'spec_helper'
RSpec.describe Gitlab::Graphql::Docs::Renderer do
describe '#contents' do
# Returns a Schema that uses the given `type`
- def mock_schema(type)
+ def mock_schema(type, field_description)
query_type = Class.new(Types::BaseObject) do
- graphql_name 'QueryType'
+ graphql_name 'Query'
- field :foo, type, null: true
+ field :foo, type, null: true do
+ description field_description
+ argument :id, GraphQL::ID_TYPE, required: false, description: 'ID of the object.'
+ end
end
- GraphQL::Schema.define(query: query_type)
+ GraphQL::Schema.define(
+ query: query_type,
+ resolve_type: ->(obj, ctx) { raise 'Not a real schema' }
+ )
end
- let_it_be(:template) { Rails.root.join('lib/gitlab/graphql/docs/templates/', 'default.md.haml') }
+ let_it_be(:template) { Rails.root.join('lib/gitlab/graphql/docs/templates/default.md.haml') }
+ let(:field_description) { 'List of objects.' }
subject(:contents) do
described_class.new(
- mock_schema(type).graphql_definition,
+ mock_schema(type, field_description).graphql_definition,
output_dir: nil,
template: template
).contents
end
- context 'A type with a field with a [Array] return type' do
+ describe 'headings' do
+ let(:type) { ::GraphQL::INT_TYPE }
+
+ it 'contains the expected sections' do
+ expect(contents.lines.map(&:chomp)).to include(
+ '## `Query` type',
+ '## Object types',
+ '## Enumeration types',
+ '## Scalar types',
+ '## Abstract types',
+ '### Unions',
+ '### Interfaces'
+ )
+ end
+ end
+
+ context 'when a field has a list type' do
let(:type) do
Class.new(Types::BaseObject) do
graphql_name 'ArrayTest'
@@ -35,19 +58,51 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
end
specify do
+ type_name = '[String!]!'
+ inner_type = 'string'
expectation = <<~DOC
- ### ArrayTest
+ ### `ArrayTest`
| Field | Type | Description |
| ----- | ---- | ----------- |
- | `foo` | String! => Array | A description. |
+ | `foo` | [`#{type_name}`](##{inner_type}) | A description. |
DOC
is_expected.to include(expectation)
end
+
+ describe 'a top level query field' do
+ let(:expectation) do
+ <<~DOC
+ ### `foo`
+
+ List of objects.
+
+ Returns [`ArrayTest`](#arraytest).
+
+ #### Arguments
+
+ | Name | Type | Description |
+ | ---- | ---- | ----------- |
+ | `id` | [`ID`](#id) | ID of the object. |
+ DOC
+ end
+
+ it 'generates the query with arguments' do
+ expect(subject).to include(expectation)
+ end
+
+ context 'when description does not end with `.`' do
+ let(:field_description) { 'List of objects' }
+
+ it 'adds the `.` to the end' do
+ expect(subject).to include(expectation)
+ end
+ end
+ end
end
- context 'A type with fields defined in reverse alphabetical order' do
+ describe 'when fields are not defined in alphabetical order' do
let(:type) do
Class.new(Types::BaseObject) do
graphql_name 'OrderingTest'
@@ -57,49 +112,56 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
end
end
- specify do
+ it 'lists the fields in alphabetical order' do
expectation = <<~DOC
- ### OrderingTest
+ ### `OrderingTest`
| Field | Type | Description |
| ----- | ---- | ----------- |
- | `bar` | String! | A description of bar field. |
- | `foo` | String! | A description of foo field. |
+ | `bar` | [`String!`](#string) | A description of bar field. |
+ | `foo` | [`String!`](#string) | A description of foo field. |
DOC
is_expected.to include(expectation)
end
end
- context 'A type with a deprecated field' do
+ context 'when a field is deprecated' do
let(:type) do
Class.new(Types::BaseObject) do
graphql_name 'DeprecatedTest'
- field :foo, GraphQL::STRING_TYPE, null: false, deprecated: { reason: 'This is deprecated', milestone: '1.10' }, description: 'A description.'
+ field :foo,
+ type: GraphQL::STRING_TYPE,
+ null: false,
+ deprecated: { reason: 'This is deprecated', milestone: '1.10' },
+ description: 'A description.'
end
end
- specify do
+ it 'includes the deprecation' do
expectation = <<~DOC
- ### DeprecatedTest
+ ### `DeprecatedTest`
| Field | Type | Description |
| ----- | ---- | ----------- |
- | `foo` **{warning-solid}** | String! | **Deprecated:** This is deprecated. Deprecated in 1.10. |
+ | `foo` **{warning-solid}** | [`String!`](#string) | **Deprecated:** This is deprecated. Deprecated in 1.10. |
DOC
is_expected.to include(expectation)
end
end
- context 'A type with an emum field' do
+ context 'when a field has an Enumeration type' do
let(:type) do
enum_type = Class.new(Types::BaseEnum) do
graphql_name 'MyEnum'
- value 'BAZ', description: 'A description of BAZ.'
- value 'BAR', description: 'A description of BAR.', deprecated: { reason: 'This is deprecated', milestone: '1.10' }
+ value 'BAZ',
+ description: 'A description of BAZ.'
+ value 'BAR',
+ description: 'A description of BAR.',
+ deprecated: { reason: 'This is deprecated', milestone: '1.10' }
end
Class.new(Types::BaseObject) do
@@ -109,9 +171,9 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
end
end
- specify do
+ it 'includes the description of the Enumeration' do
expectation = <<~DOC
- ### MyEnum
+ ### `MyEnum`
| Value | Description |
| ----- | ----------- |
@@ -122,5 +184,129 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
is_expected.to include(expectation)
end
end
+
+ context 'when a field has a global ID type' do
+ let(:type) do
+ Class.new(Types::BaseObject) do
+ graphql_name 'IDTest'
+ description 'A test for rendering IDs.'
+
+ field :foo, ::Types::GlobalIDType[::User], null: true, description: 'A user foo.'
+ end
+ end
+
+ it 'includes the field and the description of the ID, so we can link to it' do
+ type_section = <<~DOC
+ ### `IDTest`
+
+ A test for rendering IDs.
+
+ | Field | Type | Description |
+ | ----- | ---- | ----------- |
+ | `foo` | [`UserID`](#userid) | A user foo. |
+ DOC
+
+ id_section = <<~DOC
+ ### `UserID`
+
+ A `UserID` is a global ID. It is encoded as a string.
+
+ An example `UserID` is: `"gid://gitlab/User/1"`.
+ DOC
+
+ is_expected.to include(type_section, id_section)
+ end
+ end
+
+ context 'when there is an interface and a union' do
+ let(:type) do
+ user = Class.new(::Types::BaseObject)
+ user.graphql_name 'User'
+ user.field :user_field, ::GraphQL::STRING_TYPE, null: true
+ group = Class.new(::Types::BaseObject)
+ group.graphql_name 'Group'
+ group.field :group_field, ::GraphQL::STRING_TYPE, null: true
+
+ union = Class.new(::Types::BaseUnion)
+ union.graphql_name 'UserOrGroup'
+ union.description 'Either a user or a group.'
+ union.possible_types user, group
+
+ interface = Module.new
+ interface.include(::Types::BaseInterface)
+ interface.graphql_name 'Flying'
+ interface.description 'Something that can fly.'
+ interface.field :flight_speed, GraphQL::INT_TYPE, null: true, description: 'Speed in mph.'
+
+ african_swallow = Class.new(::Types::BaseObject)
+ african_swallow.graphql_name 'AfricanSwallow'
+ african_swallow.description 'A swallow from Africa.'
+ african_swallow.implements interface
+ interface.orphan_types african_swallow
+
+ Class.new(::Types::BaseObject) do
+ graphql_name 'AbstactTypeTest'
+ description 'A test for abstract types.'
+
+ field :foo, union, null: true, description: 'The foo.'
+ field :flying, interface, null: true, description: 'A flying thing.'
+ end
+ end
+
+ it 'lists the fields correctly, and includes descriptions of all the types' do
+ type_section = <<~DOC
+ ### `AbstactTypeTest`
+
+ A test for abstract types.
+
+ | Field | Type | Description |
+ | ----- | ---- | ----------- |
+ | `flying` | [`Flying`](#flying) | A flying thing. |
+ | `foo` | [`UserOrGroup`](#userorgroup) | The foo. |
+ DOC
+
+ union_section = <<~DOC
+ #### `UserOrGroup`
+
+ Either a user or a group.
+
+ One of:
+
+ - [`Group`](#group)
+ - [`User`](#user)
+ DOC
+
+ interface_section = <<~DOC
+ #### `Flying`
+
+ Something that can fly.
+
+ Implementations:
+
+ - [`AfricanSwallow`](#africanswallow)
+
+ | Field | Type | Description |
+ | ----- | ---- | ----------- |
+ | `flightSpeed` | [`Int`](#int) | Speed in mph. |
+ DOC
+
+ implementation_section = <<~DOC
+ ### `AfricanSwallow`
+
+ A swallow from Africa.
+
+ | Field | Type | Description |
+ | ----- | ---- | ----------- |
+ | `flightSpeed` | [`Int`](#int) | Speed in mph. |
+ DOC
+
+ is_expected.to include(
+ type_section,
+ union_section,
+ interface_section,
+ implementation_section
+ )
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb
index b45bb8b79d9..ec2ec4bf50d 100644
--- a/spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb
+++ b/spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Graphql::Pagination::Keyset::LastItems do
let_it_be(:merge_request) { create(:merge_request) }
- let(:scope) { MergeRequest.order_merged_at_asc.with_order_id_desc }
+ let(:scope) { MergeRequest.order_merged_at_asc }
subject { described_class.take_items(*args) }
diff --git a/spec/lib/gitlab/graphql/pagination/keyset/order_info_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/order_info_spec.rb
index eb28e6c8c0a..40ee47ece49 100644
--- a/spec/lib/gitlab/graphql/pagination/keyset/order_info_spec.rb
+++ b/spec/lib/gitlab/graphql/pagination/keyset/order_info_spec.rb
@@ -52,18 +52,6 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::OrderInfo do
end
end
- context 'when ordering by SIMILARITY' do
- let(:relation) { Project.sorted_by_similarity_desc('test', include_in_select: true) }
-
- it 'assigns the right attribute name, named function, and direction' do
- expect(order_list.count).to eq 2
- expect(order_list.first.attribute_name).to eq 'similarity'
- expect(order_list.first.named_function).to be_kind_of(Arel::Nodes::Addition)
- expect(order_list.first.named_function.to_sql).to include 'SIMILARITY('
- expect(order_list.first.sort_direction).to eq :desc
- end
- end
-
context 'when ordering by CASE', :aggregate_failuers do
let(:relation) { Project.order(Arel::Nodes::Case.new(Project.arel_table[:pending_delete]).when(true).then(100).else(1000).asc) }
diff --git a/spec/lib/gitlab/graphql/pagination/keyset/query_builder_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/query_builder_spec.rb
index fa631aa5666..31c02fd43e8 100644
--- a/spec/lib/gitlab/graphql/pagination/keyset/query_builder_spec.rb
+++ b/spec/lib/gitlab/graphql/pagination/keyset/query_builder_spec.rb
@@ -131,43 +131,5 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::QueryBuilder do
end
end
end
-
- context 'when sorting using SIMILARITY' do
- let(:relation) { Project.sorted_by_similarity_desc('test', include_in_select: true) }
- let(:arel_table) { Project.arel_table }
- let(:decoded_cursor) { { 'similarity' => 0.5, 'id' => 100 } }
- let(:similarity_function_call) { Gitlab::Database::SimilarityScore::SIMILARITY_FUNCTION_CALL_WITH_ANNOTATION }
- let(:similarity_sql) do
- [
- "(#{similarity_function_call}(COALESCE(\"projects\".\"path\", ''), 'test') * CAST('1' AS numeric))",
- "(#{similarity_function_call}(COALESCE(\"projects\".\"name\", ''), 'test') * CAST('0.7' AS numeric))",
- "(#{similarity_function_call}(COALESCE(\"projects\".\"description\", ''), 'test') * CAST('0.2' AS numeric))"
- ].join(' + ')
- end
-
- context 'when no values are nil' do
- context 'when :after' do
- it 'generates the correct condition' do
- conditions = builder.conditions.gsub(/\s+/, ' ')
-
- expect(conditions).to include "(#{similarity_sql} < 0.5)"
- expect(conditions).to include '"projects"."id" < 100'
- expect(conditions).to include "OR (#{similarity_sql} IS NULL)"
- end
- end
-
- context 'when :before' do
- let(:before_or_after) { :before }
-
- it 'generates the correct condition' do
- conditions = builder.conditions.gsub(/\s+/, ' ')
-
- expect(conditions).to include "(#{similarity_sql} > 0.5)"
- expect(conditions).to include '"projects"."id" > 100'
- expect(conditions).to include "OR ( #{similarity_sql} = 0.5"
- end
- end
- end
- end
end
end
diff --git a/spec/lib/gitlab/graphql/present/field_extension_spec.rb b/spec/lib/gitlab/graphql/present/field_extension_spec.rb
new file mode 100644
index 00000000000..5e66e16d655
--- /dev/null
+++ b/spec/lib/gitlab/graphql/present/field_extension_spec.rb
@@ -0,0 +1,143 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Gitlab::Graphql::Present::FieldExtension do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+
+ let(:object) { double(value: 'foo') }
+ let(:owner) { fresh_object_type }
+ let(:field_name) { 'value' }
+ let(:field) do
+ ::Types::BaseField.new(name: field_name, type: GraphQL::STRING_TYPE, null: true, owner: owner)
+ end
+
+ let(:base_presenter) do
+ Class.new(SimpleDelegator) do
+ def initialize(object, **options)
+ super(object)
+ @object = object
+ @options = options
+ end
+ end
+ end
+
+ def resolve_value
+ resolve_field(field, object, current_user: user, object_type: owner)
+ end
+
+ context 'when the object does not declare a presenter' do
+ it 'does not affect normal resolution' do
+ expect(resolve_value).to eq 'foo'
+ end
+ end
+
+ describe 'interactions with inheritance' do
+ def parent
+ type = fresh_object_type('Parent')
+ type.present_using(provide_foo)
+ type.field :foo, ::GraphQL::INT_TYPE, null: true
+ type.field :value, ::GraphQL::STRING_TYPE, null: true
+ type
+ end
+
+ def child
+ type = Class.new(parent)
+ type.graphql_name 'Child'
+ type.present_using(provide_bar)
+ type.field :bar, ::GraphQL::INT_TYPE, null: true
+ type
+ end
+
+ def provide_foo
+ Class.new(base_presenter) do
+ def foo
+ 100
+ end
+ end
+ end
+
+ def provide_bar
+ Class.new(base_presenter) do
+ def bar
+ 101
+ end
+ end
+ end
+
+ it 'can resolve value, foo and bar' do
+ type = child
+ value = resolve_field(:value, object, object_type: type)
+ foo = resolve_field(:foo, object, object_type: type)
+ bar = resolve_field(:bar, object, object_type: type)
+
+ expect([value, foo, bar]).to eq ['foo', 100, 101]
+ end
+ end
+
+ shared_examples 'calling the presenter method' do
+ it 'calls the presenter method' do
+ expect(resolve_value).to eq presenter.new(object, current_user: user).send(field_name)
+ end
+ end
+
+ context 'when the object declares a presenter' do
+ before do
+ owner.present_using(presenter)
+ end
+
+ context 'when the presenter overrides the original method' do
+ def twice
+ Class.new(base_presenter) do
+ def value
+ @object.value * 2
+ end
+ end
+ end
+
+ let(:presenter) { twice }
+
+ it_behaves_like 'calling the presenter method'
+ end
+
+ # This is exercised here using an explicit `resolve:` proc, but
+ # @resolver_proc values are used in field instrumentation as well.
+ context 'when the field uses a resolve proc' do
+ let(:presenter) { base_presenter }
+ let(:field) do
+ ::Types::BaseField.new(
+ name: field_name,
+ type: GraphQL::STRING_TYPE,
+ null: true,
+ owner: owner,
+ resolve: ->(obj, args, ctx) { 'Hello from a proc' }
+ )
+ end
+
+ specify { expect(resolve_value).to eq 'Hello from a proc' }
+ end
+
+ context 'when the presenter provides a new method' do
+ def presenter
+ Class.new(base_presenter) do
+ def current_username
+ "Hello #{@options[:current_user]&.username} from the presenter!"
+ end
+ end
+ end
+
+ context 'when we select the original field' do
+ it 'is unaffected' do
+ expect(resolve_value).to eq 'foo'
+ end
+ end
+
+ context 'when we select the new field' do
+ let(:field_name) { 'current_username' }
+
+ it_behaves_like 'calling the presenter method'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/query_analyzers/logger_analyzer_spec.rb b/spec/lib/gitlab/graphql/query_analyzers/logger_analyzer_spec.rb
index 138765afd8a..8450396284a 100644
--- a/spec/lib/gitlab/graphql/query_analyzers/logger_analyzer_spec.rb
+++ b/spec/lib/gitlab/graphql/query_analyzers/logger_analyzer_spec.rb
@@ -5,42 +5,6 @@ require 'spec_helper'
RSpec.describe Gitlab::Graphql::QueryAnalyzers::LoggerAnalyzer do
subject { described_class.new }
- describe '#analyze?' do
- context 'feature flag disabled' do
- before do
- stub_feature_flags(graphql_logging: false)
- end
-
- it 'disables the analyzer' do
- expect(subject.analyze?(anything)).to be_falsey
- end
- end
-
- context 'feature flag enabled by default' do
- let(:monotonic_time_before) { 42 }
- let(:monotonic_time_after) { 500 }
- let(:monotonic_time_duration) { monotonic_time_after - monotonic_time_before }
-
- it 'enables the analyzer' do
- expect(subject.analyze?(anything)).to be_truthy
- end
-
- it 'returns a duration in seconds' do
- allow(GraphQL::Analysis).to receive(:analyze_query).and_return([4, 2, [[], []]])
- allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
- allow(Gitlab::GraphqlLogger).to receive(:info)
-
- expected_duration = monotonic_time_duration
- memo = subject.initial_value(spy('query'))
-
- subject.final_value(memo)
-
- expect(memo).to have_key(:duration_s)
- expect(memo[:duration_s]).to eq(expected_duration)
- end
- end
- end
-
describe '#initial_value' do
it 'filters out sensitive variables' do
doc = GraphQL.parse <<-GRAPHQL
@@ -58,4 +22,24 @@ RSpec.describe Gitlab::Graphql::QueryAnalyzers::LoggerAnalyzer do
expect(subject.initial_value(query)[:variables]).to eq('{:body=>"[FILTERED]"}')
end
end
+
+ describe '#final_value' do
+ let(:monotonic_time_before) { 42 }
+ let(:monotonic_time_after) { 500 }
+ let(:monotonic_time_duration) { monotonic_time_after - monotonic_time_before }
+
+ it 'returns a duration in seconds' do
+ allow(GraphQL::Analysis).to receive(:analyze_query).and_return([4, 2, [[], []]])
+ allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
+ allow(Gitlab::GraphqlLogger).to receive(:info)
+
+ expected_duration = monotonic_time_duration
+ memo = subject.initial_value(spy('query'))
+
+ subject.final_value(memo)
+
+ expect(memo).to have_key(:duration_s)
+ expect(memo[:duration_s]).to eq(expected_duration)
+ end
+ end
end