diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2024-01-10 21:07:52 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2024-01-10 21:07:52 +0300 |
commit | 9dbe80c7d52d682aaf431790d9011d7c2912b85c (patch) | |
tree | 4d29f537133f2fc952858f6e7439a6cce13c41bc /spec/lib | |
parent | e1d966e6543433479a932e1e29ad538cd587699a (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/lib')
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 |