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/unidirectional_copy_trigger_spec.rb')
-rw-r--r--spec/lib/gitlab/database/unidirectional_copy_trigger_spec.rb191
1 files changed, 191 insertions, 0 deletions
diff --git a/spec/lib/gitlab/database/unidirectional_copy_trigger_spec.rb b/spec/lib/gitlab/database/unidirectional_copy_trigger_spec.rb
new file mode 100644
index 00000000000..2955c208f16
--- /dev/null
+++ b/spec/lib/gitlab/database/unidirectional_copy_trigger_spec.rb
@@ -0,0 +1,191 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::UnidirectionalCopyTrigger do
+ include Database::TriggerHelpers
+
+ let(:table_name) { '_test_table' }
+ let(:connection) { ActiveRecord::Base.connection }
+ let(:copy_trigger) { described_class.on_table(table_name) }
+
+ describe '#name' do
+ context 'when a single column name is given' do
+ subject(:trigger_name) { copy_trigger.name('id', 'other_id') }
+
+ it 'returns the trigger name' do
+ expect(trigger_name).to eq('trigger_cfce7a56a9d6')
+ end
+ end
+
+ context 'when multiple column names are given' do
+ subject(:trigger_name) { copy_trigger.name(%w[id fk_id], %w[other_id other_fk_id]) }
+
+ it 'returns the trigger name' do
+ expect(trigger_name).to eq('trigger_166626e51481')
+ end
+ end
+
+ context 'when a different number of new and old column names are given' do
+ it 'raises an error' do
+ expect do
+ copy_trigger.name(%w[id fk_id], %w[other_id])
+ end.to raise_error(ArgumentError, 'number of source and destination columns must match')
+ end
+ end
+ end
+
+ describe '#create' do
+ let(:model) { Class.new(ActiveRecord::Base) }
+
+ before do
+ connection.execute(<<~SQL)
+ CREATE TABLE #{table_name} (
+ id serial NOT NULL PRIMARY KEY,
+ other_id integer,
+ fk_id bigint,
+ other_fk_id bigint);
+ SQL
+
+ model.table_name = table_name
+ end
+
+ context 'when a single column name is given' do
+ let(:trigger_name) { 'trigger_cfce7a56a9d6' }
+
+ it 'creates the trigger and function' do
+ expect_function_not_to_exist(trigger_name)
+ expect_trigger_not_to_exist(table_name, trigger_name)
+
+ copy_trigger.create('id', 'other_id')
+
+ expect_function_to_exist(trigger_name)
+ expect_valid_function_trigger(table_name, trigger_name, trigger_name, before: %w[insert update])
+ end
+
+ it 'properly copies the column data using the trigger function' do
+ copy_trigger.create('id', 'other_id')
+
+ record = model.create!(id: 10)
+ expect(record.reload).to have_attributes(other_id: 10)
+
+ record.update!({ id: 20 })
+ expect(record.reload).to have_attributes(other_id: 20)
+ end
+ end
+
+ context 'when multiple column names are given' do
+ let(:trigger_name) { 'trigger_166626e51481' }
+
+ it 'creates the trigger and function to set all the columns' do
+ expect_function_not_to_exist(trigger_name)
+ expect_trigger_not_to_exist(table_name, trigger_name)
+
+ copy_trigger.create(%w[id fk_id], %w[other_id other_fk_id])
+
+ expect_function_to_exist(trigger_name)
+ expect_valid_function_trigger(table_name, trigger_name, trigger_name, before: %w[insert update])
+ end
+
+ it 'properly copies the columns using the trigger function' do
+ copy_trigger.create(%w[id fk_id], %w[other_id other_fk_id])
+
+ record = model.create!(id: 10, fk_id: 20)
+ expect(record.reload).to have_attributes(other_id: 10, other_fk_id: 20)
+
+ record.update!(id: 30, fk_id: 50)
+ expect(record.reload).to have_attributes(other_id: 30, other_fk_id: 50)
+ end
+ end
+
+ context 'when a custom trigger name is given' do
+ let(:trigger_name) { '_test_trigger' }
+
+ it 'creates the trigger and function with the custom name' do
+ expect_function_not_to_exist(trigger_name)
+ expect_trigger_not_to_exist(table_name, trigger_name)
+
+ copy_trigger.create('id', 'other_id', trigger_name: trigger_name)
+
+ expect_function_to_exist(trigger_name)
+ expect_valid_function_trigger(table_name, trigger_name, trigger_name, before: %w[insert update])
+ end
+ end
+
+ context 'when the trigger function already exists' do
+ let(:trigger_name) { 'trigger_cfce7a56a9d6' }
+
+ it 'does not raise an error' do
+ expect_function_not_to_exist(trigger_name)
+ expect_trigger_not_to_exist(table_name, trigger_name)
+
+ copy_trigger.create('id', 'other_id')
+
+ expect_function_to_exist(trigger_name)
+ expect_valid_function_trigger(table_name, trigger_name, trigger_name, before: %w[insert update])
+
+ copy_trigger.create('id', 'other_id')
+
+ expect_function_to_exist(trigger_name)
+ expect_valid_function_trigger(table_name, trigger_name, trigger_name, before: %w[insert update])
+ end
+ end
+
+ context 'when a different number of new and old column names are given' do
+ it 'raises an error' do
+ expect do
+ copy_trigger.create(%w[id fk_id], %w[other_id])
+ end.to raise_error(ArgumentError, 'number of source and destination columns must match')
+ end
+ end
+ end
+
+ describe '#drop' do
+ let(:trigger_name) { '_test_trigger' }
+
+ before do
+ connection.execute(<<~SQL)
+ CREATE TABLE #{table_name} (
+ id serial NOT NULL PRIMARY KEY,
+ other_id integer NOT NULL);
+
+ CREATE FUNCTION #{trigger_name}()
+ RETURNS trigger
+ LANGUAGE plpgsql AS
+ $$
+ BEGIN
+ RAISE NOTICE 'hello';
+ RETURN NEW;
+ END
+ $$;
+
+ CREATE TRIGGER #{trigger_name}
+ BEFORE INSERT OR UPDATE
+ ON #{table_name}
+ FOR EACH ROW
+ EXECUTE FUNCTION #{trigger_name}();
+ SQL
+ end
+
+ it 'drops the trigger and function for the given arguments' do
+ expect_function_to_exist(trigger_name)
+ expect_valid_function_trigger(table_name, trigger_name, trigger_name, before: %w[insert update])
+
+ copy_trigger.drop(trigger_name)
+
+ expect_trigger_not_to_exist(table_name, trigger_name)
+ expect_function_not_to_exist(trigger_name)
+ end
+
+ context 'when the trigger does not exist' do
+ it 'does not raise an error' do
+ copy_trigger.drop(trigger_name)
+
+ expect_trigger_not_to_exist(table_name, trigger_name)
+ expect_function_not_to_exist(trigger_name)
+
+ copy_trigger.drop(trigger_name)
+ end
+ end
+ end
+end