Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec/lib
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2024-01-10 21:07:52 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2024-01-10 21:07:52 +0300
commit9dbe80c7d52d682aaf431790d9011d7c2912b85c (patch)
tree4d29f537133f2fc952858f6e7439a6cce13c41bc /spec/lib
parente1d966e6543433479a932e1e29ad538cd587699a (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/lib')
-rw-r--r--spec/lib/gitlab/ci/build/rules_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb25
-rw-r--r--spec/lib/gitlab/ci/config/entry/rules_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/workflow_spec.rb35
-rw-r--r--spec/lib/gitlab/ci/config/external/file/component_spec.rb10
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb72
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb26
-rw-r--r--spec/lib/gitlab/database/partitioning/int_range_partition_spec.rb173
-rw-r--r--spec/lib/gitlab/database/partitioning/int_range_strategy_spec.rb317
-rw-r--r--spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb4
-rw-r--r--spec/lib/gitlab/quick_actions/extractor_spec.rb8
12 files changed, 679 insertions, 7 deletions
diff --git a/spec/lib/gitlab/ci/build/rules_spec.rb b/spec/lib/gitlab/ci/build/rules_spec.rb
index 99577539798..61bd9f41182 100644
--- a/spec/lib/gitlab/ci/build/rules_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules_spec.rb
@@ -254,6 +254,18 @@ RSpec.describe Gitlab::Ci::Build::Rules, feature_category: :pipeline_composition
end
end
+ context 'with auto_cancel' do
+ context 'with matching rule' do
+ let(:rule_list) { [{ if: '$VAR == null', auto_cancel: { on_new_commit: 'interruptible' } }] }
+
+ it do
+ is_expected.to eq(
+ described_class::Result.new(when: 'on_success', auto_cancel: { on_new_commit: 'interruptible' })
+ )
+ end
+ end
+ end
+
context 'with a regexp variable matching rule' do
let(:rule_list) { [{ if: '"abcde" =~ $pattern' }] }
diff --git a/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb
index cd8e35ede61..a9f891a7b50 100644
--- a/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
require_dependency 'active_model'
RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules::Rule, feature_category: :pipeline_composition do
diff --git a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
index 23b1046deb7..d5bf532c216 100644
--- a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule, feature_category: :pipeli
let(:metadata) do
{
allowed_when: %w[on_success on_failure always never manual delayed],
- allowed_keys: %i[if changes exists when start_in allow_failure variables needs]
+ allowed_keys: %i[if changes exists when start_in allow_failure variables needs auto_cancel]
}
end
@@ -341,6 +341,23 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule, feature_category: :pipeli
expect(subject.errors).to include(/variables config should be a hash/)
end
end
+
+ context 'with an invalid auto_cancel' do
+ let(:config) do
+ { if: '$THIS == "that"', auto_cancel: { on_new_commit: 'xyz' } }
+ end
+
+ before do
+ subject.compose!
+ end
+
+ it { is_expected.not_to be_valid }
+
+ it 'returns an error' do
+ expect(subject.errors).to include(
+ 'auto_cancel on new commit must be one of: conservative, interruptible, none')
+ end
+ end
end
context 'allow_failure: validation' do
@@ -421,6 +438,12 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule, feature_category: :pipeli
it { is_expected.to eq(config) }
end
+
+ context 'when it has auto_cancel' do
+ let(:config) { { if: '$THIS || $THAT', auto_cancel: { on_new_commit: 'interruptible' } } }
+
+ it { is_expected.to eq(config) }
+ end
end
describe '.default' do
diff --git a/spec/lib/gitlab/ci/config/entry/rules_spec.rb b/spec/lib/gitlab/ci/config/entry/rules_spec.rb
index 37219c743cc..0113b6c1f7f 100644
--- a/spec/lib/gitlab/ci/config/entry/rules_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/rules_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
require_dependency 'active_model'
RSpec.describe Gitlab::Ci::Config::Entry::Rules, feature_category: :pipeline_composition do
diff --git a/spec/lib/gitlab/ci/config/entry/workflow_spec.rb b/spec/lib/gitlab/ci/config/entry/workflow_spec.rb
index 01ba72ff042..dbd25010884 100644
--- a/spec/lib/gitlab/ci/config/entry/workflow_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/workflow_spec.rb
@@ -90,6 +90,41 @@ RSpec.describe Gitlab::Ci::Config::Entry::Workflow, feature_category: :pipeline_
end
end
end
+
+ context 'when rules has auto_cancel' do
+ let(:workflow_hash) { { rules: [{ if: '$VAR', auto_cancel: { on_new_commit: 'interruptible' } }] } }
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(config).to be_valid
+ end
+
+ it 'attaches no errors' do
+ expect(config.errors).to be_empty
+ end
+ end
+
+ describe '#value' do
+ it 'returns the config' do
+ expect(config.value).to eq(workflow_hash)
+ end
+ end
+
+ context 'when auto_cancel has an invalid value' do
+ let(:workflow_hash) { { rules: [{ if: '$VAR', auto_cancel: { on_new_commit: 'xyz' } }] } }
+
+ describe '#valid?' do
+ it 'is invalid' do
+ expect(config).not_to be_valid
+ end
+
+ it 'returns error' do
+ expect(config.errors).to include(
+ 'rules:rule:auto_cancel on new commit must be one of: conservative, interruptible, none')
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/config/external/file/component_spec.rb b/spec/lib/gitlab/ci/config/external/file/component_spec.rb
index 88e272ac3fd..7907837db6a 100644
--- a/spec/lib/gitlab/ci/config/external/file/component_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/component_spec.rb
@@ -146,6 +146,16 @@ RSpec.describe Gitlab::Ci::Config::External::File::Component, feature_category:
external_resource.content
end
+
+ context 'when user is missing in a context' do
+ let_it_be(:user) { nil }
+
+ it 'does not track the event' do
+ expect(::Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
+
+ external_resource.content
+ end
+ end
end
context 'when component is invalid' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb
index 732748d8c8b..787a458f0ff 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb
@@ -240,6 +240,78 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::PopulateMetadata, feature_category:
expect(pipeline.pipeline_metadata).not_to be_persisted
end
end
+
+ context 'with workflow:rules:auto_cancel' do
+ context 'with auto_cancel:on_new_commit not set and rules:workflow:auto_cancel:on_new_commit set' do
+ let(:config) do
+ {
+ variables: { MY_VAR: my_var_value },
+ workflow: {
+ auto_cancel: { on_job_failure: 'all' },
+ rules: [{ if: '$MY_VAR == "something"', auto_cancel: { on_new_commit: 'interruptible' } }]
+ },
+ rspec: { script: 'rspec' }
+ }
+ end
+
+ context 'when the rule is matched' do
+ let(:my_var_value) { 'something' }
+
+ it 'builds pipeline_metadata' do
+ run_chain
+
+ expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible')
+ expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('all')
+ end
+ end
+
+ context 'when the rule is not matched' do
+ let(:my_var_value) { 'something else' }
+
+ it 'builds pipeline_metadata' do
+ run_chain
+
+ expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('conservative')
+ expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('all')
+ end
+ end
+ end
+
+ context 'with auto_cancel:on_new_commit set and rules:workflow:auto_cancel:on_new_commit set' do
+ let(:config) do
+ {
+ variables: { MY_VAR: my_var_value },
+ workflow: {
+ auto_cancel: { on_new_commit: 'interruptible' },
+ rules: [{ if: '$MY_VAR == "something"', auto_cancel: { on_new_commit: 'none' } }]
+ },
+ rspec: { script: 'rspec' }
+ }
+ end
+
+ context 'when the rule is matched' do
+ let(:my_var_value) { 'something' }
+
+ it 'builds pipeline_metadata' do
+ run_chain
+
+ expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('none')
+ expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none')
+ end
+ end
+
+ context 'when the rule is not matched' do
+ let(:my_var_value) { 'something else' }
+
+ it 'builds pipeline_metadata' do
+ run_chain
+
+ expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible')
+ expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none')
+ end
+ end
+ end
+ end
end
context 'with both pipeline name and auto_cancel' do
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 0d91b99b5e3..4f759109b26 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -516,6 +516,32 @@ module Gitlab
})
end
end
+
+ context 'with rules and auto_cancel' do
+ let(:config) do
+ <<-YML
+ workflow:
+ rules:
+ - if: $VAR == "value"
+ auto_cancel:
+ on_new_commit: none
+ on_job_failure: none
+
+ hello:
+ script: echo world
+ YML
+ end
+
+ it 'parses workflow_rules' do
+ expect(subject.workflow_rules).to contain_exactly({
+ if: '$VAR == "value"',
+ auto_cancel: {
+ on_new_commit: 'none',
+ on_job_failure: 'none'
+ }
+ })
+ end
+ end
end
describe '#warnings' do
diff --git a/spec/lib/gitlab/database/partitioning/int_range_partition_spec.rb b/spec/lib/gitlab/database/partitioning/int_range_partition_spec.rb
new file mode 100644
index 00000000000..eadae77bdc6
--- /dev/null
+++ b/spec/lib/gitlab/database/partitioning/int_range_partition_spec.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Partitioning::IntRangePartition, feature_category: :database do
+ describe 'validate attributes' do
+ subject(:int_range_partition) { described_class.from_sql(table, partition_name, definition) }
+
+ let(:table) { 'foo' }
+ let(:partition_name) { 'foo_bar' }
+ let(:definition) { "FOR VALUES FROM ('1') TO ('10')" }
+
+ context 'when `from` is greater than `to`' do
+ let(:definition) { "FOR VALUES FROM ('10') TO ('1')" }
+
+ it 'raises an exception' do
+ expect { int_range_partition }.to raise_error(RuntimeError, '`to` must be greater than `from`')
+ end
+ end
+
+ context 'when `to` is 0' do
+ let(:definition) { "FOR VALUES FROM ('10') TO ('0')" }
+
+ it 'raises an exception' do
+ expect { int_range_partition }.to raise_error(RuntimeError, '`to` statement must be greater than 0')
+ end
+ end
+
+ context 'when `from` is 0' do
+ let(:definition) { "FOR VALUES FROM ('0') TO ('1')" }
+
+ it 'raises an exception' do
+ expect { int_range_partition }.to raise_error(RuntimeError, '`from` statement must be greater than 0')
+ end
+ end
+ end
+
+ describe '.from_sql' do
+ subject(:int_range_partition) { described_class.from_sql(table, partition_name, definition) }
+
+ let(:table) { 'foo' }
+ let(:partition_name) { 'foo_bar' }
+ let(:definition) { "FOR VALUES FROM ('1') TO ('10')" }
+
+ it 'uses specified table name' do
+ expect(int_range_partition.table).to eq(table)
+ end
+
+ it 'uses specified partition name' do
+ expect(int_range_partition.partition_name).to eq(partition_name)
+ end
+
+ it 'parses start date' do
+ expect(int_range_partition.from).to eq(1)
+ end
+
+ it 'parses end date' do
+ expect(int_range_partition.to).to eq(10)
+ end
+ end
+
+ describe '#partition_name' do
+ subject(:int_range_partition_name) do
+ described_class.new(table, from, to, partition_name: partition_name).partition_name
+ end
+
+ let(:table) { 'foo' }
+ let(:from) { '1' }
+ let(:to) { '10' }
+ let(:partition_name) { nil }
+
+ it 'uses table as prefix' do
+ expect(int_range_partition_name).to start_with(table)
+ end
+
+ it 'uses start id (from) as suffix' do
+ expect(int_range_partition_name).to end_with("_1")
+ end
+
+ context 'with partition name explicitly given' do
+ let(:partition_name) { "foo_bar" }
+
+ it 'uses given partition name' do
+ expect(int_range_partition_name).to eq(partition_name)
+ end
+ end
+ end
+
+ describe '#to_sql' do
+ subject(:to_sql) { described_class.new(table, from, to).to_sql }
+
+ let(:table) { 'foo' }
+ let(:from) { '1' }
+ let(:to) { '10' }
+
+ it 'transforms to a CREATE TABLE statement' do
+ expect(to_sql).to eq(<<~SQL)
+ CREATE TABLE IF NOT EXISTS "#{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}"."foo_1"
+ PARTITION OF "foo"
+ FOR VALUES FROM ('1') TO ('10')
+ SQL
+ end
+ end
+
+ describe 'object equality - #eql' do
+ def expect_inequality(actual, other)
+ expect(actual.eql?(other)).to be_falsey
+ expect(actual).not_to eq(other)
+ end
+
+ def expect_equality(actual, other)
+ expect(actual).to eq(other)
+ expect(actual.eql?(other)).to be_truthy
+ expect(actual.hash).to eq(other.hash)
+ end
+
+ def make_new(table: 'foo', from: '1', to: '10', partition_name: 'foo_1')
+ described_class.new(table, from, to, partition_name: partition_name)
+ end
+
+ it 'treats objects identical with identical attributes' do
+ expect_equality(make_new, make_new)
+ end
+
+ it 'different table leads to in-equality' do
+ expect_inequality(make_new, make_new(table: 'bar'))
+ end
+
+ it 'different from leads to in-equality' do
+ expect_inequality(make_new, make_new(from: '2'))
+ end
+
+ it 'different to leads to in-equality' do
+ expect_inequality(make_new, make_new(to: '11'))
+ end
+
+ it 'different partition_name leads to in-equality' do
+ expect_inequality(make_new, make_new(partition_name: 'different'))
+ end
+
+ it 'nil partition_name is ignored if auto-generated matches' do
+ expect_equality(make_new, make_new(partition_name: nil))
+ end
+ end
+
+ describe 'Comparable, #<=>' do
+ let(:table) { 'foo' }
+
+ it 'sorts by partition bounds' do
+ partitions = [
+ described_class.new(table, '100', '110', partition_name: 'p_100'),
+ described_class.new(table, '5', '10', partition_name: 'p_5'),
+ described_class.new(table, '10', '100', partition_name: 'p_10'),
+ described_class.new(table, '1', '5', partition_name: 'p_1')
+ ]
+
+ expect(partitions.sort).to eq(
+ [
+ described_class.new(table, '1', '5', partition_name: 'p_1'),
+ described_class.new(table, '5', '10', partition_name: 'p_5'),
+ described_class.new(table, '10', '100', partition_name: 'p_10'),
+ described_class.new(table, '100', '110', partition_name: 'p_100')
+ ])
+ end
+
+ it 'returns nil for partitions of different tables' do
+ one = described_class.new('foo', '1', '10')
+ two = described_class.new('bar', '1', '10')
+
+ expect(one.<=>(two)).to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/partitioning/int_range_strategy_spec.rb b/spec/lib/gitlab/database/partitioning/int_range_strategy_spec.rb
new file mode 100644
index 00000000000..19937544393
--- /dev/null
+++ b/spec/lib/gitlab/database/partitioning/int_range_strategy_spec.rb
@@ -0,0 +1,317 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Partitioning::IntRangeStrategy, feature_category: :database do
+ include Database::PartitioningHelpers
+
+ let(:connection) { ActiveRecord::Base.connection }
+ let(:model) do
+ Class.new(ActiveRecord::Base) do
+ self.table_name = '_test_partitioned_test'
+ end
+ end
+
+ after do
+ model.reset_column_information
+ end
+
+ describe '#current_partitions' do
+ subject(:current_partitions) { described_class.new(model, partitioning_key, partition_size: 10).current_partitions }
+
+ let(:partitioning_key) { double }
+ let(:table_name) { :_test_partitioned_test }
+
+ before do
+ connection.execute(<<~SQL)
+ CREATE TABLE #{table_name}
+ (id serial not null, external_id integer not null, PRIMARY KEY (id, external_id))
+ PARTITION BY RANGE (external_id);
+
+ CREATE TABLE #{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}._test_partitioned_test_1
+ PARTITION OF #{table_name}
+ FOR VALUES FROM ('1') TO ('5');
+
+ CREATE TABLE #{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}._test_partitioned_test_5
+ PARTITION OF #{table_name}
+ FOR VALUES FROM ('5') TO ('10');
+
+ CREATE TABLE #{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}._test_partitioned_test_10
+ PARTITION OF #{table_name}
+ FOR VALUES FROM ('10') TO ('100');
+
+ CREATE TABLE #{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}._test_partitioned_test_100
+ PARTITION OF #{table_name}
+ FOR VALUES FROM ('100') TO ('110');
+ SQL
+ end
+
+ it 'returns partitions order by range bound' do
+ expect(current_partitions).to eq(
+ [
+ Gitlab::Database::Partitioning::IntRangePartition.new(table_name, 1, 5,
+ partition_name: '_test_partitioned_test_1'),
+ Gitlab::Database::Partitioning::IntRangePartition.new(table_name, 5, 10,
+ partition_name: '_test_partitioned_test_5'),
+ Gitlab::Database::Partitioning::IntRangePartition.new(table_name, 10, 100,
+ partition_name: '_test_partitioned_test_10'),
+ Gitlab::Database::Partitioning::IntRangePartition.new(table_name, 100, 110,
+ partition_name: '_test_partitioned_test_100')
+ ])
+ end
+ end
+
+ describe '#extra_partitions' do
+ let(:partitioning_key) { double }
+ let(:table_name) { :_test_partitioned_test }
+
+ subject(:extra_partitions) { described_class.new(model, partitioning_key, partition_size: 10).extra_partitions }
+
+ it 'returns an empty array' do
+ expect(extra_partitions).to eq([])
+ end
+ end
+
+ describe '#missing_partitions' do
+ subject(:missing_partitions) { described_class.new(model, partitioning_key, partition_size: 10).missing_partitions }
+
+ let(:model) do
+ Class.new(ActiveRecord::Base) do
+ self.table_name = '_test_partitioned_test'
+ self.primary_key = :id
+ end
+ end
+
+ let(:partitioning_key) { :external_id }
+
+ before do
+ connection.execute(<<~SQL)
+ CREATE TABLE #{model.table_name}
+ (id serial not null, external_id integer not null, PRIMARY KEY (id, external_id))
+ PARTITION BY RANGE (external_id);
+ SQL
+ end
+
+ context 'when the current partitions are not completed' do
+ before do
+ connection.execute(<<~SQL)
+ CREATE TABLE #{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}._test_partitioned_test_11
+ PARTITION OF #{model.table_name}
+ FOR VALUES FROM ('11') TO ('21');
+ SQL
+ end
+
+ context 'when partitions have data' do
+ before do
+ model.create!(external_id: 15)
+ end
+
+ it 'returns missing partitions' do
+ expect(missing_partitions.size).to eq(7)
+
+ expect(missing_partitions).to include(
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 1, 11),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 21, 31),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 31, 41),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 41, 51),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 51, 61),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 61, 71),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 71, 81)
+ )
+
+ expect(missing_partitions).not_to include(
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 11, 21)
+ )
+ end
+ end
+
+ context 'when partitions are empty' do
+ before do
+ model.delete_all
+ end
+
+ it 'returns missing partitions' do
+ expect(missing_partitions.size).to eq(7)
+
+ expect(missing_partitions).to include(
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 1, 11),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 21, 31),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 31, 41),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 41, 51),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 51, 61),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 61, 71),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 71, 81)
+ )
+
+ expect(missing_partitions).not_to include(
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 11, 21)
+ )
+ end
+ end
+ end
+
+ context 'with existing partitions' do
+ before do
+ connection.execute(<<~SQL)
+ CREATE TABLE #{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}._test_partitioned_test_1
+ PARTITION OF #{model.table_name}
+ FOR VALUES FROM ('1') TO ('11');
+
+ CREATE TABLE #{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}._test_partitioned_test_11
+ PARTITION OF #{model.table_name}
+ FOR VALUES FROM ('11') TO ('21');
+ SQL
+ end
+
+ context 'when partitions have data' do
+ before do
+ model.create!(external_id: 1)
+ model.create!(external_id: 15)
+ end
+
+ it 'returns missing partitions' do
+ expect(missing_partitions.size).to eq(6)
+
+ expect(missing_partitions).to include(
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 21, 31),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 31, 41),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 41, 51),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 51, 61),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 61, 71),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 71, 81)
+ )
+
+ expect(missing_partitions).not_to include(
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 1, 11),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 11, 21)
+ )
+ end
+ end
+
+ context 'when partitions are empty' do
+ before do
+ model.delete_all
+ end
+
+ it 'returns missing partitions' do
+ expect(missing_partitions.size).to eq(6)
+
+ expect(missing_partitions).to include(
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 21, 31),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 31, 41),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 41, 51),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 51, 61),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 61, 71),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 71, 81)
+ )
+
+ expect(missing_partitions).not_to include(
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 1, 11),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 11, 21)
+ )
+ end
+ end
+ end
+
+ context 'without partitions' do
+ it 'returns missing partitions' do
+ expect(missing_partitions.size).to eq(6)
+
+ expect(missing_partitions).to include(
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 1, 11),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 11, 21),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 21, 31),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 31, 41),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 41, 51),
+ Gitlab::Database::Partitioning::IntRangePartition.new(model.table_name, 51, 61)
+ )
+ end
+ end
+ end
+
+ describe 'attributes' do
+ let(:partitioning_key) { :partition }
+ let(:table_name) { :_test_partitioned_test }
+ let(:partition_size) { 5 }
+
+ subject(:strategy) do
+ described_class.new(
+ model, partitioning_key,
+ partition_size: partition_size
+ )
+ end
+
+ specify do
+ expect(strategy).to have_attributes({
+ model: model,
+ partitioning_key: partitioning_key,
+ partition_size: partition_size
+ })
+ end
+ end
+
+ describe 'simulates the merge_request_diff_commits partition creation' do
+ let(:table_name) { '_test_partitioned_test' }
+ let(:model) do
+ Class.new(ApplicationRecord) do
+ include PartitionedTable
+
+ self.table_name = '_test_partitioned_test'
+ self.primary_key = :merge_request_diff_id
+
+ partitioned_by :merge_request_diff_id, strategy: :int_range, partition_size: 2
+ end
+ end
+
+ before do
+ connection.execute(<<~SQL)
+ create table #{table_name}
+ (
+ merge_request_diff_id int not null,
+ relative_order int not null,
+ created_at timestamptz,
+ primary key (merge_request_diff_id, relative_order)
+ )
+ PARTITION BY RANGE (merge_request_diff_id);
+
+ create table gitlab_partitions_dynamic.#{table_name}_1
+ PARTITION of #{table_name} FOR VALUES FROM (1) TO (3);
+
+ create table gitlab_partitions_dynamic.#{table_name}_3
+ PARTITION of #{table_name} FOR VALUES FROM (3) TO (5);
+ SQL
+ end
+
+ it 'redirects to the new partition', :aggregate_failures do
+ expect_range_partitions_for(table_name, {
+ '1' => %w[1 3],
+ '3' => %w[3 5]
+ })
+
+ expect do
+ model.create!(merge_request_diff_id: 1, relative_order: 1, created_at: Time.zone.now) # Goes in partition 1
+ end.to change { model.count }.by(1)
+
+ expect do
+ model.create!(merge_request_diff_id: 5, relative_order: 1, created_at: Time.zone.now)
+ end.to raise_error(ActiveRecord::StatementInvalid, /no partition of relation/)
+
+ Gitlab::Database::Partitioning::PartitionManager.new(model).sync_partitions # Generates more 6 partitions
+
+ expect_range_partitions_for(table_name, {
+ '1' => %w[1 3],
+ '3' => %w[3 5],
+ '5' => %w[5 7],
+ '7' => %w[7 9],
+ '9' => %w[9 11],
+ '11' => %w[11 13],
+ '13' => %w[13 15],
+ '15' => %w[15 17]
+ })
+
+ expect do
+ model.create!(merge_request_diff_id: 5, relative_order: 1, created_at: Time.zone.now) # Goes in partition 5
+ end.to change { model.count }.by(1)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb b/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb
index 9ca0a1b6e57..f4a4b0f002c 100644
--- a/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb
+++ b/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb
@@ -14,6 +14,10 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy, feature_cate
let(:next_partition_if) { double('next_partition_if') }
let(:detach_partition_if) { double('detach_partition_if') }
+ after do
+ model.reset_column_information
+ end
+
subject(:strategy) do
described_class.new(
model,
diff --git a/spec/lib/gitlab/quick_actions/extractor_spec.rb b/spec/lib/gitlab/quick_actions/extractor_spec.rb
index bb0adbc87f1..ef76b1e5fdf 100644
--- a/spec/lib/gitlab/quick_actions/extractor_spec.rb
+++ b/spec/lib/gitlab/quick_actions/extractor_spec.rb
@@ -185,21 +185,21 @@ RSpec.describe Gitlab::QuickActions::Extractor, feature_category: :team_planning
context 'at the start of content' do
it_behaves_like 'command with a single argument' do
let(:original_msg) { "/assign @joe\nworld" }
- let(:final_msg) { "\n/assign @joe\n\nworld" }
+ let(:final_msg) { "<p>/assign @joe</p>\nworld" }
end
end
context 'in the middle of content' do
it_behaves_like 'command with a single argument' do
let(:original_msg) { "hello\n/assign @joe\nworld" }
- let(:final_msg) { "hello\n\n/assign @joe\n\nworld" }
+ let(:final_msg) { "hello\n<p>/assign @joe</p>\nworld" }
end
end
context 'at the end of content' do
it_behaves_like 'command with a single argument' do
let(:original_msg) { "hello\n/assign @joe" }
- let(:final_msg) { "hello\n\n/assign @joe" }
+ let(:final_msg) { "hello\n<p>/assign @joe</p>" }
end
end
end
@@ -282,7 +282,7 @@ RSpec.describe Gitlab::QuickActions::Extractor, feature_category: :team_planning
msg, commands = extractor.extract_commands(msg)
expect(commands).to match_array [['reopen'], ['substitution', 'wow this is a thing.']]
- expect(msg).to eq "hello\nworld\n\n/reopen\n\nfoo"
+ expect(msg).to eq "hello\nworld\n<p>/reopen</p>\nfoo"
end
it 'extracts multiple commands' do