aboutsummaryrefslogtreecommitdiffstats
path: root/railties/test/application
diff options
context:
space:
mode:
Diffstat (limited to 'railties/test/application')
-rw-r--r--railties/test/application/asset_debugging_test.rb2
-rw-r--r--railties/test/application/assets_test.rb24
-rw-r--r--railties/test/application/bin_setup_test.rb20
-rw-r--r--railties/test/application/configuration_test.rb702
-rw-r--r--railties/test/application/console_test.rb43
-rw-r--r--railties/test/application/credentials_test.rb56
-rw-r--r--railties/test/application/dbconsole_test.rb20
-rw-r--r--railties/test/application/initializers/frameworks_test.rb52
-rw-r--r--railties/test/application/loading_test.rb90
-rw-r--r--railties/test/application/mailer_previews_test.rb3
-rw-r--r--railties/test/application/middleware/cookies_test.rb10
-rw-r--r--railties/test/application/middleware/exceptions_test.rb2
-rw-r--r--railties/test/application/middleware/remote_ip_test.rb4
-rw-r--r--railties/test/application/middleware/session_test.rb134
-rw-r--r--railties/test/application/middleware_test.rb19
-rw-r--r--railties/test/application/multiple_applications_test.rb28
-rw-r--r--railties/test/application/rack/logger_test.rb6
-rw-r--r--railties/test/application/rake/dbs_test.rb238
-rw-r--r--railties/test/application/rake/dev_test.rb44
-rw-r--r--railties/test/application/rake/initializers_test.rb44
-rw-r--r--railties/test/application/rake/multi_dbs_test.rb145
-rw-r--r--railties/test/application/rake/notes_test.rb1
-rw-r--r--railties/test/application/rake/routes_test.rb59
-rw-r--r--railties/test/application/rake_test.rb16
-rw-r--r--railties/test/application/runner_test.rb8
-rw-r--r--railties/test/application/server_test.rb26
-rw-r--r--railties/test/application/test_runner_test.rb138
-rw-r--r--railties/test/application/test_test.rb2
-rw-r--r--railties/test/application/url_generation_test.rb2
-rw-r--r--railties/test/application/zeitwerk_integration_test.rb371
30 files changed, 1853 insertions, 456 deletions
diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb
index 3e0f31860b..7623e8e352 100644
--- a/railties/test/application/asset_debugging_test.rb
+++ b/railties/test/application/asset_debugging_test.rb
@@ -95,7 +95,7 @@ module ApplicationTests
end
end
- test "public url methods are not over-written by the asset pipeline" do
+ test "public URL methods are not over-written by the asset pipeline" do
contents = "doesnotexist"
cases = {
asset_url: %r{http://example.org/#{contents}},
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index 4ca6d02b85..a80581211b 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -68,20 +68,6 @@ module ApplicationTests
assert_equal 'a = "/assets/rails.png";', last_response.body.strip
end
- test "assets do not require compressors until it is used" do
- app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
- add_to_env_config "production", "config.assets.compile = true"
- add_to_env_config "production", "config.assets.precompile = []"
-
- # Load app env
- app "production"
-
- assert_not defined?(Uglifier)
- get "/assets/demo.js"
- assert_match "alert()", last_response.body
- assert defined?(Uglifier)
- end
-
test "precompile creates the file, gives it the original asset's content and run in production as default" do
app_file "app/assets/config/manifest.js", "//= link_tree ../javascripts"
app_file "app/assets/javascripts/application.js", "alert();"
@@ -443,13 +429,13 @@ module ApplicationTests
end
test "digested assets are not mistakenly removed" do
- app_file "app/assets/application.js", "alert();"
+ app_file "app/assets/application.css", "div { font-weight: bold }"
add_to_config "config.assets.compile = true"
precompile!
- files = Dir["#{app_path}/public/assets/application-*.js"]
- assert_equal 1, files.length, "Expected digested application.js asset to be generated, but none found"
+ files = Dir["#{app_path}/public/assets/application-*.css"]
+ assert_equal 1, files.length, "Expected digested application.css asset to be generated, but none found"
end
test "digested assets are removed from configured path" do
@@ -464,7 +450,7 @@ module ApplicationTests
assert_equal 0, files.length, "Expected application.js asset to be removed, but still exists"
end
- test "asset urls should use the request's protocol by default" do
+ test "asset URLs should use the request's protocol by default" do
app_with_assets_in_view
add_to_config "config.asset_host = 'example.com'"
add_to_env_config "development", "config.assets.digest = false"
@@ -480,7 +466,7 @@ module ApplicationTests
assert_match('src="https://example.com/assets/application.self.js', last_response.body)
end
- test "asset urls should be protocol-relative if no request is in scope" do
+ test "asset URLs should be protocol-relative if no request is in scope" do
app_file "app/assets/images/rails.png", "notreallyapng"
app_file "app/assets/javascripts/image_loader.js.erb", "var src='<%= image_path('rails.png') %>';"
add_to_config "config.assets.precompile = %w{rails.png image_loader.js}"
diff --git a/railties/test/application/bin_setup_test.rb b/railties/test/application/bin_setup_test.rb
index 54934dbe24..a952d2466b 100644
--- a/railties/test/application/bin_setup_test.rb
+++ b/railties/test/application/bin_setup_test.rb
@@ -43,18 +43,22 @@ module ApplicationTests
# Ignore line that's only output by Bundler < 1.14
output.sub!(/^Resolving dependencies\.\.\.\n/, "")
+ # Suppress Bundler platform warnings from output
+ output.gsub!(/^The dependency .* will be unused .*\.\n/, "")
+ # Ignore warnings such as `Psych.safe_load is deprecated`
+ output.gsub!(/^warning:\s.*\n/, "")
- assert_equal(<<-OUTPUT, output)
-== Installing dependencies ==
-The Gemfile's dependencies are satisfied
+ assert_equal(<<~OUTPUT, output)
+ == Installing dependencies ==
+ The Gemfile's dependencies are satisfied
-== Preparing database ==
-Created database 'db/development.sqlite3'
-Created database 'db/test.sqlite3'
+ == Preparing database ==
+ Created database 'db/development.sqlite3'
+ Created database 'db/test.sqlite3'
-== Removing old logs and tempfiles ==
+ == Removing old logs and tempfiles ==
-== Restarting application server ==
+ == Restarting application server ==
OUTPUT
end
end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index c2699006f6..b8e167b488 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -3,6 +3,7 @@
require "isolation/abstract_unit"
require "rack/test"
require "env_helpers"
+require "set"
class ::MyMailInterceptor
def self.delivering_email(email); email; end
@@ -123,6 +124,18 @@ module ApplicationTests
assert_equal "MyLogger", Rails.application.config.logger.class.name
end
+ test "raises an error if cache does not support recyclable cache keys" do
+ build_app(initializers: true)
+ add_to_env_config "production", "config.cache_store = Class.new {}.new"
+ add_to_env_config "production", "config.active_record.cache_versioning = true"
+
+ error = assert_raise(RuntimeError) do
+ app "production"
+ end
+
+ assert_match(/You're using a cache/, error.message)
+ end
+
test "a renders exception on pending migration" do
add_to_config <<-RUBY
config.active_record.migration_error = :page_load
@@ -297,6 +310,115 @@ module ApplicationTests
assert_equal %w(noop_email).to_set, PostsMailer.instance_variable_get(:@action_methods)
end
+ test "does not eager load attribute methods in development" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post < ActiveRecord::Base
+ end
+ RUBY
+
+ app_file "config/initializers/active_record.rb", <<-RUBY
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :posts do |t|
+ t.string :title
+ end
+ end
+ RUBY
+
+ app "development"
+
+ assert_not_includes Post.instance_methods, :title
+ end
+
+ test "does not eager load attribute methods in production when the schema cache is empty" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post < ActiveRecord::Base
+ end
+ RUBY
+
+ app_file "config/initializers/active_record.rb", <<-RUBY
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :posts do |t|
+ t.string :title
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ app "production"
+
+ assert_not_includes Post.instance_methods, :title
+ end
+
+ test "eager loads attribute methods in production when the schema cache is populated" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post < ActiveRecord::Base
+ end
+ RUBY
+
+ app_file "config/initializers/active_record.rb", <<-RUBY
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :posts do |t|
+ t.string :title
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ app_file "config/initializers/schema_cache.rb", <<-RUBY
+ ActiveRecord::Base.connection.schema_cache.add("posts")
+ RUBY
+
+ app "production"
+
+ assert_includes Post.instance_methods, :title
+ end
+
+ test "does not attempt to eager load attribute methods for models that aren't connected" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post < ActiveRecord::Base
+ end
+ RUBY
+
+ app_file "config/initializers/active_record.rb", <<-RUBY
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :posts do |t|
+ t.string :title
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ app_file "app/models/comment.rb", <<-RUBY
+ class Comment < ActiveRecord::Base
+ establish_connection(adapter: "mysql2", database: "does_not_exist")
+ end
+ RUBY
+
+ assert_nothing_raised do
+ app "production"
+ end
+ end
+
test "initialize an eager loaded, cache classes app" do
add_to_config <<-RUBY
config.eager_load = true
@@ -474,45 +596,30 @@ module ApplicationTests
assert_equal "some_value", verifier.verify(message)
end
- test "application message verifier can be used when the key_generator is ActiveSupport::LegacyKeyGenerator" do
+ test "application will generate secret_key_base in tmp file if blank in development" do
app_file "config/initializers/secret_token.rb", <<-RUBY
Rails.application.credentials.secret_key_base = nil
- Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
RUBY
- app "production"
+ # For test that works even if tmp dir does not exist.
+ Dir.chdir(app_path) { FileUtils.remove_dir("tmp") }
- assert_kind_of ActiveSupport::LegacyKeyGenerator, Rails.application.key_generator
- message = app.message_verifier(:sensitive_value).generate("some_value")
- assert_equal "some_value", Rails.application.message_verifier(:sensitive_value).verify(message)
+ app "development"
+
+ assert_not_nil app.secrets.secret_key_base
+ assert File.exist?(app_path("tmp/development_secret.txt"))
end
- test "config.secret_token is deprecated" do
+ test "application will not generate secret_key_base in tmp file if blank in production" do
app_file "config/initializers/secret_token.rb", <<-RUBY
- Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
+ Rails.application.credentials.secret_key_base = nil
RUBY
- app "production"
-
- assert_deprecated(/secret_token/) do
- app.secrets
- end
- end
-
- test "secrets.secret_token is deprecated" do
- app_file "config/secrets.yml", <<-YAML
- production:
- secret_token: "b3c631c314c0bbca50c1b2843150fe33"
- YAML
-
- app "production"
-
- assert_deprecated(/secret_token/) do
- app.secrets
+ assert_raises ArgumentError do
+ app "production"
end
end
-
test "raises when secret_key_base is blank" do
app_file "config/initializers/secret_token.rb", <<-RUBY
Rails.application.credentials.secret_key_base = nil
@@ -534,23 +641,8 @@ module ApplicationTests
end
end
- test "prefer secrets.secret_token over config.secret_token" do
- app_file "config/initializers/secret_token.rb", <<-RUBY
- Rails.application.config.secret_token = ""
- RUBY
- app_file "config/secrets.yml", <<-YAML
- development:
- secret_token: 3b7cd727ee24e8444053437c36cc66c3
- YAML
-
- app "development"
-
- assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secrets.secret_token
- end
-
test "application verifier can build different verifiers" do
make_basic_app do |application|
- application.credentials.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
application.config.session_store :disabled
end
@@ -589,22 +681,6 @@ module ApplicationTests
assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secrets.secret_key_base
end
- test "config.secret_token over-writes a blank secrets.secret_token" do
- app_file "config/initializers/secret_token.rb", <<-RUBY
- Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
- RUBY
- app_file "config/secrets.yml", <<-YAML
- development:
- secret_key_base:
- secret_token:
- YAML
-
- app "development"
-
- assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.secrets.secret_token
- assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.config.secret_token
- end
-
test "custom secrets saved in config/secrets.yml are loaded in app secrets" do
app_file "config/secrets.yml", <<-YAML
development:
@@ -667,19 +743,6 @@ module ApplicationTests
assert_equal "iaminallyoursecretkeybase", app.secrets.secret_key_base
end
- test "uses ActiveSupport::LegacyKeyGenerator as app.key_generator when secrets.secret_key_base is blank" do
- app_file "config/initializers/secret_token.rb", <<-RUBY
- Rails.application.credentials.secret_key_base = nil
- Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
- RUBY
-
- app "production"
-
- assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.config.secret_token
- assert_nil app.credentials.secret_key_base
- assert_kind_of ActiveSupport::LegacyKeyGenerator, app.key_generator
- end
-
test "that nested keys are symbolized the same as parents for hashes more than one level deep" do
app_file "config/secrets.yml", <<-YAML
development:
@@ -1117,6 +1180,38 @@ module ApplicationTests
end
end
+ test "autoloaders" do
+ app "development"
+
+ config = Rails.application.config
+ assert Rails.autoloaders.zeitwerk_enabled?
+ assert_instance_of Zeitwerk::Loader, Rails.autoloaders.main
+ assert_equal "rails.main", Rails.autoloaders.main.tag
+ assert_instance_of Zeitwerk::Loader, Rails.autoloaders.once
+ assert_equal "rails.once", Rails.autoloaders.once.tag
+ assert_equal [Rails.autoloaders.main, Rails.autoloaders.once], Rails.autoloaders.to_a
+ assert_equal ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector, Rails.autoloaders.main.inflector
+ assert_equal ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector, Rails.autoloaders.once.inflector
+
+ config.autoloader = :classic
+ assert_not Rails.autoloaders.zeitwerk_enabled?
+ assert_nil Rails.autoloaders.main
+ assert_nil Rails.autoloaders.once
+ assert_equal 0, Rails.autoloaders.count
+
+ config.autoloader = :zeitwerk
+ assert Rails.autoloaders.zeitwerk_enabled?
+ assert_instance_of Zeitwerk::Loader, Rails.autoloaders.main
+ assert_equal "rails.main", Rails.autoloaders.main.tag
+ assert_instance_of Zeitwerk::Loader, Rails.autoloaders.once
+ assert_equal "rails.once", Rails.autoloaders.once.tag
+ assert_equal [Rails.autoloaders.main, Rails.autoloaders.once], Rails.autoloaders.to_a
+ assert_equal ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector, Rails.autoloaders.main.inflector
+ assert_equal ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector, Rails.autoloaders.once.inflector
+
+ assert_raises(ArgumentError) { config.autoloader = :unknown }
+ end
+
test "config.action_view.cache_template_loading with cache_classes default" do
add_to_config "config.cache_classes = true"
@@ -1433,14 +1528,12 @@ module ApplicationTests
end
test "config.session_store with :active_record_store with activerecord-session_store gem" do
- begin
- make_basic_app do |application|
- ActionDispatch::Session::ActiveRecordStore = Class.new(ActionDispatch::Session::CookieStore)
- application.config.session_store :active_record_store
- end
- ensure
- ActionDispatch::Session.send :remove_const, :ActiveRecordStore
+ make_basic_app do |application|
+ ActionDispatch::Session::ActiveRecordStore = Class.new(ActionDispatch::Session::CookieStore)
+ application.config.session_store :active_record_store
end
+ ensure
+ ActionDispatch::Session.send :remove_const, :ActiveRecordStore
end
test "config.session_store with :active_record_store without activerecord-session_store gem" do
@@ -1603,6 +1696,14 @@ module ApplicationTests
assert_kind_of Hash, Rails.application.config.database_configuration
end
+ test "autoload paths do not include asset paths" do
+ app "development"
+ ActiveSupport::Dependencies.autoload_paths.each do |path|
+ assert_not_operator path, :ends_with?, "app/assets"
+ assert_not_operator path, :ends_with?, "app/javascript"
+ end
+ end
+
test "raises with proper error message if no database configuration found" do
FileUtils.rm("#{app_path}/config/database.yml")
err = assert_raises RuntimeError do
@@ -1668,10 +1769,10 @@ module ApplicationTests
assert_equal true, Rails.application.config.action_mailer.show_previews
end
- test "config_for loads custom configuration from yaml files" do
+ test "config_for loads custom configuration from yaml accessible as symbol or string" do
app_file "config/custom.yml", <<-RUBY
development:
- key: 'custom key'
+ foo: 'bar'
RUBY
add_to_config <<-RUBY
@@ -1680,7 +1781,171 @@ module ApplicationTests
app "development"
- assert_equal "custom key", Rails.application.config.my_custom_config["key"]
+ assert_equal "bar", Rails.application.config.my_custom_config[:foo]
+ assert_equal "bar", Rails.application.config.my_custom_config["foo"]
+ end
+
+ test "config_for loads nested custom configuration from yaml as symbol keys" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ foo:
+ bar:
+ baz: 1
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "development"
+
+ assert_equal 1, Rails.application.config.my_custom_config[:foo][:bar][:baz]
+ end
+
+ test "config_for loads nested custom configuration from yaml with deprecated non-symbol access" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ foo:
+ bar:
+ baz: 1
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "development"
+
+ assert_deprecated do
+ assert_equal 1, Rails.application.config.my_custom_config["foo"]["bar"]["baz"]
+ end
+ end
+
+ test "config_for loads nested custom configuration inside array from yaml with deprecated non-symbol access" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ foo:
+ bar:
+ - baz: 1
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "development"
+
+ config = Rails.application.config.my_custom_config
+ assert_instance_of Rails::Application::NonSymbolAccessDeprecatedHash, config[:foo][:bar].first
+
+ assert_deprecated do
+ assert_equal 1, config[:foo][:bar].first["baz"]
+ end
+ end
+
+ test "config_for makes all hash methods available" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ foo: 0
+ bar:
+ baz: 1
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "development"
+
+ actual = Rails.application.config.my_custom_config
+
+ assert_equal({ foo: 0, bar: { baz: 1 } }, actual)
+ assert_equal([ :foo, :bar ], actual.keys)
+ assert_equal([ 0, baz: 1], actual.values)
+ assert_equal({ foo: 0, bar: { baz: 1 } }, actual.to_h)
+ assert_equal(0, actual[:foo])
+ assert_equal({ baz: 1 }, actual[:bar])
+ end
+
+ test "config_for generates deprecation notice when nested hash methods are called with non-symbols" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ foo:
+ bar: 1
+ baz: 2
+ qux:
+ boo: 3
+ RUBY
+
+ app "development"
+
+ actual = Rails.application.config_for("custom")[:foo]
+
+ # slice
+ assert_deprecated do
+ assert_equal({ bar: 1, baz: 2 }, actual.slice("bar", "baz"))
+ end
+
+ # except
+ assert_deprecated do
+ assert_equal({ qux: { boo: 3 } }, actual.except("bar", "baz"))
+ end
+
+ # dig
+ assert_deprecated do
+ assert_equal(3, actual.dig("qux", "boo"))
+ end
+
+ # fetch - hit
+ assert_deprecated do
+ assert_equal(1, actual.fetch("bar", 0))
+ end
+
+ # fetch - miss
+ assert_deprecated do
+ assert_equal(0, actual.fetch("does-not-exist", 0))
+ end
+
+ # fetch_values
+ assert_deprecated do
+ assert_equal([1, 2], actual.fetch_values("bar", "baz"))
+ end
+
+ # key? - hit
+ assert_deprecated do
+ assert(actual.key?("bar"))
+ end
+
+ # key? - miss
+ assert_deprecated do
+ assert_not(actual.key?("does-not-exist"))
+ end
+
+ # slice!
+ actual = Rails.application.config_for("custom")[:foo]
+
+ assert_deprecated do
+ slice = actual.slice!("bar", "baz")
+ assert_equal({ bar: 1, baz: 2 }, actual)
+ assert_equal({ qux: { boo: 3 } }, slice)
+ end
+
+ # extract!
+ actual = Rails.application.config_for("custom")[:foo]
+
+ assert_deprecated do
+ extracted = actual.extract!("bar", "baz")
+ assert_equal({ bar: 1, baz: 2 }, extracted)
+ assert_equal({ qux: { boo: 3 } }, actual)
+ end
+
+ # except!
+ actual = Rails.application.config_for("custom")[:foo]
+
+ assert_deprecated do
+ actual.except!("bar", "baz")
+ assert_equal({ qux: { boo: 3 } }, actual)
+ end
end
test "config_for uses the Pathname object if it is provided" do
@@ -1695,7 +1960,7 @@ module ApplicationTests
app "development"
- assert_equal "custom key", Rails.application.config.my_custom_config["key"]
+ assert_equal "custom key", Rails.application.config.my_custom_config[:key]
end
test "config_for raises an exception if the file does not exist" do
@@ -1725,8 +1990,29 @@ module ApplicationTests
assert_equal({}, Rails.application.config.my_custom_config)
end
- test "config_for with empty file returns an empty hash" do
+ test "config_for implements shared configuration as secrets case found" do
app_file "config/custom.yml", <<-RUBY
+ shared:
+ foo: :bar
+ test:
+ foo: :baz
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "test"
+
+ assert_equal(:baz, Rails.application.config.my_custom_config[:foo])
+ end
+
+ test "config_for implements shared configuration as secrets case not found" do
+ app_file "config/custom.yml", <<-RUBY
+ shared:
+ foo: :bar
+ test:
+ foo: :baz
RUBY
add_to_config <<-RUBY
@@ -1735,40 +2021,45 @@ module ApplicationTests
app "development"
- assert_equal({}, Rails.application.config.my_custom_config)
+ assert_equal(:bar, Rails.application.config.my_custom_config[:foo])
end
- test "default SQLite3Adapter.represent_boolean_as_integer for 5.1 is false" do
- remove_from_config '.*config\.load_defaults.*\n'
+ test "config_for with empty file returns an empty hash" do
+ app_file "config/custom.yml", <<-RUBY
+ RUBY
- app_file "app/models/post.rb", <<-RUBY
- class Post < ActiveRecord::Base
- end
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
RUBY
app "development"
- force_lazy_load_hooks { Post }
- assert_not ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer
+ assert_equal({}, Rails.application.config.my_custom_config)
end
- test "default SQLite3Adapter.represent_boolean_as_integer for new installs is true" do
+ test "represent_boolean_as_integer is deprecated" do
+ remove_from_config '.*config\.load_defaults.*\n'
+
+ app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY
+ Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
+ RUBY
+
app_file "app/models/post.rb", <<-RUBY
class Post < ActiveRecord::Base
end
RUBY
app "development"
- force_lazy_load_hooks { Post }
-
- assert ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer
+ assert_deprecated do
+ force_lazy_load_hooks { Post }
+ end
end
- test "represent_boolean_as_integer should be able to set via config.active_record.sqlite3.represent_boolean_as_integer" do
+ test "represent_boolean_as_integer raises when the value is false" do
remove_from_config '.*config\.load_defaults.*\n'
app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY
- Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
+ Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = false
RUBY
app_file "app/models/post.rb", <<-RUBY
@@ -1777,9 +2068,9 @@ module ApplicationTests
RUBY
app "development"
- force_lazy_load_hooks { Post }
-
- assert ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer
+ assert_raises(RuntimeError) do
+ force_lazy_load_hooks { Post }
+ end
end
test "config_for containing ERB tags should evaluate" do
@@ -1794,7 +2085,7 @@ module ApplicationTests
app "development"
- assert_equal "custom key", Rails.application.config.my_custom_config["key"]
+ assert_equal "custom key", Rails.application.config.my_custom_config[:key]
end
test "config_for with syntax error show a more descriptive exception" do
@@ -1827,7 +2118,7 @@ module ApplicationTests
RUBY
require "#{app_path}/config/environment"
- assert_equal "unicorn", Rails.application.config.my_custom_config["key"]
+ assert_equal "unicorn", Rails.application.config.my_custom_config[:key]
end
test "api_only is false by default" do
@@ -1981,7 +2272,9 @@ module ApplicationTests
test "ActionView::Template.finalize_compiled_template_methods is true by default" do
app "test"
- assert_equal true, ActionView::Template.finalize_compiled_template_methods
+ assert_deprecated do
+ ActionView::Template.finalize_compiled_template_methods
+ end
end
test "ActionView::Template.finalize_compiled_template_methods can be configured via config.action_view.finalize_compiled_template_methods" do
@@ -1993,7 +2286,210 @@ module ApplicationTests
app "test"
- assert_equal false, ActionView::Template.finalize_compiled_template_methods
+ assert_deprecated do
+ ActionView::Template.finalize_compiled_template_methods
+ end
+ end
+
+ test "ActiveJob::Base.return_false_on_aborted_enqueue is true by default" do
+ app "development"
+
+ assert_equal true, ActiveJob::Base.return_false_on_aborted_enqueue
+ end
+
+ test "ActiveJob::Base.return_false_on_aborted_enqueue is false in the 5.x defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+ add_to_config 'config.load_defaults "5.2"'
+
+ app "development"
+
+ assert_equal false, ActiveJob::Base.return_false_on_aborted_enqueue
+ end
+
+ test "ActiveJob::Base.return_false_on_aborted_enqueue can be configured in the new framework defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+
+ app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY
+ Rails.application.config.active_job.return_false_on_aborted_enqueue = true
+ RUBY
+
+ app "development"
+
+ assert_equal true, ActiveJob::Base.return_false_on_aborted_enqueue
+ end
+
+ test "ActiveStorage.queues[:analysis] is :active_storage_analysis by default" do
+ app "development"
+
+ assert_equal :active_storage_analysis, ActiveStorage.queues[:analysis]
+ end
+
+ test "ActiveStorage.queues[:analysis] is nil without Rails 6 defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+
+ app "development"
+
+ assert_nil ActiveStorage.queues[:analysis]
+ end
+
+ test "ActiveStorage.queues[:purge] is :active_storage_purge by default" do
+ app "development"
+
+ assert_equal :active_storage_purge, ActiveStorage.queues[:purge]
+ end
+
+ test "ActiveStorage.queues[:purge] is nil without Rails 6 defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+
+ app "development"
+
+ assert_nil ActiveStorage.queues[:purge]
+ end
+
+ test "ActionMailbox.logger is Rails.logger by default" do
+ app "development"
+
+ assert_equal Rails.logger, ActionMailbox.logger
+ end
+
+ test "ActionMailbox.logger can be configured" do
+ app_file "lib/my_logger.rb", <<-RUBY
+ require "logger"
+ class MyLogger < ::Logger
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ require "my_logger"
+ config.action_mailbox.logger = MyLogger.new(STDOUT)
+ RUBY
+
+ app "development"
+
+ assert_equal "MyLogger", ActionMailbox.logger.class.name
+ end
+
+ test "ActionMailbox.incinerate_after is 30.days by default" do
+ app "development"
+
+ assert_equal 30.days, ActionMailbox.incinerate_after
+ end
+
+ test "ActionMailbox.incinerate_after can be configured" do
+ add_to_config <<-RUBY
+ config.action_mailbox.incinerate_after = 14.days
+ RUBY
+
+ app "development"
+
+ assert_equal 14.days, ActionMailbox.incinerate_after
+ end
+
+ test "ActionMailbox.queues[:incineration] is :action_mailbox_incineration by default" do
+ app "development"
+
+ assert_equal :action_mailbox_incineration, ActionMailbox.queues[:incineration]
+ end
+
+ test "ActionMailbox.queues[:incineration] can be configured" do
+ add_to_config <<-RUBY
+ config.action_mailbox.queues.incineration = :another_queue
+ RUBY
+
+ app "development"
+
+ assert_equal :another_queue, ActionMailbox.queues[:incineration]
+ end
+
+ test "ActionMailbox.queues[:routing] is :action_mailbox_routing by default" do
+ app "development"
+
+ assert_equal :action_mailbox_routing, ActionMailbox.queues[:routing]
+ end
+
+ test "ActionMailbox.queues[:routing] can be configured" do
+ add_to_config <<-RUBY
+ config.action_mailbox.queues.routing = :another_queue
+ RUBY
+
+ app "development"
+
+ assert_equal :another_queue, ActionMailbox.queues[:routing]
+ end
+
+ test "ActionMailer::Base.delivery_job is ActionMailer::MailDeliveryJob by default" do
+ app "development"
+
+ assert_equal ActionMailer::MailDeliveryJob, ActionMailer::Base.delivery_job
+ end
+
+ test "ActionMailer::Base.delivery_job is ActionMailer::DeliveryJob in the 5.x defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+ add_to_config 'config.load_defaults "5.2"'
+
+ app "development"
+
+ assert_equal ActionMailer::DeliveryJob, ActionMailer::Base.delivery_job
+ end
+
+ test "ActionMailer::Base.delivery_job can be configured in the new framework defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+
+ app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY
+ Rails.application.config.action_mailer.delivery_job = "ActionMailer::MailDeliveryJob"
+ RUBY
+
+ app "development"
+
+ assert_equal ActionMailer::MailDeliveryJob, ActionMailer::Base.delivery_job
+ end
+
+ test "ActiveRecord::Base.filter_attributes should equal to filter_parameters" do
+ app_file "config/initializers/filter_parameters_logging.rb", <<-RUBY
+ Rails.application.config.filter_parameters += [ :password, :credit_card_number ]
+ RUBY
+ app "development"
+ assert_equal [ :password, :credit_card_number ], Rails.application.config.filter_parameters
+ assert_equal [ :password, :credit_card_number ], ActiveRecord::Base.filter_attributes
+ end
+
+ test "ActiveStorage.routes_prefix can be configured via config.active_storage.routes_prefix" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.active_storage.routes_prefix = '/files'
+ end
+ RUBY
+
+ output = rails("routes", "-g", "active_storage")
+ assert_equal <<~MESSAGE, output
+ Prefix Verb URI Pattern Controller#Action
+ rails_service_blob GET /files/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
+ rails_blob_representation GET /files/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
+ rails_disk_service GET /files/disk/:encoded_key/*filename(.:format) active_storage/disk#show
+ update_rails_disk_service PUT /files/disk/:encoded_token(.:format) active_storage/disk#update
+ rails_direct_uploads POST /files/direct_uploads(.:format) active_storage/direct_uploads#create
+ MESSAGE
+ end
+
+ test "hosts include .localhost in development" do
+ app "development"
+ assert_includes Rails.application.config.hosts, ".localhost"
+ end
+
+ test "disable_sandbox is false by default" do
+ app "development"
+
+ assert_equal false, Rails.configuration.disable_sandbox
+ end
+
+ test "disable_sandbox can be overridden" do
+ add_to_config <<-RUBY
+ config.disable_sandbox = true
+ RUBY
+
+ app "development"
+
+ assert Rails.configuration.disable_sandbox
end
private
diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb
index 4a14042cd3..db16f4cc56 100644
--- a/railties/test/application/console_test.rb
+++ b/railties/test/application/console_test.rb
@@ -25,7 +25,7 @@ class ConsoleTest < ActiveSupport::TestCase
end
def test_app_method_should_return_integration_session
- TestHelpers::Rack.send :remove_method, :app
+ TestHelpers::Rack.remove_method :app
load_environment
console_session = irb_context.app
assert_instance_of ActionDispatch::Integration::Session, console_session
@@ -109,7 +109,7 @@ class FullStackConsoleTest < ActiveSupport::TestCase
CODE
system "#{app_path}/bin/rails runner 'Post.connection.create_table :posts'"
- @master, @slave = PTY.open
+ @primary, @replica = PTY.open
end
def teardown
@@ -117,19 +117,23 @@ class FullStackConsoleTest < ActiveSupport::TestCase
end
def write_prompt(command, expected_output = nil)
- @master.puts command
- assert_output command, @master
- assert_output expected_output, @master if expected_output
- assert_output "> ", @master
+ @primary.puts command
+ assert_output command, @primary
+ assert_output expected_output, @primary if expected_output
+ assert_output "> ", @primary
end
- def spawn_console(options)
- Process.spawn(
+ def spawn_console(options, wait_for_prompt: true)
+ pid = Process.spawn(
"#{app_path}/bin/rails console #{options}",
- in: @slave, out: @slave, err: @slave
+ in: @replica, out: @replica, err: @replica
)
- assert_output "> ", @master, 30
+ if wait_for_prompt
+ assert_output "> ", @primary, 30
+ end
+
+ pid
end
def test_sandbox
@@ -138,21 +142,32 @@ class FullStackConsoleTest < ActiveSupport::TestCase
write_prompt "Post.count", "=> 0"
write_prompt "Post.create"
write_prompt "Post.count", "=> 1"
- @master.puts "quit"
+ @primary.puts "quit"
spawn_console("--sandbox")
write_prompt "Post.count", "=> 0"
write_prompt "Post.transaction { Post.create; raise }"
write_prompt "Post.count", "=> 0"
- @master.puts "quit"
+ @primary.puts "quit"
+ end
+
+ def test_sandbox_when_sandbox_is_disabled
+ add_to_config <<-RUBY
+ config.disable_sandbox = true
+ RUBY
+
+ output = `#{app_path}/bin/rails console --sandbox`
+
+ assert_includes output, "sandbox mode is disabled"
+ assert_equal 1, $?.exitstatus
end
def test_environment_option_and_irb_option
- spawn_console("test -- --verbose")
+ spawn_console("-e test -- --verbose")
write_prompt "a = 1", "a = 1"
write_prompt "puts Rails.env", "puts Rails.env\r\ntest"
- @master.puts "quit"
+ @primary.puts "quit"
end
end
diff --git a/railties/test/application/credentials_test.rb b/railties/test/application/credentials_test.rb
new file mode 100644
index 0000000000..2f6b109b50
--- /dev/null
+++ b/railties/test/application/credentials_test.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+require "env_helpers"
+
+class Rails::CredentialsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation, EnvHelpers
+
+ setup :build_app
+ teardown :teardown_app
+
+ test "reads credentials from environment specific path" do
+ write_credentials_override(:production)
+
+ app("production")
+
+ assert_equal "revealed", Rails.application.credentials.mystery
+ end
+
+ test "reads credentials from customized path and key" do
+ write_credentials_override(:staging)
+ add_to_env_config("production", "config.credentials.content_path = config.root.join('config/credentials/staging.yml.enc')")
+ add_to_env_config("production", "config.credentials.key_path = config.root.join('config/credentials/staging.key')")
+
+ app("production")
+
+ assert_equal "revealed", Rails.application.credentials.mystery
+ end
+
+ test "reads credentials using environment variable key" do
+ write_credentials_override(:production, with_key: false)
+
+ switch_env("RAILS_MASTER_KEY", credentials_key) do
+ app("production")
+
+ assert_equal "revealed", Rails.application.credentials.mystery
+ end
+ end
+
+ private
+ def write_credentials_override(name, with_key: true)
+ Dir.chdir(app_path) do
+ Dir.mkdir "config/credentials"
+ File.write "config/credentials/#{name}.key", credentials_key if with_key
+
+ # secret_key_base: secret
+ # mystery: revealed
+ File.write "config/credentials/#{name}.yml.enc",
+ "vgvKu4MBepIgZ5VHQMMPwnQNsLlWD9LKmJHu3UA/8yj6x+3fNhz3DwL9brX7UA==--qLdxHP6e34xeTAiI--nrcAsleXuo9NqiEuhntAhw=="
+ end
+ end
+
+ def credentials_key
+ "2117e775dc2024d4f49ddf3aeb585919"
+ end
+end
diff --git a/railties/test/application/dbconsole_test.rb b/railties/test/application/dbconsole_test.rb
index 8eb293c179..8c03fe4ac6 100644
--- a/railties/test/application/dbconsole_test.rb
+++ b/railties/test/application/dbconsole_test.rb
@@ -33,11 +33,11 @@ module ApplicationTests
end
RUBY
- master, slave = PTY.open
- spawn_dbconsole(slave)
- assert_output("sqlite>", master)
+ primary, replica = PTY.open
+ spawn_dbconsole(replica)
+ assert_output("sqlite>", primary)
ensure
- master.puts ".exit"
+ primary.puts ".exit"
end
def test_respect_environment_option
@@ -56,14 +56,14 @@ module ApplicationTests
database: db/production.sqlite3
YAML
- master, slave = PTY.open
- spawn_dbconsole(slave, "-e production")
- assert_output("sqlite>", master)
+ primary, replica = PTY.open
+ spawn_dbconsole(replica, "-e production")
+ assert_output("sqlite>", primary)
- master.puts "pragma database_list;"
- assert_output("production.sqlite3", master)
+ primary.puts "pragma database_list;"
+ assert_output("production.sqlite3", primary)
ensure
- master.puts ".exit"
+ primary.puts ".exit"
end
private
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 1530ea82d6..a35247fc43 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -39,7 +39,7 @@ module ApplicationTests
assert_equal expanded_path, ActionMailer::Base.view_paths[0].to_s
end
- test "allows me to configure default url options for ActionMailer" do
+ test "allows me to configure default URL options for ActionMailer" do
app_file "config/environments/development.rb", <<-RUBY
Rails.application.configure do
config.action_mailer.default_url_options = { :host => "test.rails" }
@@ -61,7 +61,7 @@ module ApplicationTests
assert_equal "https", ActionMailer::Base.default_url_options[:protocol]
end
- test "includes url helpers as action methods" do
+ test "includes URL helpers as action methods" do
app_file "config/routes.rb", <<-RUBY
Rails.application.routes.draw do
get "/foo", :to => lambda { |env| [200, {}, []] }, :as => :foo
@@ -230,35 +230,31 @@ module ApplicationTests
end
test "active record establish_connection uses Rails.env if DATABASE_URL is not set" do
- begin
- require "#{app_path}/config/environment"
- orig_database_url = ENV.delete("DATABASE_URL")
- orig_rails_env, Rails.env = Rails.env, "development"
- ActiveRecord::Base.establish_connection
- assert ActiveRecord::Base.connection
- assert_match(/#{ActiveRecord::Base.configurations[Rails.env]['database']}/, ActiveRecord::Base.connection_config[:database])
- ensure
- ActiveRecord::Base.remove_connection
- ENV["DATABASE_URL"] = orig_database_url if orig_database_url
- Rails.env = orig_rails_env if orig_rails_env
- end
+ require "#{app_path}/config/environment"
+ orig_database_url = ENV.delete("DATABASE_URL")
+ orig_rails_env, Rails.env = Rails.env, "development"
+ ActiveRecord::Base.establish_connection
+ assert ActiveRecord::Base.connection
+ assert_match(/#{ActiveRecord::Base.configurations[Rails.env]['database']}/, ActiveRecord::Base.connection_config[:database])
+ ensure
+ ActiveRecord::Base.remove_connection
+ ENV["DATABASE_URL"] = orig_database_url if orig_database_url
+ Rails.env = orig_rails_env if orig_rails_env
end
test "active record establish_connection uses DATABASE_URL even if Rails.env is set" do
- begin
- require "#{app_path}/config/environment"
- orig_database_url = ENV.delete("DATABASE_URL")
- orig_rails_env, Rails.env = Rails.env, "development"
- database_url_db_name = "db/database_url_db.sqlite3"
- ENV["DATABASE_URL"] = "sqlite3:#{database_url_db_name}"
- ActiveRecord::Base.establish_connection
- assert ActiveRecord::Base.connection
- assert_match(/#{database_url_db_name}/, ActiveRecord::Base.connection_config[:database])
- ensure
- ActiveRecord::Base.remove_connection
- ENV["DATABASE_URL"] = orig_database_url if orig_database_url
- Rails.env = orig_rails_env if orig_rails_env
- end
+ require "#{app_path}/config/environment"
+ orig_database_url = ENV.delete("DATABASE_URL")
+ orig_rails_env, Rails.env = Rails.env, "development"
+ database_url_db_name = "db/database_url_db.sqlite3"
+ ENV["DATABASE_URL"] = "sqlite3:#{database_url_db_name}"
+ ActiveRecord::Base.establish_connection
+ assert ActiveRecord::Base.connection
+ assert_match(/#{database_url_db_name}/, ActiveRecord::Base.connection_config[:database])
+ ensure
+ ActiveRecord::Base.remove_connection
+ ENV["DATABASE_URL"] = orig_database_url if orig_database_url
+ Rails.env = orig_rails_env if orig_rails_env
end
test "connections checked out during initialization are returned to the pool" do
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
index 889ad16fb8..9c98489590 100644
--- a/railties/test/application/loading_test.rb
+++ b/railties/test/application/loading_test.rb
@@ -34,6 +34,22 @@ class LoadingTest < ActiveSupport::TestCase
assert_equal "omg", p.title
end
+ test "constants without a matching file raise NameError" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post
+ NON_EXISTING_CONSTANT
+ end
+ RUBY
+
+ boot_app
+
+ e = assert_raise(NameError) { User }
+ assert_equal "uninitialized constant #{self.class}::User", e.message
+
+ e = assert_raise(NameError) { Post }
+ assert_equal "uninitialized constant Post::NON_EXISTING_CONSTANT", e.message
+ end
+
test "concerns in app are autoloaded" do
app_file "app/controllers/concerns/trackable.rb", <<-CONCERN
module Trackable
@@ -371,6 +387,72 @@ class LoadingTest < ActiveSupport::TestCase
end
end
+ test "active record query cache hooks are installed before first request in production" do
+ app_file "app/controllers/omg_controller.rb", <<-RUBY
+ begin
+ class OmgController < ActionController::Metal
+ ActiveSupport.run_load_hooks(:action_controller, self)
+ def show
+ if ActiveRecord::Base.connection.query_cache_enabled
+ self.response_body = ["Query cache is enabled."]
+ else
+ self.response_body = ["Expected ActiveRecord::Base.connection.query_cache_enabled to be true"]
+ end
+ end
+ end
+ rescue => e
+ puts "Error loading metal: \#{e.class} \#{e.message}"
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/:controller(/:action)"
+ end
+ RUBY
+
+ boot_app "production"
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ get "/omg/show"
+ assert_equal "Query cache is enabled.", last_response.body
+ end
+
+ test "active record query cache hooks are installed before first request in development" do
+ app_file "app/controllers/omg_controller.rb", <<-RUBY
+ begin
+ class OmgController < ActionController::Metal
+ ActiveSupport.run_load_hooks(:action_controller, self)
+ def show
+ if ActiveRecord::Base.connection.query_cache_enabled
+ self.response_body = ["Query cache is enabled."]
+ else
+ self.response_body = ["Expected ActiveRecord::Base.connection.query_cache_enabled to be true"]
+ end
+ end
+ end
+ rescue => e
+ puts "Error loading metal: \#{e.class} \#{e.message}"
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/:controller(/:action)"
+ end
+ RUBY
+
+ boot_app "development"
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ get "/omg/show"
+ assert_equal "Query cache is enabled.", last_response.body
+ end
+
private
def setup_ar!
@@ -382,4 +464,12 @@ class LoadingTest < ActiveSupport::TestCase
end
end
end
+
+ def boot_app(env = "development")
+ ENV["RAILS_ENV"] = env
+
+ require "#{app_path}/config/environment"
+ ensure
+ ENV.delete "RAILS_ENV"
+ end
end
diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb
index ba186bda44..fb84276b8a 100644
--- a/railties/test/application/mailer_previews_test.rb
+++ b/railties/test/application/mailer_previews_test.rb
@@ -85,6 +85,7 @@ module ApplicationTests
end
test "mailer previews are loaded from a custom preview_path" do
+ app_dir "lib/mailer_previews"
add_to_config "config.action_mailer.preview_path = '#{app_path}/lib/mailer_previews'"
mailer "notifier", <<-RUBY
@@ -254,6 +255,7 @@ module ApplicationTests
end
test "mailer previews are reloaded from a custom preview_path" do
+ app_dir "lib/mailer_previews"
add_to_config "config.action_mailer.preview_path = '#{app_path}/lib/mailer_previews'"
app("development")
@@ -818,6 +820,7 @@ module ApplicationTests
def build_app
super
app_file "config/routes.rb", "Rails.application.routes.draw do; end"
+ app_dir "test/mailers/previews"
end
def mailer(name, contents)
diff --git a/railties/test/application/middleware/cookies_test.rb b/railties/test/application/middleware/cookies_test.rb
index ecb4ee3446..fe48ef3f03 100644
--- a/railties/test/application/middleware/cookies_test.rb
+++ b/railties/test/application/middleware/cookies_test.rb
@@ -110,14 +110,14 @@ module ApplicationTests
assert_equal "signed cookie".inspect, last_response.body
get "/foo/read_raw_cookie"
- assert_equal "signed cookie", verifier_sha512.verify(last_response.body)
+ assert_equal "signed cookie", verifier_sha512.verify(last_response.body, purpose: "cookie.signed_cookie")
get "/foo/write_raw_cookie_sha256"
get "/foo/read_signed"
assert_equal "signed cookie".inspect, last_response.body
get "/foo/read_raw_cookie"
- assert_equal "signed cookie", verifier_sha512.verify(last_response.body)
+ assert_equal "signed cookie", verifier_sha512.verify(last_response.body, purpose: "cookie.signed_cookie")
end
test "encrypted cookies rotating multiple encryption keys" do
@@ -180,14 +180,14 @@ module ApplicationTests
assert_equal "encrypted cookie".inspect, last_response.body
get "/foo/read_raw_cookie"
- assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body)
+ assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body, purpose: "cookie.encrypted_cookie")
- get "/foo/write_raw_cookie_sha256"
+ get "/foo/write_raw_cookie_two"
get "/foo/read_encrypted"
assert_equal "encrypted cookie".inspect, last_response.body
get "/foo/read_raw_cookie"
- assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body)
+ assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body, purpose: "cookie.encrypted_cookie")
end
end
end
diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb
index 2d659ade8d..17df78ed4e 100644
--- a/railties/test/application/middleware/exceptions_test.rb
+++ b/railties/test/application/middleware/exceptions_test.rb
@@ -60,7 +60,7 @@ module ApplicationTests
assert_equal "YOU FAILED", last_response.body
end
- test "url generation error when action_dispatch.show_exceptions is set raises an exception" do
+ test "URL generation error when action_dispatch.show_exceptions is set raises an exception" do
controller :foo, <<-RUBY
class FooController < ActionController::Base
def index
diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb
index 83cf8a27f7..515b32080e 100644
--- a/railties/test/application/middleware/remote_ip_test.rb
+++ b/railties/test/application/middleware/remote_ip_test.rb
@@ -12,7 +12,9 @@ module ApplicationTests
remote_ip = nil
env = Rack::MockRequest.env_for("/").merge(env).merge!(
"action_dispatch.show_exceptions" => false,
- "action_dispatch.key_generator" => ActiveSupport::LegacyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33")
+ "action_dispatch.key_generator" => ActiveSupport::CachingKeyGenerator.new(
+ ActiveSupport::KeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33", iterations: 1000)
+ )
)
endpoint = Proc.new do |e|
diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb
index 9182a63ab7..479615c133 100644
--- a/railties/test/application/middleware/session_test.rb
+++ b/railties/test/application/middleware/session_test.rb
@@ -183,7 +183,7 @@ module ApplicationTests
encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher)
get "/foo/read_raw_cookie"
- assert_equal 1, encryptor.decrypt_and_verify(last_response.body)["foo"]
+ assert_equal 1, encryptor.decrypt_and_verify(last_response.body, purpose: "cookie._myapp_session")["foo"]
end
test "session upgrading signature to encryption cookie store works the same way as encrypted cookie store" do
@@ -215,8 +215,6 @@ module ApplicationTests
RUBY
add_to_config <<-RUBY
- secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
-
# Enable AEAD cookies
config.action_dispatch.use_authenticated_cookie_encryption = true
RUBY
@@ -235,69 +233,7 @@ module ApplicationTests
encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher)
get "/foo/read_raw_cookie"
- assert_equal 1, encryptor.decrypt_and_verify(last_response.body)["foo"]
- end
-
- test "session upgrading signature to encryption cookie store upgrades session to encrypted mode" do
- app_file "config/routes.rb", <<-RUBY
- Rails.application.routes.draw do
- get ':controller(/:action)'
- end
- RUBY
-
- controller :foo, <<-RUBY
- class FooController < ActionController::Base
- def write_raw_session
- # {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1}
- cookies[:_myapp_session] = "BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJTE5NjVkOTU3MjBmZmZjMTIzOTQxYmRmYjdkMmU2ODcwBjsAVEkiCGZvbwY7AEZpBg==--315fb9931921a87ae7421aec96382f0294119749"
- head :ok
- end
-
- def write_session
- session[:foo] = session[:foo] + 1
- head :ok
- end
-
- def read_session
- render plain: session[:foo]
- end
-
- def read_encrypted_cookie
- render plain: cookies.encrypted[:_myapp_session]['foo']
- end
-
- def read_raw_cookie
- render plain: cookies[:_myapp_session]
- end
- end
- RUBY
-
- add_to_config <<-RUBY
- secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
-
- # Enable AEAD cookies
- config.action_dispatch.use_authenticated_cookie_encryption = true
- RUBY
-
- require "#{app_path}/config/environment"
-
- get "/foo/write_raw_session"
- get "/foo/read_session"
- assert_equal "1", last_response.body
-
- get "/foo/write_session"
- get "/foo/read_session"
- assert_equal "2", last_response.body
-
- get "/foo/read_encrypted_cookie"
- assert_equal "2", last_response.body
-
- cipher = "aes-256-gcm"
- secret = app.key_generator.generate_key("authenticated encrypted cookie")
- encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher)
-
- get "/foo/read_raw_cookie"
- assert_equal 2, encryptor.decrypt_and_verify(last_response.body)["foo"]
+ assert_equal 1, encryptor.decrypt_and_verify(last_response.body, purpose: "cookie._myapp_session")["foo"]
end
test "session upgrading from AES-CBC-HMAC encryption to AES-GCM encryption" do
@@ -364,71 +300,7 @@ module ApplicationTests
encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher)
get "/foo/read_raw_cookie"
- assert_equal 2, encryptor.decrypt_and_verify(last_response.body)["foo"]
- ensure
- ENV["RAILS_ENV"] = old_rails_env
- end
- end
-
- test "session upgrading legacy signed cookies to new signed cookies" do
- app_file "config/routes.rb", <<-RUBY
- Rails.application.routes.draw do
- get ':controller(/:action)'
- end
- RUBY
-
- controller :foo, <<-RUBY
- class FooController < ActionController::Base
- def write_raw_session
- # {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1}
- cookies[:_myapp_session] = "BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJTE5NjVkOTU3MjBmZmZjMTIzOTQxYmRmYjdkMmU2ODcwBjsAVEkiCGZvbwY7AEZpBg==--315fb9931921a87ae7421aec96382f0294119749"
- head :ok
- end
-
- def write_session
- session[:foo] = session[:foo] + 1
- head :ok
- end
-
- def read_session
- render plain: session[:foo]
- end
-
- def read_signed_cookie
- render plain: cookies.signed[:_myapp_session]['foo']
- end
-
- def read_raw_cookie
- render plain: cookies[:_myapp_session]
- end
- end
- RUBY
-
- add_to_config <<-RUBY
- secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
- Rails.application.credentials.secret_key_base = nil
- RUBY
-
- begin
- old_rails_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "production"
-
- require "#{app_path}/config/environment"
-
- get "/foo/write_raw_session"
- get "/foo/read_session"
- assert_equal "1", last_response.body
-
- get "/foo/write_session"
- get "/foo/read_session"
- assert_equal "2", last_response.body
-
- get "/foo/read_signed_cookie"
- assert_equal "2", last_response.body
-
- verifier = ActiveSupport::MessageVerifier.new(app.secrets.secret_token)
-
- get "/foo/read_raw_cookie"
- assert_equal 2, verifier.verify(last_response.body)["foo"]
+ assert_equal 2, encryptor.decrypt_and_verify(last_response.body, purpose: "cookie._myapp_session")["foo"]
ensure
ENV["RAILS_ENV"] = old_rails_env
end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 5efaf841d4..4242daf39a 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -25,6 +25,8 @@ module ApplicationTests
boot!
assert_equal [
+ "Webpacker::DevServerProxy",
+ "ActionDispatch::HostAuthorization",
"Rack::Sendfile",
"ActionDispatch::Static",
"ActionDispatch::Executor",
@@ -56,6 +58,8 @@ module ApplicationTests
boot!
assert_equal [
+ "Webpacker::DevServerProxy",
+ "ActionDispatch::HostAuthorization",
"Rack::Sendfile",
"ActionDispatch::Static",
"ActionDispatch::Executor",
@@ -138,7 +142,7 @@ module ApplicationTests
add_to_config "config.ssl_options = { redirect: { host: 'example.com' } }"
boot!
- assert_equal [{ redirect: { host: "example.com" } }], Rails.application.middleware.first.args
+ assert_equal [{ redirect: { host: "example.com" } }], Rails.application.middleware[2].args
end
test "removing Active Record omits its middleware" do
@@ -222,35 +226,36 @@ module ApplicationTests
test "insert middleware after" do
add_to_config "config.middleware.insert_after Rack::Sendfile, Rack::Config"
boot!
- assert_equal "Rack::Config", middleware.second
+ assert_equal "Rack::Config", middleware.fourth
end
test "unshift middleware" do
add_to_config "config.middleware.unshift Rack::Config"
boot!
- assert_equal "Rack::Config", middleware.first
+ assert_equal "Rack::Config", middleware.second
end
test "Rails.cache does not respond to middleware" do
add_to_config "config.cache_store = :memory_store"
boot!
- assert_equal "Rack::Runtime", middleware.fourth
+ assert_equal "Rack::Runtime", middleware[5]
end
test "Rails.cache does respond to middleware" do
boot!
- assert_equal "Rack::Runtime", middleware.fifth
+ assert_equal "ActiveSupport::Cache::Strategy::LocalCache", middleware[5]
+ assert_equal "Rack::Runtime", middleware[6]
end
test "insert middleware before" do
add_to_config "config.middleware.insert_before Rack::Sendfile, Rack::Config"
boot!
- assert_equal "Rack::Config", middleware.first
+ assert_equal "Rack::Config", middleware.third
end
test "can't change middleware after it's built" do
boot!
- assert_raise frozen_error_class do
+ assert_raise FrozenError do
app.config.middleware.use Rack::Config
end
end
diff --git a/railties/test/application/multiple_applications_test.rb b/railties/test/application/multiple_applications_test.rb
index d6c81c1fe2..f0f1112f6b 100644
--- a/railties/test/application/multiple_applications_test.rb
+++ b/railties/test/application/multiple_applications_test.rb
@@ -100,30 +100,6 @@ module ApplicationTests
assert_nothing_raised { AppTemplate::Application.new }
end
- def test_initializers_run_on_different_applications_go_to_the_same_class
- application1 = AppTemplate::Application.new
- run_count = 0
-
- AppTemplate::Application.initializer :init0 do
- run_count += 1
- end
-
- application1.initializer :init1 do
- run_count += 1
- end
-
- AppTemplate::Application.new.initializer :init2 do
- run_count += 1
- end
-
- assert_equal 0, run_count, "Without loading the initializers, the count should be 0"
-
- # Set config.eager_load to false so that an eager_load warning doesn't pop up
- AppTemplate::Application.create { config.eager_load = false }.initialize!
-
- assert_equal 3, run_count, "There should have been three initializers that incremented the count"
- end
-
def test_consoles_run_on_different_applications_go_to_the_same_class
run_count = 0
AppTemplate::Application.console { run_count += 1 }
@@ -165,12 +141,12 @@ module ApplicationTests
app.config.some_setting = "a_different_setting"
assert_equal "a_different_setting", app.config.some_setting, "The configuration's some_setting should be set."
- new_config = Rails::Application::Configuration.new("root_of_application")
+ new_config = Rails::Application::Configuration.new(Pathname.new("root_of_application"))
new_config.some_setting = "some_setting_dude"
app.config = new_config
assert_equal "some_setting_dude", app.config.some_setting, "The configuration's some_setting should have changed."
- assert_equal "root_of_application", app.config.root, "The root should have changed to the new config's root."
+ assert_equal "root_of_application", app.config.root.to_s, "The root should have changed to the new config's root."
assert_equal new_config, app.config, "The application's config should have changed to the new config."
end
end
diff --git a/railties/test/application/rack/logger_test.rb b/railties/test/application/rack/logger_test.rb
index d949a48366..ea425d5fa5 100644
--- a/railties/test/application/rack/logger_test.rb
+++ b/railties/test/application/rack/logger_test.rb
@@ -53,6 +53,12 @@ module ApplicationTests
wait
assert_match 'Started HEAD "/"', logs
end
+
+ test "logger logs correct remote IP address" do
+ get "/", {}, { "REMOTE_ADDR" => "127.0.0.1", "HTTP_X_FORWARDED_FOR" => "1.2.3.4" }
+ wait
+ assert_match 'Started GET "/" for 1.2.3.4', logs
+ end
end
end
end
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
index 0594236b1f..258066a7e6 100644
--- a/railties/test/application/rake/dbs_test.rb
+++ b/railties/test/application/rake/dbs_test.rb
@@ -1,11 +1,12 @@
# frozen_string_literal: true
require "isolation/abstract_unit"
+require "env_helpers"
module ApplicationTests
module RakeTests
class RakeDbsTest < ActiveSupport::TestCase
- include ActiveSupport::Testing::Isolation
+ include ActiveSupport::Testing::Isolation, EnvHelpers
def setup
build_app
@@ -31,6 +32,7 @@ module ApplicationTests
output = rails("db:create")
assert_match(/Created database/, output)
assert File.exist?(expected_database)
+ yield if block_given?
assert_equal expected_database, ActiveRecord::Base.connection_config[:database] if environment_loaded
output = rails("db:drop")
assert_match(/Dropped database/, output)
@@ -38,12 +40,12 @@ module ApplicationTests
end
end
- test "db:create and db:drop without database url" do
+ test "db:create and db:drop without database URL" do
require "#{app_path}/config/environment"
db_create_and_drop ActiveRecord::Base.configurations[Rails.env]["database"]
end
- test "db:create and db:drop with database url" do
+ test "db:create and db:drop with database URL" do
require "#{app_path}/config/environment"
set_database_url
db_create_and_drop database_url_db_name
@@ -51,6 +53,7 @@ module ApplicationTests
test "db:create and db:drop respect environment setting" do
app_file "config/database.yml", <<-YAML
+ <% 1 %>
development:
database: <%= Rails.application.config.database %>
adapter: sqlite3
@@ -62,7 +65,61 @@ module ApplicationTests
end
RUBY
- db_create_and_drop "db/development.sqlite3", environment_loaded: false
+ db_create_and_drop("db/development.sqlite3", environment_loaded: false)
+ end
+
+ test "db:create and db:drop don't raise errors when loading YAML with multiline ERB" do
+ app_file "config/database.yml", <<-YAML
+ development:
+ database: <%=
+ Rails.application.config.database
+ %>
+ adapter: sqlite3
+ YAML
+
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.database = "db/development.sqlite3"
+ end
+ RUBY
+
+ db_create_and_drop("db/development.sqlite3", environment_loaded: false)
+ end
+
+ test "db:create and db:drop don't raise errors when loading YAML containing conditional statements in ERB" do
+ app_file "config/database.yml", <<-YAML
+ development:
+ <% if Rails.application.config.database %>
+ database: <%= Rails.application.config.database %>
+ <% else %>
+ database: db/default.sqlite3
+ <% end %>
+ adapter: sqlite3
+ YAML
+
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.database = "db/development.sqlite3"
+ end
+ RUBY
+
+ db_create_and_drop("db/development.sqlite3", environment_loaded: false)
+ end
+
+ test "db:create and db:drop don't raise errors when loading YAML containing multiple ERB statements on the same line" do
+ app_file "config/database.yml", <<-YAML
+ development:
+ database: <% if Rails.application.config.database %><%= Rails.application.config.database %><% else %>db/default.sqlite3<% end %>
+ adapter: sqlite3
+ YAML
+
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.database = "db/development.sqlite3"
+ end
+ RUBY
+
+ db_create_and_drop("db/development.sqlite3", environment_loaded: false)
end
def with_database_existing
@@ -83,6 +140,8 @@ module ApplicationTests
def with_bad_permissions
Dir.chdir(app_path) do
+ skip "Can't avoid permissions as root" if Process.uid.zero?
+
set_database_url
FileUtils.chmod("-w", "db")
yield
@@ -93,7 +152,7 @@ module ApplicationTests
test "db:create failure because bad permissions" do
with_bad_permissions do
output = rails("db:create", allow_failure: true)
- assert_match(/Couldn't create database/, output)
+ assert_match("Couldn't create '#{database_url_db_name}' database. Please check your configuration.", output)
assert_equal 1, $?.exitstatus
end
end
@@ -127,6 +186,59 @@ module ApplicationTests
end
end
+ test "db:truncate_all truncates all non-internal tables" do
+ Dir.chdir(app_path) do
+ rails "generate", "model", "book", "title:string"
+ rails "db:migrate"
+ require "#{app_path}/config/environment"
+ Book.create!(title: "Remote")
+ assert_equal 1, Book.count
+ schema_migrations = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ internal_metadata = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+
+ rails "db:truncate_all"
+
+ assert_equal(
+ schema_migrations,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ )
+ assert_equal(
+ internal_metadata,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+ )
+ assert_equal 0, Book.count
+ end
+ end
+
+ test "db:truncate_all does not truncate any tables when environment is protected" do
+ with_rails_env "production" do
+ Dir.chdir(app_path) do
+ rails "generate", "model", "book", "title:string"
+ rails "db:migrate"
+ require "#{app_path}/config/environment"
+ Book.create!(title: "Remote")
+ assert_equal 1, Book.count
+ schema_migrations = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ internal_metadata = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+ books = ActiveRecord::Base.connection.execute("SELECT * from \"books\"")
+
+ output = rails("db:truncate_all", allow_failure: true)
+ assert_match(/ActiveRecord::ProtectedEnvironmentError/, output)
+
+ assert_equal(
+ schema_migrations,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ )
+ assert_equal(
+ internal_metadata,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+ )
+ assert_equal 1, Book.count
+ assert_equal(books, ActiveRecord::Base.connection.execute("SELECT * from \"books\""))
+ end
+ end
+ end
+
def db_migrate_and_status(expected_database)
rails "generate", "model", "book", "title:string"
rails "db:migrate"
@@ -167,9 +279,10 @@ module ApplicationTests
def db_fixtures_load(expected_database)
Dir.chdir(app_path) do
rails "generate", "model", "book", "title:string"
+ reload
rails "db:migrate", "db:fixtures:load"
+
assert_match expected_database, ActiveRecord::Base.connection_config[:database]
- require "#{app_path}/app/models/book"
assert_equal 2, Book.count
end
end
@@ -189,8 +302,9 @@ module ApplicationTests
require "#{app_path}/config/environment"
rails "generate", "model", "admin::book", "title:string"
+ reload
rails "db:migrate", "db:fixtures:load"
- require "#{app_path}/app/models/admin/book"
+
assert_equal 2, Admin::Book.count
end
@@ -311,13 +425,12 @@ module ApplicationTests
end
test "db:setup loads schema and seeds database" do
- begin
- @old_rails_env = ENV["RAILS_ENV"]
- @old_rack_env = ENV["RACK_ENV"]
- ENV.delete "RAILS_ENV"
- ENV.delete "RACK_ENV"
+ @old_rails_env = ENV["RAILS_ENV"]
+ @old_rack_env = ENV["RACK_ENV"]
+ ENV.delete "RAILS_ENV"
+ ENV.delete "RACK_ENV"
- app_file "db/schema.rb", <<-RUBY
+ app_file "db/schema.rb", <<-RUBY
ActiveRecord::Schema.define(version: "1") do
create_table :users do |t|
t.string :name
@@ -325,16 +438,15 @@ module ApplicationTests
end
RUBY
- app_file "db/seeds.rb", <<-RUBY
- puts ActiveRecord::Base.connection_config[:database]
- RUBY
+ app_file "db/seeds.rb", <<-RUBY
+ puts ActiveRecord::Base.connection_config[:database]
+ RUBY
- database_path = rails("db:setup")
- assert_equal "development.sqlite3", File.basename(database_path.strip)
- ensure
- ENV["RAILS_ENV"] = @old_rails_env
- ENV["RACK_ENV"] = @old_rack_env
- end
+ database_path = rails("db:setup")
+ assert_equal "development.sqlite3", File.basename(database_path.strip)
+ ensure
+ ENV["RAILS_ENV"] = @old_rails_env
+ ENV["RACK_ENV"] = @old_rack_env
end
test "db:setup sets ar_internal_metadata" do
@@ -375,6 +487,88 @@ module ApplicationTests
assert_equal "test", test_environment.call
end
+
+ test "db:seed:replant truncates all non-internal tables and loads the seeds" do
+ Dir.chdir(app_path) do
+ rails "generate", "model", "book", "title:string"
+ rails "db:migrate"
+ require "#{app_path}/config/environment"
+ Book.create!(title: "Remote")
+ assert_equal 1, Book.count
+ schema_migrations = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ internal_metadata = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+
+ app_file "db/seeds.rb", <<-RUBY
+ Book.create!(title: "Rework")
+ Book.create!(title: "Ruby Under a Microscope")
+ RUBY
+
+ rails "db:seed:replant"
+
+ assert_equal(
+ schema_migrations,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ )
+ assert_equal(
+ internal_metadata,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+ )
+ assert_equal 2, Book.count
+ assert_not_predicate Book.where(title: "Remote"), :exists?
+ assert_predicate Book.where(title: "Rework"), :exists?
+ assert_predicate Book.where(title: "Ruby Under a Microscope"), :exists?
+ end
+ end
+
+ test "db:seed:replant does not truncate any tables and does not load the seeds when environment is protected" do
+ with_rails_env "production" do
+ Dir.chdir(app_path) do
+ rails "generate", "model", "book", "title:string"
+ rails "db:migrate"
+ require "#{app_path}/config/environment"
+ Book.create!(title: "Remote")
+ assert_equal 1, Book.count
+ schema_migrations = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ internal_metadata = ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+ books = ActiveRecord::Base.connection.execute("SELECT * from \"books\"")
+
+ app_file "db/seeds.rb", <<-RUBY
+ Book.create!(title: "Rework")
+ RUBY
+
+ output = rails("db:seed:replant", allow_failure: true)
+ assert_match(/ActiveRecord::ProtectedEnvironmentError/, output)
+
+ assert_equal(
+ schema_migrations,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.schema_migrations_table_name}\"")
+ )
+ assert_equal(
+ internal_metadata,
+ ActiveRecord::Base.connection.execute("SELECT * from \"#{ActiveRecord::Base.internal_metadata_table_name}\"")
+ )
+ assert_equal 1, Book.count
+ assert_equal(books, ActiveRecord::Base.connection.execute("SELECT * from \"books\""))
+ assert_not_predicate Book.where(title: "Rework"), :exists?
+ end
+ end
+ end
+
+ test "db:prepare setup the database" do
+ Dir.chdir(app_path) do
+ rails "generate", "model", "book", "title:string"
+ output = rails("db:prepare")
+ assert_match(/CreateBooks: migrated/, output)
+
+ output = rails("db:drop")
+ assert_match(/Dropped database/, output)
+
+ rails "generate", "model", "recipe", "title:string"
+ output = rails("db:prepare")
+ assert_match(/CreateBooks: migrated/, output)
+ assert_match(/CreateRecipes: migrated/, output)
+ end
+ end
end
end
end
diff --git a/railties/test/application/rake/dev_test.rb b/railties/test/application/rake/dev_test.rb
index 66e1ac9d99..a87f453075 100644
--- a/railties/test/application/rake/dev_test.rb
+++ b/railties/test/application/rake/dev_test.rb
@@ -9,6 +9,7 @@ module ApplicationTests
def setup
build_app
+ add_to_env_config("development", "config.active_support.deprecation = :stderr")
end
def teardown
@@ -17,33 +18,46 @@ module ApplicationTests
test "dev:cache creates file and outputs message" do
Dir.chdir(app_path) do
- output = rails("dev:cache")
- assert File.exist?("tmp/caching-dev.txt")
- assert_match(/Development mode is now being cached/, output)
+ stderr = capture(:stderr) do
+ output = run_rake_dev_cache
+ assert File.exist?("tmp/caching-dev.txt")
+ assert_match(/Development mode is now being cached/, output)
+ end
+ assert_match(/DEPRECATION WARNING: Using `bin\/rake dev:cache` is deprecated and will be removed in Rails 6.1/, stderr)
end
end
test "dev:cache deletes file and outputs message" do
Dir.chdir(app_path) do
- rails "dev:cache" # Create caching file.
- output = rails("dev:cache") # Delete caching file.
- assert_not File.exist?("tmp/caching-dev.txt")
- assert_match(/Development mode is no longer being cached/, output)
+ stderr = capture(:stderr) do
+ run_rake_dev_cache # Create caching file.
+ output = run_rake_dev_cache # Delete caching file.
+ assert_not File.exist?("tmp/caching-dev.txt")
+ assert_match(/Development mode is no longer being cached/, output)
+ end
+ assert_match(/DEPRECATION WARNING: Using `bin\/rake dev:cache` is deprecated and will be removed in Rails 6.1/, stderr)
end
end
test "dev:cache touches tmp/restart.txt" do
Dir.chdir(app_path) do
- rails "dev:cache"
- assert File.exist?("tmp/restart.txt")
-
- prev_mtime = File.mtime("tmp/restart.txt")
- sleep(1)
- rails "dev:cache"
- curr_mtime = File.mtime("tmp/restart.txt")
- assert_not_equal prev_mtime, curr_mtime
+ stderr = capture(:stderr) do
+ run_rake_dev_cache
+ assert File.exist?("tmp/restart.txt")
+
+ prev_mtime = File.mtime("tmp/restart.txt")
+ run_rake_dev_cache
+ curr_mtime = File.mtime("tmp/restart.txt")
+ assert_not_equal prev_mtime, curr_mtime
+ end
+ assert_match(/DEPRECATION WARNING: Using `bin\/rake dev:cache` is deprecated and will be removed in Rails 6.1/, stderr)
end
end
+
+ private
+ def run_rake_dev_cache
+ `bin/rake dev:cache`
+ end
end
end
end
diff --git a/railties/test/application/rake/initializers_test.rb b/railties/test/application/rake/initializers_test.rb
new file mode 100644
index 0000000000..8de4967021
--- /dev/null
+++ b/railties/test/application/rake/initializers_test.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class RakeInitializersTest < ActiveSupport::TestCase
+ setup :build_app
+ teardown :teardown_app
+
+ test "`rake initializers` prints out defined initializers invoked by Rails" do
+ capture(:stderr) do
+ initial_output = run_rake_initializers
+ initial_output_length = initial_output.split("\n").length
+
+ assert_operator initial_output_length, :>, 0
+ assert_not initial_output.include?("set_added_test_module")
+
+ add_to_config <<-RUBY
+ initializer(:set_added_test_module) { }
+ RUBY
+
+ final_output = run_rake_initializers
+ final_output_length = final_output.split("\n").length
+
+ assert_equal 1, (final_output_length - initial_output_length)
+ assert final_output.include?("set_added_test_module")
+ end
+ end
+
+ test "`rake initializers` outputs a deprecation warning" do
+ add_to_env_config("development", "config.active_support.deprecation = :stderr")
+
+ stderr = capture(:stderr) { run_rake_initializers }
+ assert_match(/DEPRECATION WARNING: Using `bin\/rake initializers` is deprecated and will be removed in Rails 6.1/, stderr)
+ end
+
+ private
+ def run_rake_initializers
+ Dir.chdir(app_path) { `bin/rake initializers` }
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/multi_dbs_test.rb b/railties/test/application/rake/multi_dbs_test.rb
index 07d96fcb56..147b8f94e1 100644
--- a/railties/test/application/rake/multi_dbs_test.rb
+++ b/railties/test/application/rake/multi_dbs_test.rb
@@ -16,21 +16,24 @@ module ApplicationTests
teardown_app
end
- def db_create_and_drop(namespace, expected_database, environment_loaded: true)
+ def db_create_and_drop(namespace, expected_database)
Dir.chdir(app_path) do
output = rails("db:create")
assert_match(/Created database/, output)
assert_match_namespace(namespace, output)
+ assert_no_match(/already exists/, output)
assert File.exist?(expected_database)
+
output = rails("db:drop")
assert_match(/Dropped database/, output)
assert_match_namespace(namespace, output)
+ assert_no_match(/does not exist/, output)
assert_not File.exist?(expected_database)
end
end
- def db_create_and_drop_namespace(namespace, expected_database, environment_loaded: true)
+ def db_create_and_drop_namespace(namespace, expected_database)
Dir.chdir(app_path) do
output = rails("db:create:#{namespace}")
assert_match(/Created database/, output)
@@ -52,11 +55,40 @@ module ApplicationTests
end
end
- def db_migrate_and_schema_dump_and_load(namespace, expected_database, format)
+ def db_migrate_and_migrate_status
+ Dir.chdir(app_path) do
+ generate_models_for_animals
+ rails "db:migrate"
+ output = rails "db:migrate:status"
+ assert_match(/up \d+ Create books/, output)
+ assert_match(/up \d+ Create dogs/, output)
+ end
+ end
+
+ def db_migrate_and_schema_cache_dump
+ Dir.chdir(app_path) do
+ generate_models_for_animals
+ rails "db:migrate"
+ rails "db:schema:cache:dump"
+ assert File.exist?("db/schema_cache.yml")
+ assert File.exist?("db/animals_schema_cache.yml")
+ end
+ end
+
+ def db_migrate_and_schema_cache_dump_and_schema_cache_clear
Dir.chdir(app_path) do
- rails "generate", "model", "book", "title:string"
- rails "generate", "model", "dog", "name:string"
- write_models_for_animals
+ generate_models_for_animals
+ rails "db:migrate"
+ rails "db:schema:cache:dump"
+ rails "db:schema:cache:clear"
+ assert_not File.exist?("db/schema_cache.yml")
+ assert_not File.exist?("db/animals_schema_cache.yml")
+ end
+ end
+
+ def db_migrate_and_schema_dump_and_load(format)
+ Dir.chdir(app_path) do
+ generate_models_for_animals
rails "db:migrate", "db:#{format}:dump"
if format == "schema"
@@ -81,11 +113,9 @@ module ApplicationTests
end
end
- def db_migrate_namespaced(namespace, expected_database)
+ def db_migrate_namespaced(namespace)
Dir.chdir(app_path) do
- rails "generate", "model", "book", "title:string"
- rails "generate", "model", "dog", "name:string"
- write_models_for_animals
+ generate_models_for_animals
output = rails("db:migrate:#{namespace}")
if namespace == "primary"
assert_match(/CreateBooks: migrated/, output)
@@ -95,6 +125,33 @@ module ApplicationTests
end
end
+ def db_migrate_status_namespaced(namespace)
+ Dir.chdir(app_path) do
+ generate_models_for_animals
+ output = rails("db:migrate:status:#{namespace}")
+ if namespace == "primary"
+ assert_match(/up \d+ Create books/, output)
+ else
+ assert_match(/up \d+ Create dogs/, output)
+ end
+ end
+ end
+
+ def db_prepare
+ Dir.chdir(app_path) do
+ generate_models_for_animals
+ output = rails("db:prepare")
+
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ if db_config.spec_name == "primary"
+ assert_match(/CreateBooks: migrated/, output)
+ else
+ assert_match(/CreateDogs: migrated/, output)
+ end
+ end
+ end
+ end
+
def write_models_for_animals
# make a directory for the animals migration
FileUtils.mkdir_p("#{app_path}/db/animals_migrate")
@@ -114,51 +171,81 @@ module ApplicationTests
# create the base model for dog to inherit from
File.open("#{app_path}/app/models/animals_base.rb", "w") do |file|
- file.write(<<-EOS
-class AnimalsBase < ActiveRecord::Base
- self.abstract_class = true
+ file.write(<<~EOS)
+ class AnimalsBase < ActiveRecord::Base
+ self.abstract_class = true
- establish_connection :animals
-end
-EOS
-)
+ establish_connection :animals
+ end
+ EOS
end
end
+ def generate_models_for_animals
+ rails "generate", "model", "book", "title:string"
+ rails "generate", "model", "dog", "name:string"
+ write_models_for_animals
+ reload
+ end
+
test "db:create and db:drop works on all databases for env" do
require "#{app_path}/config/environment"
- ActiveRecord::Base.configurations[Rails.env].each do |namespace, config|
- db_create_and_drop namespace, config["database"]
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ db_create_and_drop db_config.spec_name, db_config.config["database"]
end
end
test "db:create:namespace and db:drop:namespace works on specified databases" do
require "#{app_path}/config/environment"
- ActiveRecord::Base.configurations[Rails.env].each do |namespace, config|
- db_create_and_drop_namespace namespace, config["database"]
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ db_create_and_drop_namespace db_config.spec_name, db_config.config["database"]
end
end
test "db:migrate and db:schema:dump and db:schema:load works on all databases" do
require "#{app_path}/config/environment"
- ActiveRecord::Base.configurations[Rails.env].each do |namespace, config|
- db_migrate_and_schema_dump_and_load namespace, config["database"], "schema"
- end
+ db_migrate_and_schema_dump_and_load "schema"
end
test "db:migrate and db:structure:dump and db:structure:load works on all databases" do
require "#{app_path}/config/environment"
- ActiveRecord::Base.configurations[Rails.env].each do |namespace, config|
- db_migrate_and_schema_dump_and_load namespace, config["database"], "structure"
- end
+ db_migrate_and_schema_dump_and_load "structure"
end
test "db:migrate:namespace works" do
require "#{app_path}/config/environment"
- ActiveRecord::Base.configurations[Rails.env].each do |namespace, config|
- db_migrate_namespaced namespace, config["database"]
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ db_migrate_namespaced db_config.spec_name
end
end
+
+ test "db:migrate:status works on all databases" do
+ require "#{app_path}/config/environment"
+ db_migrate_and_migrate_status
+ end
+
+ test "db:migrate:status:namespace works" do
+ require "#{app_path}/config/environment"
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ db_migrate_namespaced db_config.spec_name
+ db_migrate_status_namespaced db_config.spec_name
+ end
+ end
+
+ test "db:schema:cache:dump works on all databases" do
+ require "#{app_path}/config/environment"
+ db_migrate_and_schema_cache_dump
+ end
+
+ test "db:schema:cache:clear works on all databases" do
+ require "#{app_path}/config/environment"
+ db_migrate_and_schema_cache_dump_and_schema_cache_clear
+ end
+
+ test "db:prepare works on all databases" do
+ require "#{app_path}/config/environment"
+ db_prepare
+ end
end
end
end
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
index 9e22ba84b5..60802ef7c4 100644
--- a/railties/test/application/rake/notes_test.rb
+++ b/railties/test/application/rake/notes_test.rb
@@ -10,6 +10,7 @@ module ApplicationTests
def setup
build_app
+ add_to_env_config("development", "config.active_support.deprecation = :stderr")
require "rails/all"
super
end
diff --git a/railties/test/application/rake/routes_test.rb b/railties/test/application/rake/routes_test.rb
new file mode 100644
index 0000000000..9879d1f047
--- /dev/null
+++ b/railties/test/application/rake/routes_test.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class RakeRoutesTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ setup :build_app
+ teardown :teardown_app
+
+ test "`rake routes` outputs routes" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/cart', to: 'cart#show'
+ end
+ RUBY
+
+ assert_equal <<~MESSAGE, run_rake_routes
+ Prefix Verb URI Pattern Controller#Action
+ cart GET /cart(.:format) cart#show
+ rails_amazon_inbound_emails POST /rails/action_mailbox/amazon/inbound_emails(.:format) action_mailbox/ingresses/amazon/inbound_emails#create
+ rails_mandrill_inbound_emails POST /rails/action_mailbox/mandrill/inbound_emails(.:format) action_mailbox/ingresses/mandrill/inbound_emails#create
+ rails_postmark_inbound_emails POST /rails/action_mailbox/postmark/inbound_emails(.:format) action_mailbox/ingresses/postmark/inbound_emails#create
+ rails_relay_inbound_emails POST /rails/action_mailbox/relay/inbound_emails(.:format) action_mailbox/ingresses/relay/inbound_emails#create
+ rails_sendgrid_inbound_emails POST /rails/action_mailbox/sendgrid/inbound_emails(.:format) action_mailbox/ingresses/sendgrid/inbound_emails#create
+ rails_mailgun_inbound_emails POST /rails/action_mailbox/mailgun/inbound_emails/mime(.:format) action_mailbox/ingresses/mailgun/inbound_emails#create
+ rails_conductor_inbound_emails GET /rails/conductor/action_mailbox/inbound_emails(.:format) rails/conductor/action_mailbox/inbound_emails#index
+ POST /rails/conductor/action_mailbox/inbound_emails(.:format) rails/conductor/action_mailbox/inbound_emails#create
+ new_rails_conductor_inbound_email GET /rails/conductor/action_mailbox/inbound_emails/new(.:format) rails/conductor/action_mailbox/inbound_emails#new
+ edit_rails_conductor_inbound_email GET /rails/conductor/action_mailbox/inbound_emails/:id/edit(.:format) rails/conductor/action_mailbox/inbound_emails#edit
+ rails_conductor_inbound_email GET /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#show
+ PATCH /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#update
+ PUT /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#update
+ DELETE /rails/conductor/action_mailbox/inbound_emails/:id(.:format) rails/conductor/action_mailbox/inbound_emails#destroy
+ rails_conductor_inbound_email_reroute POST /rails/conductor/action_mailbox/:inbound_email_id/reroute(.:format) rails/conductor/action_mailbox/reroutes#create
+ rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
+ rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
+ rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
+ update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
+ rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
+ MESSAGE
+ end
+
+ test "`rake routes` outputs a deprecation warning" do
+ add_to_env_config("development", "config.active_support.deprecation = :stderr")
+
+ stderr = capture(:stderr) { run_rake_routes }
+ assert_match(/DEPRECATION WARNING: Using `bin\/rake routes` is deprecated and will be removed in Rails 6.1/, stderr)
+ end
+
+ private
+ def run_rake_routes
+ Dir.chdir(app_path) { `bin/rake routes` }
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index 1522a2bbc5..fe56e3d076 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -118,7 +118,7 @@ module ApplicationTests
end
def test_code_statistics_sanity
- assert_match "Code LOC: 25 Test LOC: 0 Code to Test Ratio: 1:0.0",
+ assert_match "Code LOC: 32 Test LOC: 0 Code to Test Ratio: 1:0.0",
rails("stats")
end
@@ -145,8 +145,8 @@ module ApplicationTests
# loading a specific fixture
rails "db:fixtures:load", "FIXTURES=products"
- assert_equal 2, ::AppTemplate::Application::Product.count
- assert_equal 0, ::AppTemplate::Application::User.count
+ assert_equal 2, Product.count
+ assert_equal 0, User.count
end
def test_loading_only_yml_fixtures
@@ -160,7 +160,10 @@ module ApplicationTests
def test_scaffold_tests_pass_by_default
rails "generate", "scaffold", "user", "username:string", "password:string"
- with_rails_env("test") { rails("db:migrate") }
+ with_rails_env("test") do
+ rails("db:migrate")
+ rails("webpacker:compile")
+ end
output = rails("test")
assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output)
@@ -189,7 +192,10 @@ module ApplicationTests
rails "generate", "model", "Product"
rails "generate", "model", "Cart"
rails "generate", "scaffold", "LineItems", "product:references", "cart:belongs_to"
- with_rails_env("test") { rails("db:migrate") }
+ with_rails_env("test") do
+ rails("db:migrate")
+ rails("webpacker:compile")
+ end
output = rails("test")
assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output)
diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb
index 8f5f48c281..dfb9540093 100644
--- a/railties/test/application/runner_test.rb
+++ b/railties/test/application/runner_test.rb
@@ -105,6 +105,14 @@ module ApplicationTests
assert_match "development", rails("runner", "puts Rails.env")
end
+ def test_environment_option
+ assert_match "production", rails("runner", "-e", "production", "puts Rails.env")
+ end
+
+ def test_environment_option_is_properly_expanded
+ assert_match "production", rails("runner", "-e", "prod", "puts Rails.env")
+ end
+
def test_runner_detects_syntax_errors
output = rails("runner", "puts 'hello world", allow_failure: true)
assert_not_predicate $?, :success?
diff --git a/railties/test/application/server_test.rb b/railties/test/application/server_test.rb
index 92b991dd05..5fe1b4e6e7 100644
--- a/railties/test/application/server_test.rb
+++ b/railties/test/application/server_test.rb
@@ -18,20 +18,6 @@ module ApplicationTests
teardown_app
end
- test "deprecate support of older `config.ru`" do
- remove_file "config.ru"
- app_file "config.ru", <<-RUBY
- require_relative 'config/environment'
- run AppTemplate::Application
- RUBY
-
- server = Rails::Server.new(config: "#{app_path}/config.ru")
- server.app
-
- log = File.read(Rails.application.config.paths["log"].first)
- assert_match(/DEPRECATION WARNING: Using `Rails::Application` subclass to start the server is deprecated/, log)
- end
-
test "restart rails server with custom pid file path" do
skip "PTY unavailable" unless available_pty?
@@ -40,17 +26,17 @@ module ApplicationTests
f.puts "require 'bundler/setup'"
end
- master, slave = PTY.open
+ primary, replica = PTY.open
pid = nil
- begin
- pid = Process.spawn("#{app_path}/bin/rails server -P tmp/dummy.pid", in: slave, out: slave, err: slave)
- assert_output("Listening", master)
+ Bundler.with_original_env do
+ pid = Process.spawn("bin/rails server -b localhost -P tmp/dummy.pid", chdir: app_path, in: replica, out: replica, err: replica)
+ assert_output("Listening", primary)
rails("restart")
- assert_output("Restarting", master)
- assert_output("Inherited", master)
+ assert_output("Restarting", primary)
+ assert_output("tcp://localhost:3000", primary)
ensure
kill(pid) if pid
end
diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb
index 8e5ccf94cc..0bdd2b314d 100644
--- a/railties/test/application/test_runner_test.rb
+++ b/railties/test/application/test_runner_test.rb
@@ -16,7 +16,7 @@ module ApplicationTests
teardown_app
end
- def test_run_via_backwardscompatibility
+ def test_run_via_backwards_compatibility
require "minitest/rails_plugin"
assert_nothing_raised do
@@ -98,6 +98,17 @@ module ApplicationTests
end
end
+ def test_run_channels
+ create_test_file :channels, "foo_channel"
+ create_test_file :channels, "bar_channel"
+
+ rails("test:channels").tap do |output|
+ assert_match "FooChannelTest", output
+ assert_match "BarChannelTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
def test_run_controllers
create_test_file :controllers, "foo_controller"
create_test_file :controllers, "bar_controller"
@@ -131,6 +142,18 @@ module ApplicationTests
end
end
+ def test_run_mailboxes
+ create_test_file :mailboxes, "foo_mailbox"
+ create_test_file :mailboxes, "bar_mailbox"
+ create_test_file :models, "foo"
+
+ rails("test:mailboxes").tap do |output|
+ assert_match "FooMailboxTest", output
+ assert_match "BarMailboxTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
def test_run_functionals
create_test_file :mailers, "foo_mailer"
create_test_file :controllers, "bar_controller"
@@ -155,11 +178,11 @@ module ApplicationTests
end
def test_run_all_suites
- suites = [:models, :helpers, :unit, :controllers, :mailers, :functional, :integration, :jobs]
+ suites = [:models, :helpers, :unit, :channels, :controllers, :mailers, :functional, :integration, :jobs, :mailboxes]
suites.each { |suite| create_test_file suite, "foo_#{suite}" }
run_test_command("") .tap do |output|
suites.each { |suite| assert_match "Foo#{suite.to_s.camelize}Test", output }
- assert_match "8 runs, 8 assertions, 0 failures", output
+ assert_match "10 runs, 10 assertions, 0 failures", output
end
end
@@ -504,7 +527,7 @@ module ApplicationTests
create_test_file :models, "post", pass: false, print: false
output = run_test_command("test/models/post_test.rb")
- expect = %r{Running:\n\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/models/post_test.rb:6\]:\nwups!\n\nbin/rails test test/models/post_test.rb:4\n\n\n\n}
+ expect = %r{Running:\n\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/models/post_test.rb:6\]:\nwups!\n\nrails test test/models/post_test.rb:4\n\n\n\n}
assert_match expect, output
end
@@ -523,25 +546,84 @@ module ApplicationTests
end
def test_run_in_parallel_with_processes
+ exercise_parallelization_regardless_of_machine_core_count(with: :processes)
+
file_name = create_parallel_processes_test_file
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
output = run_test_command(file_name)
assert_match %r{Finished in.*\n2 runs, 2 assertions}, output
+ assert_no_match "create_table(:users)", output
end
def test_run_in_parallel_with_threads
- app_path("/test/test_helper.rb") do |file_name|
- file = File.read(file_name)
- file.sub!(/parallelize\(([^\)]*)\)/, "parallelize(\\1, with: :threads)")
- File.write(file_name, file)
- end
+ exercise_parallelization_regardless_of_machine_core_count(with: :threads)
file_name = create_parallel_threads_test_file
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
output = run_test_command(file_name)
assert_match %r{Finished in.*\n2 runs, 2 assertions}, output
+ assert_no_match "create_table(:users)", output
+ end
+
+ def test_run_in_parallel_with_unmarshable_exception
+ exercise_parallelization_regardless_of_machine_core_count(with: :processes)
+
+ file = app_file "test/fail_test.rb", <<-RUBY
+ require "test_helper"
+ class FailTest < ActiveSupport::TestCase
+ class BadError < StandardError
+ def initialize
+ super
+ @proc = ->{ }
+ end
+ end
+
+ test "fail" do
+ raise BadError
+ assert true
+ end
+ end
+ RUBY
+
+ output = run_test_command(file)
+
+ assert_match "DRb::DRbRemoteError: FailTest::BadError", output
+ assert_match "1 runs, 0 assertions, 0 failures, 1 errors", output
+ end
+
+ def test_run_in_parallel_with_unknown_object
+ exercise_parallelization_regardless_of_machine_core_count(with: :processes)
+
+ create_scaffold
+
+ app_file "config/environments/test.rb", <<-RUBY
+ Rails.application.configure do
+ config.action_controller.allow_forgery_protection = true
+ config.action_dispatch.show_exceptions = false
+ end
+ RUBY
+
+ output = run_test_command("-n test_should_create_user")
+
+ assert_match "ActionController::InvalidAuthenticityToken", output
end
def test_raise_error_when_specified_file_does_not_exist
@@ -553,7 +635,7 @@ module ApplicationTests
create_test_file :models, "account"
create_test_file :models, "post", pass: false
# This specifically verifies TEST for backwards compatibility with rake test
- # as bin/rails test already supports running tests from a single file more cleanly.
+ # as `rails test` already supports running tests from a single file more cleanly.
output = Dir.chdir(app_path) { `bin/rake test TEST=test/models/post_test.rb` }
assert_match "PostTest", output, "passing TEST= should run selected test"
@@ -660,6 +742,7 @@ module ApplicationTests
def test_reset_sessions_before_rollback_on_system_tests
app_file "test/system/reset_session_before_rollback_test.rb", <<-RUBY
require "application_system_test_case"
+ require "selenium/webdriver"
class ResetSessionBeforeRollbackTest < ApplicationSystemTestCase
def teardown_fixtures
@@ -685,6 +768,32 @@ module ApplicationTests
end
end
+ def test_reset_sessions_on_failed_system_test_screenshot
+ app_file "test/system/reset_sessions_on_failed_system_test_screenshot_test.rb", <<~RUBY
+ require "application_system_test_case"
+ require "selenium/webdriver"
+
+ class ResetSessionsOnFailedSystemTestScreenshotTest < ApplicationSystemTestCase
+ ActionDispatch::SystemTestCase.class_eval do
+ def take_failed_screenshot
+ raise Capybara::CapybaraError
+ end
+ end
+
+ Capybara.instance_eval do
+ def reset_sessions!
+ puts "Capybara.reset_sessions! called"
+ end
+ end
+
+ test "dummy" do
+ end
+ end
+ RUBY
+ output = run_test_command("test/system/reset_sessions_on_failed_system_test_screenshot_test.rb")
+ assert_match "Capybara.reset_sessions! called", output
+ end
+
def test_system_tests_are_not_run_with_the_default_test_command
app_file "test/system/dummy_test.rb", <<-RUBY
require "application_system_test_case"
@@ -719,6 +828,7 @@ module ApplicationTests
def test_system_tests_are_run_through_rake_test_when_given_in_TEST
app_file "test/system/dummy_test.rb", <<-RUBY
require "application_system_test_case"
+ require "selenium/webdriver"
class DummyTest < ApplicationSystemTestCase
test "something" do
@@ -885,6 +995,14 @@ module ApplicationTests
RUBY
end
+ def exercise_parallelization_regardless_of_machine_core_count(with:)
+ app_path("test/test_helper.rb") do |file_name|
+ file = File.read(file_name)
+ file.sub!(/parallelize\(([^\)]*)\)/, "parallelize(workers: 2, with: :#{with})")
+ File.write(file_name, file)
+ end
+ end
+
def create_env_test
app_file "test/unit/env_test.rb", <<-RUBY
require 'test_helper'
diff --git a/railties/test/application/test_test.rb b/railties/test/application/test_test.rb
index fb43bebfbe..83e63718df 100644
--- a/railties/test/application/test_test.rb
+++ b/railties/test/application/test_test.rb
@@ -234,7 +234,7 @@ module ApplicationTests
# 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.
+ # This test case serves as a reminder for this use case.
test "manually synchronize test schema after rollback" do
output = rails("generate", "model", "user", "name:string")
version = output.match(/(\d+)_create_users\.rb/)[1]
diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb
index f22b9fda3d..dc737089f6 100644
--- a/railties/test/application/url_generation_test.rb
+++ b/railties/test/application/url_generation_test.rb
@@ -19,6 +19,8 @@ module ApplicationTests
config.session_store :cookie_store, key: "_myapp_session"
config.active_support.deprecation = :log
config.eager_load = false
+ config.hosts << proc { true }
+ config.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
end
Rails.application.initialize!
diff --git a/railties/test/application/zeitwerk_integration_test.rb b/railties/test/application/zeitwerk_integration_test.rb
new file mode 100644
index 0000000000..b248459e47
--- /dev/null
+++ b/railties/test/application/zeitwerk_integration_test.rb
@@ -0,0 +1,371 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+require "active_support/dependencies/zeitwerk_integration"
+
+class ZeitwerkIntegrationTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def boot(env = "development")
+ app(env)
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def deps
+ ActiveSupport::Dependencies
+ end
+
+ def decorated?
+ deps.singleton_class < deps::ZeitwerkIntegration::Decorations
+ end
+
+ test "ActiveSupport::Dependencies is decorated by default" do
+ boot
+
+ assert decorated?
+ assert Rails.autoloaders.zeitwerk_enabled?
+ assert_instance_of Zeitwerk::Loader, Rails.autoloaders.main
+ assert_instance_of Zeitwerk::Loader, Rails.autoloaders.once
+ assert_equal [Rails.autoloaders.main, Rails.autoloaders.once], Rails.autoloaders.to_a
+ end
+
+ test "ActiveSupport::Dependencies is not decorated in classic mode" do
+ add_to_config "config.autoloader = :classic"
+ boot
+
+ assert_not decorated?
+ assert_not Rails.autoloaders.zeitwerk_enabled?
+ assert_nil Rails.autoloaders.main
+ assert_nil Rails.autoloaders.once
+ assert_equal 0, Rails.autoloaders.count
+ end
+
+ test "autoloaders inflect with Active Support" do
+ app_file "config/initializers/inflections.rb", <<-RUBY
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
+ inflect.acronym 'RESTful'
+ end
+ RUBY
+
+ app_file "app/controllers/restful_controller.rb", <<-RUBY
+ class RESTfulController < ApplicationController
+ end
+ RUBY
+
+ boot
+
+ basename = "restful_controller"
+ abspath = "#{Rails.root}/app/controllers/#{basename}.rb"
+ camelized = "RESTfulController"
+
+ Rails.autoloaders.each do |autoloader|
+ assert_equal camelized, autoloader.inflector.camelize(basename, abspath)
+ end
+
+ assert RESTfulController
+ end
+
+ test "constantize returns the value stored in the constant" do
+ app_file "app/models/admin/user.rb", "class Admin::User; end"
+ boot
+
+ assert_same Admin::User, deps.constantize("Admin::User")
+ end
+
+ test "constantize raises if the constant is unknown" do
+ boot
+
+ assert_raises(NameError) { deps.constantize("Admin") }
+ end
+
+ test "safe_constantize returns the value stored in the constant" do
+ app_file "app/models/admin/user.rb", "class Admin::User; end"
+ boot
+
+ assert_same Admin::User, deps.safe_constantize("Admin::User")
+ end
+
+ test "safe_constantize returns nil for unknown constants" do
+ boot
+
+ assert_nil deps.safe_constantize("Admin")
+ end
+
+ test "to_unload? says if a constant would be unloaded (main)" do
+ app_file "app/models/user.rb", "class User; end"
+ app_file "app/models/post.rb", "class Post; end"
+ boot
+
+ assert Post
+ assert deps.to_unload?("Post")
+ assert_not deps.to_unload?("User")
+ end
+
+ test "to_unload? says if a constant would be unloaded (once)" do
+ add_to_config 'config.autoload_once_paths << "#{Rails.root}/extras"'
+ app_file "extras/foo.rb", "class Foo; end"
+ app_file "extras/bar.rb", "class Bar; end"
+ boot
+
+ assert Foo
+ assert_not deps.to_unload?("Foo")
+ assert_not deps.to_unload?("Bar")
+ end
+
+ test "to_unload? says if a constant would be unloaded (reloading disabled)" do
+ app_file "app/models/user.rb", "class User; end"
+ app_file "app/models/post.rb", "class Post; end"
+ boot("production")
+
+ assert Post
+ assert_not deps.to_unload?("Post")
+ assert_not deps.to_unload?("User")
+ end
+
+ test "eager loading loads the application code" do
+ $zeitwerk_integration_test_user = false
+ $zeitwerk_integration_test_post = false
+
+ app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true"
+ app_file "app/models/post.rb", "class Post; end; $zeitwerk_integration_test_post = true"
+
+ boot("production")
+
+ assert $zeitwerk_integration_test_user
+ assert $zeitwerk_integration_test_post
+ end
+
+ test "reloading is enabled if config.cache_classes is false" do
+ boot
+
+ assert Rails.autoloaders.main.reloading_enabled?
+ assert_not Rails.autoloaders.once.reloading_enabled?
+ end
+
+ test "reloading is disabled if config.cache_classes is true" do
+ boot("production")
+
+ assert_not Rails.autoloaders.main.reloading_enabled?
+ assert_not Rails.autoloaders.once.reloading_enabled?
+ end
+
+ test "eager loading loads code in engines" do
+ $test_blog_engine_eager_loaded = false
+
+ engine("blog") do |bukkit|
+ bukkit.write("lib/blog.rb", "class BlogEngine < Rails::Engine; end")
+ bukkit.write("app/models/post.rb", "Post = $test_blog_engine_eager_loaded = true")
+ end
+
+ boot("production")
+
+ assert $test_blog_engine_eager_loaded
+ end
+
+ test "eager loading loads anything managed by Zeitwerk" do
+ $zeitwerk_integration_test_user = false
+ app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true"
+
+ $zeitwerk_integration_test_extras = false
+ app_dir "extras"
+ app_file "extras/webhook_hacks.rb", "WebhookHacks = 1; $zeitwerk_integration_test_extras = true"
+
+ require "zeitwerk"
+ autoloader = Zeitwerk::Loader.new
+ autoloader.push_dir("#{app_path}/extras")
+ autoloader.setup
+
+ boot("production")
+
+ assert $zeitwerk_integration_test_user
+ assert $zeitwerk_integration_test_extras
+ end
+
+ test "autoload directories not present in eager load paths are not eager loaded" do
+ $zeitwerk_integration_test_user = false
+ app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true"
+
+ $zeitwerk_integration_test_lib = false
+ app_dir "lib"
+ app_file "lib/webhook_hacks.rb", "WebhookHacks = 1; $zeitwerk_integration_test_lib = true"
+
+ $zeitwerk_integration_test_extras = false
+ app_dir "extras"
+ app_file "extras/websocket_hacks.rb", "WebsocketHacks = 1; $zeitwerk_integration_test_extras = true"
+
+ add_to_config "config.autoload_paths << '#{app_path}/lib'"
+ add_to_config "config.autoload_once_paths << '#{app_path}/extras'"
+
+ boot("production")
+
+ assert $zeitwerk_integration_test_user
+ assert_not $zeitwerk_integration_test_lib
+ assert_not $zeitwerk_integration_test_extras
+
+ assert WebhookHacks
+ assert WebsocketHacks
+
+ assert $zeitwerk_integration_test_lib
+ assert $zeitwerk_integration_test_extras
+ end
+
+ test "autoload_paths are set as root dirs of main, and in the same order" do
+ boot
+
+ existing_autoload_paths = deps.autoload_paths.select { |dir| File.directory?(dir) }
+ assert_equal existing_autoload_paths, Rails.autoloaders.main.dirs
+ end
+
+ test "autoload_once_paths go to the once autoloader, and in the same order" do
+ extras = %w(e1 e2 e3)
+ extras.each do |extra|
+ app_dir extra
+ add_to_config %(config.autoload_once_paths << "\#{Rails.root}/#{extra}")
+ end
+
+ boot
+
+ extras = extras.map { |extra| "#{app_path}/#{extra}" }
+ extras.each do |extra|
+ assert_not_includes Rails.autoloaders.main.dirs, extra
+ end
+ assert_equal extras, Rails.autoloaders.once.dirs
+ end
+
+ test "clear reloads the main autoloader, and does not reload the once one" do
+ boot
+
+ $zeitwerk_integration_reload_test = []
+
+ main_autoloader = Rails.autoloaders.main
+ def main_autoloader.reload
+ $zeitwerk_integration_reload_test << :main_autoloader
+ super
+ end
+
+ once_autoloader = Rails.autoloaders.once
+ def once_autoloader.reload
+ $zeitwerk_integration_reload_test << :once_autoloader
+ super
+ end
+
+ ActiveSupport::Dependencies.clear
+
+ assert_equal %i(main_autoloader), $zeitwerk_integration_reload_test
+ end
+
+ test "verbose = true sets the dependencies logger if present" do
+ boot
+
+ logger = Logger.new(File::NULL)
+ ActiveSupport::Dependencies.logger = logger
+ ActiveSupport::Dependencies.verbose = true
+
+ Rails.autoloaders.each do |autoloader|
+ assert_same logger, autoloader.logger
+ end
+ end
+
+ test "verbose = true sets the Rails logger as fallback" do
+ boot
+
+ ActiveSupport::Dependencies.verbose = true
+
+ Rails.autoloaders.each do |autoloader|
+ assert_same Rails.logger, autoloader.logger
+ end
+ end
+
+ test "verbose = false sets loggers to nil" do
+ boot
+
+ ActiveSupport::Dependencies.verbose = true
+ Rails.autoloaders.each do |autoloader|
+ assert autoloader.logger
+ end
+
+ ActiveSupport::Dependencies.verbose = false
+ Rails.autoloaders.each do |autoloader|
+ assert_nil autoloader.logger
+ end
+ end
+
+ test "unhooks" do
+ boot
+
+ assert_equal Module, Module.method(:const_missing).owner
+ assert_equal :no_op, deps.unhook!
+ end
+
+ test "autoloaders.logger=" do
+ boot
+
+ logger = ->(_msg) { }
+ Rails.autoloaders.logger = logger
+
+ Rails.autoloaders.each do |autoloader|
+ assert_same logger, autoloader.logger
+ end
+
+ Rails.autoloaders.logger = Rails.logger
+
+ Rails.autoloaders.each do |autoloader|
+ assert_same Rails.logger, autoloader.logger
+ end
+
+ Rails.autoloaders.logger = nil
+
+ Rails.autoloaders.each do |autoloader|
+ assert_nil autoloader.logger
+ end
+ end
+
+ # This is here because to guarantee classic mode works as always, Zeitwerk
+ # integration does not touch anything in classic. The descendants tracker is a
+ # very small one-liner exception. We leave its main test suite untouched, and
+ # add some minimal safety net here.
+ #
+ # When time passes, things are going to be reorganized (famous last words).
+ test "descendants tracker" do
+ class ::ZeitwerkDTIntegrationTestRoot
+ extend ActiveSupport::DescendantsTracker
+ end
+ class ::ZeitwerkDTIntegrationTestChild < ::ZeitwerkDTIntegrationTestRoot; end
+ class ::ZeitwerkDTIntegrationTestGrandchild < ::ZeitwerkDTIntegrationTestChild; end
+
+ begin
+ app_file "app/models/user.rb", "class User < ZeitwerkDTIntegrationTestRoot; end"
+ app_file "app/models/post.rb", "class Post < ZeitwerkDTIntegrationTestRoot; end"
+ app_file "app/models/tutorial.rb", "class Tutorial < Post; end"
+ boot
+
+ assert User
+ assert Tutorial
+
+ direct_descendants = [ZeitwerkDTIntegrationTestChild, User, Post].to_set
+ assert_equal direct_descendants, ZeitwerkDTIntegrationTestRoot.direct_descendants.to_set
+
+ descendants = direct_descendants.merge([ZeitwerkDTIntegrationTestGrandchild, Tutorial])
+ assert_equal descendants, ZeitwerkDTIntegrationTestRoot.descendants.to_set
+
+ ActiveSupport::DescendantsTracker.clear
+
+ direct_descendants = [ZeitwerkDTIntegrationTestChild].to_set
+ assert_equal direct_descendants, ZeitwerkDTIntegrationTestRoot.direct_descendants.to_set
+
+ descendants = direct_descendants.merge([ZeitwerkDTIntegrationTestGrandchild])
+ assert_equal descendants, ZeitwerkDTIntegrationTestRoot.descendants.to_set
+ ensure
+ Object.send(:remove_const, :ZeitwerkDTIntegrationTestRoot)
+ Object.send(:remove_const, :ZeitwerkDTIntegrationTestChild)
+ Object.send(:remove_const, :ZeitwerkDTIntegrationTestGrandchild)
+ end
+ end
+end