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:
Diffstat (limited to 'spec/lib/gitlab/database/migration_helpers/v2_spec.rb')
-rw-r--r--spec/lib/gitlab/database/migration_helpers/v2_spec.rb221
1 files changed, 221 insertions, 0 deletions
diff --git a/spec/lib/gitlab/database/migration_helpers/v2_spec.rb b/spec/lib/gitlab/database/migration_helpers/v2_spec.rb
new file mode 100644
index 00000000000..f132ecbf13b
--- /dev/null
+++ b/spec/lib/gitlab/database/migration_helpers/v2_spec.rb
@@ -0,0 +1,221 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::MigrationHelpers::V2 do
+ include Database::TriggerHelpers
+
+ let(:migration) do
+ ActiveRecord::Migration.new.extend(described_class)
+ end
+
+ before do
+ allow(migration).to receive(:puts)
+ end
+
+ shared_examples_for 'Setting up to rename a column' do
+ let(:model) { Class.new(ActiveRecord::Base) }
+
+ before do
+ model.table_name = :test_table
+ end
+
+ context 'when called inside a transaction block' do
+ before do
+ allow(migration).to receive(:transaction_open?).and_return(true)
+ end
+
+ it 'raises an error' do
+ expect do
+ migration.public_send(operation, :test_table, :original, :renamed)
+ end.to raise_error("#{operation} can not be run inside a transaction")
+ end
+ end
+
+ context 'when the existing column has a default value' do
+ before do
+ migration.change_column_default :test_table, existing_column, 'default value'
+ end
+
+ it 'raises an error' do
+ expect do
+ migration.public_send(operation, :test_table, :original, :renamed)
+ end.to raise_error("#{operation} does not currently support columns with default values")
+ end
+ end
+
+ context 'when passing a batch column' do
+ context 'when the batch column does not exist' do
+ it 'raises an error' do
+ expect do
+ migration.public_send(operation, :test_table, :original, :renamed, batch_column_name: :missing)
+ end.to raise_error('Column missing does not exist on test_table')
+ end
+ end
+
+ context 'when the batch column does exist' do
+ it 'passes it when creating the column' do
+ expect(migration).to receive(:create_column_from)
+ .with(:test_table, existing_column, added_column, type: nil, batch_column_name: :status)
+ .and_call_original
+
+ migration.public_send(operation, :test_table, :original, :renamed, batch_column_name: :status)
+ end
+ end
+ end
+
+ it 'creates the renamed column, syncing existing data' do
+ existing_record_1 = model.create!(status: 0, existing_column => 'existing')
+ existing_record_2 = model.create!(status: 0, existing_column => nil)
+
+ migration.send(operation, :test_table, :original, :renamed)
+ model.reset_column_information
+
+ expect(migration.column_exists?(:test_table, added_column)).to eq(true)
+
+ expect(existing_record_1.reload).to have_attributes(status: 0, original: 'existing', renamed: 'existing')
+ expect(existing_record_2.reload).to have_attributes(status: 0, original: nil, renamed: nil)
+ end
+
+ it 'installs triggers to sync new data' do
+ migration.public_send(operation, :test_table, :original, :renamed)
+ model.reset_column_information
+
+ new_record_1 = model.create!(status: 1, original: 'first')
+ new_record_2 = model.create!(status: 1, renamed: 'second')
+
+ expect(new_record_1.reload).to have_attributes(status: 1, original: 'first', renamed: 'first')
+ expect(new_record_2.reload).to have_attributes(status: 1, original: 'second', renamed: 'second')
+
+ new_record_1.update!(original: 'updated')
+ new_record_2.update!(renamed: nil)
+
+ expect(new_record_1.reload).to have_attributes(status: 1, original: 'updated', renamed: 'updated')
+ expect(new_record_2.reload).to have_attributes(status: 1, original: nil, renamed: nil)
+ end
+ end
+
+ describe '#rename_column_concurrently' do
+ before do
+ allow(migration).to receive(:transaction_open?).and_return(false)
+
+ migration.create_table :test_table do |t|
+ t.integer :status, null: false
+ t.text :original
+ t.text :other_column
+ end
+ end
+
+ it_behaves_like 'Setting up to rename a column' do
+ let(:operation) { :rename_column_concurrently }
+ let(:existing_column) { :original }
+ let(:added_column) { :renamed }
+ end
+
+ context 'when the column to rename does not exist' do
+ it 'raises an error' do
+ expect do
+ migration.rename_column_concurrently :test_table, :missing_column, :renamed
+ end.to raise_error('Column missing_column does not exist on test_table')
+ end
+ end
+ end
+
+ describe '#undo_cleanup_concurrent_column_rename' do
+ before do
+ allow(migration).to receive(:transaction_open?).and_return(false)
+
+ migration.create_table :test_table do |t|
+ t.integer :status, null: false
+ t.text :other_column
+ t.text :renamed
+ end
+ end
+
+ it_behaves_like 'Setting up to rename a column' do
+ let(:operation) { :undo_cleanup_concurrent_column_rename }
+ let(:existing_column) { :renamed }
+ let(:added_column) { :original }
+ end
+
+ context 'when the renamed column does not exist' do
+ it 'raises an error' do
+ expect do
+ migration.undo_cleanup_concurrent_column_rename :test_table, :original, :missing_column
+ end.to raise_error('Column missing_column does not exist on test_table')
+ end
+ end
+ end
+
+ shared_examples_for 'Cleaning up from renaming a column' do
+ let(:connection) { migration.connection }
+
+ before do
+ allow(migration).to receive(:transaction_open?).and_return(false)
+
+ migration.create_table :test_table do |t|
+ t.integer :status, null: false
+ t.text :original
+ t.text :other_column
+ end
+
+ migration.rename_column_concurrently :test_table, :original, :renamed
+ end
+
+ context 'when the helper is called repeatedly' do
+ before do
+ migration.public_send(operation, :test_table, :original, :renamed)
+ end
+
+ it 'does not make repeated attempts to cleanup' do
+ expect(migration).not_to receive(:remove_column)
+
+ expect do
+ migration.public_send(operation, :test_table, :original, :renamed)
+ end.not_to raise_error
+ end
+ end
+
+ context 'when the renamed column exists' do
+ let(:triggers) do
+ [
+ ['trigger_7cc71f92fd63', 'function_for_trigger_7cc71f92fd63', before: 'insert'],
+ ['trigger_f1a1f619636a', 'function_for_trigger_f1a1f619636a', before: 'update'],
+ ['trigger_769a49938884', 'function_for_trigger_769a49938884', before: 'update']
+ ]
+ end
+
+ it 'removes the sync triggers and renamed columns' do
+ triggers.each do |(trigger_name, function_name, event)|
+ expect_function_to_exist(function_name)
+ expect_valid_function_trigger(:test_table, trigger_name, function_name, event)
+ end
+
+ expect(migration.column_exists?(:test_table, added_column)).to eq(true)
+
+ migration.public_send(operation, :test_table, :original, :renamed)
+
+ expect(migration.column_exists?(:test_table, added_column)).to eq(false)
+
+ triggers.each do |(trigger_name, function_name, _)|
+ expect_trigger_not_to_exist(:test_table, trigger_name)
+ expect_function_not_to_exist(function_name)
+ end
+ end
+ end
+ end
+
+ describe '#undo_rename_column_concurrently' do
+ it_behaves_like 'Cleaning up from renaming a column' do
+ let(:operation) { :undo_rename_column_concurrently }
+ let(:added_column) { :renamed }
+ end
+ end
+
+ describe '#cleanup_concurrent_column_rename' do
+ it_behaves_like 'Cleaning up from renaming a column' do
+ let(:operation) { :cleanup_concurrent_column_rename }
+ let(:added_column) { :original }
+ end
+ end
+end