2019-07-25 01:21:37 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-10-07 06:30:10 -04:00
|
|
|
require 'spec_helper'
|
|
|
|
|
2017-07-10 10:24:02 -04:00
|
|
|
describe Gitlab::Database do
|
2017-03-17 09:16:47 -04:00
|
|
|
before do
|
|
|
|
stub_const('MigrationTest', Class.new { include Gitlab::Database })
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.config' do
|
|
|
|
it 'returns a Hash' do
|
|
|
|
expect(described_class.config).to be_an_instance_of(Hash)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-02-13 11:11:50 -05:00
|
|
|
describe '.adapter_name' do
|
|
|
|
it 'returns the name of the adapter' do
|
|
|
|
expect(described_class.adapter_name).to be_an_instance_of(String)
|
|
|
|
end
|
2019-03-21 16:05:01 -04:00
|
|
|
|
2019-06-13 09:12:28 -04:00
|
|
|
it 'returns Unknown when using anything else' do
|
2019-03-21 16:05:01 -04:00
|
|
|
allow(described_class).to receive(:postgresql?).and_return(false)
|
|
|
|
|
2019-06-13 09:12:28 -04:00
|
|
|
expect(described_class.human_adapter_name).to eq('Unknown')
|
2019-03-21 16:05:01 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-07-24 09:59:55 -04:00
|
|
|
describe '.human_adapter_name' do
|
|
|
|
it 'returns PostgreSQL when using PostgreSQL' do
|
|
|
|
expect(described_class.human_adapter_name).to eq('PostgreSQL')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-10-07 06:30:10 -04:00
|
|
|
describe '.postgresql?' do
|
|
|
|
subject { described_class.postgresql? }
|
|
|
|
|
|
|
|
it { is_expected.to satisfy { |val| val == true || val == false } }
|
|
|
|
end
|
2016-02-01 21:29:37 -05:00
|
|
|
|
|
|
|
describe '.version' do
|
2018-05-17 15:20:15 -04:00
|
|
|
around do |example|
|
|
|
|
described_class.instance_variable_set(:@version, nil)
|
|
|
|
example.run
|
|
|
|
described_class.instance_variable_set(:@version, nil)
|
|
|
|
end
|
|
|
|
|
2016-02-01 21:29:37 -05:00
|
|
|
context "on postgresql" do
|
|
|
|
it "extracts the version number" do
|
2017-06-21 09:48:12 -04:00
|
|
|
allow(described_class).to receive(:database_version)
|
|
|
|
.and_return("PostgreSQL 9.4.4 on x86_64-apple-darwin14.3.0")
|
2016-02-01 21:29:37 -05:00
|
|
|
|
|
|
|
expect(described_class.version).to eq '9.4.4'
|
|
|
|
end
|
|
|
|
end
|
2018-05-17 15:20:15 -04:00
|
|
|
|
|
|
|
it 'memoizes the result' do
|
|
|
|
count = ActiveRecord::QueryRecorder
|
|
|
|
.new { 2.times { described_class.version } }
|
|
|
|
.count
|
|
|
|
|
|
|
|
expect(count).to eq(1)
|
|
|
|
end
|
2016-02-01 21:29:37 -05:00
|
|
|
end
|
2016-02-15 14:13:47 -05:00
|
|
|
|
2018-07-24 07:23:54 -04:00
|
|
|
describe '.postgresql_9_or_less?' do
|
2019-07-24 09:59:55 -04:00
|
|
|
it 'returns true when using postgresql 8.4' do
|
|
|
|
allow(described_class).to receive(:version).and_return('8.4')
|
|
|
|
expect(described_class.postgresql_9_or_less?).to eq(true)
|
2018-07-24 07:23:54 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns true when using PostgreSQL 9.6' do
|
|
|
|
allow(described_class).to receive(:version).and_return('9.6')
|
|
|
|
|
|
|
|
expect(described_class.postgresql_9_or_less?).to eq(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns false when using PostgreSQL 10 or newer' do
|
|
|
|
allow(described_class).to receive(:version).and_return('10')
|
|
|
|
|
|
|
|
expect(described_class.postgresql_9_or_less?).to eq(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-03-14 11:25:05 -04:00
|
|
|
describe '.postgresql_minimum_supported_version?' do
|
2019-07-24 09:59:55 -04:00
|
|
|
it 'returns false when using PostgreSQL 9.5' do
|
|
|
|
allow(described_class).to receive(:version).and_return('9.5')
|
2019-03-14 11:25:05 -04:00
|
|
|
|
|
|
|
expect(described_class.postgresql_minimum_supported_version?).to eq(false)
|
|
|
|
end
|
|
|
|
|
2019-07-24 09:59:55 -04:00
|
|
|
it 'returns true when using PostgreSQL 9.6' do
|
|
|
|
allow(described_class).to receive(:version).and_return('9.6')
|
2019-03-14 11:25:05 -04:00
|
|
|
|
2019-07-24 09:59:55 -04:00
|
|
|
expect(described_class.postgresql_minimum_supported_version?).to eq(true)
|
|
|
|
end
|
2019-03-14 11:25:05 -04:00
|
|
|
|
2019-07-24 09:59:55 -04:00
|
|
|
it 'returns true when using PostgreSQL 10 or newer' do
|
|
|
|
allow(described_class).to receive(:version).and_return('10')
|
2019-03-14 11:25:05 -04:00
|
|
|
|
2019-07-24 09:59:55 -04:00
|
|
|
expect(described_class.postgresql_minimum_supported_version?).to eq(true)
|
2019-03-14 11:25:05 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-12-05 11:43:47 -05:00
|
|
|
describe '.replication_slots_supported?' do
|
|
|
|
it 'returns false when using PostgreSQL 9.3' do
|
|
|
|
allow(described_class).to receive(:version).and_return('9.3.1')
|
|
|
|
|
|
|
|
expect(described_class.replication_slots_supported?).to eq(false)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns true when using PostgreSQL 9.4.0 or newer' do
|
|
|
|
allow(described_class).to receive(:version).and_return('9.4.0')
|
|
|
|
|
|
|
|
expect(described_class.replication_slots_supported?).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-07-24 07:23:54 -04:00
|
|
|
describe '.pg_wal_lsn_diff' do
|
|
|
|
it 'returns old name when using PostgreSQL 9.6' do
|
|
|
|
allow(described_class).to receive(:version).and_return('9.6')
|
|
|
|
|
|
|
|
expect(described_class.pg_wal_lsn_diff).to eq('pg_xlog_location_diff')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns new name when using PostgreSQL 10 or newer' do
|
|
|
|
allow(described_class).to receive(:version).and_return('10')
|
|
|
|
|
|
|
|
expect(described_class.pg_wal_lsn_diff).to eq('pg_wal_lsn_diff')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.pg_current_wal_insert_lsn' do
|
|
|
|
it 'returns old name when using PostgreSQL 9.6' do
|
|
|
|
allow(described_class).to receive(:version).and_return('9.6')
|
|
|
|
|
|
|
|
expect(described_class.pg_current_wal_insert_lsn).to eq('pg_current_xlog_insert_location')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns new name when using PostgreSQL 10 or newer' do
|
|
|
|
allow(described_class).to receive(:version).and_return('10')
|
|
|
|
|
|
|
|
expect(described_class.pg_current_wal_insert_lsn).to eq('pg_current_wal_insert_lsn')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.pg_last_wal_receive_lsn' do
|
|
|
|
it 'returns old name when using PostgreSQL 9.6' do
|
|
|
|
allow(described_class).to receive(:version).and_return('9.6')
|
|
|
|
|
|
|
|
expect(described_class.pg_last_wal_receive_lsn).to eq('pg_last_xlog_receive_location')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns new name when using PostgreSQL 10 or newer' do
|
|
|
|
allow(described_class).to receive(:version).and_return('10')
|
|
|
|
|
|
|
|
expect(described_class.pg_last_wal_receive_lsn).to eq('pg_last_wal_receive_lsn')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.pg_last_wal_replay_lsn' do
|
|
|
|
it 'returns old name when using PostgreSQL 9.6' do
|
|
|
|
allow(described_class).to receive(:version).and_return('9.6')
|
|
|
|
|
|
|
|
expect(described_class.pg_last_wal_replay_lsn).to eq('pg_last_xlog_replay_location')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns new name when using PostgreSQL 10 or newer' do
|
|
|
|
allow(described_class).to receive(:version).and_return('10')
|
|
|
|
|
|
|
|
expect(described_class.pg_last_wal_replay_lsn).to eq('pg_last_wal_replay_lsn')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-03-14 11:25:05 -04:00
|
|
|
describe '.pg_last_xact_replay_timestamp' do
|
|
|
|
it 'returns pg_last_xact_replay_timestamp' do
|
|
|
|
expect(described_class.pg_last_xact_replay_timestamp).to eq('pg_last_xact_replay_timestamp')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-05-13 11:26:18 -04:00
|
|
|
describe '.nulls_last_order' do
|
2019-06-13 09:12:28 -04:00
|
|
|
it { expect(described_class.nulls_last_order('column', 'ASC')).to eq 'column ASC NULLS LAST'}
|
|
|
|
it { expect(described_class.nulls_last_order('column', 'DESC')).to eq 'column DESC NULLS LAST'}
|
2016-05-13 11:26:18 -04:00
|
|
|
end
|
|
|
|
|
2017-01-30 19:26:40 -05:00
|
|
|
describe '.nulls_first_order' do
|
2019-06-13 09:12:28 -04:00
|
|
|
it { expect(described_class.nulls_first_order('column', 'ASC')).to eq 'column ASC NULLS FIRST'}
|
|
|
|
it { expect(described_class.nulls_first_order('column', 'DESC')).to eq 'column DESC NULLS FIRST'}
|
2017-01-30 19:26:40 -05:00
|
|
|
end
|
|
|
|
|
2017-02-13 08:45:26 -05:00
|
|
|
describe '.with_connection_pool' do
|
|
|
|
it 'creates a new connection pool and disconnect it after used' do
|
|
|
|
closed_pool = nil
|
|
|
|
|
|
|
|
described_class.with_connection_pool(1) do |pool|
|
|
|
|
pool.with_connection do |connection|
|
|
|
|
connection.execute('SELECT 1 AS value')
|
|
|
|
end
|
|
|
|
|
|
|
|
expect(pool).to be_connected
|
|
|
|
|
|
|
|
closed_pool = pool
|
|
|
|
end
|
|
|
|
|
|
|
|
expect(closed_pool).not_to be_connected
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'disconnects the pool even an exception was raised' do
|
|
|
|
error = Class.new(RuntimeError)
|
|
|
|
closed_pool = nil
|
|
|
|
|
|
|
|
begin
|
|
|
|
described_class.with_connection_pool(1) do |pool|
|
|
|
|
pool.with_connection do |connection|
|
|
|
|
connection.execute('SELECT 1 AS value')
|
|
|
|
end
|
|
|
|
|
|
|
|
closed_pool = pool
|
|
|
|
|
|
|
|
raise error.new('boom')
|
|
|
|
end
|
|
|
|
rescue error
|
|
|
|
end
|
|
|
|
|
|
|
|
expect(closed_pool).not_to be_connected
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-06-09 07:48:25 -04:00
|
|
|
describe '.bulk_insert' do
|
|
|
|
before do
|
|
|
|
allow(described_class).to receive(:connection).and_return(connection)
|
2019-12-10 02:53:40 -05:00
|
|
|
allow(described_class).to receive(:version).and_return(version)
|
2017-06-09 07:48:25 -04:00
|
|
|
allow(connection).to receive(:quote_column_name, &:itself)
|
|
|
|
allow(connection).to receive(:quote, &:itself)
|
|
|
|
allow(connection).to receive(:execute)
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:connection) { double(:connection) }
|
|
|
|
|
|
|
|
let(:rows) do
|
|
|
|
[
|
|
|
|
{ a: 1, b: 2, c: 3 },
|
|
|
|
{ c: 6, a: 4, b: 5 }
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
2019-12-10 02:53:40 -05:00
|
|
|
let_it_be(:version) { 9.6 }
|
|
|
|
|
2017-06-09 07:48:25 -04:00
|
|
|
it 'does nothing with empty rows' do
|
|
|
|
expect(connection).not_to receive(:execute)
|
|
|
|
|
|
|
|
described_class.bulk_insert('test', [])
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'uses the ordering from the first row' do
|
|
|
|
expect(connection).to receive(:execute) do |sql|
|
|
|
|
expect(sql).to include('(1, 2, 3)')
|
|
|
|
expect(sql).to include('(4, 5, 6)')
|
|
|
|
end
|
|
|
|
|
|
|
|
described_class.bulk_insert('test', rows)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'quotes column names' do
|
|
|
|
expect(connection).to receive(:quote_column_name).with(:a)
|
|
|
|
expect(connection).to receive(:quote_column_name).with(:b)
|
|
|
|
expect(connection).to receive(:quote_column_name).with(:c)
|
|
|
|
|
|
|
|
described_class.bulk_insert('test', rows)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'quotes values' do
|
|
|
|
1.upto(6) do |i|
|
|
|
|
expect(connection).to receive(:quote).with(i)
|
|
|
|
end
|
|
|
|
|
|
|
|
described_class.bulk_insert('test', rows)
|
|
|
|
end
|
2017-06-26 12:21:40 -04:00
|
|
|
|
2017-11-27 02:10:51 -05:00
|
|
|
it 'does not quote values of a column in the disable_quote option' do
|
|
|
|
[1, 2, 4, 5].each do |i|
|
|
|
|
expect(connection).to receive(:quote).with(i)
|
|
|
|
end
|
|
|
|
|
|
|
|
described_class.bulk_insert('test', rows, disable_quote: :c)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not quote values of columns in the disable_quote option' do
|
|
|
|
[2, 5].each do |i|
|
|
|
|
expect(connection).to receive(:quote).with(i)
|
|
|
|
end
|
|
|
|
|
|
|
|
described_class.bulk_insert('test', rows, disable_quote: [:a, :c])
|
|
|
|
end
|
|
|
|
|
2017-06-26 12:21:40 -04:00
|
|
|
it 'handles non-UTF-8 data' do
|
|
|
|
expect { described_class.bulk_insert('test', [{ a: "\255" }]) }.not_to raise_error
|
|
|
|
end
|
2017-10-21 18:55:59 -04:00
|
|
|
|
|
|
|
context 'when using PostgreSQL' do
|
|
|
|
it 'allows the returning of the IDs of the inserted rows' do
|
|
|
|
result = double(:result, values: [['10']])
|
|
|
|
|
|
|
|
expect(connection)
|
|
|
|
.to receive(:execute)
|
|
|
|
.with(/RETURNING id/)
|
|
|
|
.and_return(result)
|
|
|
|
|
|
|
|
ids = described_class
|
|
|
|
.bulk_insert('test', [{ number: 10 }], return_ids: true)
|
|
|
|
|
|
|
|
expect(ids).to eq([10])
|
|
|
|
end
|
2019-12-10 02:53:40 -05:00
|
|
|
|
|
|
|
context 'with version >= 9.5' do
|
|
|
|
it 'allows setting the upsert to do nothing' do
|
|
|
|
expect(connection)
|
|
|
|
.to receive(:execute)
|
|
|
|
.with(/ON CONFLICT DO NOTHING/)
|
|
|
|
|
|
|
|
described_class
|
|
|
|
.bulk_insert('test', [{ number: 10 }], on_conflict: :do_nothing)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with version < 9.5' do
|
|
|
|
let(:version) { 9.4 }
|
2019-12-17 13:07:48 -05:00
|
|
|
|
2019-12-10 02:53:40 -05:00
|
|
|
it 'refuses setting the upsert' do
|
|
|
|
expect(connection)
|
|
|
|
.not_to receive(:execute)
|
|
|
|
.with(/ON CONFLICT/)
|
|
|
|
|
|
|
|
described_class
|
|
|
|
.bulk_insert('test', [{ number: 10 }], on_conflict: :do_nothing)
|
|
|
|
end
|
|
|
|
end
|
2017-10-21 18:55:59 -04:00
|
|
|
end
|
2017-06-09 07:48:25 -04:00
|
|
|
end
|
|
|
|
|
2017-02-13 08:45:26 -05:00
|
|
|
describe '.create_connection_pool' do
|
|
|
|
it 'creates a new connection pool with specific pool size' do
|
|
|
|
pool = described_class.create_connection_pool(5)
|
|
|
|
|
2017-02-22 09:43:01 -05:00
|
|
|
begin
|
|
|
|
expect(pool)
|
|
|
|
.to be_kind_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
|
|
|
|
|
|
|
|
expect(pool.spec.config[:pool]).to eq(5)
|
|
|
|
ensure
|
|
|
|
pool.disconnect!
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'allows setting of a custom hostname' do
|
|
|
|
pool = described_class.create_connection_pool(5, '127.0.0.1')
|
|
|
|
|
|
|
|
begin
|
|
|
|
expect(pool.spec.config[:host]).to eq('127.0.0.1')
|
|
|
|
ensure
|
|
|
|
pool.disconnect!
|
|
|
|
end
|
2017-02-13 08:45:26 -05:00
|
|
|
end
|
2019-08-23 00:19:51 -04:00
|
|
|
|
|
|
|
it 'allows setting of a custom hostname and port' do
|
|
|
|
pool = described_class.create_connection_pool(5, '127.0.0.1', 5432)
|
|
|
|
|
|
|
|
begin
|
|
|
|
expect(pool.spec.config[:host]).to eq('127.0.0.1')
|
|
|
|
expect(pool.spec.config[:port]).to eq(5432)
|
|
|
|
ensure
|
|
|
|
pool.disconnect!
|
|
|
|
end
|
|
|
|
end
|
2017-02-13 08:45:26 -05:00
|
|
|
end
|
|
|
|
|
2018-03-15 15:50:31 -04:00
|
|
|
describe '.cached_column_exists?' do
|
|
|
|
it 'only retrieves data once' do
|
|
|
|
expect(ActiveRecord::Base.connection).to receive(:columns).once.and_call_original
|
|
|
|
|
|
|
|
2.times do
|
|
|
|
expect(described_class.cached_column_exists?(:projects, :id)).to be_truthy
|
|
|
|
expect(described_class.cached_column_exists?(:projects, :bogus_column)).to be_falsey
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-15 16:12:00 -04:00
|
|
|
describe '.cached_table_exists?' do
|
|
|
|
it 'only retrieves data once per table' do
|
2018-12-15 04:06:56 -05:00
|
|
|
expect(ActiveRecord::Base.connection).to receive(:data_source_exists?).with(:projects).once.and_call_original
|
|
|
|
expect(ActiveRecord::Base.connection).to receive(:data_source_exists?).with(:bogus_table_name).once.and_call_original
|
2018-03-15 16:12:00 -04:00
|
|
|
|
|
|
|
2.times do
|
|
|
|
expect(described_class.cached_table_exists?(:projects)).to be_truthy
|
|
|
|
expect(described_class.cached_table_exists?(:bogus_table_name)).to be_falsey
|
|
|
|
end
|
|
|
|
end
|
2020-02-17 19:09:20 -05:00
|
|
|
|
|
|
|
it 'returns false when database does not exist' do
|
|
|
|
expect(ActiveRecord::Base).to receive(:connection) { raise ActiveRecord::NoDatabaseError, 'broken' }
|
|
|
|
|
|
|
|
expect(described_class.cached_table_exists?(:projects)).to be(false)
|
|
|
|
end
|
2018-03-15 16:12:00 -04:00
|
|
|
end
|
|
|
|
|
2020-01-10 07:07:47 -05:00
|
|
|
describe '.exists?' do
|
|
|
|
it 'returns true if `ActiveRecord::Base.connection` succeeds' do
|
|
|
|
expect(ActiveRecord::Base).to receive(:connection)
|
|
|
|
|
|
|
|
expect(described_class.exists?).to be(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns false if `ActiveRecord::Base.connection` fails' do
|
|
|
|
expect(ActiveRecord::Base).to receive(:connection) { raise ActiveRecord::NoDatabaseError, 'broken' }
|
|
|
|
|
|
|
|
expect(described_class.exists?).to be(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-02-15 14:13:47 -05:00
|
|
|
describe '#true_value' do
|
2019-06-13 09:12:28 -04:00
|
|
|
it 'returns correct value' do
|
2017-04-10 06:23:28 -04:00
|
|
|
expect(described_class.true_value).to eq "'t'"
|
2016-02-15 14:13:47 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#false_value' do
|
2019-06-13 09:12:28 -04:00
|
|
|
it 'returns correct value' do
|
2017-04-10 06:23:28 -04:00
|
|
|
expect(described_class.false_value).to eq "'f'"
|
2016-02-15 14:13:47 -05:00
|
|
|
end
|
|
|
|
end
|
2017-10-30 19:21:56 -04:00
|
|
|
|
2018-11-26 14:14:05 -05:00
|
|
|
describe '.read_only?' do
|
|
|
|
it 'returns false' do
|
|
|
|
expect(described_class.read_only?).to be_falsey
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-07-06 13:07:49 -04:00
|
|
|
describe '.db_read_only?' do
|
2019-06-13 09:12:28 -04:00
|
|
|
before do
|
|
|
|
allow(ActiveRecord::Base.connection).to receive(:execute).and_call_original
|
|
|
|
end
|
2018-07-06 13:07:49 -04:00
|
|
|
|
2019-06-13 09:12:28 -04:00
|
|
|
it 'detects a read only database' do
|
|
|
|
allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => "t" }])
|
2018-11-26 14:14:05 -05:00
|
|
|
|
2019-06-13 09:12:28 -04:00
|
|
|
expect(described_class.db_read_only?).to be_truthy
|
|
|
|
end
|
2018-11-26 14:14:05 -05:00
|
|
|
|
2019-06-13 09:12:28 -04:00
|
|
|
it 'detects a read only database' do
|
|
|
|
allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => true }])
|
2018-07-06 13:07:49 -04:00
|
|
|
|
2019-06-13 09:12:28 -04:00
|
|
|
expect(described_class.db_read_only?).to be_truthy
|
|
|
|
end
|
2018-11-26 14:14:05 -05:00
|
|
|
|
2019-06-13 09:12:28 -04:00
|
|
|
it 'detects a read write database' do
|
|
|
|
allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => "f" }])
|
2018-11-26 14:14:05 -05:00
|
|
|
|
2019-06-13 09:12:28 -04:00
|
|
|
expect(described_class.db_read_only?).to be_falsey
|
2018-07-06 13:07:49 -04:00
|
|
|
end
|
|
|
|
|
2019-06-13 09:12:28 -04:00
|
|
|
it 'detects a read write database' do
|
|
|
|
allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => false }])
|
2018-07-06 13:07:49 -04:00
|
|
|
|
2019-06-13 09:12:28 -04:00
|
|
|
expect(described_class.db_read_only?).to be_falsey
|
2018-07-06 13:07:49 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-10-30 19:21:56 -04:00
|
|
|
describe '#sanitize_timestamp' do
|
|
|
|
let(:max_timestamp) { Time.at((1 << 31) - 1) }
|
|
|
|
|
|
|
|
subject { described_class.sanitize_timestamp(timestamp) }
|
|
|
|
|
|
|
|
context 'with a timestamp smaller than MAX_TIMESTAMP_VALUE' do
|
|
|
|
let(:timestamp) { max_timestamp - 10.years }
|
|
|
|
|
|
|
|
it 'returns the given timestamp' do
|
|
|
|
expect(subject).to eq(timestamp)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with a timestamp larger than MAX_TIMESTAMP_VALUE' do
|
|
|
|
let(:timestamp) { max_timestamp + 1.second }
|
|
|
|
|
|
|
|
it 'returns MAX_TIMESTAMP_VALUE' do
|
|
|
|
expect(subject).to eq(max_timestamp)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2015-10-07 06:30:10 -04:00
|
|
|
end
|