210 lines
7.6 KiB
Ruby
210 lines
7.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
RSpec.describe Gitlab::Pagination::Keyset::ColumnOrderDefinition do
|
|
let_it_be(:project_name_column) do
|
|
described_class.new(
|
|
attribute_name: :name,
|
|
order_expression: Project.arel_table[:name].asc,
|
|
nullable: :not_nullable,
|
|
distinct: true
|
|
)
|
|
end
|
|
|
|
let_it_be(:project_name_lower_column) do
|
|
described_class.new(
|
|
attribute_name: :name,
|
|
order_expression: Project.arel_table[:name].lower.desc,
|
|
nullable: :not_nullable,
|
|
distinct: true
|
|
)
|
|
end
|
|
|
|
let_it_be(:project_calculated_column_expression) do
|
|
# COALESCE("projects"."description", 'No Description')
|
|
Arel::Nodes::NamedFunction.new('COALESCE', [
|
|
Project.arel_table[:description],
|
|
Arel.sql("'No Description'")
|
|
])
|
|
end
|
|
|
|
let_it_be(:project_calculated_column) do
|
|
described_class.new(
|
|
attribute_name: :name,
|
|
column_expression: project_calculated_column_expression,
|
|
order_expression: project_calculated_column_expression.asc,
|
|
nullable: :not_nullable,
|
|
distinct: true
|
|
)
|
|
end
|
|
|
|
describe '#order_direction' do
|
|
context 'inferring order_direction from order_expression' do
|
|
it { expect(project_name_column).to be_ascending_order }
|
|
it { expect(project_name_column).not_to be_descending_order }
|
|
|
|
it { expect(project_name_lower_column).to be_descending_order }
|
|
it { expect(project_name_lower_column).not_to be_ascending_order }
|
|
|
|
it { expect(project_calculated_column).to be_ascending_order }
|
|
it { expect(project_calculated_column).not_to be_descending_order }
|
|
|
|
it 'raises error when order direction cannot be infered' do
|
|
expect do
|
|
described_class.new(
|
|
attribute_name: :name,
|
|
column_expression: Project.arel_table[:name],
|
|
order_expression: 'name asc',
|
|
reversed_order_expression: 'name desc',
|
|
nullable: :not_nullable,
|
|
distinct: true
|
|
)
|
|
end.to raise_error(RuntimeError, /Invalid or missing `order_direction`/)
|
|
end
|
|
|
|
it 'does not raise error when order direction is explicitly given' do
|
|
column_order_definition = described_class.new(
|
|
attribute_name: :name,
|
|
column_expression: Project.arel_table[:name],
|
|
order_expression: 'name asc',
|
|
reversed_order_expression: 'name desc',
|
|
order_direction: :asc,
|
|
nullable: :not_nullable,
|
|
distinct: true
|
|
)
|
|
|
|
expect(column_order_definition).to be_ascending_order
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#column_expression' do
|
|
context 'inferring column_expression from order_expression' do
|
|
it 'infers the correct column expression' do
|
|
column_order_definition = described_class.new(attribute_name: :name, order_expression: Project.arel_table[:name].asc)
|
|
|
|
expect(column_order_definition.column_expression).to eq(Project.arel_table[:name])
|
|
end
|
|
|
|
it 'raises error when raw string is given as order expression' do
|
|
expect do
|
|
described_class.new(attribute_name: :name, order_expression: 'name DESC')
|
|
end.to raise_error(RuntimeError, /Couldn't calculate the column expression. Please pass an ARel node/)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#reversed_order_expression' do
|
|
it 'raises error when order cannot be reversed automatically' do
|
|
expect do
|
|
described_class.new(
|
|
attribute_name: :name,
|
|
column_expression: Project.arel_table[:name],
|
|
order_expression: 'name asc',
|
|
order_direction: :asc,
|
|
nullable: :not_nullable,
|
|
distinct: true
|
|
)
|
|
end.to raise_error(RuntimeError, /Couldn't determine reversed order/)
|
|
end
|
|
end
|
|
|
|
describe '#reverse' do
|
|
it { expect(project_name_column.reverse.order_expression).to eq(Project.arel_table[:name].desc) }
|
|
it { expect(project_name_column.reverse).to be_descending_order }
|
|
|
|
it { expect(project_calculated_column.reverse.order_expression).to eq(project_calculated_column_expression.desc) }
|
|
it { expect(project_calculated_column.reverse).to be_descending_order }
|
|
|
|
context 'when reversed_order_expression is given' do
|
|
it 'uses the given expression' do
|
|
column_order_definition = described_class.new(
|
|
attribute_name: :name,
|
|
column_expression: Project.arel_table[:name],
|
|
order_expression: 'name asc',
|
|
reversed_order_expression: 'name desc',
|
|
order_direction: :asc,
|
|
nullable: :not_nullable,
|
|
distinct: true
|
|
)
|
|
|
|
expect(column_order_definition.reverse.order_expression).to eq('name desc')
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#nullable' do
|
|
context 'when the column is nullable' do
|
|
let(:nulls_last_order) do
|
|
described_class.new(
|
|
attribute_name: :name,
|
|
column_expression: Project.arel_table[:name],
|
|
order_expression: MergeRequest::Metrics.arel_table[:merged_at].desc.nulls_last,
|
|
reversed_order_expression: MergeRequest::Metrics.arel_table[:merged_at].asc.nulls_first,
|
|
order_direction: :desc,
|
|
nullable: :nulls_last, # null values are always last
|
|
distinct: false
|
|
)
|
|
end
|
|
|
|
it 'requires the position of the null values in the result' do
|
|
expect(nulls_last_order).to be_nulls_last
|
|
end
|
|
|
|
it 'reverses nullable correctly' do
|
|
expect(nulls_last_order.reverse).to be_nulls_first
|
|
end
|
|
|
|
it 'raises error when invalid nullable value is given' do
|
|
expect do
|
|
described_class.new(
|
|
attribute_name: :name,
|
|
column_expression: Project.arel_table[:name],
|
|
order_expression: MergeRequest::Metrics.arel_table[:merged_at].desc.nulls_last,
|
|
reversed_order_expression: MergeRequest::Metrics.arel_table[:merged_at].asc.nulls_first,
|
|
order_direction: :desc,
|
|
nullable: true,
|
|
distinct: false
|
|
)
|
|
end.to raise_error(RuntimeError, /Invalid `nullable` is given/)
|
|
end
|
|
|
|
it 'raises error when the column is nullable and distinct' do
|
|
expect do
|
|
described_class.new(
|
|
attribute_name: :name,
|
|
column_expression: Project.arel_table[:name],
|
|
order_expression: MergeRequest::Metrics.arel_table[:merged_at].desc.nulls_last,
|
|
reversed_order_expression: MergeRequest::Metrics.arel_table[:merged_at].asc.nulls_first,
|
|
order_direction: :desc,
|
|
nullable: :nulls_last,
|
|
distinct: true
|
|
)
|
|
end.to raise_error(RuntimeError, /Invalid column definition/)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#order_direction_as_sql_string" do
|
|
let(:nulls_last_order) do
|
|
described_class.new(
|
|
attribute_name: :name,
|
|
column_expression: Project.arel_table[:name],
|
|
order_expression: MergeRequest::Metrics.arel_table[:merged_at].desc.nulls_last,
|
|
reversed_order_expression: MergeRequest::Metrics.arel_table[:merged_at].asc.nulls_first,
|
|
order_direction: :desc,
|
|
nullable: :nulls_last, # null values are always last
|
|
distinct: false
|
|
)
|
|
end
|
|
|
|
it { expect(project_name_column.order_direction_as_sql_string).to eq('ASC') }
|
|
it { expect(project_name_column.reverse.order_direction_as_sql_string).to eq('DESC') }
|
|
it { expect(project_name_lower_column.order_direction_as_sql_string).to eq('DESC') }
|
|
it { expect(project_name_lower_column.reverse.order_direction_as_sql_string).to eq('ASC') }
|
|
it { expect(nulls_last_order.order_direction_as_sql_string).to eq('DESC NULLS LAST') }
|
|
it { expect(nulls_last_order.reverse.order_direction_as_sql_string).to eq('ASC NULLS FIRST') }
|
|
end
|
|
end
|