diff options
Diffstat (limited to 'spec/lib/gitlab/graphql')
4 files changed, 214 insertions, 67 deletions
diff --git a/spec/lib/gitlab/graphql/known_operations_spec.rb b/spec/lib/gitlab/graphql/known_operations_spec.rb index 411c0876f82..3ebfefbb43c 100644 --- a/spec/lib/gitlab/graphql/known_operations_spec.rb +++ b/spec/lib/gitlab/graphql/known_operations_spec.rb @@ -19,7 +19,7 @@ RSpec.describe Gitlab::Graphql::KnownOperations do describe "#from_query" do where(:query_string, :expected) do - "query { helloWorld }" | described_class::ANONYMOUS + "query { helloWorld }" | described_class::UNKNOWN "query fuzzyyy { helloWorld }" | described_class::UNKNOWN "query foo { helloWorld }" | described_class::Operation.new("foo") end @@ -35,13 +35,13 @@ RSpec.describe Gitlab::Graphql::KnownOperations do describe "#operations" do it "returns array of known operations" do - expect(subject.operations.map(&:name)).to match_array(%w(anonymous unknown foo bar)) + expect(subject.operations.map(&:name)).to match_array(%w(unknown foo bar)) end end describe "Operation#to_caller_id" do where(:query_string, :expected) do - "query { helloWorld }" | "graphql:#{described_class::ANONYMOUS.name}" + "query { helloWorld }" | "graphql:#{described_class::UNKNOWN.name}" "query foo { helloWorld }" | "graphql:foo" end diff --git a/spec/lib/gitlab/graphql/pagination/active_record_array_connection_spec.rb b/spec/lib/gitlab/graphql/pagination/active_record_array_connection_spec.rb new file mode 100644 index 00000000000..320c6b52308 --- /dev/null +++ b/spec/lib/gitlab/graphql/pagination/active_record_array_connection_spec.rb @@ -0,0 +1,135 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Graphql::Pagination::ActiveRecordArrayConnection do + using RSpec::Parameterized::TableSyntax + + let_it_be(:items) { create_list(:package_build_info, 3) } + + let_it_be(:context) do + GraphQL::Query::Context.new( + query: GraphQL::Query.new(GitlabSchema, document: nil, context: {}, variables: {}), + values: {}, + object: nil + ) + end + + let(:first) { nil } + let(:last) { nil } + let(:after) { nil } + let(:before) { nil } + let(:max_page_size) { nil } + + let(:connection) do + described_class.new( + items, + context: context, + first: first, + last: last, + after: after, + before: before, + max_page_size: max_page_size + ) + end + + it_behaves_like 'a connection with collection methods' + + it_behaves_like 'a redactable connection' do + let(:unwanted) { items[1] } + end + + describe '#nodes' do + subject { connection.nodes } + + it { is_expected.to match_array(items) } + + context 'with first set' do + let(:first) { 2 } + + it { is_expected.to match_array([items[0], items[1]]) } + end + + context 'with last set' do + let(:last) { 2 } + + it { is_expected.to match_array([items[1], items[2]]) } + end + end + + describe '#next_page?' do + subject { connection.next_page? } + + where(:before, :first, :max_page_size, :result) do + nil | nil | nil | false + 1 | nil | nil | true + nil | 1 | nil | true + nil | 10 | nil | false + nil | 1 | 1 | true + nil | 1 | 10 | true + nil | 10 | 10 | false + end + + with_them do + it { is_expected.to eq(result) } + end + end + + describe '#previous_page?' do + subject { connection.previous_page? } + + where(:after, :last, :max_page_size, :result) do + nil | nil | nil | false + 1 | nil | nil | true + nil | 1 | nil | true + nil | 10 | nil | false + nil | 1 | 1 | true + nil | 1 | 10 | true + nil | 10 | 10 | false + end + + with_them do + it { is_expected.to eq(result) } + end + end + + describe '#cursor_for' do + let(:item) { items[0] } + let(:expected_result) do + GitlabSchema.cursor_encoder.encode( + Gitlab::Json.dump(id: item.id.to_s), + nonce: true + ) + end + + subject { connection.cursor_for(item) } + + it { is_expected.to eq(expected_result) } + + context 'with a BatchLoader::GraphQL item' do + let_it_be(:user) { create(:user) } + + let(:item) { ::Gitlab::Graphql::Loaders::BatchModelLoader.new(::User, user.id).find } + let(:expected_result) do + GitlabSchema.cursor_encoder.encode( + Gitlab::Json.dump(id: user.id.to_s), + nonce: true + ) + end + + it { is_expected.to eq(expected_result) } + end + end + + describe '#dup' do + subject { connection.dup } + + it 'properly handles items duplication' do + connection2 = subject + + connection2 << create(:package_build_info) + + expect(connection.items).not_to eq(connection2.items) + end + end +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 0741088c915..86e7d4e344c 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 @@ -19,8 +19,8 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: 'last_repository_check_at', column_expression: Project.arel_table[:last_repository_check_at], - order_expression: Gitlab::Database.nulls_last_order('last_repository_check_at', :asc), - reversed_order_expression: Gitlab::Database.nulls_last_order('last_repository_check_at', :desc), + order_expression: Project.arel_table[:last_repository_check_at].asc.nulls_last, + reversed_order_expression: Project.arel_table[:last_repository_check_at].desc.nulls_last, order_direction: :asc, nullable: :nulls_last, distinct: false) @@ -30,8 +30,8 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: 'last_repository_check_at', column_expression: Project.arel_table[:last_repository_check_at], - order_expression: Gitlab::Database.nulls_last_order('last_repository_check_at', :desc), - reversed_order_expression: Gitlab::Database.nulls_last_order('last_repository_check_at', :asc), + order_expression: Project.arel_table[:last_repository_check_at].desc.nulls_last, + reversed_order_expression: Project.arel_table[:last_repository_check_at].asc.nulls_last, order_direction: :desc, nullable: :nulls_last, distinct: false) @@ -256,11 +256,6 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do end end - # rubocop: disable RSpec/EmptyExampleGroup - context 'when ordering uses LOWER' do - end - # rubocop: enable RSpec/EmptyExampleGroup - context 'when ordering by similarity' do let_it_be(:project1) { create(:project, name: 'test') } let_it_be(:project2) { create(:project, name: 'testing') } diff --git a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb index b511a294f97..f31ec6c09fd 100644 --- a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb +++ b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb @@ -77,6 +77,17 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do expect(decoded_cursor(cursor)).to eq('id' => project.id.to_s) end + context 'when SimpleOrderBuilder cannot build keyset paginated query' do + it 'increments the `old_keyset_pagination_usage` counter', :prometheus do + expect(Gitlab::Pagination::Keyset::SimpleOrderBuilder).to receive(:build).and_return([false, nil]) + + decoded_cursor(cursor) + + counter = Gitlab::Metrics.registry.get(:old_keyset_pagination_usage) + expect(counter.get(model: 'Project')).to eq(1) + end + end + context 'when an order is specified' do let(:nodes) { Project.order(:updated_at) } @@ -222,91 +233,97 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do end end - context 'when multiple orders with nil values are defined' do - let!(:project1) { create(:project, last_repository_check_at: 10.days.ago) } # Asc: project5 Desc: project3 - let!(:project2) { create(:project, last_repository_check_at: nil) } # Asc: project1 Desc: project1 - let!(:project3) { create(:project, last_repository_check_at: 5.days.ago) } # Asc: project3 Desc: project5 - let!(:project4) { create(:project, last_repository_check_at: nil) } # Asc: project2 Desc: project2 - let!(:project5) { create(:project, last_repository_check_at: 20.days.ago) } # Asc: project4 Desc: project4 + context 'when ordering uses LOWER' do + let!(:project1) { create(:project, name: 'A') } # Asc: project1 Desc: project4 + let!(:project2) { create(:project, name: 'c') } # Asc: project5 Desc: project2 + let!(:project3) { create(:project, name: 'b') } # Asc: project3 Desc: project3 + let!(:project4) { create(:project, name: 'd') } # Asc: project2 Desc: project5 + let!(:project5) { create(:project, name: 'a') } # Asc: project4 Desc: project1 context 'when ascending' do let(:nodes) do - Project.order(Arel.sql('projects.last_repository_check_at IS NULL')).order(last_repository_check_at: :asc).order(id: :asc) + Project.order(Arel::Table.new(:projects)['name'].lower.asc).order(id: :asc) end - let(:ascending_nodes) { [project5, project1, project3, project2, project4] } + let(:ascending_nodes) { [project1, project5, project3, project2, project4] } it_behaves_like 'nodes are in ascending order' - - context 'when before cursor value is NULL' do - let(:arguments) { { before: encoded_cursor(project4) } } - - it 'returns all projects before the cursor' do - expect(subject.sliced_nodes).to eq([project5, project1, project3, project2]) - end - end - - context 'when after cursor value is NULL' do - let(:arguments) { { after: encoded_cursor(project2) } } - - it 'returns all projects after the cursor' do - expect(subject.sliced_nodes).to eq([project4]) - end - end end context 'when descending' do let(:nodes) do - Project.order(Arel.sql('projects.last_repository_check_at IS NULL')).order(last_repository_check_at: :desc).order(id: :asc) + Project.order(Arel::Table.new(:projects)['name'].lower.desc).order(id: :desc) end - let(:descending_nodes) { [project3, project1, project5, project2, project4] } + let(:descending_nodes) { [project4, project2, project3, project5, project1] } it_behaves_like 'nodes are in descending order' + end + end - context 'when before cursor value is NULL' do - let(:arguments) { { before: encoded_cursor(project4) } } + context 'NULLS order' do + using RSpec::Parameterized::TableSyntax - it 'returns all projects before the cursor' do - expect(subject.sliced_nodes).to eq([project3, project1, project5, project2]) - end - end + let_it_be(:issue1) { create(:issue, relative_position: nil) } + let_it_be(:issue2) { create(:issue, relative_position: 100) } + let_it_be(:issue3) { create(:issue, relative_position: 200) } + let_it_be(:issue4) { create(:issue, relative_position: nil) } + let_it_be(:issue5) { create(:issue, relative_position: 300) } + + context 'when ascending NULLS LAST (ties broken by id DESC implicitly)' do + let(:ascending_nodes) { [issue2, issue3, issue5, issue4, issue1] } - context 'when after cursor value is NULL' do - let(:arguments) { { after: encoded_cursor(project2) } } + where(:nodes) do + [ + lazy { Issue.order(Issue.arel_table[:relative_position].asc.nulls_last) } + ] + end - it 'returns all projects after the cursor' do - expect(subject.sliced_nodes).to eq([project4]) - end + with_them do + it_behaves_like 'nodes are in ascending order' end end - end - context 'when ordering uses LOWER' do - let!(:project1) { create(:project, name: 'A') } # Asc: project1 Desc: project4 - let!(:project2) { create(:project, name: 'c') } # Asc: project5 Desc: project2 - let!(:project3) { create(:project, name: 'b') } # Asc: project3 Desc: project3 - let!(:project4) { create(:project, name: 'd') } # Asc: project2 Desc: project5 - let!(:project5) { create(:project, name: 'a') } # Asc: project4 Desc: project1 + context 'when descending NULLS LAST (ties broken by id DESC implicitly)' do + let(:descending_nodes) { [issue5, issue3, issue2, issue4, issue1] } - context 'when ascending' do - let(:nodes) do - Project.order(Arel::Table.new(:projects)['name'].lower.asc).order(id: :asc) + where(:nodes) do + [ + lazy { Issue.order(Issue.arel_table[:relative_position].desc.nulls_last) } +] end - let(:ascending_nodes) { [project1, project5, project3, project2, project4] } - - it_behaves_like 'nodes are in ascending order' + with_them do + it_behaves_like 'nodes are in descending order' + end end - context 'when descending' do - let(:nodes) do - Project.order(Arel::Table.new(:projects)['name'].lower.desc).order(id: :desc) + context 'when ascending NULLS FIRST with a tie breaker' do + let(:ascending_nodes) { [issue1, issue4, issue2, issue3, issue5] } + + where(:nodes) do + [ + lazy { Issue.order(Issue.arel_table[:relative_position].asc.nulls_first).order(id: :asc) } +] end - let(:descending_nodes) { [project4, project2, project3, project5, project1] } + with_them do + it_behaves_like 'nodes are in ascending order' + end + end - it_behaves_like 'nodes are in descending order' + context 'when descending NULLS FIRST with a tie breaker' do + let(:descending_nodes) { [issue1, issue4, issue5, issue3, issue2] } + + where(:nodes) do + [ + lazy { Issue.order(Issue.arel_table[:relative_position].desc.nulls_first).order(id: :asc) } +] + end + + with_them do + it_behaves_like 'nodes are in descending order' + end end end |