diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-20 10:00:54 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-20 10:00:54 +0000 |
commit | 3cccd102ba543e02725d247893729e5c73b38295 (patch) | |
tree | f36a04ec38517f5deaaacb5acc7d949688d1e187 /spec/migrations | |
parent | 205943281328046ef7b4528031b90fbda70c75ac (diff) | |
download | gitlab-ce-3cccd102ba543e02725d247893729e5c73b38295.tar.gz |
Add latest changes from gitlab-org/gitlab@14-10-stable-eev14.10.0-rc42
Diffstat (limited to 'spec/migrations')
21 files changed, 868 insertions, 22 deletions
diff --git a/spec/migrations/20210430134202_copy_adoption_snapshot_namespace_spec.rb b/spec/migrations/20210430134202_copy_adoption_snapshot_namespace_spec.rb index 598da495195..ed18820ec8d 100644 --- a/spec/migrations/20210430134202_copy_adoption_snapshot_namespace_spec.rb +++ b/spec/migrations/20210430134202_copy_adoption_snapshot_namespace_spec.rb @@ -36,7 +36,6 @@ RSpec.describe CopyAdoptionSnapshotNamespace, :migration, schema: 20210430124630 runner_configured: true, pipeline_succeeded: true, deploy_succeeded: true, - security_scan_succeeded: true, end_time: Time.zone.now.end_of_month } diff --git a/spec/migrations/20211214012507_backfill_incident_issue_escalation_statuses_spec.rb b/spec/migrations/20211214012507_backfill_incident_issue_escalation_statuses_spec.rb index a17fee6bab2..791c0595f0e 100644 --- a/spec/migrations/20211214012507_backfill_incident_issue_escalation_statuses_spec.rb +++ b/spec/migrations/20211214012507_backfill_incident_issue_escalation_statuses_spec.rb @@ -10,27 +10,10 @@ RSpec.describe BackfillIncidentIssueEscalationStatuses do let(:namespace) { namespaces.create!(name: 'foo', path: 'foo') } let(:project) { projects.create!(namespace_id: namespace.id) } - before do - stub_const("#{described_class.name}::BATCH_SIZE", 1) - end - - it 'schedules jobs for incident issues' do - issue_1 = issues.create!(project_id: project.id) # non-incident issue - incident_1 = issues.create!(project_id: project.id, issue_type: 1) - incident_2 = issues.create!(project_id: project.id, issue_type: 1) - - Sidekiq::Testing.fake! do - freeze_time do - migrate! + # Backfill removed - see db/migrate/20220321234317_remove_all_issuable_escalation_statuses.rb. + it 'does nothing' do + issues.create!(project_id: project.id, issue_type: 1) - expect(described_class::MIGRATION).to be_scheduled_delayed_migration( - 2.minutes, issue_1.id, issue_1.id) - expect(described_class::MIGRATION).to be_scheduled_delayed_migration( - 4.minutes, incident_1.id, incident_1.id) - expect(described_class::MIGRATION).to be_scheduled_delayed_migration( - 6.minutes, incident_2.id, incident_2.id) - expect(BackgroundMigrationWorker.jobs.size).to eq(3) - end - end + expect { migrate! }.not_to change { BackgroundMigrationWorker.jobs.size } end end diff --git a/spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb b/spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb new file mode 100644 index 00000000000..39398fa058d --- /dev/null +++ b/spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe BackfillNamespaceStatisticsWithDependencyProxySize do + let_it_be(:groups) { table(:namespaces) } + let_it_be(:group1) { groups.create!(id: 10, name: 'test1', path: 'test1', type: 'Group') } + let_it_be(:group2) { groups.create!(id: 20, name: 'test2', path: 'test2', type: 'Group') } + let_it_be(:group3) { groups.create!(id: 30, name: 'test3', path: 'test3', type: 'Group') } + let_it_be(:group4) { groups.create!(id: 40, name: 'test4', path: 'test4', type: 'Group') } + + let_it_be(:dependency_proxy_blobs) { table(:dependency_proxy_blobs) } + let_it_be(:dependency_proxy_manifests) { table(:dependency_proxy_manifests) } + + let_it_be(:group1_manifest) { create_manifest(10, 10) } + let_it_be(:group2_manifest) { create_manifest(20, 20) } + let_it_be(:group3_manifest) { create_manifest(30, 30) } + + let_it_be(:group1_blob) { create_blob(10, 10) } + let_it_be(:group2_blob) { create_blob(20, 20) } + let_it_be(:group3_blob) { create_blob(30, 30) } + + describe '#up' do + it 'correctly schedules background migrations' do + stub_const("#{described_class}::BATCH_SIZE", 2) + + Sidekiq::Testing.fake! do + freeze_time do + migrate! + + aggregate_failures do + expect(described_class::MIGRATION) + .to be_scheduled_migration([10, 30], ['dependency_proxy_size']) + + expect(described_class::MIGRATION) + .to be_scheduled_delayed_migration(2.minutes, [20], ['dependency_proxy_size']) + + expect(BackgroundMigrationWorker.jobs.size).to eq(2) + end + end + end + end + end + + def create_manifest(group_id, size) + dependency_proxy_manifests.create!( + group_id: group_id, + size: size, + file_name: 'test-file', + file: 'test', + digest: 'abc123' + ) + end + + def create_blob(group_id, size) + dependency_proxy_blobs.create!( + group_id: group_id, + size: size, + file_name: 'test-file', + file: 'test' + ) + end +end diff --git a/spec/migrations/20220223124428_schedule_merge_topics_with_same_name_spec.rb b/spec/migrations/20220223124428_schedule_merge_topics_with_same_name_spec.rb new file mode 100644 index 00000000000..d9f6729475c --- /dev/null +++ b/spec/migrations/20220223124428_schedule_merge_topics_with_same_name_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe ScheduleMergeTopicsWithSameName do + let(:topics) { table(:topics) } + + describe '#up' do + before do + stub_const("#{described_class}::BATCH_SIZE", 2) + + topics.create!(name: 'topic1') + topics.create!(name: 'Topic2') + topics.create!(name: 'Topic3') + topics.create!(name: 'Topic4') + topics.create!(name: 'topic2') + topics.create!(name: 'topic3') + topics.create!(name: 'topic4') + topics.create!(name: 'TOPIC2') + topics.create!(name: 'topic5') + end + + it 'schedules MergeTopicsWithSameName background jobs', :aggregate_failures do + Sidekiq::Testing.fake! do + freeze_time do + migrate! + + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, %w[topic2 topic3]) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(4.minutes, %w[topic4]) + expect(BackgroundMigrationWorker.jobs.size).to eq(2) + end + end + end + end +end diff --git a/spec/migrations/20220315171129_cleanup_draft_data_from_faulty_regex_spec.rb b/spec/migrations/20220315171129_cleanup_draft_data_from_faulty_regex_spec.rb new file mode 100644 index 00000000000..925f1e573be --- /dev/null +++ b/spec/migrations/20220315171129_cleanup_draft_data_from_faulty_regex_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe CleanupDraftDataFromFaultyRegex do + let(:merge_requests) { table(:merge_requests) } + + let!(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') } + let!(:project) { table(:projects).create!(namespace_id: namespace.id) } + + let(:default_mr_values) do + { + target_project_id: project.id, + draft: true, + source_branch: 'master', + target_branch: 'feature' + } + end + + let!(:known_good_1) { merge_requests.create!(default_mr_values.merge(title: "Draft: Test Title")) } + let!(:known_good_2) { merge_requests.create!(default_mr_values.merge(title: "WIP: Test Title")) } + let!(:known_bad_1) { merge_requests.create!(default_mr_values.merge(title: "Known bad title drafts")) } + let!(:known_bad_2) { merge_requests.create!(default_mr_values.merge(title: "Known bad title wip")) } + + describe '#up' do + it 'schedules CleanupDraftDataFromFaultyRegex background jobs filtering for eligble MRs' do + stub_const("#{described_class}::BATCH_SIZE", 2) + allow(Gitlab).to receive(:com?).and_return(true) + + freeze_time do + migrate! + + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, known_bad_1.id, known_bad_2.id) + + expect(BackgroundMigrationWorker.jobs.size).to eq(1) + end + end + end +end diff --git a/spec/migrations/20220316202640_populate_container_repositories_migration_plan_spec.rb b/spec/migrations/20220316202640_populate_container_repositories_migration_plan_spec.rb new file mode 100644 index 00000000000..7b5c8254163 --- /dev/null +++ b/spec/migrations/20220316202640_populate_container_repositories_migration_plan_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe PopulateContainerRepositoriesMigrationPlan, :aggregate_failures do + let_it_be(:namespaces) { table(:namespaces) } + let_it_be(:projects) { table(:projects) } + let_it_be(:container_repositories) { table(:container_repositories) } + + let!(:namespace) { namespaces.create!(id: 1, name: 'namespace', path: 'namespace') } + let!(:project) { projects.create!(id: 1, name: 'project', path: 'project', namespace_id: 1) } + let!(:container_repository1) { container_repositories.create!(name: 'container_repository1', project_id: 1) } + let!(:container_repository2) { container_repositories.create!(name: 'container_repository2', project_id: 1) } + let!(:container_repository3) { container_repositories.create!(name: 'container_repository3', project_id: 1) } + + before do + stub_const("#{described_class.name}::BATCH_SIZE", 2) + end + + it 'schedules jobs for container_repositories to populate migration_state' do + Sidekiq::Testing.fake! do + freeze_time do + migrate! + + expect(described_class::MIGRATION).to be_scheduled_delayed_migration( + 2.minutes, container_repository1.id, container_repository2.id) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration( + 4.minutes, container_repository3.id, container_repository3.id) + expect(BackgroundMigrationWorker.jobs.size).to eq(2) + end + end + end +end diff --git a/spec/migrations/20220321234317_remove_all_issuable_escalation_statuses_spec.rb b/spec/migrations/20220321234317_remove_all_issuable_escalation_statuses_spec.rb new file mode 100644 index 00000000000..44e20df1130 --- /dev/null +++ b/spec/migrations/20220321234317_remove_all_issuable_escalation_statuses_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe RemoveAllIssuableEscalationStatuses do + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:issues) { table(:issues) } + let(:statuses) { table(:incident_management_issuable_escalation_statuses) } + let(:namespace) { namespaces.create!(name: 'foo', path: 'foo') } + let(:project) { projects.create!(namespace_id: namespace.id) } + + it 'removes all escalation status records' do + issue = issues.create!(project_id: project.id, issue_type: 1) + statuses.create!(issue_id: issue.id) + + expect { migrate! }.to change(statuses, :count).from(1).to(0) + end +end diff --git a/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb b/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb new file mode 100644 index 00000000000..fbd5fe546fa --- /dev/null +++ b/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true +require 'spec_helper' +require_migration! + +RSpec.describe UpdatePagesOnboardingState do + let(:migration) { described_class.new } + let!(:namespaces) { table(:namespaces) } + let!(:projects) { table(:projects) } + let!(:project_pages_metadata) { table(:project_pages_metadata) } + + let!(:namespace1) { namespaces.create!(name: 'foo', path: 'foo') } + let!(:namespace2) { namespaces.create!(name: 'bar', path: 'bar') } + let!(:project1) { projects.create!(namespace_id: namespace1.id) } + let!(:project2) { projects.create!(namespace_id: namespace2.id) } + let!(:pages_metadata1) do + project_pages_metadata.create!( + project_id: project1.id, + deployed: true, + onboarding_complete: false + ) + end + + let!(:pages_metadata2) do + project_pages_metadata.create!( + project_id: project2.id, + deployed: false, + onboarding_complete: false + ) + end + + describe '#up' do + before do + migration.up + end + + it 'sets the onboarding_complete attribute to the value of deployed' do + expect(pages_metadata1.reload.onboarding_complete).to eq(true) + expect(pages_metadata2.reload.onboarding_complete).to eq(false) + end + end + + describe '#down' do + before do + migration.up + migration.down + end + + it 'sets all onboarding_complete attributes to false' do + expect(pages_metadata1.reload.onboarding_complete).to eq(false) + expect(pages_metadata2.reload.onboarding_complete).to eq(false) + end + end +end diff --git a/spec/migrations/20220324032250_migrate_shimo_confluence_service_category_spec.rb b/spec/migrations/20220324032250_migrate_shimo_confluence_service_category_spec.rb new file mode 100644 index 00000000000..38db6d51e7e --- /dev/null +++ b/spec/migrations/20220324032250_migrate_shimo_confluence_service_category_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe MigrateShimoConfluenceServiceCategory, :migration do + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:integrations) { table(:integrations) } + + before do + namespace = namespaces.create!(name: 'test', path: 'test') + projects.create!(id: 1, namespace_id: namespace.id, name: 'gitlab', path: 'gitlab') + integrations.create!(id: 1, active: true, type_new: "Integrations::SlackSlashCommands", + category: 'chat', project_id: 1) + integrations.create!(id: 3, active: true, type_new: "Integrations::Confluence", category: 'common', project_id: 1) + integrations.create!(id: 5, active: true, type_new: "Integrations::Shimo", category: 'common', project_id: 1) + end + + describe '#up' do + it 'correctly schedules background migrations', :aggregate_failures do + stub_const("#{described_class.name}::BATCH_SIZE", 2) + + Sidekiq::Testing.fake! do + freeze_time do + migrate! + + expect(described_class::MIGRATION).to be_scheduled_migration(3, 5) + expect(BackgroundMigrationWorker.jobs.size).to eq(1) + end + end + end + end +end diff --git a/spec/migrations/20220329175119_remove_leftover_ci_job_artifact_deletions_spec.rb b/spec/migrations/20220329175119_remove_leftover_ci_job_artifact_deletions_spec.rb new file mode 100644 index 00000000000..13884007af2 --- /dev/null +++ b/spec/migrations/20220329175119_remove_leftover_ci_job_artifact_deletions_spec.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true +require 'spec_helper' + +require_migration! + +RSpec.describe RemoveLeftoverCiJobArtifactDeletions do + let(:deleted_records) { table(:loose_foreign_keys_deleted_records) } + + target_table_name = Ci::JobArtifact.table_name + + let(:pending_record1) do + deleted_records.create!( + id: 1, + fully_qualified_table_name: "public.#{target_table_name}", + primary_key_value: 1, + status: 1 + ) + end + + let(:pending_record2) do + deleted_records.create!( + id: 2, + fully_qualified_table_name: "public.#{target_table_name}", + primary_key_value: 2, + status: 1 + ) + end + + let(:other_pending_record1) do + deleted_records.create!( + id: 3, + fully_qualified_table_name: 'public.projects', + primary_key_value: 1, + status: 1 + ) + end + + let(:other_pending_record2) do + deleted_records.create!( + id: 4, + fully_qualified_table_name: 'public.ci_builds', + primary_key_value: 1, + status: 1 + ) + end + + let(:processed_record1) do + deleted_records.create!( + id: 5, + fully_qualified_table_name: 'public.external_pull_requests', + primary_key_value: 3, + status: 2 + ) + end + + let(:other_processed_record1) do + deleted_records.create!( + id: 6, + fully_qualified_table_name: 'public.ci_builds', + primary_key_value: 2, + status: 2 + ) + end + + let!(:persisted_ids_before) do + [ + pending_record1, + pending_record2, + other_pending_record1, + other_pending_record2, + processed_record1, + other_processed_record1 + ].map(&:id).sort + end + + let!(:persisted_ids_after) do + [ + other_pending_record1, + other_pending_record2, + processed_record1, + other_processed_record1 + ].map(&:id).sort + end + + def all_ids + deleted_records.all.map(&:id).sort + end + + it 'deletes pending external_pull_requests records' do + expect { migrate! }.to change { all_ids }.from(persisted_ids_before).to(persisted_ids_after) + end +end diff --git a/spec/migrations/20220412143552_consume_remaining_encrypt_integration_property_jobs_spec.rb b/spec/migrations/20220412143552_consume_remaining_encrypt_integration_property_jobs_spec.rb new file mode 100644 index 00000000000..4a1b68a5a85 --- /dev/null +++ b/spec/migrations/20220412143552_consume_remaining_encrypt_integration_property_jobs_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require_migration! + +RSpec.describe ConsumeRemainingEncryptIntegrationPropertyJobs, :migration do + subject(:migration) { described_class.new } + + let(:integrations) { table(:integrations) } + let(:bg_migration_class) { ::Gitlab::BackgroundMigration::EncryptIntegrationProperties } + let(:bg_migration) { instance_double(bg_migration_class) } + + before do + stub_const("#{described_class.name}::BATCH_SIZE", 2) + end + + it 'performs remaining background migrations', :aggregate_failures do + # Already migrated + integrations.create!(properties: some_props, encrypted_properties: 'abc') + integrations.create!(properties: some_props, encrypted_properties: 'def') + integrations.create!(properties: some_props, encrypted_properties: 'xyz') + # update required + record1 = integrations.create!(properties: some_props) + record2 = integrations.create!(properties: some_props) + record3 = integrations.create!(properties: some_props) + # No update required + integrations.create!(properties: nil) + integrations.create!(properties: nil) + + expect(Gitlab::BackgroundMigration).to receive(:steal).with(bg_migration_class.name.demodulize) + expect(bg_migration_class).to receive(:new).twice.and_return(bg_migration) + expect(bg_migration).to receive(:perform).with(record1.id, record2.id) + expect(bg_migration).to receive(:perform).with(record3.id, record3.id) + + migrate! + end + + def some_props + { iid: generate(:iid), url: generate(:url), username: generate(:username) }.to_json + end +end diff --git a/spec/migrations/add_epics_relative_position_spec.rb b/spec/migrations/add_epics_relative_position_spec.rb new file mode 100644 index 00000000000..f3b7dd1727b --- /dev/null +++ b/spec/migrations/add_epics_relative_position_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require_migration! + +RSpec.describe AddEpicsRelativePosition, :migration do + let(:groups) { table(:namespaces) } + let(:epics) { table(:epics) } + let(:users) { table(:users) } + let(:user) { users.create!(name: 'user', email: 'email@example.org', projects_limit: 100) } + let(:group) { groups.create!(name: 'gitlab', path: 'gitlab-org', type: 'Group') } + + let!(:epic1) { epics.create!(title: 'epic 1', title_html: 'epic 1', author_id: user.id, group_id: group.id, iid: 1) } + let!(:epic2) { epics.create!(title: 'epic 2', title_html: 'epic 2', author_id: user.id, group_id: group.id, iid: 2) } + let!(:epic3) { epics.create!(title: 'epic 3', title_html: 'epic 3', author_id: user.id, group_id: group.id, iid: 3) } + + it 'does nothing if epics table contains relative_position' do + expect { migrate! }.not_to change { epics.pluck(:relative_position) } + end + + it 'adds relative_position if missing and backfills it with ID value', :aggregate_failures do + ActiveRecord::Base.connection.execute('ALTER TABLE epics DROP relative_position') + + migrate! + + expect(epics.pluck(:relative_position)).to match_array([epic1.id * 500, epic2.id * 500, epic3.id * 500]) + end +end diff --git a/spec/migrations/backfill_group_features_spec.rb b/spec/migrations/backfill_group_features_spec.rb new file mode 100644 index 00000000000..922d54f43be --- /dev/null +++ b/spec/migrations/backfill_group_features_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe BackfillGroupFeatures, :migration do + let(:migration) { described_class::MIGRATION } + + describe '#up' do + it 'schedules background jobs for each batch of namespaces' do + migrate! + + expect(migration).to have_scheduled_batched_migration( + table_name: :namespaces, + column_name: :id, + job_arguments: [described_class::BATCH_SIZE], + interval: described_class::INTERVAL, + batch_size: described_class::BATCH_SIZE + ) + end + end + + describe '#down' do + it 'deletes all batched migration records' do + migrate! + schema_migrate_down! + + expect(migration).not_to have_scheduled_batched_migration + end + end +end diff --git a/spec/migrations/backfill_namespace_id_for_project_routes_spec.rb b/spec/migrations/backfill_namespace_id_for_project_routes_spec.rb new file mode 100644 index 00000000000..28edd17731f --- /dev/null +++ b/spec/migrations/backfill_namespace_id_for_project_routes_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe BackfillNamespaceIdForProjectRoutes, :migration do + let(:migration) { described_class::MIGRATION } + + describe '#up' do + it 'schedules background jobs for each batch of group members' do + migrate! + + expect(migration).to have_scheduled_batched_migration( + table_name: :routes, + column_name: :id, + interval: described_class::INTERVAL + ) + end + end + + describe '#down' do + it 'deletes all batched migration records' do + migrate! + schema_migrate_down! + + expect(migration).not_to have_scheduled_batched_migration + end + end +end diff --git a/spec/migrations/backfill_work_item_type_id_on_issues_spec.rb b/spec/migrations/backfill_work_item_type_id_on_issues_spec.rb new file mode 100644 index 00000000000..6798b0cc7e8 --- /dev/null +++ b/spec/migrations/backfill_work_item_type_id_on_issues_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe BackfillWorkItemTypeIdOnIssues, :migration do + let_it_be(:migration) { described_class::MIGRATION } + let_it_be(:interval) { 2.minutes } + let_it_be(:issue_type_enum) { { issue: 0, incident: 1, test_case: 2, requirement: 3, task: 4 } } + let_it_be(:base_work_item_type_ids) do + table(:work_item_types).where(namespace_id: nil).order(:base_type).each_with_object({}) do |type, hash| + hash[type.base_type] = type.id + end + end + + describe '#up' do + it 'correctly schedules background migrations' do + Sidekiq::Testing.fake! do + freeze_time do + migrate! + + scheduled_migrations = Gitlab::Database::BackgroundMigration::BatchedMigration.where(job_class_name: migration) + work_item_types = table(:work_item_types).where(namespace_id: nil) + + expect(scheduled_migrations.count).to eq(work_item_types.count) + + [:issue, :incident, :test_case, :requirement, :task].each do |issue_type| + expect(migration).to have_scheduled_batched_migration( + table_name: :issues, + column_name: :id, + job_arguments: [issue_type_enum[issue_type], base_work_item_type_ids[issue_type_enum[issue_type]]], + interval: interval, + batch_size: described_class::BATCH_SIZE, + max_batch_size: described_class::MAX_BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE, + batch_class_name: described_class::BATCH_CLASS_NAME + ) + end + end + end + end + end + + describe '#down' do + it 'deletes all batched migration records' do + migrate! + schema_migrate_down! + + expect(migration).not_to have_scheduled_batched_migration + end + end +end diff --git a/spec/migrations/cleanup_after_fixing_issue_when_admin_changed_primary_email_spec.rb b/spec/migrations/cleanup_after_fixing_issue_when_admin_changed_primary_email_spec.rb new file mode 100644 index 00000000000..eda57545c7a --- /dev/null +++ b/spec/migrations/cleanup_after_fixing_issue_when_admin_changed_primary_email_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe CleanupAfterFixingIssueWhenAdminChangedPrimaryEmail, :sidekiq do + let(:migration) { described_class.new } + let(:users) { table(:users) } + let(:emails) { table(:emails) } + + let!(:user_1) { users.create!(name: 'confirmed-user-1', email: 'confirmed-1@example.com', confirmed_at: 3.days.ago, projects_limit: 100) } + let!(:user_2) { users.create!(name: 'confirmed-user-2', email: 'confirmed-2@example.com', confirmed_at: 1.day.ago, projects_limit: 100) } + let!(:user_3) { users.create!(name: 'confirmed-user-3', email: 'confirmed-3@example.com', confirmed_at: 1.day.ago, projects_limit: 100) } + let!(:user_4) { users.create!(name: 'unconfirmed-user', email: 'unconfirmed@example.com', confirmed_at: nil, projects_limit: 100) } + + let!(:email_1) { emails.create!(email: 'confirmed-1@example.com', user_id: user_1.id, confirmed_at: 1.day.ago) } + let!(:email_2) { emails.create!(email: 'other_2@example.com', user_id: user_2.id, confirmed_at: 1.day.ago) } + + before do + stub_const("#{described_class.name}::BATCH_SIZE", 2) + end + + it 'adds the primary email to emails for leftover confirmed users that do not have their primary email in the emails table', :aggregate_failures do + original_email_1_confirmed_at = email_1.reload.confirmed_at + + expect { migration.up }.to change { emails.count }.by(2) + + expect(emails.find_by(user_id: user_2.id, email: 'confirmed-2@example.com').confirmed_at).to eq(user_2.reload.confirmed_at) + expect(emails.find_by(user_id: user_3.id, email: 'confirmed-3@example.com').confirmed_at).to eq(user_3.reload.confirmed_at) + expect(email_1.reload.confirmed_at).to eq(original_email_1_confirmed_at) + + expect(emails.exists?(user_id: user_4.id)).to be(false) + end + + it 'continues in case of errors with one email' do + allow(Email).to receive(:create) { raise 'boom!' } + + expect { migration.up }.not_to raise_error + end +end diff --git a/spec/migrations/finalize_project_namespaces_backfill_spec.rb b/spec/migrations/finalize_project_namespaces_backfill_spec.rb new file mode 100644 index 00000000000..3d0b0ec13fe --- /dev/null +++ b/spec/migrations/finalize_project_namespaces_backfill_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe FinalizeProjectNamespacesBackfill, :migration do + let(:batched_migrations) { table(:batched_background_migrations) } + + let_it_be(:migration) { described_class::MIGRATION } + + describe '#up' do + shared_examples 'raises migration not finished exception' do + it 'raises exception' do + expect { migrate! }.to raise_error(/Expected batched background migration for the given configuration to be marked as 'finished'/) + end + end + + context 'when project namespace backfilling migration is missing' do + it 'warns migration not found' do + expect(Gitlab::AppLogger) + .to receive(:warn).with(/Could not find batched background migration for the given configuration:/) + + migrate! + end + end + + context 'with backfilling migration present' do + let!(:project_namespace_backfill) do + batched_migrations.create!( + job_class_name: 'ProjectNamespaces::BackfillProjectNamespaces', + table_name: :projects, + column_name: :id, + job_arguments: [nil, 'up'], + interval: 2.minutes, + min_value: 1, + max_value: 2, + batch_size: 1000, + sub_batch_size: 200, + status: 3 # finished + ) + end + + context 'when project namespace backfilling migration finished successfully' do + it 'does not raise exception' do + expect { migrate! }.not_to raise_error(/Expected batched background migration for the given configuration to be marked as 'finished'/) + end + end + + context 'when project namespace backfilling migration is paused' do + using RSpec::Parameterized::TableSyntax + + where(:status, :description) do + 0 | 'paused' + 1 | 'active' + 4 | 'failed' + 5 | 'finalizing' + end + + with_them do + before do + project_namespace_backfill.update!(status: status) + end + + it_behaves_like 'raises migration not finished exception' + end + end + end + end +end diff --git a/spec/migrations/finalize_traversal_ids_background_migrations_spec.rb b/spec/migrations/finalize_traversal_ids_background_migrations_spec.rb new file mode 100644 index 00000000000..74d6447e6a7 --- /dev/null +++ b/spec/migrations/finalize_traversal_ids_background_migrations_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration!('finalize_traversal_ids_background_migrations') + +RSpec.describe FinalizeTraversalIdsBackgroundMigrations, :migration do + shared_context 'incomplete background migration' do + before do + # Jobs enqueued in Sidekiq. + Sidekiq::Testing.disable! do + BackgroundMigrationWorker.perform_in(10, job_class_name, [1, 2, 100]) + BackgroundMigrationWorker.perform_in(20, job_class_name, [3, 4, 100]) + end + + # Jobs tracked in the database. + # table(:background_migration_jobs).create!( + Gitlab::Database::BackgroundMigrationJob.create!( + class_name: job_class_name, + arguments: [5, 6, 100], + status: Gitlab::Database::BackgroundMigrationJob.statuses['pending'] + ) + # table(:background_migration_jobs).create!( + Gitlab::Database::BackgroundMigrationJob.create!( + class_name: job_class_name, + arguments: [7, 8, 100], + status: Gitlab::Database::BackgroundMigrationJob.statuses['succeeded'] + ) + end + end + + context 'BackfillNamespaceTraversalIdsRoots background migration' do + let(:job_class_name) { 'BackfillNamespaceTraversalIdsRoots' } + + include_context 'incomplete background migration' + + before do + migrate! + end + + it_behaves_like( + 'finalized tracked background migration', + Gitlab::BackgroundMigration::BackfillNamespaceTraversalIdsRoots + ) + end + + context 'BackfillNamespaceTraversalIdsChildren background migration' do + let(:job_class_name) { 'BackfillNamespaceTraversalIdsChildren' } + + include_context 'incomplete background migration' + + before do + migrate! + end + + it_behaves_like( + 'finalized tracked background migration', + Gitlab::BackgroundMigration::BackfillNamespaceTraversalIdsChildren + ) + end +end diff --git a/spec/migrations/fix_and_backfill_project_namespaces_for_projects_with_duplicate_name_spec.rb b/spec/migrations/fix_and_backfill_project_namespaces_for_projects_with_duplicate_name_spec.rb new file mode 100644 index 00000000000..44a2220b2ad --- /dev/null +++ b/spec/migrations/fix_and_backfill_project_namespaces_for_projects_with_duplicate_name_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe FixAndBackfillProjectNamespacesForProjectsWithDuplicateName, :migration do + let(:projects) { table(:projects) } + let(:namespaces) { table(:namespaces) } + + let!(:group) { namespaces.create!(name: 'group1', path: 'group1', type: 'Group') } + let!(:project_namespace) { namespaces.create!(name: 'project2', path: 'project2', type: 'Project') } + let!(:project1) { projects.create!(name: 'project1', path: 'project1', project_namespace_id: nil, namespace_id: group.id, visibility_level: 20) } + let!(:project2) { projects.create!(name: 'project2', path: 'project2', project_namespace_id: project_namespace.id, namespace_id: group.id, visibility_level: 20) } + let!(:project3) { projects.create!(name: 'project3', path: 'project3', project_namespace_id: nil, namespace_id: group.id, visibility_level: 20) } + let!(:project4) { projects.create!(name: 'project4', path: 'project4', project_namespace_id: nil, namespace_id: group.id, visibility_level: 20) } + + describe '#up' do + it 'schedules background migrations' do + Sidekiq::Testing.fake! do + freeze_time do + described_class.new.up + + migration = described_class::MIGRATION + + expect(migration).to be_scheduled_delayed_migration(2.minutes, project1.id, project4.id) + expect(BackgroundMigrationWorker.jobs.size).to eq 1 + end + end + end + + context 'in batches' do + before do + stub_const('FixAndBackfillProjectNamespacesForProjectsWithDuplicateName::BATCH_SIZE', 2) + end + + it 'schedules background migrations' do + Sidekiq::Testing.fake! do + freeze_time do + described_class.new.up + + migration = described_class::MIGRATION + + expect(migration).to be_scheduled_delayed_migration(2.minutes, project1.id, project3.id) + expect(migration).to be_scheduled_delayed_migration(4.minutes, project4.id, project4.id) + expect(BackgroundMigrationWorker.jobs.size).to eq 2 + end + end + end + end + end +end diff --git a/spec/migrations/remove_wiki_notes_spec.rb b/spec/migrations/remove_wiki_notes_spec.rb new file mode 100644 index 00000000000..2ffebdee106 --- /dev/null +++ b/spec/migrations/remove_wiki_notes_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe RemoveWikiNotes, :migration do + let(:notes) { table(:notes) } + + it 'removes all wiki notes' do + notes.create!(id: 97, note: 'Wiki note', noteable_type: 'Wiki') + notes.create!(id: 98, note: 'Commit note', noteable_type: 'Commit') + notes.create!(id: 110, note: 'Issue note', noteable_type: 'Issue') + notes.create!(id: 242, note: 'MergeRequest note', noteable_type: 'MergeRequest') + + expect(notes.where(noteable_type: 'Wiki').size).to eq(1) + + expect { migrate! }.to change { notes.count }.by(-1) + + expect(notes.where(noteable_type: 'Wiki').size).to eq(0) + end + + context 'when not staging nor com' do + it 'does not remove notes' do + allow(::Gitlab).to receive(:com?).and_return(false) + allow(::Gitlab).to receive(:dev_or_test_env?).and_return(false) + allow(::Gitlab).to receive(:staging?).and_return(false) + + notes.create!(id: 97, note: 'Wiki note', noteable_type: 'Wiki') + + expect { migrate! }.not_to change { notes.count } + end + end +end diff --git a/spec/migrations/replace_work_item_type_backfill_next_batch_strategy_spec.rb b/spec/migrations/replace_work_item_type_backfill_next_batch_strategy_spec.rb new file mode 100644 index 00000000000..5e22fc06973 --- /dev/null +++ b/spec/migrations/replace_work_item_type_backfill_next_batch_strategy_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe ReplaceWorkItemTypeBackfillNextBatchStrategy, :migration do + describe '#up' do + it 'sets the new strategy for existing migrations' do + migrations = create_migrations(described_class::OLD_STRATEGY_CLASS, 2) + + expect do + migrate! + + migrations.each(&:reload) + end.to change { migrations.pluck(:batch_class_name).uniq }.from([described_class::OLD_STRATEGY_CLASS]) + .to([described_class::NEW_STRATEGY_CLASS]) + end + end + + describe '#down' do + it 'sets the old strategy for existing migrations' do + migrations = create_migrations(described_class::NEW_STRATEGY_CLASS, 2) + + expect do + migrate! + schema_migrate_down! + + migrations.each(&:reload) + end.to change { migrations.pluck(:batch_class_name).uniq }.from([described_class::NEW_STRATEGY_CLASS]) + .to([described_class::OLD_STRATEGY_CLASS]) + end + end + + def create_migrations(batch_class_name, count) + Array.new(2) { |index| create_background_migration(batch_class_name, [index]) } + end + + def create_background_migration(batch_class_name, job_arguments) + migrations_table = table(:batched_background_migrations) + + migrations_table.create!( + batch_class_name: batch_class_name, + job_class_name: described_class::JOB_CLASS_NAME, + max_value: 10, + batch_size: 5, + sub_batch_size: 1, + interval: 2.minutes, + table_name: :issues, + column_name: :id, + total_tuple_count: 10_000, + pause_ms: 100, + job_arguments: job_arguments + ) + end +end |