debian-mirror-gitlab/spec/tasks/gitlab/db/validate_config_rake_spec.rb
2022-10-11 01:57:18 +05:30

246 lines
8.4 KiB
Ruby

# frozen_string_literal: true
require 'rake_helper'
RSpec.describe 'gitlab:db:validate_config', :silence_stdout, :suppress_gitlab_schemas_validate_connection do
# We don't need to delete this data since it only modifies `ar_internal_metadata`
# which would not be cleaned either by `DbCleaner`
self.use_transactional_tests = false
before :all do
Rake.application.rake_require 'active_record/railties/databases'
Rake.application.rake_require 'tasks/seed_fu'
Rake.application.rake_require 'tasks/gitlab/db/validate_config'
# empty task as env is already loaded
Rake::Task.define_task :environment
end
context "when validating config" do
let(:main_database_config) do
Rails.application.config.load_database_yaml
.dig('test', 'main')
.slice('adapter', 'encoding', 'database', 'username', 'password', 'host')
.symbolize_keys
end
let(:additional_database_config) do
# Use built-in postgres database
main_database_config.merge(database: 'postgres')
end
around do |example|
with_reestablished_active_record_base(reconnect: true) do
with_db_configs(test: test_config) do
example.run
end
end
end
shared_examples 'validates successfully' do
it 'by default' do
expect { run_rake_task('gitlab:db:validate_config') }.not_to output(/Database config validation failure/).to_stderr
expect { run_rake_task('gitlab:db:validate_config') }.not_to raise_error
end
it 'for production' do
allow(Gitlab).to receive(:dev_or_test_env?).and_return(false)
expect { run_rake_task('gitlab:db:validate_config') }.not_to output(/Database config validation failure/).to_stderr
expect { run_rake_task('gitlab:db:validate_config') }.not_to raise_error
end
it 'always re-establishes ActiveRecord::Base connection to main config' do
run_rake_task('gitlab:db:validate_config')
expect(ActiveRecord::Base.connection_db_config.configuration_hash).to include(main_database_config) # rubocop: disable Database/MultipleDatabases
end
it 'if GITLAB_VALIDATE_DATABASE_CONFIG is set' do
stub_env('GITLAB_VALIDATE_DATABASE_CONFIG', '1')
allow(Gitlab).to receive(:dev_or_test_env?).and_return(false)
expect { run_rake_task('gitlab:db:validate_config') }.not_to output(/Database config validation failure/).to_stderr
expect { run_rake_task('gitlab:db:validate_config') }.not_to raise_error
end
context 'when finding the initializer fails' do
where(:raised_error) { [ActiveRecord::NoDatabaseError, ActiveRecord::ConnectionNotEstablished, PG::ConnectionBad] }
with_them do
it "does not raise an error for #{params[:raised_error]}" do
allow(ActiveRecord::Base.connection).to receive(:select_one).and_raise(raised_error) # rubocop: disable Database/MultipleDatabases
expect { run_rake_task('gitlab:db:validate_config') }.not_to output(/Database config validation failure/).to_stderr
expect { run_rake_task('gitlab:db:validate_config') }.not_to raise_error
end
end
end
end
shared_examples 'raises an error' do |match|
it 'by default' do
expect { run_rake_task('gitlab:db:validate_config') }.to raise_error(match)
end
it 'for production' do
allow(Gitlab).to receive(:dev_or_test_env?).and_return(false)
expect { run_rake_task('gitlab:db:validate_config') }.to raise_error(match)
end
it 'always re-establishes ActiveRecord::Base connection to main config' do
expect { run_rake_task('gitlab:db:validate_config') }.to raise_error(match)
expect(ActiveRecord::Base.connection_db_config.configuration_hash).to include(main_database_config) # rubocop: disable Database/MultipleDatabases
end
it 'if GITLAB_VALIDATE_DATABASE_CONFIG=1' do
stub_env('GITLAB_VALIDATE_DATABASE_CONFIG', '1')
expect { run_rake_task('gitlab:db:validate_config') }.to raise_error(match)
end
it 'to stderr if GITLAB_VALIDATE_DATABASE_CONFIG=0' do
stub_env('GITLAB_VALIDATE_DATABASE_CONFIG', '0')
expect { run_rake_task('gitlab:db:validate_config') }.to output(match).to_stderr
end
end
context 'when only main: is specified' do
let(:test_config) do
{
main: main_database_config
}
end
it_behaves_like 'validates successfully'
context 'when config is pointing to incorrect server' do
let(:test_config) do
{
main: main_database_config.merge(port: 11235)
}
end
it_behaves_like 'validates successfully'
end
context 'when config is pointing to non-existent database' do
let(:test_config) do
{
main: main_database_config.merge(database: 'non_existent_database')
}
end
it_behaves_like 'validates successfully'
end
end
context 'when main: uses database_tasks=false' do
let(:test_config) do
{
main: main_database_config.merge(database_tasks: false)
}
end
it_behaves_like 'raises an error', /The 'main' is required to use 'database_tasks: true'/
end
context 'when many configurations share the same database' do
context 'when no database_tasks is specified, assumes true' do
let(:test_config) do
{
main: main_database_config,
ci: main_database_config
}
end
it_behaves_like 'raises an error', /Many configurations \(main, ci\) share the same database/
end
context 'when database_tasks is specified' do
let(:test_config) do
{
main: main_database_config.merge(database_tasks: true),
ci: main_database_config.merge(database_tasks: true)
}
end
it_behaves_like 'raises an error', /Many configurations \(main, ci\) share the same database/
end
context "when there's no main: but something different, as currently we only can share with main:" do
let(:test_config) do
{
archive: main_database_config,
ci: main_database_config.merge(database_tasks: false)
}
end
it_behaves_like 'raises an error', /The 'ci' is expecting to share configuration with 'main', but no such is to be found/
end
end
context 'when ci: uses different database' do
context 'and does not specify database_tasks which indicates using dedicated database' do
let(:test_config) do
{
main: main_database_config,
ci: additional_database_config
}
end
it_behaves_like 'validates successfully'
end
context 'and does specify database_tasks=false which indicates sharing with main:' do
let(:test_config) do
{
main: main_database_config,
ci: additional_database_config.merge(database_tasks: false)
}
end
it_behaves_like 'raises an error', /The 'ci' since it is using 'database_tasks: false' should share database with 'main:'/
end
end
context 'one of the databases is in read-only mode' do
let(:test_config) do
{
main: main_database_config
}
end
let(:exception) { ActiveRecord::StatementInvalid.new("READONLY") }
before do
allow(exception).to receive(:cause).and_return(PG::ReadOnlySqlTransaction.new("cannot execute UPSERT in a read-only transaction"))
allow(ActiveRecord::InternalMetadata).to receive(:upsert).at_least(:once).and_raise(exception)
end
it_behaves_like 'validates successfully'
end
end
%w[db:migrate db:schema:load db:schema:dump].each do |task|
context "when running #{task}" do
it "does run gitlab:db:validate_config before" do
expect(Rake::Task['gitlab:db:validate_config']).to receive(:execute).and_return(true)
expect(Rake::Task[task]).to receive(:execute).and_return(true)
Rake::Task['gitlab:db:validate_config'].reenable
run_rake_task(task)
end
end
end
def with_db_configs(test: test_config)
current_configurations = ActiveRecord::Base.configurations # rubocop:disable Database/MultipleDatabases
ActiveRecord::Base.configurations = { test: test_config }
yield
ensure
ActiveRecord::Base.configurations = current_configurations
end
end