From 34cdc7ce27a9da6c1f5ad17a87357cbc7ef7dbb8 Mon Sep 17 00:00:00 2001 From: Craig McNamara Date: Fri, 12 Dec 2025 22:42:58 -0800 Subject: [PATCH 1/3] Add `include_hidden` option Default to `false` Make it so databases with `database_tasks: false` can be seen and cleaned --- lib/database_rewinder.rb | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/database_rewinder.rb b/lib/database_rewinder.rb index 2b3f7bb..012f3a6 100644 --- a/lib/database_rewinder.rb +++ b/lib/database_rewinder.rb @@ -7,8 +7,11 @@ class << self # Set your DB configuration here if you'd like to use something else than the AR configuration attr_writer :database_configuration + # Set to true to include hidden database configurations (those with database_tasks: false) + attr_accessor :include_hidden + def init - @cleaners, @table_names_cache, @clean_all, @only, @except, @database_configuration = [], {}, false + @cleaners, @table_names_cache, @clean_all, @only, @except, @database_configuration, @include_hidden = [], {}, false, nil, nil, nil, false end def database_configuration @@ -40,13 +43,20 @@ def record_inserted_table(connection, sql) config = connection.instance_variable_get(:'@config') database = config[:database] host = config[:host] + schema_search_path = config[:schema_search_path] #NOTE What's the best way to get the app dir besides Rails.root? I know Dir.pwd here might not be the right solution, but it should work in most cases... root_dir = defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : Dir.pwd cleaner = cleaners.detect do |c| if (config[:adapter] == 'sqlite3') && (config[:database] != ':memory:') File.expand_path(c.db, root_dir) == File.expand_path(database, root_dir) else - c.db == database && c.host == host + # For PostgreSQL with schema_search_path, match by schema as well as db/host + cleaner_schema = c.config['schema_search_path'] + if schema_search_path || cleaner_schema + c.db == database && c.host == host && cleaner_schema == schema_search_path + else + c.db == database && c.host == host + end end end or return @@ -129,9 +139,9 @@ def traditional_configuration_for(connection_name) def multiple_database_configuration_for(connection_name) if (ActiveRecord::VERSION::MAJOR >= 7) || ((ActiveRecord::VERSION::MAJOR >= 6) && (ActiveRecord::VERSION::MINOR >= 1)) - database_configuration.configs_for(name: connection_name) + database_configuration.configs_for(name: connection_name, include_hidden: include_hidden) else - database_configuration.configs_for(spec_name: connection_name) + database_configuration.configs_for(spec_name: connection_name, include_hidden: include_hidden) end end end From f8c686a9ebbef9521ec047d064c3af069f5db968 Mon Sep 17 00:00:00 2001 From: Craig McNamara Date: Fri, 12 Dec 2025 22:58:07 -0800 Subject: [PATCH 2/3] Use a clearer name --- lib/database_rewinder.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/database_rewinder.rb b/lib/database_rewinder.rb index 012f3a6..4ba1289 100644 --- a/lib/database_rewinder.rb +++ b/lib/database_rewinder.rb @@ -51,9 +51,9 @@ def record_inserted_table(connection, sql) File.expand_path(c.db, root_dir) == File.expand_path(database, root_dir) else # For PostgreSQL with schema_search_path, match by schema as well as db/host - cleaner_schema = c.config['schema_search_path'] - if schema_search_path || cleaner_schema - c.db == database && c.host == host && cleaner_schema == schema_search_path + cleaner_schema_search_path = c.config['schema_search_path'] + if schema_search_path || cleaner_schema_search_path + c.db == database && c.host == host && cleaner_schema_search_path == schema_search_path else c.db == database && c.host == host end From 979e33486fd6ea2cdde624fa99bda5e7894ea8dd Mon Sep 17 00:00:00 2001 From: Craig McNamara Date: Fri, 12 Dec 2025 23:08:48 -0800 Subject: [PATCH 3/3] Specs for the new feautres --- test/database_rewinder_test.rb | 112 +++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/test/database_rewinder_test.rb b/test/database_rewinder_test.rb index ea4b929..0812616 100644 --- a/test/database_rewinder_test.rb +++ b/test/database_rewinder_test.rb @@ -290,6 +290,118 @@ def perform_clean(options) end end + sub_test_case '.include_hidden' do + teardown do + DatabaseRewinder.database_configuration = nil + DatabaseRewinder.include_hidden = false + end + + test 'defaults to false' do + assert_equal false, DatabaseRewinder.include_hidden + end + + test 'can be set to true' do + DatabaseRewinder.include_hidden = true + assert_equal true, DatabaseRewinder.include_hidden + end + + test 'passes include_hidden to configs_for when looking up database configurations' do + hidden_db_config = {'test' => {'hidden_db' => {'adapter' => 'sqlite3', 'database' => ':memory:', 'database_tasks' => false}}} + DatabaseRewinder.database_configuration = ActiveRecord::DatabaseConfigurations.new(hidden_db_config) + + assert_raises(RuntimeError, 'Database configuration named "hidden_db" is not configured.') do + DatabaseRewinder['hidden_db'] + end + + DatabaseRewinder.include_hidden = true + + cleaner = DatabaseRewinder['hidden_db'] + assert_equal 'hidden_db', cleaner.connection_name + end + end + + sub_test_case '.record_inserted_table with schema_search_path' do + setup do + DatabaseRewinder.init + end + + teardown do + DatabaseRewinder.database_configuration = nil + end + + test 'matches cleaner by schema_search_path when present' do + cleaner1 = DatabaseRewinder::Cleaner.new( + config: {'database' => 'testdb', 'host' => 'localhost', 'schema_search_path' => 'public'}, + connection_name: 'test_public', + only: nil, + except: nil + ) + cleaner2 = DatabaseRewinder::Cleaner.new( + config: {'database' => 'testdb', 'host' => 'localhost', 'schema_search_path' => 'other_schema'}, + connection_name: 'test_other', + only: nil, + except: nil + ) + DatabaseRewinder.instance_variable_set(:@cleaners, [cleaner1, cleaner2]) + + connection = Struct.new(:pool).new(nil) + connection.instance_variable_set(:@config, { + adapter: 'postgresql', + database: 'testdb', + host: 'localhost', + schema_search_path: 'other_schema' + }) + + DatabaseRewinder.record_inserted_table(connection, 'INSERT INTO foos (name) VALUES (?)') + + assert_equal [], cleaner1.inserted_tables + assert_equal ['foos'], cleaner2.inserted_tables + end + + test 'matches cleaner without schema_search_path when neither have it' do + cleaner = DatabaseRewinder::Cleaner.new( + config: {'database' => 'testdb', 'host' => 'localhost'}, + connection_name: 'test', + only: nil, + except: nil + ) + DatabaseRewinder.instance_variable_set(:@cleaners, [cleaner]) + + connection = Struct.new(:pool).new(nil) + connection.instance_variable_set(:@config, { + adapter: 'postgresql', + database: 'testdb', + host: 'localhost' + }) + + DatabaseRewinder.record_inserted_table(connection, 'INSERT INTO bars (name) VALUES (?)') + + assert_equal ['bars'], cleaner.inserted_tables + end + + test 'does not match cleaner when schema_search_path differs' do + cleaner = DatabaseRewinder::Cleaner.new( + config: {'database' => 'testdb', 'host' => 'localhost', 'schema_search_path' => 'public'}, + connection_name: 'test', + only: nil, + except: nil + ) + DatabaseRewinder.instance_variable_set(:@cleaners, [cleaner]) + + connection = Struct.new(:pool).new(nil) + connection.instance_variable_set(:@config, { + adapter: 'postgresql', + database: 'testdb', + host: 'localhost', + schema_search_path: 'other_schema' + }) + + DatabaseRewinder.record_inserted_table(connection, 'INSERT INTO foos (name) VALUES (?)') + + assert_equal [], cleaner.inserted_tables + end + end + sub_test_case '.strategy=' do sub_test_case 'call first with options' do setup do