196 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			196 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # 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, connection: connection) }
 | |
| 
 | |
|   before do
 | |
|     connection.schema_cache.clear!
 | |
|   end
 | |
| 
 | |
|   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
 |