# # force TestFixtures to begin transaction with all shards. # require "active_record/fixtures" require "active_record/turntable/util" module ActiveRecord class FixtureSet extend ActiveRecord::Turntable::Util # rubocop:disable Style/MultilineMethodCallBraceLayout unless ar52_or_later? def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base) fixture_set_names = Array(fixture_set_names).map(&:to_s) class_names = ClassCache.new class_names, config # FIXME: Apparently JK uses this. connection = block_given? ? yield : ActiveRecord::Base.connection files_to_read = fixture_set_names.reject { |fs_name| fixture_is_cached?(connection, fs_name) } unless files_to_read.empty? connection.disable_referential_integrity do fixtures_map = {} fixture_sets = files_to_read.map do |fs_name| klass = class_names[fs_name] conn = klass ? klass.connection : connection fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new conn, fs_name, klass, ::File.join(fixtures_directory, fs_name)) end update_all_loaded_fixtures fixtures_map ActiveRecord::Base.force_transaction_all_shards!(requires_new: true) do deleted_tables = Hash.new { |h, k| h[k] = Set.new } fixture_sets.each do |fs| conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection table_rows = fs.table_rows table_rows.each_key do |table| unless deleted_tables[conn].include? table conn.delete "DELETE FROM #{conn.quote_table_name(table)}", "Fixture Delete" end deleted_tables[conn] << table end table_rows.each do |fixture_set_name, rows| rows.each do |row| conn.insert_fixture(row, fixture_set_name) end end # Cap primary key sequences to max(pk). if connection.respond_to?(:reset_pk_sequence!) connection.reset_pk_sequence!(fs.table_name) end end end cache_fixtures(connection, fixtures_map) end end cached_fixtures(connection, fixture_set_names) end end # rubocop:enable Style/MultilineMethodCallLayout end module TestFixtures # rubocop:disable Style/ClassVars, Style/RedundantException def setup_fixtures(config = ActiveRecord::Base) if pre_loaded_fixtures && !use_transactional_fixtures raise RuntimeError, "pre_loaded_fixtures requires use_transactional_fixtures" end @fixture_cache = {} @fixture_connections = [] @@already_loaded_fixtures ||= {} @connection_subscriber = nil # Load fixtures once and begin transaction. if run_in_transaction? if @@already_loaded_fixtures[self.class] @loaded_fixtures = @@already_loaded_fixtures[self.class] else @loaded_fixtures = load_fixtures(config) @@already_loaded_fixtures[self.class] = @loaded_fixtures end # Begin transactions for connections already established ActiveRecord::Base.force_connect_all_shards! @fixture_connections = enlist_fixture_connections @fixture_connections.each do |connection| connection.begin_transaction joinable: false if ActiveRecord::Turntable::Util.ar51_or_later? connection.pool.lock_thread = true end end if ActiveRecord::Turntable::Util.ar51_or_later? # When connections are established in the future, begin a transaction too @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload| spec_name = payload[:spec_name] if payload.key?(:spec_name) if spec_name begin connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name) rescue ConnectionNotEstablished connection = nil end if connection && !@fixture_connections.include?(connection) connection.begin_transaction joinable: false connection.pool.lock_thread = true @fixture_connections << connection end end end end # Load fixtures for every test. else ActiveRecord::FixtureSet.reset_cache @@already_loaded_fixtures[self.class] = nil @loaded_fixtures = load_fixtures(config) end # Instantiate fixtures for every test if requested. instantiate_fixtures if use_instantiated_fixtures end # rubocop:enable Style/ClassVars, Style/RedundantException end end