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
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-08-18 13:50:51 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-08-18 13:50:51 +0300
commitdb384e6b19af03b4c3c82a5760d83a3fd79f7982 (patch)
tree34beaef37df5f47ccbcf5729d7583aae093cffa0 /spec/migrations
parent54fd7b1bad233e3944434da91d257fa7f63c3996 (diff)
Add latest changes from gitlab-org/gitlab@16-3-stable-eev16.3.0-rc42
Diffstat (limited to 'spec/migrations')
-rw-r--r--spec/migrations/20221219122320_copy_clickhouse_connection_string_to_encrypted_var_spec.rb2
-rw-r--r--spec/migrations/20230327123333_backfill_product_analytics_data_collector_host_spec.rb2
-rw-r--r--spec/migrations/20230612232000_queue_backfill_dismissal_reason_in_vulnerability_reads_spec.rb26
-rw-r--r--spec/migrations/20230712145557_queue_backfill_missing_vulnerability_dismissal_details_spec.rb26
-rw-r--r--spec/migrations/20230714015909_add_index_for_member_expiring_query_spec.rb16
-rw-r--r--spec/migrations/20230719083202_backfill_project_statistics_storage_size_without_pipeline_artifacts_size_spec.rb44
-rw-r--r--spec/migrations/20230723203612_backfill_default_branch_protection_application_setting_spec.rb50
-rw-r--r--spec/migrations/20230724071541_queue_backfill_default_branch_protection_namespace_setting_spec.rb26
-rw-r--r--spec/migrations/20230724164745_queue_delete_orphaned_transferred_project_approval_rules_spec.rb26
-rw-r--r--spec/migrations/20230728174927_add_epic_work_item_type_spec.rb91
-rw-r--r--spec/migrations/20230801150214_retry_cleanup_bigint_conversion_for_events_for_gitlab_com_spec.rb147
-rw-r--r--spec/migrations/20230802085923_queue_fix_allow_descendants_override_disabled_shared_runners_spec.rb26
-rw-r--r--spec/migrations/20230803125434_add_has_merge_request_on_vulnerability_reads_trigger_spec.rb178
-rw-r--r--spec/migrations/20230804053643_add_ticket_work_item_type_spec.rb97
-rw-r--r--spec/migrations/20230807083334_add_linked_items_work_item_widget_spec.rb10
-rw-r--r--spec/migrations/20230809104753_swap_epic_user_mentions_note_id_to_bigint_for_self_hosts_spec.rb149
-rw-r--r--spec/migrations/20230810103534_swap_suggestions_note_id_to_bigint_for_self_hosts_spec.rb149
-rw-r--r--spec/migrations/20230810123044_swap_snippet_user_mentions_note_id_to_bigint_for_self_hosts_spec.rb125
-rw-r--r--spec/migrations/20230811103941_swap_vulnerability_user_mentions_note_id_to_bigint_for_self_hosts_spec.rb133
-rw-r--r--spec/migrations/20230814144045_swap_timelogs_note_id_to_bigint_for_self_hosts_spec.rb125
-rw-r--r--spec/migrations/add_expiry_notified_at_to_member_spec.rb21
-rw-r--r--spec/migrations/cleanup_conversion_big_int_ci_build_needs_self_managed_spec.rb107
-rw-r--r--spec/migrations/ensure_commit_user_mentions_note_id_bigint_backfill_is_finished_for_self_managed_spec.rb35
-rw-r--r--spec/migrations/ensure_todos_bigint_backfill_completed_for_self_managed_spec.rb35
-rw-r--r--spec/migrations/swap_commit_user_mentions_note_id_to_bigint_for_self_managed_spec.rb123
-rw-r--r--spec/migrations/swap_events_target_id_to_bigint_for_gitlab_dot_com_spec.rb67
-rw-r--r--spec/migrations/swap_todos_note_id_to_bigint_for_self_managed_spec.rb159
27 files changed, 1993 insertions, 2 deletions
diff --git a/spec/migrations/20221219122320_copy_clickhouse_connection_string_to_encrypted_var_spec.rb b/spec/migrations/20221219122320_copy_clickhouse_connection_string_to_encrypted_var_spec.rb
index 7ff033ab0c2..48702e866e0 100644
--- a/spec/migrations/20221219122320_copy_clickhouse_connection_string_to_encrypted_var_spec.rb
+++ b/spec/migrations/20221219122320_copy_clickhouse_connection_string_to_encrypted_var_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CopyClickhouseConnectionStringToEncryptedVar, feature_category: :product_analytics do
+RSpec.describe CopyClickhouseConnectionStringToEncryptedVar, feature_category: :product_analytics_data_management do
let!(:migration) { described_class.new }
let(:setting) { table(:application_settings).create!(clickhouse_connection_string: 'https://example.com/test') }
diff --git a/spec/migrations/20230327123333_backfill_product_analytics_data_collector_host_spec.rb b/spec/migrations/20230327123333_backfill_product_analytics_data_collector_host_spec.rb
index 253512c9194..05cc065e6c3 100644
--- a/spec/migrations/20230327123333_backfill_product_analytics_data_collector_host_spec.rb
+++ b/spec/migrations/20230327123333_backfill_product_analytics_data_collector_host_spec.rb
@@ -3,7 +3,7 @@
require "spec_helper"
require_migration!
-RSpec.describe BackfillProductAnalyticsDataCollectorHost, feature_category: :product_analytics do
+RSpec.describe BackfillProductAnalyticsDataCollectorHost, feature_category: :product_analytics_data_management do
let!(:application_settings) { table(:application_settings) }
describe '#up' do
diff --git a/spec/migrations/20230612232000_queue_backfill_dismissal_reason_in_vulnerability_reads_spec.rb b/spec/migrations/20230612232000_queue_backfill_dismissal_reason_in_vulnerability_reads_spec.rb
new file mode 100644
index 00000000000..56bfa50abd0
--- /dev/null
+++ b/spec/migrations/20230612232000_queue_backfill_dismissal_reason_in_vulnerability_reads_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe QueueBackfillDismissalReasonInVulnerabilityReads, feature_category: :vulnerability_management do
+ let!(:batched_migration) { described_class::MIGRATION }
+
+ it 'schedules a new batched migration' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).to have_scheduled_batched_migration(
+ table_name: :vulnerability_reads,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL,
+ batch_size: described_class::BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE
+ )
+ }
+ end
+ end
+end
diff --git a/spec/migrations/20230712145557_queue_backfill_missing_vulnerability_dismissal_details_spec.rb b/spec/migrations/20230712145557_queue_backfill_missing_vulnerability_dismissal_details_spec.rb
new file mode 100644
index 00000000000..6595164da41
--- /dev/null
+++ b/spec/migrations/20230712145557_queue_backfill_missing_vulnerability_dismissal_details_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe QueueBackfillMissingVulnerabilityDismissalDetails, feature_category: :vulnerability_management do
+ let!(:batched_migration) { described_class::MIGRATION }
+
+ it 'schedules a new batched migration' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).to have_scheduled_batched_migration(
+ table_name: :vulnerabilities,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL,
+ batch_size: described_class::BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE
+ )
+ }
+ end
+ end
+end
diff --git a/spec/migrations/20230714015909_add_index_for_member_expiring_query_spec.rb b/spec/migrations/20230714015909_add_index_for_member_expiring_query_spec.rb
new file mode 100644
index 00000000000..524354ecc9a
--- /dev/null
+++ b/spec/migrations/20230714015909_add_index_for_member_expiring_query_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe AddIndexForMemberExpiringQuery, :migration, feature_category: :groups_and_projects do
+ let(:index_name) { 'index_members_on_expiring_at_access_level_id' }
+
+ it 'correctly migrates up and down' do
+ expect(subject).not_to be_index_exists_by_name(:members, index_name)
+
+ migrate!
+
+ expect(subject).to be_index_exists_by_name(:members, index_name)
+ end
+end
diff --git a/spec/migrations/20230719083202_backfill_project_statistics_storage_size_without_pipeline_artifacts_size_spec.rb b/spec/migrations/20230719083202_backfill_project_statistics_storage_size_without_pipeline_artifacts_size_spec.rb
new file mode 100644
index 00000000000..c3183a5da1b
--- /dev/null
+++ b/spec/migrations/20230719083202_backfill_project_statistics_storage_size_without_pipeline_artifacts_size_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe BackfillProjectStatisticsStorageSizeWithoutPipelineArtifactsSize, feature_category: :consumables_cost_management do
+ let!(:batched_migration) { described_class::MIGRATION }
+
+ it 'does not schedule background jobs when Gitlab.org_or_com? is false' do
+ allow(Gitlab).to receive(:dev_or_test_env?).and_return(false)
+ allow(Gitlab).to receive(:org_or_com?).and_return(false)
+
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+ end
+ end
+
+ it 'schedules a new batched migration' do
+ allow(Gitlab).to receive(:dev_or_test_env?).and_return(false)
+ allow(Gitlab).to receive(:org_or_com?).and_return(true)
+
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).to have_scheduled_batched_migration(
+ table_name: :project_statistics,
+ column_name: :project_id,
+ interval: described_class::DELAY_INTERVAL,
+ batch_size: described_class::BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE
+ )
+ }
+ end
+ end
+end
diff --git a/spec/migrations/20230723203612_backfill_default_branch_protection_application_setting_spec.rb b/spec/migrations/20230723203612_backfill_default_branch_protection_application_setting_spec.rb
new file mode 100644
index 00000000000..dcb65e22196
--- /dev/null
+++ b/spec/migrations/20230723203612_backfill_default_branch_protection_application_setting_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe BackfillDefaultBranchProtectionApplicationSetting, :migration, feature_category: :database do
+ let(:application_settings_table) { table(:application_settings) }
+
+ before do
+ 5.times do |branch_protection|
+ application_settings_table.create!(default_branch_protection: branch_protection,
+ default_branch_protection_defaults: {})
+ end
+ end
+
+ it 'schedules a new batched migration' do
+ reversible_migration do |migration|
+ migration.before -> {
+ 5.times do |branch_protection|
+ expect(migrated_attribute(branch_protection)).to eq({})
+ end
+ }
+
+ migration.after -> {
+ expect(migrated_attribute(0)).to eq({ "allow_force_push" => true,
+ "allowed_to_merge" => [{ "access_level" => 30 }],
+ "allowed_to_push" => [{ "access_level" => 30 }] })
+ expect(migrated_attribute(1)).to eq({ "allow_force_push" => false,
+ "allowed_to_merge" => [{ "access_level" => 30 }],
+ "allowed_to_push" => [{ "access_level" => 30 }] })
+ expect(migrated_attribute(2)).to eq({ "allow_force_push" => false,
+ "allowed_to_merge" => [{ "access_level" => 40 }],
+ "allowed_to_push" => [{ "access_level" => 40 }] })
+ expect(migrated_attribute(3)).to eq({ "allow_force_push" => true,
+ "allowed_to_merge" => [{ "access_level" => 30 }],
+ "allowed_to_push" => [{ "access_level" => 40 }] })
+ expect(migrated_attribute(4)).to eq({ "allow_force_push" => true,
+ "allowed_to_merge" => [{ "access_level" => 30 }],
+ "allowed_to_push" => [{ "access_level" => 40 }],
+ "developer_can_initial_push" => true })
+ }
+ end
+ end
+
+ def migrated_attribute(branch_protection)
+ application_settings_table
+ .where(default_branch_protection: branch_protection)
+ .last.default_branch_protection_defaults
+ end
+end
diff --git a/spec/migrations/20230724071541_queue_backfill_default_branch_protection_namespace_setting_spec.rb b/spec/migrations/20230724071541_queue_backfill_default_branch_protection_namespace_setting_spec.rb
new file mode 100644
index 00000000000..5ba8c6b853c
--- /dev/null
+++ b/spec/migrations/20230724071541_queue_backfill_default_branch_protection_namespace_setting_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe QueueBackfillDefaultBranchProtectionNamespaceSetting, feature_category: :database do
+ let!(:batched_migration) { described_class::MIGRATION }
+
+ it 'schedules a new batched migration' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).to have_scheduled_batched_migration(
+ table_name: :namespace_settings,
+ column_name: :namespace_id,
+ interval: described_class::DELAY_INTERVAL,
+ batch_size: described_class::BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE
+ )
+ }
+ end
+ end
+end
diff --git a/spec/migrations/20230724164745_queue_delete_orphaned_transferred_project_approval_rules_spec.rb b/spec/migrations/20230724164745_queue_delete_orphaned_transferred_project_approval_rules_spec.rb
new file mode 100644
index 00000000000..e4fa7216ae2
--- /dev/null
+++ b/spec/migrations/20230724164745_queue_delete_orphaned_transferred_project_approval_rules_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe QueueDeleteOrphanedTransferredProjectApprovalRules, feature_category: :security_policy_management do
+ let!(:batched_migration) { described_class::MIGRATION }
+
+ it 'schedules a new batched migration' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).to have_scheduled_batched_migration(
+ table_name: :approval_project_rules,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL,
+ batch_size: described_class::BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE
+ )
+ }
+ end
+ end
+end
diff --git a/spec/migrations/20230728174927_add_epic_work_item_type_spec.rb b/spec/migrations/20230728174927_add_epic_work_item_type_spec.rb
new file mode 100644
index 00000000000..8f0227950e1
--- /dev/null
+++ b/spec/migrations/20230728174927_add_epic_work_item_type_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe AddEpicWorkItemType, :migration, feature_category: :team_planning do
+ include MigrationHelpers::WorkItemTypesHelper
+
+ let(:work_item_types) { table(:work_item_types) }
+ let(:work_item_widget_definitions) { table(:work_item_widget_definitions) }
+ let(:work_item_hierarchy_restrictions) { table(:work_item_hierarchy_restrictions) }
+ let(:base_types) do
+ {
+ issue: 0,
+ incident: 1,
+ test_case: 2,
+ requirement: 3,
+ task: 4,
+ objective: 5,
+ key_result: 6,
+ epic: 7
+ }
+ end
+
+ after(:all) do
+ # Make sure base types are recreated after running the migration
+ # because migration specs are not run in a transaction
+ reset_work_item_types
+ end
+
+ before do
+ reset_db_state_prior_to_migration
+ end
+
+ it 'adds the epic type, widget definitions and hierarchy restrictions', :aggregate_failures do
+ expect do
+ migrate!
+ end.to change { work_item_types.count }.by(1)
+ .and(change { work_item_widget_definitions.count }.by(10))
+ .and(change { work_item_hierarchy_restrictions.count }.by(2))
+
+ epic_type = work_item_types.last
+ issue_type = work_item_types.find_by!(namespace_id: nil, base_type: base_types[:issue])
+
+ expect(work_item_types.pluck(:base_type)).to include(base_types[:epic])
+ expect(
+ work_item_widget_definitions.where(work_item_type_id: epic_type.id).pluck(:widget_type)
+ ).to match_array(described_class::EPIC_WIDGETS.values)
+ expect(
+ work_item_hierarchy_restrictions.where(parent_type_id: epic_type.id).pluck(:child_type_id, :maximum_depth)
+ ).to contain_exactly([epic_type.id, 9], [issue_type.id, 1])
+ end
+
+ it 'skips creating the new type an it\'s definitions' do
+ work_item_types.find_or_create_by!(
+ name: 'Epic', namespace_id: nil, base_type: base_types[:epic], icon_name: 'issue-type-epic'
+ )
+
+ expect do
+ migrate!
+ end.to not_change(work_item_types, :count)
+ .and(not_change(work_item_widget_definitions, :count))
+ .and(not_change(work_item_hierarchy_restrictions, :count))
+ end
+
+ def reset_db_state_prior_to_migration
+ # Database needs to be in a similar state as when this migration was created
+ work_item_types.delete_all
+ work_item_types.find_or_create_by!(
+ name: 'Issue', namespace_id: nil, base_type: base_types[:issue], icon_name: 'issue-type-issue'
+ )
+ work_item_types.find_or_create_by!(
+ name: 'Incident', namespace_id: nil, base_type: base_types[:incident], icon_name: 'issue-type-incident'
+ )
+ work_item_types.find_or_create_by!(
+ name: 'Test Case', namespace_id: nil, base_type: base_types[:test_case], icon_name: 'issue-type-test-case'
+ )
+ work_item_types.find_or_create_by!(
+ name: 'Requirement', namespace_id: nil, base_type: base_types[:requirement], icon_name: 'issue-type-requirements'
+ )
+ work_item_types.find_or_create_by!(
+ name: 'Task', namespace_id: nil, base_type: base_types[:task], icon_name: 'issue-type-task'
+ )
+ work_item_types.find_or_create_by!(
+ name: 'Objective', namespace_id: nil, base_type: base_types[:objective], icon_name: 'issue-type-objective'
+ )
+ work_item_types.find_or_create_by!(
+ name: 'Key Result', namespace_id: nil, base_type: base_types[:key_result], icon_name: 'issue-type-keyresult'
+ )
+ end
+end
diff --git a/spec/migrations/20230801150214_retry_cleanup_bigint_conversion_for_events_for_gitlab_com_spec.rb b/spec/migrations/20230801150214_retry_cleanup_bigint_conversion_for_events_for_gitlab_com_spec.rb
new file mode 100644
index 00000000000..0eac9f28fcd
--- /dev/null
+++ b/spec/migrations/20230801150214_retry_cleanup_bigint_conversion_for_events_for_gitlab_com_spec.rb
@@ -0,0 +1,147 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require_migration!
+
+RSpec.describe RetryCleanupBigintConversionForEventsForGitlabCom, :migration, feature_category: :database do
+ let(:migration) { described_class.new }
+ let(:connection) { migration.connection }
+ let(:column_name) { 'target_id_convert_to_bigint' }
+ let(:events_table) { table(:events) }
+
+ before do
+ allow(migration).to receive(:should_run?).and_return(should_run?)
+ end
+
+ shared_examples 'skips the up migration' do
+ it "doesn't calls cleanup_conversion_of_integer_to_bigint method" do
+ disable_migrations_output do
+ expect(migration).not_to receive(:cleanup_conversion_of_integer_to_bigint)
+
+ migration.up
+ end
+ end
+ end
+
+ shared_examples 'skips the down migration' do
+ it "doesn't calls restore_conversion_of_integer_to_bigint method" do
+ disable_migrations_output do
+ expect(migration).not_to receive(:restore_conversion_of_integer_to_bigint)
+
+ migration.down
+ end
+ end
+ end
+
+ describe '#up' do
+ context 'when column still exists' do
+ before do
+ # Ensures the correct state of db before the test
+ connection.execute('ALTER TABLE events ADD COLUMN IF NOT EXISTS target_id_convert_to_bigint integer')
+ connection.execute('CREATE OR REPLACE FUNCTION trigger_cd1aeb22b34a() RETURNS trigger LANGUAGE plpgsql AS $$
+ BEGIN NEW."target_id_convert_to_bigint" := NEW."target_id"; RETURN NEW; END; $$;')
+ connection.execute('DROP TRIGGER IF EXISTS trigger_cd1aeb22b34a ON events')
+ connection.execute('CREATE TRIGGER trigger_cd1aeb22b34a BEFORE INSERT OR UPDATE ON events FOR EACH ROW EXECUTE
+ FUNCTION trigger_cd1aeb22b34a()')
+ end
+
+ context 'when is GitLab.com, dev, or test' do
+ let(:should_run?) { true }
+
+ it 'drop the temporary columns' do
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ events_table.reset_column_information
+ expect(events_table.columns.find { |c| c.name == 'target_id_convert_to_bigint' }).not_to be_nil
+ }
+
+ migration.after -> {
+ events_table.reset_column_information
+ expect(events_table.columns.find { |c| c.name == 'target_id_convert_to_bigint' }).to be_nil
+ }
+ end
+ end
+ end
+ end
+
+ context 'when is a self-managed instance' do
+ let(:should_run?) { false }
+
+ it_behaves_like 'skips the up migration'
+ end
+ end
+
+ context 'when column not exists' do
+ before do
+ connection.execute('ALTER TABLE events DROP COLUMN IF EXISTS target_id_convert_to_bigint')
+ end
+
+ context 'when is GitLab.com, dev, or test' do
+ let(:should_run?) { true }
+
+ it_behaves_like 'skips the up migration'
+ end
+
+ context 'when is a self-managed instance' do
+ let(:should_run?) { false }
+
+ it_behaves_like 'skips the up migration'
+ end
+ end
+ end
+
+ describe '#down' do
+ context 'when column still exists' do
+ before do
+ # Ensures the correct state of db before the test
+ connection.execute('ALTER TABLE events ADD COLUMN IF NOT EXISTS target_id_convert_to_bigint integer')
+ connection.execute('CREATE OR REPLACE FUNCTION trigger_cd1aeb22b34a() RETURNS trigger LANGUAGE plpgsql AS $$
+ BEGIN NEW."target_id_convert_to_bigint" := NEW."target_id"; RETURN NEW; END; $$;')
+ connection.execute('DROP TRIGGER IF EXISTS trigger_cd1aeb22b34a ON events')
+ connection.execute('CREATE TRIGGER trigger_cd1aeb22b34a BEFORE INSERT OR UPDATE ON events FOR EACH ROW EXECUTE
+ FUNCTION trigger_cd1aeb22b34a()')
+ end
+
+ context 'when is GitLab.com, dev, or test' do
+ let(:should_run?) { true }
+
+ it_behaves_like 'skips the down migration'
+ end
+
+ context 'when is a self-managed instance' do
+ let(:should_run?) { false }
+
+ it_behaves_like 'skips the down migration'
+ end
+ end
+
+ context 'when column not exists' do
+ before do
+ connection.execute('ALTER TABLE events DROP COLUMN IF EXISTS target_id_convert_to_bigint')
+ end
+
+ context 'when is GitLab.com, dev, or test' do
+ let(:should_run?) { true }
+
+ it 'restore the temporary columns' do
+ disable_migrations_output do
+ migration.down
+
+ column = events_table.columns.find { |c| c.name == 'target_id_convert_to_bigint' }
+
+ expect(column).not_to be_nil
+ expect(column.sql_type).to eq('integer')
+ end
+ end
+ end
+
+ context 'when is a self-managed instance' do
+ let(:should_run?) { false }
+
+ it_behaves_like 'skips the down migration'
+ end
+ end
+ end
+end
diff --git a/spec/migrations/20230802085923_queue_fix_allow_descendants_override_disabled_shared_runners_spec.rb b/spec/migrations/20230802085923_queue_fix_allow_descendants_override_disabled_shared_runners_spec.rb
new file mode 100644
index 00000000000..c296ba24d9d
--- /dev/null
+++ b/spec/migrations/20230802085923_queue_fix_allow_descendants_override_disabled_shared_runners_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe QueueFixAllowDescendantsOverrideDisabledSharedRunners, feature_category: :runner_fleet do
+ let!(:batched_migration) { described_class::MIGRATION }
+
+ it 'schedules a new batched migration' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).to have_scheduled_batched_migration(
+ table_name: :namespaces,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL,
+ batch_size: described_class::BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE
+ )
+ }
+ end
+ end
+end
diff --git a/spec/migrations/20230803125434_add_has_merge_request_on_vulnerability_reads_trigger_spec.rb b/spec/migrations/20230803125434_add_has_merge_request_on_vulnerability_reads_trigger_spec.rb
new file mode 100644
index 00000000000..374a494fbec
--- /dev/null
+++ b/spec/migrations/20230803125434_add_has_merge_request_on_vulnerability_reads_trigger_spec.rb
@@ -0,0 +1,178 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require_migration!
+
+RSpec.describe AddHasMergeRequestOnVulnerabilityReadsTrigger, feature_category: :vulnerability_management do
+ let(:migration) { described_class.new }
+ let(:vulnerability_reads) { table(:vulnerability_reads) }
+ let(:merge_requests) { table(:merge_requests) }
+ let(:merge_request_links) { table(:vulnerability_merge_request_links) }
+ let(:vulnerabilities) { table(:vulnerabilities) }
+ let(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
+
+ let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let(:user) { table(:users).create!(id: 13, email: 'author@example.com', username: 'author', projects_limit: 10) }
+ let(:project) { table(:projects).create!(id: 123, namespace_id: namespace.id, project_namespace_id: namespace.id) }
+
+ let(:scanner) do
+ table(:vulnerability_scanners).create!(
+ project_id: project.id, external_id: 'test 1', name: 'test scanner 1')
+ end
+
+ let(:merge_request) do
+ create_merge_request!(
+ target_project_id: project.id,
+ source_branch: "other",
+ target_branch: "main",
+ author_id: user.id,
+ title: 'Feedback Merge Request 1'
+ )
+ end
+
+ let(:vulnerability) do
+ create_vulnerability!(
+ project_id: project.id,
+ author_id: user.id
+ )
+ end
+
+ let(:identifier) do
+ table(:vulnerability_identifiers).create!(
+ project_id: project.id,
+ external_type: 'uuid-v5',
+ external_id: 'uuid-v5',
+ fingerprint: '7e394d1b1eb461a7406d7b1e08f057a1cf11287a',
+ name: 'Identifier for UUIDv5')
+ end
+
+ let(:vulnerability_read) { vulnerability_reads.first }
+
+ before do
+ create_finding!(
+ vulnerability_id: vulnerability.id,
+ project_id: project.id,
+ scanner_id: scanner.id,
+ primary_identifier_id: identifier.id
+ )
+
+ vulnerability_read.reload
+ end
+
+ describe '#up' do
+ before do
+ migrate!
+ end
+
+ describe 'INSERT trigger' do
+ it 'updates has_merge_request in vulnerability_reads' do
+ expect do
+ merge_request_links.create!(
+ vulnerability_id: vulnerability.id, merge_request_id: merge_request.id)
+ end.to change { vulnerability_read.reload.has_merge_request }.from(false).to(true)
+ end
+ end
+
+ describe 'DELETE trigger' do
+ let(:merge_request2) do
+ create_merge_request!(
+ target_project_id: project.id,
+ source_branch: "other_2",
+ target_branch: "main",
+ author_id: user.id,
+ title: 'Feedback Merge Request 2'
+ )
+ end
+
+ it 'does not change has_merge_request when there exists another merge_request' do
+ merge_request_link1 = merge_request_links.create!(
+ vulnerability_id: vulnerability.id, merge_request_id: merge_request.id)
+
+ merge_request_links.create!(
+ vulnerability_id: vulnerability.id, merge_request_id: merge_request2.id)
+
+ expect do
+ merge_request_link1.delete
+ end.not_to change { vulnerability_read.reload.has_merge_request }
+ end
+
+ it 'unsets has_merge_request when all merge_requests are deleted' do
+ merge_request_link1 = merge_request_links.create!(
+ vulnerability_id: vulnerability.id, merge_request_id: merge_request.id)
+
+ merge_request_link2 = merge_request_links.create!(
+ vulnerability_id: vulnerability.id, merge_request_id: merge_request2.id)
+
+ expect do
+ merge_request_link1.delete
+ merge_request_link2.delete
+ end.to change { vulnerability_read.reload.has_merge_request }.from(true).to(false)
+ end
+ end
+ end
+
+ describe '#down' do
+ before do
+ migration.up
+ migration.down
+ end
+
+ it 'drops the trigger' do
+ expect do
+ merge_request_links.create!(
+ vulnerability_id: vulnerability.id, merge_request_id: merge_request.id)
+ end.not_to change { vulnerability_read.reload.has_merge_request }
+ end
+ end
+
+ private
+
+ def create_vulnerability!(project_id:, author_id:, title: 'test', severity: 7, confidence: 7, report_type: 0)
+ vulnerabilities.create!(
+ project_id: project_id,
+ author_id: author_id,
+ title: title,
+ severity: severity,
+ confidence: confidence,
+ report_type: report_type
+ )
+ end
+
+ def create_merge_request!(overrides = {})
+ attrs = {
+ target_project_id: project.id,
+ source_branch: "other",
+ target_branch: "main",
+ author_id: user.id,
+ title: 'Feedback Merge Request'
+ }.merge(overrides)
+
+ merge_requests.create!(attrs)
+ end
+
+ # rubocop:disable Metrics/ParameterLists
+ def create_finding!(
+ vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
+ name: "test", severity: 7, confidence: 7, report_type: 0,
+ project_fingerprint: '123qweasdzxc', location: { "image" => "alpine:3.4" }, location_fingerprint: 'test',
+ metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
+ vulnerabilities_findings.create!(
+ vulnerability_id: vulnerability_id,
+ project_id: project_id,
+ name: name,
+ severity: severity,
+ confidence: confidence,
+ report_type: report_type,
+ project_fingerprint: project_fingerprint,
+ scanner_id: scanner_id,
+ primary_identifier_id: primary_identifier_id,
+ location: location,
+ location_fingerprint: location_fingerprint,
+ metadata_version: metadata_version,
+ raw_metadata: raw_metadata,
+ uuid: uuid
+ )
+ end
+ # rubocop:enable Metrics/ParameterLists
+end
diff --git a/spec/migrations/20230804053643_add_ticket_work_item_type_spec.rb b/spec/migrations/20230804053643_add_ticket_work_item_type_spec.rb
new file mode 100644
index 00000000000..9a6eeb44254
--- /dev/null
+++ b/spec/migrations/20230804053643_add_ticket_work_item_type_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe AddTicketWorkItemType, :migration, feature_category: :service_desk do
+ include MigrationHelpers::WorkItemTypesHelper
+
+ let(:work_item_types) { table(:work_item_types) }
+ let(:work_item_widget_definitions) { table(:work_item_widget_definitions) }
+ let(:work_item_hierarchy_restrictions) { table(:work_item_hierarchy_restrictions) }
+ let(:base_types) do
+ {
+ issue: 0,
+ incident: 1,
+ test_case: 2,
+ requirement: 3,
+ task: 4,
+ objective: 5,
+ key_result: 6,
+ epic: 7,
+ ticket: 8
+ }
+ end
+
+ after(:all) do
+ # Make sure base types are recreated after running the migration
+ # because migration specs are not run in a transaction
+ reset_work_item_types
+ end
+
+ before do
+ reset_db_state_prior_to_migration
+ end
+
+ it 'adds the ticket type, widget definitions and hierarchy restrictions', :aggregate_failures do
+ expect do
+ migrate!
+ end.to change { work_item_types.count }.by(1)
+ .and(change { work_item_widget_definitions.count }.by(13))
+ .and(change { work_item_hierarchy_restrictions.count }.by(2))
+
+ ticket_type = work_item_types.last
+ issue_type = work_item_types.find_by!(namespace_id: nil, base_type: base_types[:issue])
+
+ expect(work_item_types.pluck(:base_type)).to include(base_types[:ticket])
+ expect(
+ work_item_widget_definitions.where(work_item_type_id: ticket_type.id).pluck(:widget_type)
+ ).to match_array(described_class::TICKET_WIDGETS.values)
+ expect(
+ work_item_hierarchy_restrictions.where(parent_type_id: ticket_type.id).pluck(:child_type_id, :maximum_depth)
+ ).to contain_exactly([ticket_type.id, 1], [issue_type.id, 1])
+ end
+
+ it "skips creating the new type and it's definitions when it already exists" do
+ work_item_types.find_or_create_by!(
+ name: 'Ticket', namespace_id: nil, base_type: base_types[:ticket], icon_name: 'issue-type-issue'
+ )
+
+ expect do
+ migrate!
+ end.to not_change(work_item_types, :count)
+ .and(not_change(work_item_widget_definitions, :count))
+ .and(not_change(work_item_hierarchy_restrictions, :count))
+ end
+
+ it "skips creating the new type and it's definitions when type creation fails" do
+ allow(described_class::MigrationWorkItemType).to receive(:create)
+ .and_return(described_class::MigrationWorkItemType.new)
+
+ expect do
+ migrate!
+ end.to not_change(work_item_types, :count)
+ .and(not_change(work_item_widget_definitions, :count))
+ .and(not_change(work_item_hierarchy_restrictions, :count))
+ end
+
+ def reset_db_state_prior_to_migration
+ # Database needs to be in a similar state as when this migration was created
+ work_item_types.delete_all
+
+ {
+ issue: { name: 'Issue', icon_name: 'issue-type-issue' },
+ incident: { name: 'Incident', icon_name: 'issue-type-incident' },
+ test_case: { name: 'Test Case', icon_name: 'issue-type-test-case' },
+ requirement: { name: 'Requirement', icon_name: 'issue-type-requirements' },
+ task: { name: 'Task', icon_name: 'issue-type-task' },
+ objective: { name: 'Objective', icon_name: 'issue-type-objective' },
+ key_result: { name: 'Key Result', icon_name: 'issue-type-keyresult' },
+ epic: { name: 'Epic', icon_name: 'issue-type-epic' }
+ }.each do |type, opts|
+ work_item_types.find_or_create_by!(
+ name: opts[:name], namespace_id: nil, base_type: base_types[type], icon_name: opts[:icon_name]
+ )
+ end
+ end
+end
diff --git a/spec/migrations/20230807083334_add_linked_items_work_item_widget_spec.rb b/spec/migrations/20230807083334_add_linked_items_work_item_widget_spec.rb
new file mode 100644
index 00000000000..cd6da15403f
--- /dev/null
+++ b/spec/migrations/20230807083334_add_linked_items_work_item_widget_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe AddLinkedItemsWorkItemWidget, :migration, feature_category: :portfolio_management do
+ it_behaves_like 'migration that adds widget to work items definitions', widget_name: 'Linked items' do
+ let(:work_item_type_count) { 8 }
+ end
+end
diff --git a/spec/migrations/20230809104753_swap_epic_user_mentions_note_id_to_bigint_for_self_hosts_spec.rb b/spec/migrations/20230809104753_swap_epic_user_mentions_note_id_to_bigint_for_self_hosts_spec.rb
new file mode 100644
index 00000000000..7dcd40db88a
--- /dev/null
+++ b/spec/migrations/20230809104753_swap_epic_user_mentions_note_id_to_bigint_for_self_hosts_spec.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SwapEpicUserMentionsNoteIdToBigintForSelfHosts, feature_category: :database do
+ describe '#up' do
+ context 'when GitLab.com, dev, or test' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE epic_user_mentions ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE epic_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swap the columns' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(true)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ epic_user_mentions = table(:epic_user_mentions)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ epic_user_mentions.reset_column_information
+
+ expect(epic_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(epic_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be nil
+ }
+
+ migration.after -> {
+ epic_user_mentions.reset_column_information
+
+ expect(epic_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(epic_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be nil
+ }
+ end
+ end
+ end
+ end
+
+ context 'when self-managed instance with the columns already swapped' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE epic_user_mentions ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE epic_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint integer')
+ end
+
+ it 'does not swap the columns' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ epic_user_mentions = table(:epic_user_mentions)
+
+ migrate!
+
+ expect(epic_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(epic_user_mentions.columns.find do |c|
+ c.name == 'note_id_convert_to_bigint'
+ end.sql_type).to eq('integer')
+ end
+ end
+
+ context 'when self-managed instance with the `note_id_convert_to_bigint` column already dropped ' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE epic_user_mentions ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE epic_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swap the columns' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ epic_user_mentions = table(:epic_user_mentions)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ epic_user_mentions.reset_column_information
+
+ expect(epic_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(epic_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be nil
+ }
+
+ migration.after -> {
+ epic_user_mentions.reset_column_information
+
+ expect(epic_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(epic_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be nil
+ }
+ end
+ end
+ end
+ end
+
+ context 'when self-managed instance' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE epic_user_mentions ALTER COLUMN note_id TYPE integer')
+ connection.execute('ALTER TABLE epic_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint bigint')
+ connection.execute('ALTER TABLE epic_user_mentions ALTER COLUMN note_id_convert_to_bigint TYPE bigint')
+ connection.execute('DROP INDEX IF EXISTS index_epic_user_mentions_on_note_id_convert_to_bigint')
+ connection.execute('CREATE OR REPLACE FUNCTION trigger_c5a5f48f12b0() RETURNS trigger LANGUAGE plpgsql AS $$
+ BEGIN NEW."note_id_convert_to_bigint" := NEW."note_id"; RETURN NEW; END; $$;')
+ end
+
+ it 'swaps the columns' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ epic_user_mentions = table(:epic_user_mentions)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ epic_user_mentions.reset_column_information
+
+ expect(epic_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('integer')
+ expect(epic_user_mentions.columns.find do |c|
+ c.name == 'note_id_convert_to_bigint'
+ end.sql_type).to eq('bigint')
+ }
+
+ migration.after -> {
+ epic_user_mentions.reset_column_information
+
+ expect(epic_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(epic_user_mentions.columns.find do |c|
+ c.name == 'note_id_convert_to_bigint'
+ end.sql_type).to eq('integer')
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/migrations/20230810103534_swap_suggestions_note_id_to_bigint_for_self_hosts_spec.rb b/spec/migrations/20230810103534_swap_suggestions_note_id_to_bigint_for_self_hosts_spec.rb
new file mode 100644
index 00000000000..79583fb0a21
--- /dev/null
+++ b/spec/migrations/20230810103534_swap_suggestions_note_id_to_bigint_for_self_hosts_spec.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SwapSuggestionsNoteIdToBigintForSelfHosts, feature_category: :database do
+ describe '#up' do
+ context 'when GitLab.com, dev, or test' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE suggestions ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE suggestions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swap the columns' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(true)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ suggestions = table(:suggestions)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ suggestions.reset_column_information
+
+ expect(suggestions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(suggestions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be nil
+ }
+
+ migration.after -> {
+ suggestions.reset_column_information
+
+ expect(suggestions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(suggestions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be nil
+ }
+ end
+ end
+ end
+ end
+
+ context 'when self-managed instance with the columns already swapped' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE suggestions ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE suggestions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint integer')
+ end
+
+ it 'does not swap the columns' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ suggestions = table(:suggestions)
+
+ migrate!
+
+ expect(suggestions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(suggestions.columns.find do |c|
+ c.name == 'note_id_convert_to_bigint'
+ end.sql_type).to eq('integer')
+ end
+ end
+
+ context 'when self-managed instance with the `note_id_convert_to_bigint` column already dropped ' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE suggestions ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE suggestions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swap the columns' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ suggestions = table(:suggestions)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ suggestions.reset_column_information
+
+ expect(suggestions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(suggestions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be nil
+ }
+
+ migration.after -> {
+ suggestions.reset_column_information
+
+ expect(suggestions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(suggestions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be nil
+ }
+ end
+ end
+ end
+ end
+
+ context 'when self-managed instance' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE suggestions ALTER COLUMN note_id TYPE integer')
+ connection.execute('ALTER TABLE suggestions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint bigint')
+ connection.execute('ALTER TABLE suggestions ALTER COLUMN note_id_convert_to_bigint TYPE bigint')
+ connection.execute('DROP INDEX IF EXISTS index_suggestions_on_note_id_convert_to_bigint')
+ connection.execute('CREATE OR REPLACE FUNCTION trigger_ee7956d805e6() RETURNS trigger LANGUAGE plpgsql AS $$
+ BEGIN NEW."note_id_convert_to_bigint" := NEW."note_id"; RETURN NEW; END; $$;')
+ end
+
+ it 'swaps the columns' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ suggestions = table(:suggestions)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ suggestions.reset_column_information
+
+ expect(suggestions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('integer')
+ expect(suggestions.columns.find do |c|
+ c.name == 'note_id_convert_to_bigint'
+ end.sql_type).to eq('bigint')
+ }
+
+ migration.after -> {
+ suggestions.reset_column_information
+
+ expect(suggestions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(suggestions.columns.find do |c|
+ c.name == 'note_id_convert_to_bigint'
+ end.sql_type).to eq('integer')
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/migrations/20230810123044_swap_snippet_user_mentions_note_id_to_bigint_for_self_hosts_spec.rb b/spec/migrations/20230810123044_swap_snippet_user_mentions_note_id_to_bigint_for_self_hosts_spec.rb
new file mode 100644
index 00000000000..92b73901fec
--- /dev/null
+++ b/spec/migrations/20230810123044_swap_snippet_user_mentions_note_id_to_bigint_for_self_hosts_spec.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SwapSnippetUserMentionsNoteIdToBigintForSelfHosts, feature_category: :database do
+ let(:connection) { described_class.new.connection }
+ let(:snippet_user_mentions) { table(:snippet_user_mentions) }
+
+ shared_examples 'column `note_id_convert_to_bigint` is already dropped' do
+ before do
+ connection.execute('ALTER TABLE snippet_user_mentions ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE snippet_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swaps the columns' do
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ snippet_user_mentions.reset_column_information
+
+ expect(snippet_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(snippet_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
+ }
+
+ migration.after -> {
+ snippet_user_mentions.reset_column_information
+
+ expect(snippet_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(snippet_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
+ }
+ end
+ end
+ end
+ end
+
+ describe '#up' do
+ before do
+ # rubocop:disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to(
+ receive(:com_or_dev_or_test_but_not_jh?).and_return(com_or_dev_or_test_but_not_jh?)
+ )
+ # rubocop:enable RSpec/AnyInstanceOf
+ end
+
+ context 'when GitLab.com, dev, or test' do
+ let(:com_or_dev_or_test_but_not_jh?) { true }
+
+ it_behaves_like 'column `note_id_convert_to_bigint` is already dropped'
+ end
+
+ context 'when self-managed instance with the `note_id_convert_to_bigint` column already dropped' do
+ let(:com_or_dev_or_test_but_not_jh?) { false }
+
+ it_behaves_like 'column `note_id_convert_to_bigint` is already dropped'
+ end
+
+ context 'when self-managed instance columns already swapped' do
+ let(:com_or_dev_or_test_but_not_jh?) { false }
+
+ before do
+ connection.execute('ALTER TABLE snippet_user_mentions ALTER COLUMN note_id TYPE bigint')
+ connection.execute(
+ 'ALTER TABLE snippet_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint integer'
+ )
+
+ disable_migrations_output { migrate! }
+ end
+
+ after do
+ connection.execute('ALTER TABLE snippet_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swaps the columns' do
+ expect(snippet_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(snippet_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+ eq('integer')
+ )
+ end
+ end
+
+ context 'when self-managed instance' do
+ let(:com_or_dev_or_test_but_not_jh?) { false }
+
+ before do
+ connection.execute('ALTER TABLE snippet_user_mentions ALTER COLUMN note_id TYPE integer')
+ connection.execute(
+ 'ALTER TABLE snippet_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint bigint'
+ )
+ connection.execute('ALTER TABLE snippet_user_mentions ALTER COLUMN note_id_convert_to_bigint TYPE bigint')
+ connection.execute('DROP INDEX IF EXISTS index_snippet_user_mentions_on_note_id_convert_to_bigint CASCADE')
+ connection.execute('CREATE OR REPLACE FUNCTION trigger_bfc6e47be8cc() RETURNS trigger LANGUAGE plpgsql AS $$
+ BEGIN NEW."note_id_convert_to_bigint" := NEW."note_id"; RETURN NEW; END; $$;')
+ end
+
+ after do
+ connection.execute('ALTER TABLE snippet_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'swaps the columns' do
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ snippet_user_mentions.reset_column_information
+
+ expect(snippet_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('integer')
+ expect(snippet_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+ eq('bigint')
+ )
+ }
+
+ migration.after -> {
+ snippet_user_mentions.reset_column_information
+
+ expect(snippet_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(snippet_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+ eq('integer')
+ )
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/migrations/20230811103941_swap_vulnerability_user_mentions_note_id_to_bigint_for_self_hosts_spec.rb b/spec/migrations/20230811103941_swap_vulnerability_user_mentions_note_id_to_bigint_for_self_hosts_spec.rb
new file mode 100644
index 00000000000..c1aa2d4daec
--- /dev/null
+++ b/spec/migrations/20230811103941_swap_vulnerability_user_mentions_note_id_to_bigint_for_self_hosts_spec.rb
@@ -0,0 +1,133 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SwapVulnerabilityUserMentionsNoteIdToBigintForSelfHosts, feature_category: :database do
+ let(:connection) { described_class.new.connection }
+ let(:vulnerability_user_mentions) { table(:vulnerability_user_mentions) }
+
+ shared_examples 'column `note_id_convert_to_bigint` is already dropped' do
+ before do
+ connection.execute('ALTER TABLE vulnerability_user_mentions ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE vulnerability_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swaps the columns' do
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ vulnerability_user_mentions.reset_column_information
+
+ expect(vulnerability_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(vulnerability_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
+ }
+
+ migration.after -> {
+ vulnerability_user_mentions.reset_column_information
+
+ expect(vulnerability_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(vulnerability_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
+ }
+ end
+ end
+ end
+ end
+
+ describe '#up' do
+ before do
+ # rubocop:disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to(
+ receive(:com_or_dev_or_test_but_not_jh?).and_return(com_or_dev_or_test_but_not_jh?)
+ )
+ # rubocop:enable RSpec/AnyInstanceOf
+ end
+
+ context 'when GitLab.com, dev, or test' do
+ let(:com_or_dev_or_test_but_not_jh?) { true }
+
+ it_behaves_like 'column `note_id_convert_to_bigint` is already dropped'
+ end
+
+ context 'when self-managed instance with the `note_id_convert_to_bigint` column already dropped' do
+ let(:com_or_dev_or_test_but_not_jh?) { false }
+
+ it_behaves_like 'column `note_id_convert_to_bigint` is already dropped'
+ end
+
+ context 'when self-managed instance columns already swapped' do
+ let(:com_or_dev_or_test_but_not_jh?) { false }
+
+ before do
+ connection.execute('ALTER TABLE vulnerability_user_mentions ALTER COLUMN note_id TYPE bigint')
+ connection.execute(
+ 'ALTER TABLE vulnerability_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint integer'
+ )
+
+ disable_migrations_output { migrate! }
+ end
+
+ after do
+ connection.execute('ALTER TABLE vulnerability_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swaps the columns' do
+ expect(vulnerability_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(vulnerability_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+ eq('integer')
+ )
+ end
+ end
+
+ context 'when self-managed instance' do
+ let(:com_or_dev_or_test_but_not_jh?) { false }
+
+ before do
+ connection.execute('ALTER TABLE vulnerability_user_mentions ALTER COLUMN note_id TYPE integer')
+ connection.execute(
+ 'ALTER TABLE vulnerability_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint bigint'
+ )
+ connection.execute('ALTER TABLE vulnerability_user_mentions ALTER COLUMN note_id_convert_to_bigint TYPE bigint')
+ connection.execute(
+ 'DROP INDEX IF EXISTS index_vulnerability_user_mentions_on_note_id_convert_to_bigint CASCADE'
+ )
+ connection.execute('CREATE OR REPLACE FUNCTION trigger_0e214b8a14f2() RETURNS trigger LANGUAGE plpgsql AS $$
+ BEGIN NEW."note_id_convert_to_bigint" := NEW."note_id"; RETURN NEW; END; $$;')
+ end
+
+ after do
+ connection.execute('ALTER TABLE vulnerability_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'swaps the columns' do
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ vulnerability_user_mentions.reset_column_information
+
+ expect(vulnerability_user_mentions.columns.find do |c|
+ c.name == 'note_id'
+ end.sql_type).to eq('integer')
+ expect(vulnerability_user_mentions.columns.find do |c|
+ c.name == 'note_id_convert_to_bigint'
+ end.sql_type).to(
+ eq('bigint')
+ )
+ }
+
+ migration.after -> {
+ vulnerability_user_mentions.reset_column_information
+
+ expect(vulnerability_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(vulnerability_user_mentions.columns.find do |c|
+ c.name == 'note_id_convert_to_bigint'
+ end.sql_type).to(
+ eq('integer')
+ )
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/migrations/20230814144045_swap_timelogs_note_id_to_bigint_for_self_hosts_spec.rb b/spec/migrations/20230814144045_swap_timelogs_note_id_to_bigint_for_self_hosts_spec.rb
new file mode 100644
index 00000000000..41b9f0bf4e9
--- /dev/null
+++ b/spec/migrations/20230814144045_swap_timelogs_note_id_to_bigint_for_self_hosts_spec.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SwapTimelogsNoteIdToBigintForSelfHosts, feature_category: :database do
+ let(:connection) { described_class.new.connection }
+ let(:timelogs) { table(:timelogs) }
+
+ shared_examples 'column `note_id_convert_to_bigint` is already dropped' do
+ before do
+ connection.execute('ALTER TABLE timelogs ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE timelogs DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swaps the columns' do
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ timelogs.reset_column_information
+
+ expect(timelogs.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(timelogs.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
+ }
+
+ migration.after -> {
+ timelogs.reset_column_information
+
+ expect(timelogs.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(timelogs.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
+ }
+ end
+ end
+ end
+ end
+
+ describe '#up' do
+ before do
+ # rubocop:disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to(
+ receive(:com_or_dev_or_test_but_not_jh?).and_return(com_or_dev_or_test_but_not_jh?)
+ )
+ # rubocop:enable RSpec/AnyInstanceOf
+ end
+
+ context 'when GitLab.com, dev, or test' do
+ let(:com_or_dev_or_test_but_not_jh?) { true }
+
+ it_behaves_like 'column `note_id_convert_to_bigint` is already dropped'
+ end
+
+ context 'when self-managed instance with the `note_id_convert_to_bigint` column already dropped' do
+ let(:com_or_dev_or_test_but_not_jh?) { false }
+
+ it_behaves_like 'column `note_id_convert_to_bigint` is already dropped'
+ end
+
+ context 'when self-managed instance columns already swapped' do
+ let(:com_or_dev_or_test_but_not_jh?) { false }
+
+ before do
+ connection.execute('ALTER TABLE timelogs ALTER COLUMN note_id TYPE bigint')
+ connection.execute(
+ 'ALTER TABLE timelogs ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint integer'
+ )
+
+ disable_migrations_output { migrate! }
+ end
+
+ after do
+ connection.execute('ALTER TABLE timelogs DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swaps the columns' do
+ expect(timelogs.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(timelogs.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+ eq('integer')
+ )
+ end
+ end
+
+ context 'when self-managed instance' do
+ let(:com_or_dev_or_test_but_not_jh?) { false }
+
+ before do
+ connection.execute('ALTER TABLE timelogs ALTER COLUMN note_id TYPE integer')
+ connection.execute(
+ 'ALTER TABLE timelogs ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint bigint'
+ )
+ connection.execute('ALTER TABLE timelogs ALTER COLUMN note_id_convert_to_bigint TYPE bigint')
+ connection.execute('DROP INDEX IF EXISTS index_timelogs_on_note_id_convert_to_bigint CASCADE')
+ connection.execute('CREATE OR REPLACE FUNCTION trigger_428d92773fe7() RETURNS trigger LANGUAGE plpgsql AS $$
+ BEGIN NEW."note_id_convert_to_bigint" := NEW."note_id"; RETURN NEW; END; $$;')
+ end
+
+ after do
+ connection.execute('ALTER TABLE timelogs DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'swaps the columns' do
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ timelogs.reset_column_information
+
+ expect(timelogs.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('integer')
+ expect(timelogs.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+ eq('bigint')
+ )
+ }
+
+ migration.after -> {
+ timelogs.reset_column_information
+
+ expect(timelogs.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(timelogs.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+ eq('integer')
+ )
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/migrations/add_expiry_notified_at_to_member_spec.rb b/spec/migrations/add_expiry_notified_at_to_member_spec.rb
new file mode 100644
index 00000000000..30eaf06529e
--- /dev/null
+++ b/spec/migrations/add_expiry_notified_at_to_member_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe AddExpiryNotifiedAtToMember, feature_category: :system_access do
+ let(:members) { table(:members) }
+
+ it 'correctly migrates up and down' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(members.column_names).not_to include('expiry_notified_at')
+ }
+
+ migration.after -> {
+ members.reset_column_information
+ expect(members.column_names).to include('expiry_notified_at')
+ }
+ end
+ end
+end
diff --git a/spec/migrations/cleanup_conversion_big_int_ci_build_needs_self_managed_spec.rb b/spec/migrations/cleanup_conversion_big_int_ci_build_needs_self_managed_spec.rb
new file mode 100644
index 00000000000..03a8356c721
--- /dev/null
+++ b/spec/migrations/cleanup_conversion_big_int_ci_build_needs_self_managed_spec.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe CleanupConversionBigIntCiBuildNeedsSelfManaged, feature_category: :database do
+ after do
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE ci_build_needs DROP COLUMN IF EXISTS id_convert_to_bigint')
+ end
+
+ describe '#up' do
+ context 'when it is GitLab.com, dev, or test but not JiHu' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE ci_build_needs DROP COLUMN IF EXISTS id_convert_to_bigint')
+ end
+
+ it 'does nothing' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(true)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ ci_build_needs = table(:ci_build_needs)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ ci_build_needs.reset_column_information
+
+ expect(ci_build_needs.columns.find { |c| c.name == 'id_convert_to_bigint' }).to be nil
+ }
+
+ migration.after -> {
+ ci_build_needs.reset_column_information
+
+ expect(ci_build_needs.columns.find { |c| c.name == 'id_convert_to_bigint' }).to be nil
+ }
+ end
+ end
+ end
+ end
+
+ context 'when there is a self-managed instance with the temporary column already dropped' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE ci_build_needs ALTER COLUMN id TYPE bigint')
+ connection.execute('ALTER TABLE ci_build_needs DROP COLUMN IF EXISTS id_convert_to_bigint')
+ end
+
+ it 'does nothing' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ ci_build_needs = table(:ci_build_needs)
+
+ migrate!
+
+ expect(ci_build_needs.columns.find { |c| c.name == 'id' }.sql_type).to eq('bigint')
+ expect(ci_build_needs.columns.find { |c| c.name == 'id_convert_to_bigint' }).to be nil
+ end
+ end
+
+ context 'when there is a self-managed instance with the temporary columns' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE ci_build_needs ALTER COLUMN id TYPE bigint')
+ connection.execute('ALTER TABLE ci_build_needs ADD COLUMN IF NOT EXISTS id_convert_to_bigint integer')
+ end
+
+ it 'drops the temporary column' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ ci_build_needs = table(:ci_build_needs)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ ci_build_needs.reset_column_information
+
+ expect(ci_build_needs.columns.find { |c| c.name == 'id' }.sql_type).to eq('bigint')
+ expect(ci_build_needs.columns.find do |c|
+ c.name == 'id_convert_to_bigint'
+ end.sql_type).to eq('integer')
+ }
+
+ migration.after -> {
+ ci_build_needs.reset_column_information
+
+ expect(ci_build_needs.columns.find { |c| c.name == 'id' }.sql_type).to eq('bigint')
+ expect(ci_build_needs.columns.find { |c| c.name == 'id_convert_to_bigint' }).to be nil
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/migrations/ensure_commit_user_mentions_note_id_bigint_backfill_is_finished_for_self_managed_spec.rb b/spec/migrations/ensure_commit_user_mentions_note_id_bigint_backfill_is_finished_for_self_managed_spec.rb
new file mode 100644
index 00000000000..b281403204b
--- /dev/null
+++ b/spec/migrations/ensure_commit_user_mentions_note_id_bigint_backfill_is_finished_for_self_managed_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe EnsureCommitUserMentionsNoteIdBigintBackfillIsFinishedForSelfManaged, feature_category: :database do
+ describe '#up' do
+ let(:migration_arguments) do
+ {
+ job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
+ table_name: 'commit_user_mentions',
+ column_name: 'id',
+ job_arguments: [['note_id'], ['note_id_convert_to_bigint']]
+ }
+ end
+
+ it 'ensures the migration is completed for self-managed instances' do
+ expect_next_instance_of(described_class) do |instance|
+ expect(instance).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ expect(instance).to receive(:ensure_batched_background_migration_is_finished).with(migration_arguments)
+ end
+
+ migrate!
+ end
+
+ it 'skips the check for GitLab.com, dev, or test' do
+ expect_next_instance_of(described_class) do |instance|
+ expect(instance).to receive(:com_or_dev_or_test_but_not_jh?).and_return(true)
+ expect(instance).not_to receive(:ensure_batched_background_migration_is_finished)
+ end
+
+ migrate!
+ end
+ end
+end
diff --git a/spec/migrations/ensure_todos_bigint_backfill_completed_for_self_managed_spec.rb b/spec/migrations/ensure_todos_bigint_backfill_completed_for_self_managed_spec.rb
new file mode 100644
index 00000000000..5187441377e
--- /dev/null
+++ b/spec/migrations/ensure_todos_bigint_backfill_completed_for_self_managed_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe EnsureTodosBigintBackfillCompletedForSelfManaged, feature_category: :database do
+ describe '#up' do
+ let(:migration_arguments) do
+ {
+ job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
+ table_name: 'todos',
+ column_name: 'id',
+ job_arguments: [['note_id'], ['note_id_convert_to_bigint']]
+ }
+ end
+
+ it 'ensures the migration is completed for self-managed instances' do
+ expect_next_instance_of(described_class) do |instance|
+ expect(instance).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ expect(instance).to receive(:ensure_batched_background_migration_is_finished).with(migration_arguments)
+ end
+
+ migrate!
+ end
+
+ it 'skips the check for GitLab.com, dev, or test' do
+ expect_next_instance_of(described_class) do |instance|
+ expect(instance).to receive(:com_or_dev_or_test_but_not_jh?).and_return(true)
+ expect(instance).not_to receive(:ensure_batched_background_migration_is_finished)
+ end
+
+ migrate!
+ end
+ end
+end
diff --git a/spec/migrations/swap_commit_user_mentions_note_id_to_bigint_for_self_managed_spec.rb b/spec/migrations/swap_commit_user_mentions_note_id_to_bigint_for_self_managed_spec.rb
new file mode 100644
index 00000000000..b35da2f78db
--- /dev/null
+++ b/spec/migrations/swap_commit_user_mentions_note_id_to_bigint_for_self_managed_spec.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SwapCommitUserMentionsNoteIdToBigintForSelfManaged, feature_category: :database do
+ let(:connection) { described_class.new.connection }
+ let(:commit_user_mentions) { table(:commit_user_mentions) }
+
+ shared_examples 'column `note_id_convert_to_bigint` is already dropped' do
+ before do
+ connection.execute('ALTER TABLE commit_user_mentions ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE commit_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swaps the columns' do
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ commit_user_mentions.reset_column_information
+
+ expect(commit_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(commit_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
+ }
+
+ migration.after -> {
+ commit_user_mentions.reset_column_information
+
+ expect(commit_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(commit_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
+ }
+ end
+ end
+ end
+ end
+
+ describe '#up' do
+ before do
+ # rubocop:disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to(
+ receive(:com_or_dev_or_test_but_not_jh?).and_return(com_or_dev_or_test_but_not_jh?)
+ )
+ # rubocop:enable RSpec/AnyInstanceOf
+ end
+
+ context 'when GitLab.com, dev, or test' do
+ let(:com_or_dev_or_test_but_not_jh?) { true }
+
+ it_behaves_like 'column `note_id_convert_to_bigint` is already dropped'
+ end
+
+ context 'when self-managed instance with the `note_id_convert_to_bigint` column already dropped' do
+ let(:com_or_dev_or_test_but_not_jh?) { false }
+
+ it_behaves_like 'column `note_id_convert_to_bigint` is already dropped'
+ end
+
+ context 'when self-managed instance columns already swapped' do
+ let(:com_or_dev_or_test_but_not_jh?) { false }
+
+ before do
+ connection.execute('ALTER TABLE commit_user_mentions ALTER COLUMN note_id TYPE bigint')
+ connection.execute(
+ 'ALTER TABLE commit_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint integer'
+ )
+
+ disable_migrations_output { migrate! }
+ end
+
+ after do
+ connection.execute('ALTER TABLE commit_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swaps the columns' do
+ expect(commit_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(commit_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+ eq('integer')
+ )
+ end
+ end
+
+ context 'when self-managed instance' do
+ let(:com_or_dev_or_test_but_not_jh?) { false }
+
+ before do
+ connection.execute('ALTER TABLE commit_user_mentions ALTER COLUMN note_id TYPE integer')
+ connection.execute('ALTER TABLE commit_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint bigint')
+ connection.execute('ALTER TABLE commit_user_mentions ALTER COLUMN note_id_convert_to_bigint TYPE bigint')
+ connection.execute('DROP INDEX IF EXISTS index_commit_user_mentions_on_note_id_convert_to_bigint CASCADE')
+ connection.execute('CREATE OR REPLACE FUNCTION trigger_17c3a95ee58a() RETURNS trigger LANGUAGE plpgsql AS $$
+ BEGIN NEW."note_id_convert_to_bigint" := NEW."note_id"; RETURN NEW; END; $$;')
+ end
+
+ after do
+ connection.execute('ALTER TABLE commit_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'swaps the columns' do
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ commit_user_mentions.reset_column_information
+
+ expect(commit_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('integer')
+ expect(commit_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+ eq('bigint')
+ )
+ }
+
+ migration.after -> {
+ commit_user_mentions.reset_column_information
+
+ expect(commit_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(commit_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+ eq('integer')
+ )
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/migrations/swap_events_target_id_to_bigint_for_gitlab_dot_com_spec.rb b/spec/migrations/swap_events_target_id_to_bigint_for_gitlab_dot_com_spec.rb
new file mode 100644
index 00000000000..a3dc73ecc38
--- /dev/null
+++ b/spec/migrations/swap_events_target_id_to_bigint_for_gitlab_dot_com_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SwapEventsTargetIdToBigintForGitlabDotCom, feature_category: :database do
+ describe '#up' do
+ before do
+ # A we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE events ALTER COLUMN target_id TYPE integer')
+ connection.execute('ALTER TABLE events ALTER COLUMN target_id_convert_to_bigint TYPE bigint')
+ end
+
+ # rubocop: disable RSpec/AnyInstanceOf
+ it 'swaps the integer and bigint columns for GitLab.com, dev, or test' do
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(true)
+
+ events = table(:events)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ events.reset_column_information
+
+ expect(events.columns.find { |c| c.name == 'target_id' }.sql_type).to eq('integer')
+ expect(events.columns.find { |c| c.name == 'target_id_convert_to_bigint' }.sql_type).to eq('bigint')
+ }
+
+ migration.after -> {
+ events.reset_column_information
+
+ expect(events.columns.find { |c| c.name == 'target_id' }.sql_type).to eq('bigint')
+ expect(events.columns.find { |c| c.name == 'target_id_convert_to_bigint' }.sql_type)
+ .to eq('integer')
+ }
+ end
+ end
+ end
+
+ it 'is a no-op for other instances' do
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+
+ events = table(:events)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ events.reset_column_information
+
+ expect(events.columns.find { |c| c.name == 'target_id' }.sql_type).to eq('integer')
+ expect(events.columns.find { |c| c.name == 'target_id_convert_to_bigint' }.sql_type).to eq('bigint')
+ }
+
+ migration.after -> {
+ events.reset_column_information
+
+ expect(events.columns.find { |c| c.name == 'target_id' }.sql_type).to eq('integer')
+ expect(events.columns.find { |c| c.name == 'target_id_convert_to_bigint' }.sql_type).to eq('bigint')
+ }
+ end
+ end
+ end
+ # rubocop: enable RSpec/AnyInstanceOf
+ end
+end
diff --git a/spec/migrations/swap_todos_note_id_to_bigint_for_self_managed_spec.rb b/spec/migrations/swap_todos_note_id_to_bigint_for_self_managed_spec.rb
new file mode 100644
index 00000000000..525e4fbcd8d
--- /dev/null
+++ b/spec/migrations/swap_todos_note_id_to_bigint_for_self_managed_spec.rb
@@ -0,0 +1,159 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SwapTodosNoteIdToBigintForSelfManaged, feature_category: :database do
+ describe '#up' do
+ context 'when GitLab.com, dev, or test' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE todos ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE todos DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swap the columns' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(true)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ todos = table(:todos)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ todos.reset_column_information
+
+ expect(todos.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(todos.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be nil
+ }
+
+ migration.after -> {
+ todos.reset_column_information
+
+ expect(todos.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(todos.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be nil
+ }
+ end
+ end
+ end
+ end
+
+ context 'when self-managed instance with the columns already swapped' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE todos ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE todos ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint integer')
+ end
+
+ after do
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE todos DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swap the columns' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ todos = table(:todos)
+
+ migrate!
+
+ expect(todos.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(todos.columns.find do |c|
+ c.name == 'note_id_convert_to_bigint'
+ end.sql_type).to eq('integer')
+ end
+ end
+
+ context 'when self-managed instance with the `note_id_convert_to_bigint` column already dropped ' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE todos ALTER COLUMN note_id TYPE bigint')
+ connection.execute('ALTER TABLE todos DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'does not swap the columns' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ todos = table(:todos)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ todos.reset_column_information
+
+ expect(todos.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(todos.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be nil
+ }
+
+ migration.after -> {
+ todos.reset_column_information
+
+ expect(todos.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(todos.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be nil
+ }
+ end
+ end
+ end
+ end
+
+ context 'when self-managed instance' do
+ before do
+ # As we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE todos ALTER COLUMN note_id TYPE integer')
+ connection.execute('ALTER TABLE todos ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint bigint')
+ connection.execute('ALTER TABLE todos ALTER COLUMN note_id_convert_to_bigint TYPE bigint')
+ connection.execute('DROP INDEX IF EXISTS index_todos_on_note_id_convert_to_bigint')
+ connection.execute('CREATE OR REPLACE FUNCTION trigger_dca935e3a712() RETURNS trigger LANGUAGE plpgsql AS $$
+ BEGIN NEW."note_id_convert_to_bigint" := NEW."note_id"; RETURN NEW; END; $$;')
+ end
+
+ after do
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE todos DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+ end
+
+ it 'swaps the columns' do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+ # rubocop: enable RSpec/AnyInstanceOf
+
+ todos = table(:todos)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ todos.reset_column_information
+
+ expect(todos.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('integer')
+ expect(todos.columns.find do |c|
+ c.name == 'note_id_convert_to_bigint'
+ end.sql_type).to eq('bigint')
+ }
+
+ migration.after -> {
+ todos.reset_column_information
+
+ expect(todos.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+ expect(todos.columns.find do |c|
+ c.name == 'note_id_convert_to_bigint'
+ end.sql_type).to eq('integer')
+ }
+ end
+ end
+ end
+ end
+ end
+end