Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync test DB from schema using its SHA1 #36870

Merged
merged 1 commit into from Aug 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 9 additions & 5 deletions activerecord/lib/active_record/migration.rb
Expand Up @@ -587,18 +587,22 @@ def check_pending!(connection = Base.connection)
end

def load_schema_if_pending!
if Base.connection.migration_context.needs_migration? || !Base.connection.migration_context.any_migrations?
current_config = Base.connection_config
all_configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)

unless all_configs.all? { |config| Tasks::DatabaseTasks.schema_up_to_date?(config.config) }
# Roundtrip to Rake to allow plugins to hook into database initialization.
root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
FileUtils.cd(root) do
current_config = Base.connection_config
Base.clear_all_connections!
system("bin/rails db:test:prepare")
# Establish a new connection, the old database may be gone (db:test:prepare uses purge)
Base.establish_connection(current_config)
end
check_pending!
end

# Establish a new connection, the old database may be gone (db:test:prepare uses purge)
Base.establish_connection(current_config)

check_pending!
end

def maintain_test_schema! #:nodoc:
Expand Down
15 changes: 15 additions & 0 deletions activerecord/lib/active_record/tasks/database_tasks.rb
Expand Up @@ -332,10 +332,21 @@ def load_schema(configuration, format = ActiveRecord::Base.schema_format, file =
end
ActiveRecord::InternalMetadata.create_table
ActiveRecord::InternalMetadata[:environment] = environment
ActiveRecord::InternalMetadata[:schema_sha1] = schema_sha1(file)
ensure
Migration.verbose = verbose_was
end

def schema_up_to_date?(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env, spec_name = "primary")
file ||= dump_filename(spec_name, format)

return true unless File.exist?(file)

ActiveRecord::Base.establish_connection(configuration)
return false unless ActiveRecord::InternalMetadata.table_exists?
ActiveRecord::InternalMetadata[:schema_sha1] == schema_sha1(file)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to stop this information in the database? Can't it be a file on tmp?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question 鉂わ笍

I think the database is the best place for it, we want to store this exactly per-database: my_app_test separate from my_app_development, my_app_test-1 separate from my_app_test-2 when using parallelism. So storing it in the database itself I think accomplishes this the best. It also handles databases being dropped/restored from backup/etc.

I also think it's similar enough to the other information we store in the DB: very similar to schema migrations (metadata about the "structure" of the DB) and somewhat similar to environment (it records the state at creation/load) that it "feels" right to me.

end

def dump_schema(configuration, format = ActiveRecord::Base.schema_format, spec_name = "primary") # :nodoc:
require "active_record/schema_dumper"
filename = dump_filename(spec_name, format)
Expand Down Expand Up @@ -467,6 +478,10 @@ def each_local_configuration
def local_database?(configuration)
configuration["host"].blank? || LOCAL_HOSTS.include?(configuration["host"])
end

def schema_sha1(file)
Digest::SHA1.hexdigest(File.read(file))
end
end
end
end
9 changes: 1 addition & 8 deletions railties/test/application/test_test.rb
Expand Up @@ -232,10 +232,7 @@ class UserTest < ActiveSupport::TestCase
assert_successful_test_run("models/user_test.rb")
end

# TODO: would be nice if we could detect the schema change automatically.
# For now, the user has to synchronize the schema manually.
# This test case serves as a reminder for this use case.
test "manually synchronize test schema after rollback" do
test "automatically synchronizes test schema after rollback" do
output = rails("generate", "model", "user", "name:string")
version = output.match(/(\d+)_create_users\.rb/)[1]

Expand Down Expand Up @@ -268,10 +265,6 @@ class UserTest < ActiveSupport::TestCase
end
RUBY

assert_successful_test_run "models/user_test.rb"

rails "db:test:prepare"

assert_unsuccessful_run "models/user_test.rb", <<-ASSERTION
Expected: ["id", "name"]
Actual: ["id", "name", "age"]
Expand Down