1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
|
# frozen_string_literal: true
require "config"
require "stringio"
require "active_record"
require "cases/test_case"
require "active_support/dependencies"
require "active_support/logger"
require "active_support/core_ext/kernel/singleton_class"
require "support/config"
require "support/connection"
# TODO: Move all these random hacks into the ARTest namespace and into the support/ dir
Thread.abort_on_exception = true
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
# Disable available locale checks to avoid warnings running the test suite.
I18n.enforce_available_locales = false
# Connect to the database
ARTest.connect
# Quote "type" if it's a reserved word for the current connection.
QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name("type")
def current_adapter?(*types)
types.any? do |type|
ActiveRecord::ConnectionAdapters.const_defined?(type) &&
ActiveRecord::Base.connection.is_a?(ActiveRecord::ConnectionAdapters.const_get(type))
end
end
def in_memory_db?
current_adapter?(:SQLite3Adapter) &&
ActiveRecord::Base.connection_pool.db_config.database == ":memory:"
end
def mysql_enforcing_gtid_consistency?
current_adapter?(:Mysql2Adapter) && "ON" == ActiveRecord::Base.connection.show_variable("enforce_gtid_consistency")
end
def supports_default_expression?
if current_adapter?(:PostgreSQLAdapter)
true
elsif current_adapter?(:Mysql2Adapter)
conn = ActiveRecord::Base.connection
!conn.mariadb? && conn.database_version >= "8.0.13"
end
end
def supports_text_column_with_default?
if current_adapter?(:Mysql2Adapter)
conn = ActiveRecord::Base.connection
conn.mariadb? && conn.database_version >= "10.2.1"
else
true
end
end
%w[
supports_savepoints?
supports_partial_index?
supports_partitioned_indexes?
supports_expression_index?
supports_insert_returning?
supports_insert_on_duplicate_skip?
supports_insert_on_duplicate_update?
supports_insert_conflict_target?
supports_optimizer_hints?
supports_datetime_with_precision?
].each do |method_name|
define_method method_name do
ActiveRecord::Base.connection.public_send(method_name)
end
end
def with_env_tz(new_tz = "US/Eastern")
old_tz, ENV["TZ"] = ENV["TZ"], new_tz
yield
ensure
old_tz ? ENV["TZ"] = old_tz : ENV.delete("TZ")
end
def with_timezone_config(cfg)
verify_default_timezone_config
old_default_zone = ActiveRecord::Base.default_timezone
old_awareness = ActiveRecord::Base.time_zone_aware_attributes
old_zone = Time.zone
if cfg.has_key?(:default)
ActiveRecord::Base.default_timezone = cfg[:default]
end
if cfg.has_key?(:aware_attributes)
ActiveRecord::Base.time_zone_aware_attributes = cfg[:aware_attributes]
end
if cfg.has_key?(:zone)
Time.zone = cfg[:zone]
end
yield
ensure
ActiveRecord::Base.default_timezone = old_default_zone
ActiveRecord::Base.time_zone_aware_attributes = old_awareness
Time.zone = old_zone
end
# This method makes sure that tests don't leak global state related to time zones.
EXPECTED_ZONE = nil
EXPECTED_DEFAULT_TIMEZONE = :utc
EXPECTED_TIME_ZONE_AWARE_ATTRIBUTES = false
def verify_default_timezone_config
if Time.zone != EXPECTED_ZONE
$stderr.puts <<-MSG
\n#{self}
Global state `Time.zone` was leaked.
Expected: #{EXPECTED_ZONE}
Got: #{Time.zone}
MSG
end
if ActiveRecord::Base.default_timezone != EXPECTED_DEFAULT_TIMEZONE
$stderr.puts <<-MSG
\n#{self}
Global state `ActiveRecord::Base.default_timezone` was leaked.
Expected: #{EXPECTED_DEFAULT_TIMEZONE}
Got: #{ActiveRecord::Base.default_timezone}
MSG
end
if ActiveRecord::Base.time_zone_aware_attributes != EXPECTED_TIME_ZONE_AWARE_ATTRIBUTES
$stderr.puts <<-MSG
\n#{self}
Global state `ActiveRecord::Base.time_zone_aware_attributes` was leaked.
Expected: #{EXPECTED_TIME_ZONE_AWARE_ATTRIBUTES}
Got: #{ActiveRecord::Base.time_zone_aware_attributes}
MSG
end
end
def enable_extension!(extension, connection)
return false unless connection.supports_extensions?
return connection.reconnect! if connection.extension_enabled?(extension)
connection.enable_extension extension
connection.commit_db_transaction if connection.transaction_open?
connection.reconnect!
end
def disable_extension!(extension, connection)
return false unless connection.supports_extensions?
return true unless connection.extension_enabled?(extension)
connection.disable_extension extension
connection.reconnect!
end
def clean_up_legacy_connection_handlers
handler = ActiveRecord::Base.default_connection_handler
ActiveRecord::Base.connection_handlers = {}
handler.connection_pool_names.each do |name|
next if ["ActiveRecord::Base", "ARUnit2Model", "Contact", "ContactSti", "FirstAbstractClass", "SecondAbstractClass"].include?(name)
handler.send(:owner_to_pool_manager).delete(name)
end
end
def clean_up_connection_handler
handler = ActiveRecord::Base.connection_handler
handler.instance_variable_get(:@owner_to_pool_manager).each do |owner, pool_manager|
pool_manager.role_names.each do |role_name|
next if role_name == ActiveRecord::Base.default_role
pool_manager.remove_role(role_name)
end
end
end
def load_schema
# silence verbose schema loading
original_stdout = $stdout
$stdout = StringIO.new
adapter_name = ActiveRecord::Base.connection.adapter_name.downcase
adapter_specific_schema_file = SCHEMA_ROOT + "/#{adapter_name}_specific_schema.rb"
load SCHEMA_ROOT + "/schema.rb"
if File.exist?(adapter_specific_schema_file)
load adapter_specific_schema_file
end
ActiveRecord::FixtureSet.reset_cache
ensure
$stdout = original_stdout
end
load_schema
class SQLSubscriber
attr_reader :logged
attr_reader :payloads
def initialize
@logged = []
@payloads = []
end
def start(name, id, payload)
@payloads << payload
@logged << [payload[:sql].squish, payload[:name], payload[:binds]]
end
def finish(name, id, payload); end
end
module InTimeZone
private
def in_time_zone(zone)
old_zone = Time.zone
old_tz = ActiveRecord::Base.time_zone_aware_attributes
Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
yield
ensure
Time.zone = old_zone
ActiveRecord::Base.time_zone_aware_attributes = old_tz
end
end
|