136 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| require 'spec_helper'
 | |
| 
 | |
| RSpec.describe Gitlab::Database::SharedModel, feature_category: :database do
 | |
|   describe 'using an external connection' do
 | |
|     let!(:original_connection) { described_class.connection }
 | |
|     let(:new_connection) { double('connection') }
 | |
| 
 | |
|     it 'overrides the connection for the duration of the block', :aggregate_failures do
 | |
|       expect_original_connection_around do
 | |
|         described_class.using_connection(new_connection) do
 | |
|           expect(described_class.connection).to be(new_connection)
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     it 'does not affect connections in other threads', :aggregate_failures do
 | |
|       expect_original_connection_around do
 | |
|         described_class.using_connection(new_connection) do
 | |
|           expect(described_class.connection).to be(new_connection)
 | |
| 
 | |
|           Thread.new do
 | |
|             expect(described_class.connection).not_to be(new_connection)
 | |
|           end.join
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     it 'raises an error if the connection does not include `:gitlab_shared` schema' do
 | |
|       allow(Gitlab::Database)
 | |
|         .to receive(:gitlab_schemas_for_connection)
 | |
|         .with(new_connection)
 | |
|         .and_return([:gitlab_main])
 | |
| 
 | |
|       expect_original_connection_around do
 | |
|         expect do
 | |
|           described_class.using_connection(new_connection) {}
 | |
|         end.to raise_error(/Cannot set `SharedModel` to connection/)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context 'when multiple connection overrides are nested', :aggregate_failures do
 | |
|       let(:second_connection) { double('connection') }
 | |
| 
 | |
|       it 'allows the nesting with the same connection object' do
 | |
|         expect_original_connection_around do
 | |
|           described_class.using_connection(new_connection) do
 | |
|             expect(described_class.connection).to be(new_connection)
 | |
| 
 | |
|             described_class.using_connection(new_connection) do
 | |
|               expect(described_class.connection).to be(new_connection)
 | |
|             end
 | |
| 
 | |
|             expect(described_class.connection).to be(new_connection)
 | |
|           end
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       it 'raises an error if the connection is changed' do
 | |
|         expect_original_connection_around do
 | |
|           described_class.using_connection(new_connection) do
 | |
|             expect(described_class.connection).to be(new_connection)
 | |
| 
 | |
|             expect do
 | |
|               described_class.using_connection(second_connection) {}
 | |
|             end.to raise_error(/Cannot change connection for Gitlab::Database::SharedModel/)
 | |
| 
 | |
|             expect(described_class.connection).to be(new_connection)
 | |
|           end
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context 'when the block raises an error', :aggregate_failures do
 | |
|       it 're-raises the error, removing the overridden connection' do
 | |
|         expect_original_connection_around do
 | |
|           expect do
 | |
|             described_class.using_connection(new_connection) do
 | |
|               expect(described_class.connection).to be(new_connection)
 | |
| 
 | |
|               raise 'here comes an error!'
 | |
|             end
 | |
|           end.to raise_error(RuntimeError, 'here comes an error!')
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe '#connection_db_config' do
 | |
|     let!(:original_connection) { shared_model_class.connection }
 | |
|     let!(:original_connection_db_config) { shared_model_class.connection_db_config }
 | |
|     let(:shared_model) { shared_model_class.new }
 | |
|     let(:shared_model_class) do
 | |
|       Class.new(described_class) do
 | |
|         self.table_name = 'postgres_async_indexes'
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     it 'returns the class connection_db_config' do
 | |
|       expect(shared_model.connection_db_config).to eq(described_class.connection_db_config)
 | |
|     end
 | |
| 
 | |
|     context 'when switching the class connection' do
 | |
|       before do
 | |
|         skip_if_multiple_databases_not_setup
 | |
|       end
 | |
| 
 | |
|       let(:new_base_model) { Ci::ApplicationRecord }
 | |
|       let(:new_connection) { new_base_model.connection }
 | |
| 
 | |
|       it 'returns the db_config of the used connection when using load balancing' do
 | |
|         expect_original_connection_around do
 | |
|           described_class.using_connection(new_connection) do
 | |
|             expect(shared_model.connection_db_config).to eq(new_base_model.connection_db_config)
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         # it restores the connection_db_config afterwards
 | |
|         expect(shared_model.connection_db_config).to eq(original_connection_db_config)
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def expect_original_connection_around
 | |
|     # For safety, ensure our original connection is distinct from our double
 | |
|     # This should be the case, but in case of something leaking we should verify
 | |
|     expect(original_connection).not_to be(new_connection)
 | |
|     expect(described_class.connection).to be(original_connection)
 | |
| 
 | |
|     yield
 | |
| 
 | |
|     expect(described_class.connection).to be(original_connection)
 | |
|   end
 | |
| end
 |