diff options
Diffstat (limited to 'spec/lib/gitlab/graphql')
10 files changed, 191 insertions, 88 deletions
diff --git a/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb index 0c548e1ce32..ac512e28e7b 100644 --- a/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb +++ b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb @@ -103,4 +103,36 @@ RSpec.describe Gitlab::Graphql::Authorize::AuthorizeResource do .to contain_exactly(:base_authorization, :sub_authorization) end end + + describe 'authorizes_object?' do + it 'is false by default' do + a_class = Class.new do + include Gitlab::Graphql::Authorize::AuthorizeResource + end + + expect(a_class).not_to be_authorizes_object + end + + it 'is true after calling authorizes_object!' do + a_class = Class.new do + include Gitlab::Graphql::Authorize::AuthorizeResource + + authorizes_object! + end + + expect(a_class).to be_authorizes_object + end + + it 'is true if a parent authorizes_object' do + parent = Class.new do + include Gitlab::Graphql::Authorize::AuthorizeResource + + authorizes_object! + end + + child = Class.new(parent) + + expect(child).to be_authorizes_object + end + end end diff --git a/spec/lib/gitlab/graphql/markdown_field_spec.rb b/spec/lib/gitlab/graphql/markdown_field_spec.rb index ed3f19d8cf2..974951ab30c 100644 --- a/spec/lib/gitlab/graphql/markdown_field_spec.rb +++ b/spec/lib/gitlab/graphql/markdown_field_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe Gitlab::Graphql::MarkdownField do include Gitlab::Routing + include GraphqlHelpers describe '.markdown_field' do it 'creates the field with some default attributes' do @@ -21,21 +22,12 @@ RSpec.describe Gitlab::Graphql::MarkdownField do expect { class_with_markdown_field(:test_html, null: true, resolver: 'not really') } .to raise_error(expected_error) end - - # TODO: remove as part of https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27536 - # so that until that time, the developer check is there - it 'raises when passing a resolve block' do - expect { class_with_markdown_field(:test_html, null: true, resolve: -> (_, _, _) { 'not really' } ) } - .to raise_error(expected_error) - end end context 'resolving markdown' do let_it_be(:note) { build(:note, note: '# Markdown!') } let_it_be(:expected_markdown) { '<h1 data-sourcepos="1:1-1:11" dir="auto">Markdown!</h1>' } - let_it_be(:query_type) { GraphQL::ObjectType.new } - let_it_be(:schema) { GraphQL::Schema.define(query: query_type, mutation: nil)} - let_it_be(:query) { GraphQL::Query.new(schema, document: nil, context: {}, variables: {}) } + let_it_be(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } let_it_be(:context) { GraphQL::Query::Context.new(query: query, values: {}, object: nil) } let(:type_class) { class_with_markdown_field(:note_html, null: false) } @@ -55,6 +47,20 @@ RSpec.describe Gitlab::Graphql::MarkdownField do end end + context 'when a block is passed for the resolved object' do + let(:type_class) do + class_with_markdown_field(:note_html, null: false) do |resolved_object| + resolved_object.object + end + end + + let(:type_instance) { type_class.authorized_new(class_wrapped_object(note), context) } + + it 'renders markdown from the same property as the field name without the `_html` suffix' do + expect(field.resolve(type_instance, {}, context)).to eq(expected_markdown) + end + end + describe 'basic verification that references work' do let_it_be(:project) { create(:project, :public) } @@ -83,12 +89,22 @@ RSpec.describe Gitlab::Graphql::MarkdownField do end end - def class_with_markdown_field(name, **args) + def class_with_markdown_field(name, **args, &blk) Class.new(Types::BaseObject) do prepend Gitlab::Graphql::MarkdownField graphql_name 'MarkdownFieldTest' - markdown_field name, **args + markdown_field name, **args, &blk end end + + def class_wrapped_object(object) + Class.new do + def initialize(object) + @object = object + end + + attr_accessor :object + end.new(object) + end end diff --git a/spec/lib/gitlab/graphql/negatable_arguments_spec.rb b/spec/lib/gitlab/graphql/negatable_arguments_spec.rb index 71ef75836c0..04ee1c1b820 100644 --- a/spec/lib/gitlab/graphql/negatable_arguments_spec.rb +++ b/spec/lib/gitlab/graphql/negatable_arguments_spec.rb @@ -25,7 +25,9 @@ RSpec.describe Gitlab::Graphql::NegatableArguments do expect(test_resolver.arguments['not'].type.arguments.keys).to match_array(['foo']) end - it 'defines all arguments passed as block even if called multiple times' do + # TODO: suffers from the `DuplicateNamesError` error. skip until we upgrade + # to the graphql 2.0 gem https://gitlab.com/gitlab-org/gitlab/-/issues/363131 + xit 'defines all arguments passed as block even if called multiple times' do test_resolver.negated do argument :foo, GraphQL::Types::String, required: false end diff --git a/spec/lib/gitlab/graphql/pagination/keyset/connection_generic_keyset_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/connection_generic_keyset_spec.rb index b6c3cb4e04a..97613edee5e 100644 --- a/spec/lib/gitlab/graphql/pagination/keyset/connection_generic_keyset_spec.rb +++ b/spec/lib/gitlab/graphql/pagination/keyset/connection_generic_keyset_spec.rb @@ -9,9 +9,7 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do # The spec will be merged with connection_spec.rb in the future. let(:nodes) { Project.all.order(id: :asc) } let(:arguments) { {} } - let(:query_type) { GraphQL::ObjectType.new } - let(:schema) { GraphQL::Schema.define(query: query_type, mutation: nil)} - let(:context) { GraphQL::Query::Context.new(query: query_double(schema: schema), values: nil, object: nil) } + let(:context) { GraphQL::Query::Context.new(query: query_double, values: nil, object: nil) } let_it_be(:column_order_id) { Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(attribute_name: 'id', order_expression: Project.arel_table[:id].asc) } let_it_be(:column_order_id_desc) { Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(attribute_name: 'id', order_expression: Project.arel_table[:id].desc) } diff --git a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb index a4ba288b7f1..61a79d90546 100644 --- a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb +++ b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb @@ -7,9 +7,7 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do let(:nodes) { Project.all.order(id: :asc) } let(:arguments) { {} } - let(:query_type) { GraphQL::ObjectType.new } - let(:schema) { GraphQL::Schema.define(query: query_type, mutation: nil)} - let(:context) { GraphQL::Query::Context.new(query: query_double(schema: schema), values: nil, object: nil) } + let(:context) { GraphQL::Query::Context.new(query: query_double, values: nil, object: nil) } subject(:connection) do described_class.new(nodes, **{ context: context, max_page_size: 3 }.merge(arguments)) diff --git a/spec/lib/gitlab/graphql/present/field_extension_spec.rb b/spec/lib/gitlab/graphql/present/field_extension_spec.rb index 5f0f444e0bb..49992d7b71f 100644 --- a/spec/lib/gitlab/graphql/present/field_extension_spec.rb +++ b/spec/lib/gitlab/graphql/present/field_extension_spec.rb @@ -143,23 +143,6 @@ RSpec.describe Gitlab::Graphql::Present::FieldExtension do 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::Types::String, - 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 diff --git a/spec/lib/gitlab/graphql/query_analyzers/ast/logger_analyzer_spec.rb b/spec/lib/gitlab/graphql/query_analyzers/ast/logger_analyzer_spec.rb new file mode 100644 index 00000000000..a5274d49fdb --- /dev/null +++ b/spec/lib/gitlab/graphql/query_analyzers/ast/logger_analyzer_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Graphql::QueryAnalyzers::AST::LoggerAnalyzer do + let(:query) { GraphQL::Query.new(GitlabSchema, document: document, context: {}, variables: { body: 'some note' }) } + let(:document) do + GraphQL.parse <<-GRAPHQL + mutation createNote($body: String!) { + createNote(input: {noteableId: "gid://gitlab/Noteable/1", body: $body}) { + note { + id + } + } + } + GRAPHQL + end + + describe '#result' do + let(:monotonic_time_before) { 42 } + let(:monotonic_time_after) { 500 } + let(:monotonic_time_duration) { monotonic_time_after - monotonic_time_before } + + before do + RequestStore.store[:graphql_logs] = nil + + allow(Gitlab::Metrics::System).to receive(:monotonic_time) + .and_return(monotonic_time_before, monotonic_time_before, + monotonic_time_before, monotonic_time_before, + monotonic_time_after) + end + + it 'returns the complexity, depth, duration, etc' do + results = GraphQL::Analysis::AST.analyze_query(query, [described_class], multiplex_analyzers: []) + result = results.first + + expect(result[:duration_s]).to eq monotonic_time_duration + expect(result[:depth]).to eq 3 + expect(result[:complexity]).to eq 3 + expect(result[:used_fields]).to eq ['Note.id', 'CreateNotePayload.note', 'Mutation.createNote'] + expect(result[:used_deprecated_fields]).to eq [] + + request = result.except(:duration_s).merge({ + operation_name: 'createNote', + variables: { body: "[FILTERED]" }.to_s + }) + + expect(RequestStore.store[:graphql_logs]).to match([request]) + end + end +end diff --git a/spec/lib/gitlab/graphql/query_analyzers/ast/recursion_analyzer_spec.rb b/spec/lib/gitlab/graphql/query_analyzers/ast/recursion_analyzer_spec.rb new file mode 100644 index 00000000000..997fb129f42 --- /dev/null +++ b/spec/lib/gitlab/graphql/query_analyzers/ast/recursion_analyzer_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Graphql::QueryAnalyzers::AST::RecursionAnalyzer do + let(:query) { GraphQL::Query.new(GitlabSchema, document: document, context: {}, variables: { body: 'some note' }) } + + context 'when recursion threshold not exceeded' do + let(:document) do + GraphQL.parse <<-GRAPHQL + query recurse { + group(fullPath: "h5bp") { + projects { + nodes { + name + group { + projects { + nodes { + name + } + } + } + } + } + } + } + GRAPHQL + end + + it 'returns the complexity, depth, duration, etc' do + result = GraphQL::Analysis::AST.analyze_query(query, [described_class], multiplex_analyzers: []) + + expect(result.first).to be_nil + end + end + + context 'when recursion threshold exceeded' do + let(:document) do + GraphQL.parse <<-GRAPHQL + query recurse { + group(fullPath: "h5bp") { + projects { + nodes { + name + group { + projects { + nodes { + name + group { + projects { + nodes { + name + } + } + } + } + } + } + } + } + } + } + GRAPHQL + end + + it 'returns error' do + result = GraphQL::Analysis::AST.analyze_query(query, [described_class], multiplex_analyzers: []) + + expect(result.first.is_a?(GraphQL::AnalysisError)).to be_truthy + 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 deleted file mode 100644 index dee8f9e3c64..00000000000 --- a/spec/lib/gitlab/graphql/query_analyzers/logger_analyzer_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::Graphql::QueryAnalyzers::LoggerAnalyzer do - let(:initial_value) { analyzer.initial_value(query) } - let(:analyzer) { described_class.new } - let(:query) { GraphQL::Query.new(GitlabSchema, document: document, context: {}, variables: { body: "some note" }) } - let(:document) do - GraphQL.parse <<-GRAPHQL - mutation createNote($body: String!) { - createNote(input: {noteableId: "1", body: $body}) { - note { - id - } - } - } - GRAPHQL - 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 } - let(:memo) { initial_value } - - subject(:final_value) { analyzer.final_value(memo) } - - before do - RequestStore.store[:graphql_logs] = nil - - 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) - end - - it 'inserts duration in seconds to memo and sets request store' do - expect { final_value }.to change { memo[:duration_s] }.to(monotonic_time_duration) - .and change { RequestStore.store[:graphql_logs] }.to([{ - complexity: 4, - depth: 2, - operation_name: query.operation_name, - used_deprecated_fields: [], - used_fields: [], - variables: { body: "[FILTERED]" }.to_s - }]) - end - end -end diff --git a/spec/lib/gitlab/graphql/tracers/logger_tracer_spec.rb b/spec/lib/gitlab/graphql/tracers/logger_tracer_spec.rb index 5bc077a963e..20792fb4554 100644 --- a/spec/lib/gitlab/graphql/tracers/logger_tracer_spec.rb +++ b/spec/lib/gitlab/graphql/tracers/logger_tracer_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Gitlab::Graphql::Tracers::LoggerTracer do use Gitlab::Graphql::Tracers::LoggerTracer use Gitlab::Graphql::Tracers::TimerTracer - query_analyzer Gitlab::Graphql::QueryAnalyzers::LoggerAnalyzer.new + query_analyzer Gitlab::Graphql::QueryAnalyzers::AST::LoggerAnalyzer query Graphql::FakeQueryType end @@ -20,11 +20,11 @@ RSpec.describe Gitlab::Graphql::Tracers::LoggerTracer do end end - it "logs every query", :aggregate_failures do + it "logs every query", :aggregate_failures, :unlimited_max_formatted_output_length do variables = { name: "Ada Lovelace" } query_string = 'query fooOperation($name: String) { helloWorld(message: $name) }' - # Build an actual query so we don't have to hardocde the "fingerprint" calculations + # Build an actual query so we don't have to hardcode the "fingerprint" calculations query = GraphQL::Query.new(dummy_schema, query_string, variables: variables) expect(::Gitlab::GraphqlLogger).to receive(:info).with({ |