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.rb169
-rw-r--r--railties/test/application/assets_test.rb522
-rw-r--r--railties/test/application/bin_setup_test.rb60
-rw-r--r--railties/test/application/configuration/custom_test.rb45
-rw-r--r--railties/test/application/configuration_test.rb1746
-rw-r--r--railties/test/application/console_test.rb156
-rw-r--r--railties/test/application/current_attributes_integration_test.rb84
-rw-r--r--railties/test/application/dbconsole_test.rb76
-rw-r--r--railties/test/application/generators_test.rb198
-rw-r--r--railties/test/application/help_test.rb23
-rw-r--r--railties/test/application/initializers/frameworks_test.rb274
-rw-r--r--railties/test/application/initializers/hooks_test.rb89
-rw-r--r--railties/test/application/initializers/i18n_test.rb294
-rw-r--r--railties/test/application/initializers/load_path_test.rb109
-rw-r--r--railties/test/application/initializers/notifications_test.rb55
-rw-r--r--railties/test/application/integration_test_case_test.rb73
-rw-r--r--railties/test/application/loading_test.rb371
-rw-r--r--railties/test/application/mailer_previews_test.rb785
-rw-r--r--railties/test/application/middleware/cache_test.rb179
-rw-r--r--railties/test/application/middleware/cookies_test.rb46
-rw-r--r--railties/test/application/middleware/exceptions_test.rb138
-rw-r--r--railties/test/application/middleware/remote_ip_test.rb78
-rw-r--r--railties/test/application/middleware/sendfile_test.rb73
-rw-r--r--railties/test/application/middleware/session_test.rb457
-rw-r--r--railties/test/application/middleware/static_test.rb68
-rw-r--r--railties/test/application/middleware_test.rb310
-rw-r--r--railties/test/application/multiple_applications_test.rb175
-rw-r--r--railties/test/application/paths_test.rb82
-rw-r--r--railties/test/application/per_request_digest_cache_test.rb70
-rw-r--r--railties/test/application/rack/logger_test.rb56
-rw-r--r--railties/test/application/rackup_test.rb42
-rw-r--r--railties/test/application/rake/dbs_test.rb318
-rw-r--r--railties/test/application/rake/dev_test.rb43
-rw-r--r--railties/test/application/rake/framework_test.rb46
-rw-r--r--railties/test/application/rake/log_test.rb33
-rw-r--r--railties/test/application/rake/migrations_test.rb304
-rw-r--r--railties/test/application/rake/notes_test.rb168
-rw-r--r--railties/test/application/rake/restart_test.rb47
-rw-r--r--railties/test/application/rake/tmp_test.rb43
-rw-r--r--railties/test/application/rake_test.rb395
-rw-r--r--railties/test/application/rendering_test.rb44
-rw-r--r--railties/test/application/routing_test.rb680
-rw-r--r--railties/test/application/runner_test.rb133
-rw-r--r--railties/test/application/test_runner_test.rb769
-rw-r--r--railties/test/application/test_test.rb338
-rw-r--r--railties/test/application/url_generation_test.rb57
-rw-r--r--railties/test/application/version_test.rb24
47 files changed, 10345 insertions, 0 deletions
diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb
new file mode 100644
index 0000000000..3e17a1efa5
--- /dev/null
+++ b/railties/test/application/asset_debugging_test.rb
@@ -0,0 +1,169 @@
+require "isolation/abstract_unit"
+require "rack/test"
+
+module ApplicationTests
+ class AssetDebuggingTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ build_app(initializers: true)
+ end
+
+ app_file "app/assets/javascripts/application.js", "//= require_tree ."
+ app_file "app/assets/javascripts/xmlhr.js", "function f1() { alert(); }"
+ app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'application' %>"
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/posts', to: "posts#index"
+ end
+ RUBY
+
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
+ class PostsController < ActionController::Base
+ end
+ RUBY
+
+ ENV["RAILS_ENV"] = "production"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ def get(*)
+ Kernel.silence_warnings { super }
+ end
+
+ test "assets are concatenated when debug is off and compile is off either if debug_assets param is provided" do
+ # config.assets.debug and config.assets.compile are false for production environment
+ ENV["RAILS_ENV"] = "production"
+ output = Dir.chdir(app_path) { `bin/rails assets:precompile --trace 2>&1` }
+ assert $?.success?, output
+
+ # Load app env
+ app "production"
+
+ class ::PostsController < ActionController::Base ; end
+
+ # the debug_assets params isn't used if compile is off
+ get "/posts?debug_assets=true"
+ assert_match(/<script src="\/assets\/application-([0-z]+)\.js"><\/script>/, last_response.body)
+ assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"><\/script>/, last_response.body)
+ end
+
+ test "assets aren't concatenated when compile is true is on and debug_assets params is true" do
+ add_to_env_config "production", "config.assets.compile = true"
+
+ # Load app env
+ app "production"
+
+ class ::PostsController < ActionController::Base ; end
+
+ get "/posts?debug_assets=true"
+ assert_match(/<script src="\/assets\/application(\.self)?-([0-z]+)\.js\?body=1"><\/script>/, last_response.body)
+ assert_match(/<script src="\/assets\/xmlhr(\.self)?-([0-z]+)\.js\?body=1"><\/script>/, last_response.body)
+ end
+
+ test "public path and tag methods are not over-written by the asset pipeline" do
+ contents = "doesnotexist"
+ cases = {
+ asset_path: %r{/#{contents}},
+ image_path: %r{/images/#{contents}},
+ video_path: %r{/videos/#{contents}},
+ audio_path: %r{/audios/#{contents}},
+ font_path: %r{/fonts/#{contents}},
+ javascript_path: %r{/javascripts/#{contents}},
+ stylesheet_path: %r{/stylesheets/#{contents}},
+ image_tag: %r{<img src="/images/#{contents}"},
+ favicon_link_tag: %r{<link rel="shortcut icon" type="image/x-icon" href="/images/#{contents}" />},
+ stylesheet_link_tag: %r{<link rel="stylesheet" media="screen" href="/stylesheets/#{contents}.css" />},
+ javascript_include_tag: %r{<script src="/javascripts/#{contents}.js">},
+ audio_tag: %r{<audio src="/audios/#{contents}"></audio>},
+ video_tag: %r{<video src="/videos/#{contents}"></video>}
+ }
+
+ cases.each do |(view_method, tag_match)|
+ app_file "app/views/posts/index.html.erb", "<%= #{view_method} '#{contents}', skip_pipeline: true %>"
+
+ app "development"
+
+ class ::PostsController < ActionController::Base ; end
+
+ get "/posts?debug_assets=true"
+
+ body = last_response.body
+ assert_match(tag_match, body, "Expected `#{view_method}` to produce a match to #{tag_match}, but did not: #{body}")
+ end
+ end
+
+ test "public url methods are not over-written by the asset pipeline" do
+ contents = "doesnotexist"
+ cases = {
+ asset_url: %r{http://example.org/#{contents}},
+ image_url: %r{http://example.org/images/#{contents}},
+ video_url: %r{http://example.org/videos/#{contents}},
+ audio_url: %r{http://example.org/audios/#{contents}},
+ font_url: %r{http://example.org/fonts/#{contents}},
+ javascript_url: %r{http://example.org/javascripts/#{contents}},
+ stylesheet_url: %r{http://example.org/stylesheets/#{contents}},
+ }
+
+ cases.each do |(view_method, tag_match)|
+ app_file "app/views/posts/index.html.erb", "<%= #{view_method} '#{contents}', skip_pipeline: true %>"
+
+ app "development"
+
+ class ::PostsController < ActionController::Base ; end
+
+ get "/posts?debug_assets=true"
+
+ body = last_response.body
+ assert_match(tag_match, body, "Expected `#{view_method}` to produce a match to #{tag_match}, but did not: #{body}")
+ end
+ end
+
+ test "{ skip_pipeline: true } does not use the asset pipeline" do
+ cases = {
+ /\/assets\/application-.*.\.js/ => {},
+ /application.js/ => { skip_pipeline: true },
+ }
+ cases.each do |(tag_match, options_hash)|
+ app_file "app/views/posts/index.html.erb", "<%= asset_path('application.js', #{options_hash}) %>"
+
+ app "development"
+
+ class ::PostsController < ActionController::Base ; end
+
+ get "/posts?debug_assets=true"
+
+ body = last_response.body.strip
+ assert_match(tag_match, body, "Expected `asset_path` with `#{options_hash}` to produce a match to #{tag_match}, but did not: #{body}")
+ end
+ end
+
+ test "public_compute_asset_path does not use the asset pipeline" do
+ cases = {
+ compute_asset_path: /\/assets\/application-.*.\.js/,
+ public_compute_asset_path: /application.js/,
+ }
+
+ cases.each do |(view_method, tag_match)|
+ app_file "app/views/posts/index.html.erb", "<%= #{ view_method } 'application.js' %>"
+
+ app "development"
+
+ class ::PostsController < ActionController::Base ; end
+
+ get "/posts?debug_assets=true"
+
+ body = last_response.body.strip
+ assert_match(tag_match, body, "Expected `#{view_method}` to produce a match to #{ tag_match }, but did not: #{ body }")
+ end
+ end
+ end
+end
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
new file mode 100644
index 0000000000..f38cacd6da
--- /dev/null
+++ b/railties/test/application/assets_test.rb
@@ -0,0 +1,522 @@
+require "isolation/abstract_unit"
+require "rack/test"
+require "active_support/json"
+
+module ApplicationTests
+ class AssetsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app(initializers: true)
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def precompile!(env = nil)
+ with_env env.to_h do
+ quietly do
+ precompile_task = "bin/rails assets:precompile --trace 2>&1"
+ output = Dir.chdir(app_path) { %x[ #{precompile_task} ] }
+ assert $?.success?, output
+ output
+ end
+ end
+ end
+
+ def with_env(env)
+ env.each { |k, v| ENV[k.to_s] = v }
+ yield
+ ensure
+ env.each_key { |k| ENV.delete k.to_s }
+ end
+
+ def clean_assets!
+ quietly do
+ assert Dir.chdir(app_path) { system("bin/rails assets:clobber") }
+ end
+ end
+
+ def assert_file_exists(filename)
+ globbed = Dir[filename]
+ assert globbed.one?, "Found #{globbed.size} files matching #{filename}. All files in the directory: #{Dir.entries(File.dirname(filename)).inspect}"
+ end
+
+ def assert_no_file_exists(filename)
+ assert !File.exist?(filename), "#{filename} does exist"
+ end
+
+ test "assets routes have higher priority" do
+ app_file "app/assets/images/rails.png", "notactuallyapng"
+ app_file "app/assets/javascripts/demo.js.erb", "a = <%= image_path('rails.png').inspect %>;"
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '*path', to: lambda { |env| [200, { "Content-Type" => "text/html" }, ["Not an asset"]] }
+ end
+ RUBY
+
+ add_to_env_config "development", "config.assets.digest = false"
+
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ require "#{app_path}/config/environment"
+ end
+
+ get "/assets/demo.js"
+ 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 !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();"
+ app_file "app/assets/javascripts/foo/application.js", "alert();"
+
+ precompile!
+
+ files = Dir["#{app_path}/public/assets/application-*.js"]
+ files << Dir["#{app_path}/public/assets/foo/application-*.js"].first
+ files.each do |file|
+ assert_not_nil file, "Expected application.js asset to be generated, but none found"
+ assert_equal "alert();\n", File.read(file)
+ end
+ end
+
+ def test_precompile_does_not_hit_the_database
+ app_file "app/assets/config/manifest.js", "//= link_tree ../javascripts"
+ app_file "app/assets/javascripts/application.js", "alert();"
+ app_file "app/assets/javascripts/foo/application.js", "alert();"
+ app_file "app/controllers/users_controller.rb", <<-eoruby
+ class UsersController < ApplicationController; end
+ eoruby
+ app_file "app/models/user.rb", <<-eoruby
+ class User < ActiveRecord::Base; raise 'should not be reached'; end
+ eoruby
+
+ precompile! \
+ RAILS_ENV: "production",
+ DATABASE_URL: "postgresql://baduser:badpass@127.0.0.1/dbname"
+
+ files = Dir["#{app_path}/public/assets/application-*.js"]
+ files << Dir["#{app_path}/public/assets/foo/application-*.js"].first
+ files.each do |file|
+ assert_not_nil file, "Expected application.js asset to be generated, but none found"
+ assert_equal "alert();".strip, File.read(file).strip
+ end
+ end
+
+ test "precompile application.js and application.css and all other non JS/CSS files" do
+ app_file "app/assets/javascripts/application.js", "alert();"
+ app_file "app/assets/stylesheets/application.css", "body{}"
+
+ app_file "app/assets/javascripts/someapplication.js", "alert();"
+ app_file "app/assets/stylesheets/someapplication.css", "body{}"
+
+ app_file "app/assets/javascripts/something.min.js", "alert();"
+ app_file "app/assets/stylesheets/something.min.css", "body{}"
+
+ app_file "app/assets/javascripts/something.else.js.erb", "alert();"
+ app_file "app/assets/stylesheets/something.else.css.erb", "body{}"
+
+ images_should_compile = ["a.png", "happyface.png", "happy_face.png", "happy.face.png",
+ "happy-face.png", "happy.happy_face.png", "happy_happy.face.png",
+ "happy.happy.face.png", "-happy.png", "-happy.face.png",
+ "_happy.face.png", "_happy.png"]
+
+ images_should_compile.each do |filename|
+ app_file "app/assets/images/#{filename}", "happy"
+ end
+
+ precompile!
+
+ images_should_compile = ["a-*.png", "happyface-*.png", "happy_face-*.png", "happy.face-*.png",
+ "happy-face-*.png", "happy.happy_face-*.png", "happy_happy.face-*.png",
+ "happy.happy.face-*.png", "-happy-*.png", "-happy.face-*.png",
+ "_happy.face-*.png", "_happy-*.png"]
+
+ images_should_compile.each do |filename|
+ assert_file_exists("#{app_path}/public/assets/#{filename}")
+ end
+
+ assert_file_exists("#{app_path}/public/assets/application-*.js")
+ assert_file_exists("#{app_path}/public/assets/application-*.css")
+
+ assert_no_file_exists("#{app_path}/public/assets/someapplication-*.js")
+ assert_no_file_exists("#{app_path}/public/assets/someapplication-*.css")
+
+ assert_no_file_exists("#{app_path}/public/assets/something.min-*.js")
+ assert_no_file_exists("#{app_path}/public/assets/something.min-*.css")
+
+ assert_no_file_exists("#{app_path}/public/assets/something.else-*.js")
+ assert_no_file_exists("#{app_path}/public/assets/something.else-*.css")
+ end
+
+ test "precompile something.js for directory containing index file" do
+ add_to_config "config.assets.precompile = [ 'something.js' ]"
+ app_file "app/assets/javascripts/something/index.js.erb", "alert();"
+
+ precompile!
+
+ assert_file_exists("#{app_path}/public/assets/something/index-*.js")
+ end
+
+ test "precompile use assets defined in app env config" do
+ add_to_env_config "production", 'config.assets.precompile = [ "something.js" ]'
+ app_file "app/assets/javascripts/something.js.erb", "alert();"
+
+ precompile! RAILS_ENV: "production"
+
+ assert_file_exists("#{app_path}/public/assets/something-*.js")
+ end
+
+ test "sprockets cache is not shared between environments" do
+ app_file "app/assets/images/rails.png", "notactuallyapng"
+ app_file "app/assets/stylesheets/application.css.erb", "body { background: '<%= asset_path('rails.png') %>'; }"
+ add_to_env_config "production", 'config.assets.prefix = "production_assets"'
+
+ precompile!
+
+ assert_file_exists("#{app_path}/public/assets/application-*.css")
+
+ file = Dir["#{app_path}/public/assets/application-*.css"].first
+ assert_match(/assets\/rails-([0-z]+)\.png/, File.read(file))
+
+ precompile! RAILS_ENV: "production"
+
+ assert_file_exists("#{app_path}/public/production_assets/application-*.css")
+
+ file = Dir["#{app_path}/public/production_assets/application-*.css"].first
+ assert_match(/production_assets\/rails-([0-z]+)\.png/, File.read(file))
+ end
+
+ test "precompile use assets defined in app config and reassigned in app env config" do
+ add_to_config 'config.assets.precompile = [ "something_manifest.js" ]'
+ add_to_env_config "production", 'config.assets.precompile += [ "another_manifest.js" ]'
+
+ app_file "app/assets/config/something_manifest.js", "//= link something.js"
+ app_file "app/assets/config/another_manifest.js", "//= link another.js"
+
+ app_file "app/assets/javascripts/something.js.erb", "alert();"
+ app_file "app/assets/javascripts/another.js.erb", "alert();"
+
+ precompile! RAILS_ENV: "production"
+
+ assert_file_exists("#{app_path}/public/assets/something_manifest-*.js")
+ assert_file_exists("#{app_path}/public/assets/something-*.js")
+ assert_file_exists("#{app_path}/public/assets/another_manifest-*.js")
+ assert_file_exists("#{app_path}/public/assets/another-*.js")
+ end
+
+ test "asset pipeline should use a Sprockets::CachedEnvironment when config.assets.digest is true" do
+ add_to_config "config.action_controller.perform_caching = false"
+ add_to_env_config "production", "config.assets.compile = true"
+
+ # Load app env
+ app "production"
+
+ assert_equal Sprockets::CachedEnvironment, Rails.application.assets.class
+ end
+
+ test "precompile creates a manifest file with all the assets listed" do
+ app_file "app/assets/images/rails.png", "notactuallyapng"
+ app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
+ app_file "app/assets/javascripts/application.js", "alert();"
+
+ precompile!
+
+ manifest = Dir["#{app_path}/public/assets/.sprockets-manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ assert_match(/application-([0-z]+)\.js/, assets["assets"]["application.js"])
+ assert_match(/application-([0-z]+)\.css/, assets["assets"]["application.css"])
+ end
+
+ test "the manifest file should be saved by default in the same assets folder" do
+ app_file "app/assets/javascripts/application.js", "alert();"
+ add_to_config "config.assets.prefix = '/x'"
+
+ precompile!
+
+ manifest = Dir["#{app_path}/public/x/.sprockets-manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ assert_match(/application-([0-z]+)\.js/, assets["assets"]["application.js"])
+ end
+
+ test "assets do not require any assets group gem when manifest file is present" do
+ app_file "app/assets/javascripts/application.js", "alert();"
+ add_to_env_config "production", "config.public_file_server.enabled = true"
+
+ precompile! RAILS_ENV: "production"
+
+ manifest = Dir["#{app_path}/public/assets/.sprockets-manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ asset_path = assets["assets"]["application.js"]
+
+ # Load app env
+ app "production"
+
+ # Checking if Uglifier is defined we can know if Sprockets was reached or not
+ assert !defined?(Uglifier)
+ get "/assets/#{asset_path}"
+ assert_match "alert()", last_response.body
+ assert !defined?(Uglifier)
+ end
+
+ test "precompile properly refers files referenced with asset_path" do
+ app_file "app/assets/images/rails.png", "notactuallyapng"
+ app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
+
+ precompile!
+
+ file = Dir["#{app_path}/public/assets/application-*.css"].first
+ assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file))
+ end
+
+ test "precompile shouldn't use the digests present in manifest.json" do
+ app_file "app/assets/images/rails.png", "notactuallyapng"
+
+ app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
+
+ precompile! RAILS_ENV: "production"
+
+ manifest = Dir["#{app_path}/public/assets/.sprockets-manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ asset_path = assets["assets"]["application.css"]
+
+ app_file "app/assets/images/rails.png", "p { url: change }"
+
+ precompile!
+
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ assert_not_equal asset_path, assets["assets"]["application.css"]
+ end
+
+ test "precompile appends the MD5 hash to files referenced with asset_path and run in production with digest true" do
+ app_file "app/assets/images/rails.png", "notactuallyapng"
+ app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
+
+ precompile! RAILS_ENV: "production"
+
+ file = Dir["#{app_path}/public/assets/application-*.css"].first
+ assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file))
+ end
+
+ test "precompile should handle utf8 filenames" do
+ filename = "レイルズ.png"
+ app_file "app/assets/images/#{filename}", "not an image really"
+ app_file "app/assets/config/manifest.js", "//= link_tree ../images"
+ add_to_config "config.assets.precompile = %w(manifest.js)"
+
+ precompile!
+
+ manifest = Dir["#{app_path}/public/assets/.sprockets-manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ assert asset_path = assets["assets"].find { |(k, _)| k && k =~ /.png/ }[1]
+
+ # Load app env
+ app "development"
+
+ get "/assets/#{URI.parser.escape(asset_path)}"
+ assert_match "not an image really", last_response.body
+ assert_file_exists("#{app_path}/public/assets/#{asset_path}")
+ end
+
+ test "assets are cleaned up properly" do
+ app_file "public/assets/application.js", "alert();"
+ app_file "public/assets/application.css", "a { color: green; }"
+ app_file "public/assets/subdir/broken.png", "not really an image file"
+
+ clean_assets!
+
+ files = Dir["#{app_path}/public/assets/**/*"]
+ assert_equal 0, files.length, "Expected no assets, but found #{files.join(', ')}"
+ end
+
+ test "assets routes are not drawn when compilation is disabled" do
+ app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
+ add_to_config "config.assets.compile = false"
+
+ # Load app env
+ app "production"
+
+ get "/assets/demo.js"
+ assert_equal 404, last_response.status
+ end
+
+ test "does not stream session cookies back" do
+ app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/omg', :to => "omg#index"
+ end
+ RUBY
+
+ add_to_env_config "development", "config.assets.digest = false"
+
+ # Load app env
+ app "development"
+
+ class ::OmgController < ActionController::Base
+ def index
+ flash[:cool_story] = true
+ render plain: "ok"
+ end
+ end
+
+ get "/omg"
+ assert_equal "ok", last_response.body
+
+ get "/assets/demo.js"
+ assert_match "alert()", last_response.body
+ assert_nil last_response.headers["Set-Cookie"]
+ end
+
+ test "files in any assets/ directories are not added to Sprockets" do
+ %w[app lib vendor].each do |dir|
+ app_file "#{dir}/assets/#{dir}_test.erb", "testing"
+ end
+
+ app_file "app/assets/javascripts/demo.js", "alert();"
+
+ add_to_env_config "development", "config.assets.digest = false"
+
+ # Load app env
+ app "development"
+
+ get "/assets/demo.js"
+ assert_match "alert();", last_response.body
+ assert_equal 200, last_response.status
+ end
+
+ test "assets are concatenated when debug is off and compile is off either if debug_assets param is provided" do
+ app_with_assets_in_view
+
+ # config.assets.debug and config.assets.compile are false for production environment
+ precompile! RAILS_ENV: "production"
+
+ # Load app env
+ app "production"
+
+ class ::PostsController < ActionController::Base ; end
+
+ # the debug_assets params isn't used if compile is off
+ get "/posts?debug_assets=true"
+ assert_match(/<script src="\/assets\/application-([0-z]+)\.js"><\/script>/, last_response.body)
+ assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"><\/script>/, last_response.body)
+ end
+
+ test "assets can access model information when precompiling" do
+ app_file "app/models/post.rb", "class Post; end"
+ app_file "app/assets/javascripts/application.js", "//= require_tree ."
+ app_file "app/assets/javascripts/xmlhr.js.erb", "<%= Post.name %>"
+
+ precompile!
+
+ assert_match(/Post;/, File.read(Dir["#{app_path}/public/assets/application-*.js"].first))
+ end
+
+ test "initialization on the assets group should set assets_dir" do
+ require "#{app_path}/config/application"
+ Rails.application.initialize!(:assets)
+ assert_not_nil Rails.application.config.action_controller.assets_dir
+ end
+
+ test "enhancements to assets:precompile should only run once" do
+ app_file "lib/tasks/enhance.rake", "Rake::Task['assets:precompile'].enhance { puts 'enhancement' }"
+ output = precompile!
+ assert_equal 1, output.scan("enhancement").size
+ end
+
+ test "digested assets are not mistakenly removed" do
+ app_file "app/assets/application.js", "alert();"
+ 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"
+ end
+
+ test "digested assets are removed from configured path" do
+ app_file "public/production_assets/application.js", "alert();"
+ add_to_env_config "production", "config.assets.prefix = 'production_assets'"
+
+ ENV["RAILS_ENV"] = nil
+
+ clean_assets!
+
+ files = Dir["#{app_path}/public/production_assets/application-*.js"]
+ 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
+ app_with_assets_in_view
+ add_to_config "config.asset_host = 'example.com'"
+ add_to_env_config "development", "config.assets.digest = false"
+
+ # Load app env
+ app "development"
+
+ class ::PostsController < ActionController::Base; end
+
+ get "/posts", {}, "HTTPS" => "off"
+ assert_match('src="http://example.com/assets/application.self.js', last_response.body)
+ get "/posts", {}, "HTTPS" => "on"
+ 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
+ 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}"
+ add_to_config "config.asset_host = 'example.com'"
+ add_to_env_config "development", "config.assets.digest = false"
+
+ precompile!
+
+ assert_match "src='//example.com/assets/rails.png'", File.read(Dir["#{app_path}/public/assets/image_loader-*.js"].first)
+ end
+
+ test "asset paths should use RAILS_RELATIVE_URL_ROOT by default" do
+ ENV["RAILS_RELATIVE_URL_ROOT"] = "/sub/uri"
+ app_file "app/assets/images/rails.png", "notreallyapng"
+ app_file "app/assets/javascripts/app.js.erb", "var src='<%= image_path('rails.png') %>';"
+ add_to_config "config.assets.precompile = %w{rails.png app.js}"
+ add_to_env_config "development", "config.assets.digest = false"
+
+ precompile!
+
+ assert_match "src='/sub/uri/assets/rails.png'", File.read(Dir["#{app_path}/public/assets/app-*.js"].first)
+ end
+
+ private
+
+ def app_with_assets_in_view
+ app_file "app/assets/javascripts/application.js", "//= require_tree ."
+ app_file "app/assets/javascripts/xmlhr.js", "function f1() { alert(); }"
+ app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'application' %>"
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/posts', :to => "posts#index"
+ end
+ RUBY
+ end
+ end
+end
diff --git a/railties/test/application/bin_setup_test.rb b/railties/test/application/bin_setup_test.rb
new file mode 100644
index 0000000000..0fb995900f
--- /dev/null
+++ b/railties/test/application/bin_setup_test.rb
@@ -0,0 +1,60 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class BinSetupTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def test_bin_setup
+ Dir.chdir(app_path) do
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: 20140423102712) do
+ create_table(:articles) {}
+ end
+ RUBY
+
+ list_tables = lambda { `bin/rails runner 'p ActiveRecord::Base.connection.tables'`.strip }
+ File.write("log/test.log", "zomg!")
+
+ assert_equal "[]", list_tables.call
+ assert_equal 5, File.size("log/test.log")
+ assert_not File.exist?("tmp/restart.txt")
+ `bin/setup 2>&1`
+ assert_equal 0, File.size("log/test.log")
+ assert_equal '["articles", "schema_migrations", "ar_internal_metadata"]', list_tables.call
+ assert File.exist?("tmp/restart.txt")
+ end
+ end
+
+ def test_bin_setup_output
+ Dir.chdir(app_path) do
+ app_file "db/schema.rb", ""
+
+ output = `bin/setup 2>&1`
+
+ # Ignore line that's only output by Bundler < 1.14
+ output.sub!(/^Resolving dependencies\.\.\.\n/, "")
+
+ assert_equal(<<-OUTPUT, output)
+== Installing dependencies ==
+The Gemfile's dependencies are satisfied
+
+== Preparing database ==
+Created database 'db/development.sqlite3'
+Created database 'db/test.sqlite3'
+
+== Removing old logs and tempfiles ==
+
+== Restarting application server ==
+ OUTPUT
+ end
+ end
+ end
+end
diff --git a/railties/test/application/configuration/custom_test.rb b/railties/test/application/configuration/custom_test.rb
new file mode 100644
index 0000000000..8360b7bf4b
--- /dev/null
+++ b/railties/test/application/configuration/custom_test.rb
@@ -0,0 +1,45 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module ConfigurationTests
+ class CustomTest < ActiveSupport::TestCase
+ def setup
+ build_app
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "access custom configuration point" do
+ add_to_config <<-RUBY
+ config.x.payment_processing.schedule = :daily
+ config.x.payment_processing.retries = 3
+ config.x.super_debugger = true
+ config.x.hyper_debugger = false
+ config.x.nil_debugger = nil
+ RUBY
+ require_environment
+
+ x = Rails.configuration.x
+ assert_equal :daily, x.payment_processing.schedule
+ assert_equal 3, x.payment_processing.retries
+ assert_equal true, x.super_debugger
+ assert_equal false, x.hyper_debugger
+ assert_nil x.nil_debugger
+ assert_nil x.i_do_not_exist.zomg
+
+ # test that custom configuration responds to all messages
+ assert_equal true, x.respond_to?(:i_do_not_exist)
+ assert_kind_of Method, x.method(:i_do_not_exist)
+ assert_kind_of ActiveSupport::OrderedOptions, x.i_do_not_exist
+ end
+
+ private
+ def require_environment
+ require "#{app_path}/config/environment"
+ end
+ end
+ end
+end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
new file mode 100644
index 0000000000..9f62ca8eb8
--- /dev/null
+++ b/railties/test/application/configuration_test.rb
@@ -0,0 +1,1746 @@
+require "isolation/abstract_unit"
+require "rack/test"
+require "env_helpers"
+
+class ::MyMailInterceptor
+ def self.delivering_email(email); email; end
+end
+
+class ::MyOtherMailInterceptor < ::MyMailInterceptor; end
+
+class ::MyPreviewMailInterceptor
+ def self.previewing_email(email); email; end
+end
+
+class ::MyOtherPreviewMailInterceptor < ::MyPreviewMailInterceptor; end
+
+class ::MyMailObserver
+ def self.delivered_email(email); email; end
+end
+
+class ::MyOtherMailObserver < ::MyMailObserver; end
+
+module ApplicationTests
+ class ConfigurationTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+ include EnvHelpers
+
+ def new_app
+ File.expand_path("#{app_path}/../new_app")
+ end
+
+ def copy_app
+ FileUtils.cp_r(app_path, new_app)
+ end
+
+ def app(env = "development")
+ @app ||= begin
+ ENV["RAILS_ENV"] = env
+
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ require "#{app_path}/config/environment"
+ end
+
+ Rails.application
+ ensure
+ ENV.delete "RAILS_ENV"
+ end
+ end
+
+ def setup
+ build_app
+ suppress_default_config
+ end
+
+ def teardown
+ teardown_app
+ FileUtils.rm_rf(new_app) if File.directory?(new_app)
+ end
+
+ def suppress_default_config
+ FileUtils.mv("#{app_path}/config/environments", "#{app_path}/config/__environments__")
+ end
+
+ def restore_default_config
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ FileUtils.mv("#{app_path}/config/__environments__", "#{app_path}/config/environments")
+ end
+
+ test "Rails.env does not set the RAILS_ENV environment variable which would leak out into rake tasks" do
+ require "rails"
+
+ switch_env "RAILS_ENV", nil do
+ Rails.env = "development"
+ assert_equal "development", Rails.env
+ assert_nil ENV["RAILS_ENV"]
+ end
+ end
+
+ test "Rails.env falls back to development if RAILS_ENV is blank and RACK_ENV is nil" do
+ with_rails_env("") do
+ assert_equal "development", Rails.env
+ end
+ end
+
+ test "Rails.env falls back to development if RACK_ENV is blank and RAILS_ENV is nil" do
+ with_rack_env("") do
+ assert_equal "development", Rails.env
+ end
+ end
+
+ test "By default logs tags are not set in development" do
+ restore_default_config
+
+ with_rails_env "development" do
+ app "development"
+ assert Rails.application.config.log_tags.blank?
+ end
+ end
+
+ test "By default logs are tagged with :request_id in production" do
+ restore_default_config
+
+ with_rails_env "production" do
+ app "production"
+ assert_equal [:request_id], Rails.application.config.log_tags
+ end
+ end
+
+ test "lib dir is on LOAD_PATH during config" do
+ app_file "lib/my_logger.rb", <<-RUBY
+ require "logger"
+ class MyLogger < ::Logger
+ end
+ RUBY
+ add_to_top_of_config <<-RUBY
+ require 'my_logger'
+ config.logger = MyLogger.new STDOUT
+ RUBY
+
+ app "development"
+
+ assert_equal "MyLogger", Rails.application.config.logger.class.name
+ end
+
+ test "a renders exception on pending migration" do
+ add_to_config <<-RUBY
+ config.active_record.migration_error = :page_load
+ config.consider_all_requests_local = true
+ config.action_dispatch.show_exceptions = true
+ RUBY
+
+ app_file "db/migrate/20140708012246_create_user.rb", <<-RUBY
+ class CreateUser < ActiveRecord::Migration::Current
+ def change
+ create_table :users
+ end
+ end
+ RUBY
+
+ app "development"
+
+ ActiveRecord::Migrator.migrations_paths = ["#{app_path}/db/migrate"]
+
+ begin
+ get "/foo"
+ assert_equal 500, last_response.status
+ assert_match "ActiveRecord::PendingMigrationError", last_response.body
+ ensure
+ ActiveRecord::Migrator.migrations_paths = nil
+ end
+ end
+
+ test "Rails.groups returns available groups" do
+ require "rails"
+
+ Rails.env = "development"
+ assert_equal [:default, "development"], Rails.groups
+ assert_equal [:default, "development", :assets], Rails.groups(assets: [:development])
+ assert_equal [:default, "development", :another, :assets], Rails.groups(:another, assets: %w(development))
+
+ Rails.env = "test"
+ assert_equal [:default, "test"], Rails.groups(assets: [:development])
+
+ ENV["RAILS_GROUPS"] = "javascripts,stylesheets"
+ assert_equal [:default, "test", "javascripts", "stylesheets"], Rails.groups
+ end
+
+ test "Rails.application is nil until app is initialized" do
+ require "rails"
+ assert_nil Rails.application
+ app "development"
+ assert_equal AppTemplate::Application.instance, Rails.application
+ end
+
+ test "Rails.application responds to all instance methods" do
+ app "development"
+ assert_equal Rails.application.routes_reloader, AppTemplate::Application.routes_reloader
+ end
+
+ test "Rails::Application responds to paths" do
+ app "development"
+ assert_equal ["#{app_path}/app/views"], AppTemplate::Application.paths["app/views"].expanded
+ end
+
+ test "the application root is set correctly" do
+ app "development"
+ assert_equal Pathname.new(app_path), Rails.application.root
+ end
+
+ test "the application root can be seen from the application singleton" do
+ app "development"
+ assert_equal Pathname.new(app_path), AppTemplate::Application.root
+ end
+
+ test "the application root can be set" do
+ copy_app
+ add_to_config <<-RUBY
+ config.root = '#{new_app}'
+ RUBY
+
+ use_frameworks []
+
+ app "development"
+
+ assert_equal Pathname.new(new_app), Rails.application.root
+ end
+
+ test "the application root is Dir.pwd if there is no config.ru" do
+ File.delete("#{app_path}/config.ru")
+
+ use_frameworks []
+
+ Dir.chdir("#{app_path}") do
+ app "development"
+ assert_equal Pathname.new("#{app_path}"), Rails.application.root
+ end
+ end
+
+ test "Rails.root should be a Pathname" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ RUBY
+
+ app "development"
+
+ assert_instance_of Pathname, Rails.root
+ end
+
+ test "Rails.public_path should be a Pathname" do
+ add_to_config <<-RUBY
+ config.paths["public"] = "somewhere"
+ RUBY
+
+ app "development"
+
+ assert_instance_of Pathname, Rails.public_path
+ end
+
+ test "initialize an eager loaded, cache classes app" do
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ app "development"
+
+ assert_equal :require, ActiveSupport::Dependencies.mechanism
+ end
+
+ test "application is always added to eager_load namespaces" do
+ app "development"
+ assert_includes Rails.application.config.eager_load_namespaces, AppTemplate::Application
+ end
+
+ test "the application can be eager loaded even when there are no frameworks" do
+ FileUtils.rm_rf("#{app_path}/app/models/application_record.rb")
+ FileUtils.rm_rf("#{app_path}/app/mailers/application_mailer.rb")
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ use_frameworks []
+
+ assert_nothing_raised do
+ app "development"
+ end
+ end
+
+ test "filter_parameters should be able to set via config.filter_parameters" do
+ add_to_config <<-RUBY
+ config.filter_parameters += [ :foo, 'bar', lambda { |key, value|
+ value = value.reverse if key =~ /baz/
+ }]
+ RUBY
+
+ assert_nothing_raised do
+ app "development"
+ end
+ end
+
+ test "filter_parameters should be able to set via config.filter_parameters in an initializer" do
+ app_file "config/initializers/filter_parameters_logging.rb", <<-RUBY
+ Rails.application.config.filter_parameters += [ :password, :foo, 'bar' ]
+ RUBY
+
+ app "development"
+
+ assert_equal [:password, :foo, "bar"], Rails.application.env_config["action_dispatch.parameter_filter"]
+ end
+
+ test "config.to_prepare is forwarded to ActionDispatch" do
+ $prepared = false
+
+ add_to_config <<-RUBY
+ config.to_prepare do
+ $prepared = true
+ end
+ RUBY
+
+ assert !$prepared
+
+ app "development"
+
+ get "/"
+ assert $prepared
+ end
+
+ def assert_utf8
+ assert_equal Encoding::UTF_8, Encoding.default_external
+ assert_equal Encoding::UTF_8, Encoding.default_internal
+ end
+
+ test "skipping config.encoding still results in 'utf-8' as the default" do
+ app "development"
+ assert_utf8
+ end
+
+ test "config.encoding sets the default encoding" do
+ add_to_config <<-RUBY
+ config.encoding = "utf-8"
+ RUBY
+
+ app "development"
+ assert_utf8
+ end
+
+ test "config.paths.public sets Rails.public_path" do
+ add_to_config <<-RUBY
+ config.paths["public"] = "somewhere"
+ RUBY
+
+ app "development"
+ assert_equal Pathname.new(app_path).join("somewhere"), Rails.public_path
+ end
+
+ test "In production mode, config.public_file_server.enabled is off by default" do
+ restore_default_config
+
+ with_rails_env "production" do
+ app "production"
+ assert_not app.config.public_file_server.enabled
+ end
+ end
+
+ test "In production mode, config.public_file_server.enabled is enabled when RAILS_SERVE_STATIC_FILES is set" do
+ restore_default_config
+
+ with_rails_env "production" do
+ switch_env "RAILS_SERVE_STATIC_FILES", "1" do
+ app "production"
+ assert app.config.public_file_server.enabled
+ end
+ end
+ end
+
+ test "In production mode, STDOUT logging is enabled when RAILS_LOG_TO_STDOUT is set" do
+ restore_default_config
+
+ with_rails_env "production" do
+ switch_env "RAILS_LOG_TO_STDOUT", "1" do
+ app "production"
+ assert ActiveSupport::Logger.logger_outputs_to?(app.config.logger, STDOUT)
+ end
+ end
+ end
+
+ test "In production mode, config.public_file_server.enabled is disabled when RAILS_SERVE_STATIC_FILES is blank" do
+ restore_default_config
+
+ with_rails_env "production" do
+ switch_env "RAILS_SERVE_STATIC_FILES", " " do
+ app "production"
+ assert_not app.config.public_file_server.enabled
+ end
+ end
+ end
+
+ test "Use key_generator when secret_key_base is set" do
+ make_basic_app do |application|
+ application.secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
+ application.config.session_store :disabled
+ end
+
+ class ::OmgController < ActionController::Base
+ def index
+ cookies.signed[:some_key] = "some_value"
+ render plain: cookies[:some_key]
+ end
+ end
+
+ get "/"
+
+ secret = app.key_generator.generate_key("signed cookie")
+ verifier = ActiveSupport::MessageVerifier.new(secret)
+ assert_equal "some_value", verifier.verify(last_response.body)
+ end
+
+ test "application verifier can be used in the entire application" do
+ make_basic_app do |application|
+ application.secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
+ application.config.session_store :disabled
+ end
+
+ message = app.message_verifier(:sensitive_value).generate("some_value")
+
+ assert_equal "some_value", Rails.application.message_verifier(:sensitive_value).verify(message)
+
+ secret = app.key_generator.generate_key("sensitive_value")
+ verifier = ActiveSupport::MessageVerifier.new(secret)
+ assert_equal "some_value", verifier.verify(message)
+ end
+
+ test "application message verifier can be used when the key_generator is ActiveSupport::LegacyKeyGenerator" 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:
+ YAML
+
+ app "development"
+
+ assert_equal app.env_config["action_dispatch.key_generator"], Rails.application.key_generator
+ assert_equal app.env_config["action_dispatch.key_generator"].class, ActiveSupport::LegacyKeyGenerator
+ message = app.message_verifier(:sensitive_value).generate("some_value")
+ assert_equal "some_value", Rails.application.message_verifier(:sensitive_value).verify(message)
+ end
+
+ test "warns when secrets.secret_key_base is blank and config.secret_token is set" 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:
+ YAML
+
+ app "development"
+
+ assert_deprecated(/You didn't set `secret_key_base`./) do
+ app.env_config
+ end
+ end
+
+ test "raise when secrets.secret_key_base is not a type of string" do
+ app_file "config/secrets.yml", <<-YAML
+ development:
+ secret_key_base: 123
+ YAML
+
+ app "development"
+
+ assert_raise(ArgumentError) do
+ app.key_generator
+ 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.secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
+ application.config.session_store :disabled
+ end
+
+ default_verifier = app.message_verifier(:sensitive_value)
+ text_verifier = app.message_verifier(:text)
+
+ message = text_verifier.generate("some_value")
+
+ assert_equal "some_value", text_verifier.verify(message)
+ assert_raises ActiveSupport::MessageVerifier::InvalidSignature do
+ default_verifier.verify(message)
+ end
+
+ assert_equal default_verifier.object_id, app.message_verifier(:sensitive_value).object_id
+ assert_not_equal default_verifier.object_id, text_verifier.object_id
+ end
+
+ test "secrets.secret_key_base is used when config/secrets.yml is present" do
+ app_file "config/secrets.yml", <<-YAML
+ development:
+ secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
+ YAML
+
+ app "development"
+ assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secrets.secret_key_base
+ end
+
+ test "secret_key_base is copied from config to secrets when not set" do
+ remove_file "config/secrets.yml"
+ app_file "config/initializers/secret_token.rb", <<-RUBY
+ Rails.application.config.secret_key_base = "3b7cd727ee24e8444053437c36cc66c3"
+ RUBY
+
+ app "development"
+ 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:
+ secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
+ aws_access_key_id: myamazonaccesskeyid
+ aws_secret_access_key: myamazonsecretaccesskey
+ YAML
+
+ app "development"
+
+ assert_equal "myamazonaccesskeyid", app.secrets.aws_access_key_id
+ assert_equal "myamazonsecretaccesskey", app.secrets.aws_secret_access_key
+ end
+
+ test "shared secrets saved in config/secrets.yml are loaded in app secrets" do
+ app_file "config/secrets.yml", <<-YAML
+ shared:
+ api_key: 3b7cd727
+ YAML
+
+ app "development"
+
+ assert_equal "3b7cd727", app.secrets.api_key
+ end
+
+ test "shared secrets will yield to environment specific secrets" do
+ app_file "config/secrets.yml", <<-YAML
+ shared:
+ api_key: 3b7cd727
+
+ development:
+ api_key: abc12345
+ YAML
+
+ app "development"
+
+ assert_equal "abc12345", app.secrets.api_key
+ end
+
+ test "blank config/secrets.yml does not crash the loading process" do
+ app_file "config/secrets.yml", <<-YAML
+ YAML
+
+ app "development"
+
+ assert_nil app.secrets.not_defined
+ end
+
+ test "config.secret_key_base over-writes a blank secrets.secret_key_base" do
+ app_file "config/initializers/secret_token.rb", <<-RUBY
+ Rails.application.config.secret_key_base = "iaminallyoursecretkeybase"
+ RUBY
+ app_file "config/secrets.yml", <<-YAML
+ development:
+ secret_key_base:
+ YAML
+
+ app "development"
+
+ 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.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
+ RUBY
+ app_file "config/secrets.yml", <<-YAML
+ development:
+ secret_key_base:
+ YAML
+
+ app "development"
+
+ assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.config.secret_token
+ assert_nil app.secrets.secret_key_base
+ assert_equal app.key_generator.class, ActiveSupport::LegacyKeyGenerator
+ end
+
+ test "uses ActiveSupport::LegacyKeyGenerator with config.secret_token as app.key_generator when secrets.secret_key_base is blank" do
+ app_file "config/initializers/secret_token.rb", <<-RUBY
+ Rails.application.config.secret_token = ""
+ RUBY
+ app_file "config/secrets.yml", <<-YAML
+ development:
+ secret_key_base:
+ YAML
+
+ app "development"
+
+ assert_equal "", app.config.secret_token
+ assert_nil app.secrets.secret_key_base
+ e = assert_raise ArgumentError do
+ app.key_generator
+ end
+ assert_match(/\AA secret is required/, e.message)
+ 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:
+ smtp_settings:
+ address: "smtp.example.com"
+ user_name: "postmaster@example.com"
+ password: "697361616320736c6f616e2028656c6f7265737429"
+ YAML
+
+ app "development"
+
+ assert_equal "697361616320736c6f616e2028656c6f7265737429", app.secrets.smtp_settings[:password]
+ end
+
+ test "protect from forgery is the default in a new app" do
+ make_basic_app
+
+ class ::OmgController < ActionController::Base
+ def index
+ render inline: "<%= csrf_meta_tags %>"
+ end
+ end
+
+ get "/"
+ assert last_response.body =~ /csrf\-param/
+ end
+
+ test "default form builder specified as a string" do
+ app_file "config/initializers/form_builder.rb", <<-RUBY
+ class CustomFormBuilder < ActionView::Helpers::FormBuilder
+ def text_field(attribute, *args)
+ label(attribute) + super(attribute, *args)
+ end
+ end
+ Rails.configuration.action_view.default_form_builder = "CustomFormBuilder"
+ RUBY
+
+ app_file "app/models/post.rb", <<-RUBY
+ class Post
+ include ActiveModel::Model
+ attr_accessor :name
+ end
+ RUBY
+
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
+ class PostsController < ApplicationController
+ def index
+ render inline: "<%= begin; form_for(Post.new) {|f| f.text_field(:name)}; rescue => e; e.to_s; end %>"
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ RUBY
+
+ app "development"
+
+ get "/posts"
+ assert_match(/label/, last_response.body)
+ end
+
+ test "form_with can be configured with form_with_generates_remote_forms" do
+ app_file "config/initializers/form_builder.rb", <<-RUBY
+ Rails.configuration.action_view.form_with_generates_remote_forms = false
+ RUBY
+
+ app_file "app/models/post.rb", <<-RUBY
+ class Post
+ include ActiveModel::Model
+ attr_accessor :name
+ end
+ RUBY
+
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
+ class PostsController < ApplicationController
+ def index
+ render inline: "<%= begin; form_with(model: Post.new) {|f| f.text_field(:name)}; rescue => e; e.to_s; end %>"
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ RUBY
+
+ app "development"
+
+ get "/posts"
+ assert_no_match(/data-remote/, last_response.body)
+ end
+
+ test "form_with generates remote forms by default" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post
+ include ActiveModel::Model
+ attr_accessor :name
+ end
+ RUBY
+
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
+ class PostsController < ApplicationController
+ def index
+ render inline: "<%= begin; form_with(model: Post.new) {|f| f.text_field(:name)}; rescue => e; e.to_s; end %>"
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ RUBY
+
+ app "development"
+
+ get "/posts"
+ assert_match(/data-remote/, last_response.body)
+ end
+
+ test "default method for update can be changed" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post
+ include ActiveModel::Model
+ def to_key; [1]; end
+ def persisted?; true; end
+ end
+ RUBY
+
+ token = "cf50faa3fe97702ca1ae"
+
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
+ class PostsController < ApplicationController
+ def show
+ render inline: "<%= begin; form_for(Post.new) {}; rescue => e; e.to_s; end %>"
+ end
+
+ def update
+ render plain: "update"
+ end
+
+ private
+
+ def form_authenticity_token(*args); token; end # stub the authenticity token
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ RUBY
+
+ app "development"
+
+ params = { authenticity_token: token }
+
+ get "/posts/1"
+ assert_match(/patch/, last_response.body)
+
+ patch "/posts/1", params
+ assert_match(/update/, last_response.body)
+
+ patch "/posts/1", params
+ assert_equal 200, last_response.status
+
+ put "/posts/1", params
+ assert_match(/update/, last_response.body)
+
+ put "/posts/1", params
+ assert_equal 200, last_response.status
+ end
+
+ test "request forgery token param can be changed" do
+ make_basic_app do |application|
+ application.config.action_controller.request_forgery_protection_token = "_xsrf_token_here"
+ end
+
+ class ::OmgController < ActionController::Base
+ def index
+ render inline: "<%= csrf_meta_tags %>"
+ end
+ end
+
+ get "/"
+ assert_match "_xsrf_token_here", last_response.body
+ end
+
+ test "sets ActionDispatch.test_app" do
+ make_basic_app
+ assert_equal Rails.application, ActionDispatch.test_app
+ end
+
+ test "sets ActionDispatch::Response.default_charset" do
+ make_basic_app do |application|
+ application.config.action_dispatch.default_charset = "utf-16"
+ end
+
+ assert_equal "utf-16", ActionDispatch::Response.default_charset
+ end
+
+ test "registers interceptors with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.interceptors = MyMailInterceptor
+ RUBY
+
+ app "development"
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [::MyMailInterceptor], ::Mail.class_variable_get(:@@delivery_interceptors)
+ end
+
+ test "registers multiple interceptors with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.interceptors = [MyMailInterceptor, "MyOtherMailInterceptor"]
+ RUBY
+
+ app "development"
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [::MyMailInterceptor, ::MyOtherMailInterceptor], ::Mail.class_variable_get(:@@delivery_interceptors)
+ end
+
+ test "registers preview interceptors with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.preview_interceptors = MyPreviewMailInterceptor
+ RUBY
+
+ app "development"
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [ActionMailer::InlinePreviewInterceptor, ::MyPreviewMailInterceptor], ActionMailer::Base.preview_interceptors
+ end
+
+ test "registers multiple preview interceptors with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.preview_interceptors = [MyPreviewMailInterceptor, "MyOtherPreviewMailInterceptor"]
+ RUBY
+
+ app "development"
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [ActionMailer::InlinePreviewInterceptor, MyPreviewMailInterceptor, MyOtherPreviewMailInterceptor], ActionMailer::Base.preview_interceptors
+ end
+
+ test "default preview interceptor can be removed" do
+ app_file "config/initializers/preview_interceptors.rb", <<-RUBY
+ ActionMailer::Base.preview_interceptors.delete(ActionMailer::InlinePreviewInterceptor)
+ RUBY
+
+ app "development"
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [], ActionMailer::Base.preview_interceptors
+ end
+
+ test "registers observers with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.observers = MyMailObserver
+ RUBY
+
+ app "development"
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [::MyMailObserver], ::Mail.class_variable_get(:@@delivery_notification_observers)
+ end
+
+ test "registers multiple observers with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.observers = [MyMailObserver, "MyOtherMailObserver"]
+ RUBY
+
+ app "development"
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [::MyMailObserver, ::MyOtherMailObserver], ::Mail.class_variable_get(:@@delivery_notification_observers)
+ end
+
+ test "allows setting the queue name for the ActionMailer::DeliveryJob" do
+ add_to_config <<-RUBY
+ config.action_mailer.deliver_later_queue_name = 'test_default'
+ RUBY
+
+ app "development"
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal "test_default", ActionMailer::Base.class_variable_get(:@@deliver_later_queue_name)
+ end
+
+ test "valid timezone is setup correctly" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.time_zone = "Wellington"
+ RUBY
+
+ app "development"
+
+ assert_equal "Wellington", Rails.application.config.time_zone
+ end
+
+ test "raises when an invalid timezone is defined in the config" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.time_zone = "That big hill over yonder hill"
+ RUBY
+
+ assert_raise(ArgumentError) do
+ app "development"
+ end
+ end
+
+ test "valid beginning of week is setup correctly" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.beginning_of_week = :wednesday
+ RUBY
+
+ app "development"
+
+ assert_equal :wednesday, Rails.application.config.beginning_of_week
+ end
+
+ test "raises when an invalid beginning of week is defined in the config" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.beginning_of_week = :invalid
+ RUBY
+
+ assert_raise(ArgumentError) do
+ app "development"
+ end
+ end
+
+ test "config.action_view.cache_template_loading with cache_classes default" do
+ add_to_config "config.cache_classes = true"
+
+ app "development"
+ require "action_view/base"
+
+ assert_equal true, ActionView::Resolver.caching?
+ end
+
+ test "config.action_view.cache_template_loading without cache_classes default" do
+ add_to_config "config.cache_classes = false"
+
+ app "development"
+ require "action_view/base"
+
+ assert_equal false, ActionView::Resolver.caching?
+ end
+
+ test "config.action_view.cache_template_loading = false" do
+ add_to_config <<-RUBY
+ config.cache_classes = true
+ config.action_view.cache_template_loading = false
+ RUBY
+
+ app "development"
+ require "action_view/base"
+
+ assert_equal false, ActionView::Resolver.caching?
+ end
+
+ test "config.action_view.cache_template_loading = true" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ config.action_view.cache_template_loading = true
+ RUBY
+
+ app "development"
+ require "action_view/base"
+
+ assert_equal true, ActionView::Resolver.caching?
+ end
+
+ test "config.action_view.cache_template_loading with cache_classes in an environment" do
+ build_app(initializers: true)
+ add_to_env_config "development", "config.cache_classes = false"
+
+ # These requires are to emulate an engine loading Action View before the application
+ require "action_view"
+ require "action_view/railtie"
+ require "action_view/base"
+
+ app "development"
+
+ assert_equal false, ActionView::Resolver.caching?
+ end
+
+ test "config.action_dispatch.show_exceptions is sent in env" do
+ make_basic_app do |application|
+ application.config.action_dispatch.show_exceptions = true
+ end
+
+ class ::OmgController < ActionController::Base
+ def index
+ render plain: request.env["action_dispatch.show_exceptions"]
+ end
+ end
+
+ get "/"
+ assert_equal "true", last_response.body
+ end
+
+ test "config.action_controller.wrap_parameters is set in ActionController::Base" do
+ app_file "config/initializers/wrap_parameters.rb", <<-RUBY
+ ActionController::Base.wrap_parameters format: [:json]
+ RUBY
+
+ app_file "app/models/post.rb", <<-RUBY
+ class Post
+ def self.attribute_names
+ %w(title)
+ end
+ end
+ RUBY
+
+ app_file "app/controllers/application_controller.rb", <<-RUBY
+ class ApplicationController < ActionController::Base
+ protect_from_forgery with: :reset_session # as we are testing API here
+ end
+ RUBY
+
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
+ class PostsController < ApplicationController
+ def create
+ render plain: params[:post].inspect
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ RUBY
+
+ app "development"
+
+ post "/posts.json", '{ "title": "foo", "name": "bar" }', "CONTENT_TYPE" => "application/json"
+ assert_equal '<ActionController::Parameters {"title"=>"foo"} permitted: false>', last_response.body
+ end
+
+ test "config.action_controller.permit_all_parameters = true" do
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
+ class PostsController < ActionController::Base
+ def create
+ render plain: params[:post].permitted? ? "permitted" : "forbidden"
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ config.action_controller.permit_all_parameters = true
+ RUBY
+
+ app "development"
+
+ post "/posts", post: { "title" => "zomg" }
+ assert_equal "permitted", last_response.body
+ end
+
+ test "config.action_controller.action_on_unpermitted_parameters = :raise" do
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
+ class PostsController < ActionController::Base
+ def create
+ render plain: params.require(:post).permit(:name)
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ config.action_controller.action_on_unpermitted_parameters = :raise
+ RUBY
+
+ app "development"
+
+ force_lazy_load_hooks { ActionController::Base }
+
+ assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
+
+ post "/posts", post: { "title" => "zomg" }
+ assert_match "We're sorry, but something went wrong", last_response.body
+ end
+
+ test "config.action_controller.always_permitted_parameters are: controller, action by default" do
+ app "development"
+
+ force_lazy_load_hooks { ActionController::Base }
+
+ assert_equal %w(controller action), ActionController::Parameters.always_permitted_parameters
+ end
+
+ test "config.action_controller.always_permitted_parameters = ['controller', 'action', 'format']" do
+ add_to_config <<-RUBY
+ config.action_controller.always_permitted_parameters = %w( controller action format )
+ RUBY
+
+ app "development"
+
+ force_lazy_load_hooks { ActionController::Base }
+
+ assert_equal %w( controller action format ), ActionController::Parameters.always_permitted_parameters
+ end
+
+ test "config.action_controller.always_permitted_parameters = ['controller','action','format'] does not raise exception" do
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
+ class PostsController < ActionController::Base
+ def create
+ render plain: params.permit(post: [:title])
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ config.action_controller.always_permitted_parameters = %w( controller action format )
+ config.action_controller.action_on_unpermitted_parameters = :raise
+ RUBY
+
+ app "development"
+
+ force_lazy_load_hooks { ActionController::Base }
+
+ assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
+
+ post "/posts", post: { "title" => "zomg" }, format: "json"
+ assert_equal 200, last_response.status
+ end
+
+ test "config.action_controller.action_on_unpermitted_parameters is :log by default on development" do
+ app "development"
+
+ force_lazy_load_hooks { ActionController::Base }
+
+ assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters
+ end
+
+ test "config.action_controller.action_on_unpermitted_parameters is :log by default on test" do
+ app "test"
+
+ force_lazy_load_hooks { ActionController::Base }
+
+ assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters
+ end
+
+ test "config.action_controller.action_on_unpermitted_parameters is false by default on production" do
+ app "production"
+
+ force_lazy_load_hooks { ActionController::Base }
+
+ assert_equal false, ActionController::Parameters.action_on_unpermitted_parameters
+ end
+
+ test "config.action_controller.default_protect_from_forgery is true by default" do
+ app "development"
+
+ assert_equal true, ActionController::Base.default_protect_from_forgery
+ assert_includes ActionController::Base.__callbacks[:process_action].map(&:filter), :verify_authenticity_token
+ end
+
+ test "config.action_controller.permit_all_parameters can be configured in an initializer" do
+ app_file "config/initializers/permit_all_parameters.rb", <<-RUBY
+ Rails.application.config.action_controller.permit_all_parameters = true
+ RUBY
+
+ app "development"
+
+ force_lazy_load_hooks { ActionController::Base }
+ assert_equal true, ActionController::Parameters.permit_all_parameters
+ end
+
+ test "config.action_controller.always_permitted_parameters can be configured in an initializer" do
+ app_file "config/initializers/always_permitted_parameters.rb", <<-RUBY
+ Rails.application.config.action_controller.always_permitted_parameters = []
+ RUBY
+
+ app "development"
+
+ force_lazy_load_hooks { ActionController::Base }
+ assert_equal [], ActionController::Parameters.always_permitted_parameters
+ end
+
+ test "config.action_controller.action_on_unpermitted_parameters can be configured in an initializer" do
+ app_file "config/initializers/action_on_unpermitted_parameters.rb", <<-RUBY
+ Rails.application.config.action_controller.action_on_unpermitted_parameters = :raise
+ RUBY
+
+ app "development"
+
+ force_lazy_load_hooks { ActionController::Base }
+ assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
+ end
+
+ test "config.action_dispatch.ignore_accept_header" do
+ make_basic_app do |application|
+ application.config.action_dispatch.ignore_accept_header = true
+ end
+
+ class ::OmgController < ActionController::Base
+ def index
+ respond_to do |format|
+ format.html { render plain: "HTML" }
+ format.xml { render plain: "XML" }
+ end
+ end
+ end
+
+ get "/", {}, "HTTP_ACCEPT" => "application/xml"
+ assert_equal "HTML", last_response.body
+
+ get "/", { format: :xml }, "HTTP_ACCEPT" => "application/xml"
+ assert_equal "XML", last_response.body
+ end
+
+ test "Rails.application#env_config exists and include some existing parameters" do
+ make_basic_app
+
+ assert_equal app.env_config["action_dispatch.parameter_filter"], app.config.filter_parameters
+ assert_equal app.env_config["action_dispatch.show_exceptions"], app.config.action_dispatch.show_exceptions
+ assert_equal app.env_config["action_dispatch.logger"], Rails.logger
+ assert_equal app.env_config["action_dispatch.backtrace_cleaner"], Rails.backtrace_cleaner
+ assert_equal app.env_config["action_dispatch.key_generator"], Rails.application.key_generator
+ end
+
+ test "config.colorize_logging default is true" do
+ make_basic_app
+ assert app.config.colorize_logging
+ 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
+ end
+ end
+
+ test "config.session_store with :active_record_store without activerecord-session_store gem" do
+ e = assert_raise RuntimeError do
+ make_basic_app do |application|
+ application.config.session_store :active_record_store
+ end
+ end
+ assert_match(/activerecord-session_store/, e.message)
+ end
+
+ test "default session store initializer does not overwrite the user defined session store even if it is disabled" do
+ make_basic_app do |application|
+ application.config.session_store :disabled
+ end
+
+ assert_nil app.config.session_store
+ end
+
+ test "default session store initializer sets session store to cookie store" do
+ session_options = { key: "_myapp_session", cookie_only: true }
+ make_basic_app
+
+ assert_equal ActionDispatch::Session::CookieStore, app.config.session_store
+ assert_equal session_options, app.config.session_options
+ end
+
+ test "config.log_level with custom logger" do
+ make_basic_app do |application|
+ application.config.logger = Logger.new(STDOUT)
+ application.config.log_level = :info
+ end
+ assert_equal Logger::INFO, Rails.logger.level
+ end
+
+ test "respond_to? accepts include_private" do
+ make_basic_app
+
+ assert_not Rails.configuration.respond_to?(:method_missing)
+ assert Rails.configuration.respond_to?(:method_missing, true)
+ end
+
+ test "config.active_record.dump_schema_after_migration is false on production" do
+ build_app
+
+ app "production"
+
+ assert_not ActiveRecord::Base.dump_schema_after_migration
+ end
+
+ test "config.active_record.dump_schema_after_migration is true by default on development" do
+ app "development"
+
+ assert ActiveRecord::Base.dump_schema_after_migration
+ end
+
+ test "config.annotations wrapping SourceAnnotationExtractor::Annotation class" do
+ make_basic_app do |application|
+ application.config.annotations.register_extensions("coffee") do |tag|
+ /#\s*(#{tag}):?\s*(.*)$/
+ end
+ end
+
+ assert_not_nil SourceAnnotationExtractor::Annotation.extensions[/\.(coffee)$/]
+ end
+
+ test "rake_tasks block works at instance level" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.ran_block = false
+
+ rake_tasks do
+ config.ran_block = true
+ end
+ end
+ RUBY
+
+ app "development"
+ assert_not Rails.configuration.ran_block
+
+ require "rake"
+ require "rake/testtask"
+ require "rdoc/task"
+
+ Rails.application.load_tasks
+ assert Rails.configuration.ran_block
+ end
+
+ test "generators block works at instance level" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.ran_block = false
+
+ generators do
+ config.ran_block = true
+ end
+ end
+ RUBY
+
+ app "development"
+ assert_not Rails.configuration.ran_block
+
+ Rails.application.load_generators
+ assert Rails.configuration.ran_block
+ end
+
+ test "console block works at instance level" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.ran_block = false
+
+ console do
+ config.ran_block = true
+ end
+ end
+ RUBY
+
+ app "development"
+ assert_not Rails.configuration.ran_block
+
+ Rails.application.load_console
+ assert Rails.configuration.ran_block
+ end
+
+ test "runner block works at instance level" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.ran_block = false
+
+ runner do
+ config.ran_block = true
+ end
+ end
+ RUBY
+
+ app "development"
+ assert_not Rails.configuration.ran_block
+
+ Rails.application.load_runner
+ assert Rails.configuration.ran_block
+ end
+
+ test "loading the first existing database configuration available" do
+ app_file "config/environments/development.rb", <<-RUBY
+
+ Rails.application.configure do
+ config.paths.add 'config/database', with: 'config/nonexistent.yml'
+ config.paths['config/database'] << 'config/database.yml'
+ end
+ RUBY
+
+ app "development"
+
+ assert_kind_of Hash, Rails.application.config.database_configuration
+ end
+
+ test "raises with proper error message if no database configuration found" do
+ FileUtils.rm("#{app_path}/config/database.yml")
+ app "development"
+ err = assert_raises RuntimeError do
+ Rails.application.config.database_configuration
+ end
+ assert_match "config/database", err.message
+ end
+
+ test "loads database.yml using shared keys" do
+ app_file "config/database.yml", <<-YAML
+ shared:
+ username: bobby
+ adapter: sqlite3
+
+ development:
+ database: 'dev_db'
+ YAML
+
+ app "development"
+
+ ar_config = Rails.application.config.database_configuration
+ assert_equal "sqlite3", ar_config["development"]["adapter"]
+ assert_equal "bobby", ar_config["development"]["username"]
+ assert_equal "dev_db", ar_config["development"]["database"]
+ end
+
+ test "loads database.yml using shared keys for undefined environments" do
+ app_file "config/database.yml", <<-YAML
+ shared:
+ username: bobby
+ adapter: sqlite3
+ database: 'dev_db'
+ YAML
+
+ app "development"
+
+ ar_config = Rails.application.config.database_configuration
+ assert_equal "sqlite3", ar_config["development"]["adapter"]
+ assert_equal "bobby", ar_config["development"]["username"]
+ assert_equal "dev_db", ar_config["development"]["database"]
+ end
+
+ test "config.action_mailer.show_previews defaults to true in development" do
+ app "development"
+
+ assert Rails.application.config.action_mailer.show_previews
+ end
+
+ test "config.action_mailer.show_previews defaults to false in production" do
+ app "production"
+
+ assert_equal false, Rails.application.config.action_mailer.show_previews
+ end
+
+ test "config.action_mailer.show_previews can be set in the configuration file" do
+ add_to_config <<-RUBY
+ config.action_mailer.show_previews = true
+ RUBY
+
+ app "production"
+
+ assert_equal true, Rails.application.config.action_mailer.show_previews
+ end
+
+ test "config_for loads custom configuration from yaml files" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ key: 'custom key'
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "development"
+
+ assert_equal "custom key", Rails.application.config.my_custom_config["key"]
+ end
+
+ test "config_for uses the Pathname object if it is provided" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ key: 'custom key'
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for(Pathname.new(Rails.root.join("config/custom.yml")))
+ RUBY
+
+ app "development"
+
+ 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
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ exception = assert_raises(RuntimeError) do
+ app "development"
+ end
+
+ assert_equal "Could not load configuration. No such file - #{app_path}/config/custom.yml", exception.message
+ end
+
+ test "config_for without the environment configured returns an empty hash" do
+ app_file "config/custom.yml", <<-RUBY
+ test:
+ key: 'custom key'
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "development"
+
+ assert_equal({}, Rails.application.config.my_custom_config)
+ end
+
+ test "config_for with empty file returns an empty hash" do
+ app_file "config/custom.yml", <<-RUBY
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "development"
+
+ assert_equal({}, Rails.application.config.my_custom_config)
+ end
+
+ test "default SQLite3Adapter.represent_boolean_as_integer for 5.1 is false" do
+ remove_from_config '.*config\.load_defaults.*\n'
+ add_to_top_of_config <<-RUBY
+ config.load_defaults 5.1
+ RUBY
+ app_file "app/models/post.rb", <<-RUBY
+ class Post < ActiveRecord::Base
+ end
+ RUBY
+
+ app "development"
+ force_lazy_load_hooks { Post }
+
+ assert_not ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer
+ end
+
+ test "default SQLite3Adapter.represent_boolean_as_integer for new installs is true" do
+ 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
+ end
+
+ test "represent_boolean_as_integer should be able to set via config.active_record.sqlite3.represent_boolean_as_integer" do
+ remove_from_config '.*config\.load_defaults.*\n'
+
+ app_file "config/initializers/new_framework_defaults_5_2.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
+ end
+
+ test "config_for containing ERB tags should evaluate" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ key: <%= 'custom key' %>
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app "development"
+
+ assert_equal "custom key", Rails.application.config.my_custom_config["key"]
+ end
+
+ test "config_for with syntax error show a more descriptive exception" do
+ app_file "config/custom.yml", <<-RUBY
+ development:
+ key: foo:
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ exception = assert_raises(RuntimeError) do
+ app "development"
+ end
+
+ assert_match "YAML syntax error occurred while parsing", exception.message
+ end
+
+ test "config_for allows overriding the environment" do
+ app_file "config/custom.yml", <<-RUBY
+ test:
+ key: 'walrus'
+ production:
+ key: 'unicorn'
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom', env: 'production')
+ RUBY
+ require "#{app_path}/config/environment"
+
+ assert_equal "unicorn", Rails.application.config.my_custom_config["key"]
+ end
+
+ test "api_only is false by default" do
+ app "development"
+ refute Rails.application.config.api_only
+ end
+
+ test "api_only generator config is set when api_only is set" do
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+ app "development"
+
+ Rails.application.load_generators
+ assert Rails.configuration.api_only
+ end
+
+ test "debug_exception_response_format is :api by default if api_only is enabled" do
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+ app "development"
+
+ assert_equal :api, Rails.configuration.debug_exception_response_format
+ end
+
+ test "debug_exception_response_format can be overridden" do
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.debug_exception_response_format = :default
+ end
+ RUBY
+
+ app "development"
+
+ assert_equal :default, Rails.configuration.debug_exception_response_format
+ end
+
+ test "controller force_ssl declaration can be used even if session_store is disabled" do
+ make_basic_app do |application|
+ application.config.session_store :disabled
+ end
+
+ class ::OmgController < ActionController::Base
+ force_ssl
+
+ def index
+ render plain: "Yay! You're on Rails!"
+ end
+ end
+
+ get "/"
+
+ assert_equal 301, last_response.status
+ assert_equal "https://example.org/", last_response.location
+ end
+
+ private
+ def force_lazy_load_hooks
+ yield # Tasty clarifying sugar, homie! We only need to reference a constant to load it.
+ end
+ end
+end
diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb
new file mode 100644
index 0000000000..31bef82ccc
--- /dev/null
+++ b/railties/test/application/console_test.rb
@@ -0,0 +1,156 @@
+require "isolation/abstract_unit"
+require "console_helpers"
+
+class ConsoleTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def load_environment(sandbox = false)
+ require "#{rails_root}/config/environment"
+ Rails.application.sandbox = sandbox
+ Rails.application.load_console
+ end
+
+ def irb_context
+ Object.new.extend(Rails::ConsoleMethods)
+ end
+
+ def test_app_method_should_return_integration_session
+ TestHelpers::Rack.send :remove_method, :app
+ load_environment
+ console_session = irb_context.app
+ assert_instance_of ActionDispatch::Integration::Session, console_session
+ end
+
+ def test_app_can_access_path_helper_method
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#index'
+ end
+ RUBY
+
+ load_environment
+ console_session = irb_context.app
+ assert_equal "/foo", console_session.foo_path
+ end
+
+ def test_new_session_should_return_integration_session
+ load_environment
+ session = irb_context.new_session
+ assert_instance_of ActionDispatch::Integration::Session, session
+ end
+
+ def test_reload_should_fire_preparation_and_cleanup_callbacks
+ load_environment
+ a = b = c = nil
+
+ # TODO: These should be defined on the initializer
+ ActiveSupport::Reloader.to_complete { a = b = c = 1 }
+ ActiveSupport::Reloader.to_complete { b = c = 2 }
+ ActiveSupport::Reloader.to_prepare { c = 3 }
+
+ irb_context.reload!(false)
+
+ assert_equal 1, a
+ assert_equal 2, b
+ assert_equal 3, c
+ end
+
+ def test_reload_should_reload_constants
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ attr_accessor :name
+ end
+ MODEL
+
+ load_environment
+ assert User.new.respond_to?(:name)
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ attr_accessor :name, :age
+ end
+ MODEL
+
+ assert !User.new.respond_to?(:age)
+ irb_context.reload!(false)
+ assert User.new.respond_to?(:age)
+ end
+
+ def test_access_to_helpers
+ load_environment
+ helper = irb_context.helper
+ assert_not_nil helper
+ assert_instance_of ActionView::Base, helper
+ assert_equal "Once upon a time in a world...",
+ helper.truncate("Once upon a time in a world far far away")
+ end
+end
+
+class FullStackConsoleTest < ActiveSupport::TestCase
+ include ConsoleHelpers
+
+ def setup
+ skip "PTY unavailable" unless available_pty?
+
+ build_app
+ app_file "app/models/post.rb", <<-CODE
+ class Post < ActiveRecord::Base
+ end
+ CODE
+ system "#{app_path}/bin/rails runner 'Post.connection.create_table :posts'"
+
+ @master, @slave = PTY.open
+ end
+
+ def teardown
+ teardown_app
+ 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
+ end
+
+ def spawn_console(options)
+ Process.spawn(
+ "#{app_path}/bin/rails console #{options}",
+ in: @slave, out: @slave, err: @slave
+ )
+
+ assert_output "> ", @master, 30
+ end
+
+ def test_sandbox
+ spawn_console("--sandbox")
+
+ write_prompt "Post.count", "=> 0"
+ write_prompt "Post.create"
+ write_prompt "Post.count", "=> 1"
+ @master.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"
+ end
+
+ def test_environment_option_and_irb_option
+ spawn_console("test -- --verbose")
+
+ write_prompt "a = 1", "a = 1"
+ write_prompt "puts Rails.env", "puts Rails.env\r\ntest"
+ @master.puts "quit"
+ end
+end
diff --git a/railties/test/application/current_attributes_integration_test.rb b/railties/test/application/current_attributes_integration_test.rb
new file mode 100644
index 0000000000..5653ec0be1
--- /dev/null
+++ b/railties/test/application/current_attributes_integration_test.rb
@@ -0,0 +1,84 @@
+require "isolation/abstract_unit"
+require "rack/test"
+
+class CurrentAttributesIntegrationTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ setup do
+ build_app
+
+ app_file "app/models/current.rb", <<-RUBY
+ class Current < ActiveSupport::CurrentAttributes
+ attribute :customer
+
+ resets { Time.zone = "UTC" }
+
+ def customer=(customer)
+ super
+ Time.zone = customer.try(:time_zone)
+ end
+ end
+ RUBY
+
+ app_file "app/models/customer.rb", <<-RUBY
+ class Customer < Struct.new(:name)
+ def time_zone
+ "Copenhagen"
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/customers/:action", controller: :customers
+ end
+ RUBY
+
+ app_file "app/controllers/customers_controller.rb", <<-RUBY
+ class CustomersController < ApplicationController
+ def set_current_customer
+ Current.customer = Customer.new("david")
+ render :index
+ end
+
+ def set_no_customer
+ render :index
+ end
+ end
+ RUBY
+
+ app_file "app/views/customers/index.html.erb", <<-RUBY
+ <%= Current.customer.try(:name) || 'noone' %>,<%= Time.zone.name %>
+ RUBY
+
+ require "#{app_path}/config/environment"
+ end
+
+ teardown :teardown_app
+
+ test "current customer is assigned and cleared" do
+ get "/customers/set_current_customer"
+ assert_equal 200, last_response.status
+ assert_match(/david,Copenhagen/, last_response.body)
+
+ get "/customers/set_no_customer"
+ assert_equal 200, last_response.status
+ assert_match(/noone,UTC/, last_response.body)
+ end
+
+ test "resets after execution" do
+ assert_nil Current.customer
+ assert_equal "UTC", Time.zone.name
+
+ Rails.application.executor.wrap do
+ Current.customer = Customer.new("david")
+
+ assert_equal "david", Current.customer.name
+ assert_equal "Copenhagen", Time.zone.name
+ end
+
+ assert_nil Current.customer
+ assert_equal "UTC", Time.zone.name
+ end
+end
diff --git a/railties/test/application/dbconsole_test.rb b/railties/test/application/dbconsole_test.rb
new file mode 100644
index 0000000000..5d89d0e44d
--- /dev/null
+++ b/railties/test/application/dbconsole_test.rb
@@ -0,0 +1,76 @@
+require "isolation/abstract_unit"
+require "console_helpers"
+
+module ApplicationTests
+ class DBConsoleTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include ConsoleHelpers
+
+ def setup
+ skip "PTY unavailable" unless available_pty?
+
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def test_use_value_defined_in_environment_file_in_database_yml
+ Dir.chdir(app_path) do
+ app_file "config/database.yml", <<-YAML
+ development:
+ database: <%= Rails.application.config.database %>
+ adapter: sqlite3
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+ timeout: 5000
+ YAML
+
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.database = "db/development.sqlite3"
+ end
+ RUBY
+ end
+
+ master, slave = PTY.open
+ spawn_dbconsole(slave)
+ assert_output("sqlite>", master)
+ ensure
+ master.puts ".exit"
+ end
+
+ def test_respect_environment_option
+ Dir.chdir(app_path) do
+ app_file "config/database.yml", <<-YAML
+ default: &default
+ adapter: sqlite3
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+ timeout: 5000
+
+ development:
+ <<: *default
+ database: db/development.sqlite3
+
+ production:
+ <<: *default
+ database: db/production.sqlite3
+ YAML
+ end
+
+ master, slave = PTY.open
+ spawn_dbconsole(slave, "-e production")
+ assert_output("sqlite>", master)
+
+ master.puts "pragma database_list;"
+ assert_output("production.sqlite3", master)
+ ensure
+ master.puts ".exit"
+ end
+
+ private
+ def spawn_dbconsole(fd, options = nil)
+ Process.spawn("#{app_path}/bin/rails dbconsole #{options}", in: fd, out: fd, err: fd)
+ end
+ end
+end
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
new file mode 100644
index 0000000000..fe581db286
--- /dev/null
+++ b/railties/test/application/generators_test.rb
@@ -0,0 +1,198 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class GeneratorsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app_const
+ @app_const ||= Class.new(Rails::Application)
+ end
+
+ def with_config
+ require "rails/all"
+ require "rails/generators"
+ yield app_const.config
+ end
+
+ def with_bare_config
+ require "rails"
+ require "rails/generators"
+ yield app_const.config
+ end
+
+ test "allow running plugin new generator inside Rails app directory" do
+ FileUtils.cd(rails_root) { `ruby bin/rails plugin new vendor/plugins/bukkits` }
+ assert File.exist?(File.join(rails_root, "vendor/plugins/bukkits/test/dummy/config/application.rb"))
+ end
+
+ test "generators default values" do
+ with_bare_config do |c|
+ assert_equal(true, c.generators.colorize_logging)
+ assert_equal({}, c.generators.aliases)
+ assert_equal({}, c.generators.options)
+ assert_equal({}, c.generators.fallbacks)
+ end
+ end
+
+ test "generators set rails options" do
+ with_bare_config do |c|
+ c.generators.orm = :data_mapper
+ c.generators.test_framework = :rspec
+ c.generators.helper = false
+ expected = { rails: { orm: :data_mapper, test_framework: :rspec, helper: false } }
+ assert_equal(expected, c.generators.options)
+ end
+ end
+
+ test "generators set rails aliases" do
+ with_config do |c|
+ c.generators.aliases = { rails: { test_framework: "-w" } }
+ expected = { rails: { test_framework: "-w" } }
+ assert_equal expected, c.generators.aliases
+ end
+ end
+
+ test "generators aliases, options, templates and fallbacks on initialization" do
+ add_to_config <<-RUBY
+ config.generators.rails aliases: { test_framework: "-w" }
+ config.generators.orm :data_mapper
+ config.generators.test_framework :rspec
+ config.generators.fallbacks[:shoulda] = :test_unit
+ config.generators.templates << "some/where"
+ RUBY
+
+ # Initialize the application
+ require "#{app_path}/config/environment"
+ Rails.application.load_generators
+
+ assert_equal :rspec, Rails::Generators.options[:rails][:test_framework]
+ assert_equal "-w", Rails::Generators.aliases[:rails][:test_framework]
+ assert_equal Hash[shoulda: :test_unit], Rails::Generators.fallbacks
+ assert_equal ["some/where"], Rails::Generators.templates_path
+ end
+
+ test "generators no color on initialization" do
+ add_to_config <<-RUBY
+ config.generators.colorize_logging = false
+ RUBY
+
+ # Initialize the application
+ require "#{app_path}/config/environment"
+ Rails.application.load_generators
+
+ assert_equal Thor::Base.shell, Thor::Shell::Basic
+ end
+
+ test "generators with hashes for options and aliases" do
+ with_bare_config do |c|
+ c.generators do |g|
+ g.orm :data_mapper, migration: false
+ g.plugin aliases: { generator: "-g" },
+ generator: true
+ end
+
+ expected = {
+ rails: { orm: :data_mapper },
+ plugin: { generator: true },
+ data_mapper: { migration: false }
+ }
+
+ assert_equal expected, c.generators.options
+ assert_equal({ plugin: { generator: "-g" } }, c.generators.aliases)
+ end
+ end
+
+ test "generators with string and hash for options should generate symbol keys" do
+ with_bare_config do |c|
+ c.generators do |g|
+ g.orm "data_mapper", migration: false
+ end
+
+ expected = {
+ rails: { orm: :data_mapper },
+ data_mapper: { migration: false }
+ }
+
+ assert_equal expected, c.generators.options
+ end
+ end
+
+ test "api only generators hide assets, helper, js and css namespaces and set api option" do
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+
+ # Initialize the application
+ require "#{app_path}/config/environment"
+ Rails.application.load_generators
+
+ assert_includes Rails::Generators.hidden_namespaces, "assets"
+ assert_includes Rails::Generators.hidden_namespaces, "helper"
+ assert_includes Rails::Generators.hidden_namespaces, "js"
+ assert_includes Rails::Generators.hidden_namespaces, "css"
+ assert Rails::Generators.options[:rails][:api]
+ assert_equal false, Rails::Generators.options[:rails][:assets]
+ assert_equal false, Rails::Generators.options[:rails][:helper]
+ assert_nil Rails::Generators.options[:rails][:template_engine]
+ end
+
+ test "api only generators allow overriding generator options" do
+ add_to_config <<-RUBY
+ config.generators.helper = true
+ config.api_only = true
+ config.generators.template_engine = :my_template
+ RUBY
+
+ # Initialize the application
+ require "#{app_path}/config/environment"
+ Rails.application.load_generators
+
+ assert Rails::Generators.options[:rails][:api]
+ assert Rails::Generators.options[:rails][:helper]
+ assert_equal :my_template, Rails::Generators.options[:rails][:template_engine]
+ end
+
+ test "api only generator generate mailer views" do
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+
+ FileUtils.cd(rails_root) { `bin/rails generate mailer notifier foo` }
+ assert File.exist?(File.join(rails_root, "app/views/notifier_mailer/foo.text.erb"))
+ assert File.exist?(File.join(rails_root, "app/views/notifier_mailer/foo.html.erb"))
+ end
+
+ test "ARGV is mutated as expected" do
+ require "#{app_path}/config/environment"
+ Rails::Command.const_set("APP_PATH", "rails/all")
+
+ FileUtils.cd(rails_root) do
+ ARGV = ["mailer", "notifier", "foo"]
+ Rails::Command.const_set("ARGV", ARGV)
+ quietly { Rails::Command.invoke :generate, ARGV }
+
+ assert_equal ["notifier", "foo"], ARGV
+ end
+
+ Rails::Command.send(:remove_const, "APP_PATH")
+ end
+
+ test "help does not show hidden namespaces" do
+ FileUtils.cd(rails_root) do
+ output = `bin/rails generate --help`
+ assert_no_match "active_record:migration", output
+
+ output = `bin/rails destroy --help`
+ assert_no_match "active_record:migration", output
+ end
+ end
+ end
+end
diff --git a/railties/test/application/help_test.rb b/railties/test/application/help_test.rb
new file mode 100644
index 0000000000..0c3fe8bfa3
--- /dev/null
+++ b/railties/test/application/help_test.rb
@@ -0,0 +1,23 @@
+require "isolation/abstract_unit"
+
+class HelpTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "command works" do
+ output = Dir.chdir(app_path) { `bin/rails help` }
+ assert_match "The most common rails commands are", output
+ end
+
+ test "short-cut alias works" do
+ output = Dir.chdir(app_path) { `bin/rails -h` }
+ assert_match "The most common rails commands are", output
+ end
+end
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
new file mode 100644
index 0000000000..eb2c578f91
--- /dev/null
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -0,0 +1,274 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class FrameworksTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ # AC & AM
+ test "set load paths set only if action controller or action mailer are in use" do
+ assert_nothing_raised do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ RUBY
+
+ use_frameworks []
+ require "#{app_path}/config/environment"
+ end
+ end
+
+ test "sets action_controller and action_mailer load paths" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ expanded_path = File.expand_path("app/views", app_path)
+ assert_equal expanded_path, ActionController::Base.view_paths[0].to_s
+ assert_equal expanded_path, ActionMailer::Base.view_paths[0].to_s
+ end
+
+ 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" }
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_equal "test.rails", ActionMailer::Base.default_url_options[:host]
+ end
+
+ test "Default to HTTPS for ActionMailer URLs when force_ssl is on" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.force_ssl = true
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_equal "https", ActionMailer::Base.default_url_options[:protocol]
+ end
+
+ 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
+ end
+ RUBY
+
+ app_file "app/mailers/foo.rb", <<-RUBY
+ class Foo < ActionMailer::Base
+ def notify
+ end
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert Foo.method_defined?(:foo_url)
+ assert Foo.method_defined?(:main_app)
+ end
+
+ test "allows to not load all helpers for controllers" do
+ add_to_config "config.action_controller.include_all_helpers = false"
+
+ app_file "app/controllers/application_controller.rb", <<-RUBY
+ class ApplicationController < ActionController::Base
+ end
+ RUBY
+
+ app_file "app/controllers/foo_controller.rb", <<-RUBY
+ class FooController < ApplicationController
+ def included_helpers
+ render :inline => "<%= from_app_helper -%> <%= from_foo_helper %>"
+ end
+
+ def not_included_helper
+ render :inline => "<%= respond_to?(:from_bar_helper) -%>"
+ end
+ end
+ RUBY
+
+ app_file "app/helpers/application_helper.rb", <<-RUBY
+ module ApplicationHelper
+ def from_app_helper
+ "from_app_helper"
+ end
+ end
+ RUBY
+
+ app_file "app/helpers/foo_helper.rb", <<-RUBY
+ module FooHelper
+ def from_foo_helper
+ "from_foo_helper"
+ end
+ end
+ RUBY
+
+ app_file "app/helpers/bar_helper.rb", <<-RUBY
+ module BarHelper
+ def from_bar_helper
+ "from_bar_helper"
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/:controller(/:action)"
+ end
+ RUBY
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ get "/foo/included_helpers"
+ assert_equal "from_app_helper from_foo_helper", last_response.body
+
+ get "/foo/not_included_helper"
+ assert_equal "false", last_response.body
+ end
+
+ test "action_controller api executes using all the middleware stack" do
+ add_to_config "config.api_only = true"
+
+ app_file "app/controllers/application_controller.rb", <<-RUBY
+ class ApplicationController < ActionController::API
+ end
+ RUBY
+
+ app_file "app/controllers/omg_controller.rb", <<-RUBY
+ class OmgController < ApplicationController
+ def show
+ render json: { omg: 'omg' }
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/:controller(/:action)"
+ end
+ RUBY
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ get "omg/show"
+ assert_equal '{"omg":"omg"}', last_response.body
+ end
+
+ # AD
+ test "action_dispatch extensions are applied to ActionDispatch" do
+ add_to_config "config.action_dispatch.tld_length = 2"
+ require "#{app_path}/config/environment"
+ assert_equal 2, ActionDispatch::Http::URL.tld_length
+ end
+
+ test "assignment config.encoding to default_charset" do
+ charset = "Shift_JIS"
+ add_to_config "config.encoding = '#{charset}'"
+ require "#{app_path}/config/environment"
+ assert_equal charset, ActionDispatch::Response.default_charset
+ end
+
+ # AS
+ test "if there's no config.active_support.bare, all of ActiveSupport is required" do
+ use_frameworks []
+ require "#{app_path}/config/environment"
+ assert_nothing_raised { [1, 2, 3].sample }
+ end
+
+ test "config.active_support.bare does not require all of ActiveSupport" do
+ add_to_config "config.active_support.bare = true"
+
+ use_frameworks []
+
+ Dir.chdir("#{app_path}/app") do
+ require "#{app_path}/config/environment"
+ assert_raises(NoMethodError) { "hello".exclude? "lo" }
+ end
+ end
+
+ # AR
+ test "active_record extensions are applied to ActiveRecord" do
+ add_to_config "config.active_record.table_name_prefix = 'tbl_'"
+ require "#{app_path}/config/environment"
+ assert_equal "tbl_", ActiveRecord::Base.table_name_prefix
+ end
+
+ test "database middleware doesn't initialize when activerecord is not in frameworks" do
+ use_frameworks []
+ require "#{app_path}/config/environment"
+ assert_nil defined?(ActiveRecord::Base)
+ end
+
+ test "use schema cache dump" do
+ Dir.chdir(app_path) do
+ `rails generate model post title:string;
+ bin/rails db:migrate db:schema:cache:dump`
+ end
+ require "#{app_path}/config/environment"
+ ActiveRecord::Base.connection.drop_table("posts") # force drop posts table for test.
+ assert ActiveRecord::Base.connection.schema_cache.data_sources("posts")
+ end
+
+ test "expire schema cache dump" do
+ Dir.chdir(app_path) do
+ `rails generate model post title:string;
+ bin/rails db:migrate db:schema:cache:dump db:rollback`
+ end
+ require "#{app_path}/config/environment"
+ assert !ActiveRecord::Base.connection.schema_cache.data_sources("posts")
+ 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
+ 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
+ end
+
+ test "connections checked out during initialization are returned to the pool" do
+ app_file "config/initializers/active_record.rb", <<-RUBY
+ ActiveRecord::Base.connection
+ RUBY
+ require "#{app_path}/config/environment"
+ assert !ActiveRecord::Base.connection_pool.active_connection?
+ end
+ end
+end
diff --git a/railties/test/application/initializers/hooks_test.rb b/railties/test/application/initializers/hooks_test.rb
new file mode 100644
index 0000000000..36926c50ff
--- /dev/null
+++ b/railties/test/application/initializers/hooks_test.rb
@@ -0,0 +1,89 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class HooksTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "load initializers" do
+ app_file "config/initializers/foo.rb", "$foo = true"
+ require "#{app_path}/config/environment"
+ assert $foo
+ end
+
+ test "hooks block works correctly without eager_load (before_eager_load is not called)" do
+ add_to_config <<-RUBY
+ $initialization_callbacks = []
+ config.root = "#{app_path}"
+ config.eager_load = false
+ config.before_configuration { $initialization_callbacks << 1 }
+ config.before_initialize { $initialization_callbacks << 2 }
+ config.before_eager_load { Boom }
+ config.after_initialize { $initialization_callbacks << 3 }
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_equal [1, 2, 3], $initialization_callbacks
+ end
+
+ test "hooks block works correctly with eager_load" do
+ add_to_config <<-RUBY
+ $initialization_callbacks = []
+ config.root = "#{app_path}"
+ config.eager_load = true
+ config.before_configuration { $initialization_callbacks << 1 }
+ config.before_initialize { $initialization_callbacks << 2 }
+ config.before_eager_load { $initialization_callbacks << 3 }
+ config.after_initialize { $initialization_callbacks << 4 }
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_equal [1, 2, 3, 4], $initialization_callbacks
+ end
+
+ test "after_initialize runs after frameworks have been initialized" do
+ $activerecord_configurations = nil
+ add_to_config <<-RUBY
+ config.after_initialize { $activerecord_configurations = ActiveRecord::Base.configurations }
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert $activerecord_configurations
+ assert $activerecord_configurations["development"]
+ end
+
+ test "after_initialize happens after to_prepare in development" do
+ $order = []
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ config.after_initialize { $order << :after_initialize }
+ config.to_prepare { $order << :to_prepare }
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_equal [:to_prepare, :after_initialize], $order
+ end
+
+ test "after_initialize happens after to_prepare in production" do
+ $order = []
+ add_to_config <<-RUBY
+ config.cache_classes = true
+ config.after_initialize { $order << :after_initialize }
+ config.to_prepare { $order << :to_prepare }
+ RUBY
+
+ require "#{app_path}/config/application"
+ Rails.env.replace "production"
+ require "#{app_path}/config/environment"
+ assert_equal [:to_prepare, :after_initialize], $order
+ end
+ end
+end
diff --git a/railties/test/application/initializers/i18n_test.rb b/railties/test/application/initializers/i18n_test.rb
new file mode 100644
index 0000000000..cee198bd01
--- /dev/null
+++ b/railties/test/application/initializers/i18n_test.rb
@@ -0,0 +1,294 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class I18nTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ require "rails/all"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def load_app
+ require "#{app_path}/config/environment"
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ def assert_fallbacks(fallbacks)
+ fallbacks.each do |locale, expected|
+ actual = I18n.fallbacks[locale]
+ assert_equal expected, actual, "expected fallbacks for #{locale.inspect} to be #{expected.inspect}, but were #{actual.inspect}"
+ end
+ end
+
+ def assert_no_fallbacks
+ assert_not_includes I18n.backend.class.included_modules, I18n::Backend::Fallbacks
+ end
+
+ # Locales
+ test "setting another default locale" do
+ add_to_config <<-RUBY
+ config.i18n.default_locale = :de
+ RUBY
+
+ load_app
+ assert_equal :de, I18n.default_locale
+ end
+
+ # Load paths
+ test "no config locales directory present should return empty load path" do
+ FileUtils.rm_rf "#{app_path}/config/locales"
+ load_app
+ assert_equal [], Rails.application.config.i18n.load_path
+ end
+
+ test "locale files should be added to the load path" do
+ app_file "config/another_locale.yml", "en:\nfoo: ~"
+
+ add_to_config <<-RUBY
+ config.i18n.load_path << config.root.join("config/another_locale.yml").to_s
+ RUBY
+
+ load_app
+ assert_equal [
+ "#{app_path}/config/locales/en.yml", "#{app_path}/config/another_locale.yml"
+ ], Rails.application.config.i18n.load_path
+
+ assert_includes I18n.load_path, "#{app_path}/config/locales/en.yml"
+ assert_includes I18n.load_path, "#{app_path}/config/another_locale.yml"
+ end
+
+ test "load_path is populated before eager loaded models" do
+ add_to_config <<-RUBY
+ config.cache_classes = true
+ RUBY
+
+ app_file "config/locales/en.yml", <<-YAML
+en:
+ foo: "1"
+ YAML
+
+ app_file "app/models/foo.rb", <<-RUBY
+ class Foo < ActiveRecord::Base
+ @foo = I18n.t(:foo)
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/i18n', :to => lambda { |env| [200, {}, [Foo.instance_variable_get('@foo')]] }
+ end
+ RUBY
+
+ require "rack/test"
+ extend Rack::Test::Methods
+ load_app
+
+ get "/i18n"
+ assert_equal "1", last_response.body
+ end
+
+ test "locales are reloaded if they change between requests" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file "config/locales/en.yml", <<-YAML
+en:
+ foo: "1"
+ YAML
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/i18n', :to => lambda { |env| [200, {}, [I18n.t(:foo)]] }
+ end
+ RUBY
+
+ require "rack/test"
+ extend Rack::Test::Methods
+ load_app
+
+ get "/i18n"
+ assert_equal "1", last_response.body
+
+ # Wait a full second so we have time for changes to propagate
+ sleep(1)
+
+ app_file "config/locales/en.yml", <<-YAML
+en:
+ foo: "2"
+ YAML
+
+ get "/i18n"
+ assert_equal "2", last_response.body
+ end
+
+ test "new locale files are loaded" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file "config/locales/en.yml", <<-YAML
+en:
+ foo: "1"
+ YAML
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/i18n', :to => lambda { |env| [200, {}, [I18n.t(:foo)]] }
+ end
+ RUBY
+
+ require "rack/test"
+ extend Rack::Test::Methods
+ load_app
+
+ get "/i18n"
+ assert_equal "1", last_response.body
+
+ # Wait a full second so we have time for changes to propagate
+ sleep(1)
+
+ remove_file "config/locales/en.yml"
+ app_file "config/locales/custom.en.yml", <<-YAML
+en:
+ foo: "2"
+ YAML
+
+ get "/i18n"
+ assert_equal "2", last_response.body
+ end
+
+ test "I18n.load_path is reloaded" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file "config/locales/en.yml", <<-YAML
+en:
+ foo: "1"
+ YAML
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/i18n', :to => lambda { |env| [200, {}, [I18n.load_path.inspect]] }
+ end
+ RUBY
+
+ require "rack/test"
+ extend Rack::Test::Methods
+ load_app
+
+ get "/i18n"
+
+ assert_match "en.yml", last_response.body
+
+ # Wait a full second so we have time for changes to propagate
+ sleep(1)
+
+ app_file "config/locales/fr.yml", <<-YAML
+fr:
+ foo: "2"
+ YAML
+
+ get "/i18n"
+ assert_match "fr.yml", last_response.body
+ assert_match "en.yml", last_response.body
+ end
+
+ # Fallbacks
+ test "not using config.i18n.fallbacks does not initialize I18n.fallbacks" do
+ I18n.backend = Class.new(I18n::Backend::Simple).new
+ load_app
+ assert_no_fallbacks
+ end
+
+ test "config.i18n.fallbacks = true initializes I18n.fallbacks with default settings" do
+ I18n::Railtie.config.i18n.fallbacks = true
+ load_app
+ assert_includes I18n.backend.class.included_modules, I18n::Backend::Fallbacks
+ assert_fallbacks de: [:de, :en]
+ end
+
+ test "config.i18n.fallbacks = true initializes I18n.fallbacks with default settings even when backend changes" do
+ I18n::Railtie.config.i18n.fallbacks = true
+ I18n::Railtie.config.i18n.backend = Class.new(I18n::Backend::Simple).new
+ load_app
+ assert_includes I18n.backend.class.included_modules, I18n::Backend::Fallbacks
+ assert_fallbacks de: [:de, :en]
+ end
+
+ test "config.i18n.fallbacks.defaults = [:'en-US'] initializes fallbacks with en-US as a fallback default" do
+ I18n::Railtie.config.i18n.fallbacks.defaults = [:'en-US']
+ load_app
+ assert_fallbacks de: [:de, :'en-US', :en]
+ end
+
+ test "config.i18n.fallbacks.map = { :ca => :'es-ES' } initializes fallbacks with a mapping ca => es-ES" do
+ I18n::Railtie.config.i18n.fallbacks.map = { ca: :'es-ES' }
+ load_app
+ assert_fallbacks ca: [:ca, :"es-ES", :es, :en]
+ end
+
+ test "[shortcut] config.i18n.fallbacks = [:'en-US'] initializes fallbacks with en-US as a fallback default" do
+ I18n::Railtie.config.i18n.fallbacks = [:'en-US']
+ load_app
+ assert_fallbacks de: [:de, :'en-US', :en]
+ end
+
+ test "[shortcut] config.i18n.fallbacks = [{ :ca => :'es-ES' }] initializes fallbacks with a mapping ca => es-ES" do
+ I18n::Railtie.config.i18n.fallbacks = [{ ca: :'es-ES' }]
+ load_app
+ assert_fallbacks ca: [:ca, :"es-ES", :es, :en]
+ end
+
+ test "[shortcut] config.i18n.fallbacks = [:'en-US', { :ca => :'es-ES' }] initializes fallbacks with the given arguments" do
+ I18n::Railtie.config.i18n.fallbacks = [:'en-US', { ca: :'es-ES' }]
+ load_app
+ assert_fallbacks ca: [:ca, :"es-ES", :es, :'en-US', :en]
+ end
+
+ test "[shortcut] config.i18n.fallbacks = { ca: :en } initializes fallbacks with a mapping ca => :en" do
+ I18n::Railtie.config.i18n.fallbacks = { ca: :en }
+ load_app
+ assert_fallbacks ca: [:ca, :en]
+ end
+
+ test "disable config.i18n.enforce_available_locales" do
+ add_to_config <<-RUBY
+ config.i18n.enforce_available_locales = false
+ config.i18n.default_locale = :fr
+ RUBY
+
+ load_app
+ assert_equal false, I18n.enforce_available_locales
+
+ assert_nothing_raised do
+ I18n.locale = :es
+ end
+ end
+
+ test "default config.i18n.enforce_available_locales does not override I18n.enforce_available_locales" do
+ I18n.enforce_available_locales = false
+
+ add_to_config <<-RUBY
+ config.i18n.default_locale = :fr
+ RUBY
+
+ load_app
+ assert_equal false, I18n.enforce_available_locales
+
+ assert_nothing_raised do
+ I18n.locale = :es
+ end
+ end
+ end
+end
diff --git a/railties/test/application/initializers/load_path_test.rb b/railties/test/application/initializers/load_path_test.rb
new file mode 100644
index 0000000000..dbefb22837
--- /dev/null
+++ b/railties/test/application/initializers/load_path_test.rb
@@ -0,0 +1,109 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class LoadPathTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "initializing an application adds the application paths to the load path" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_includes $:, "#{app_path}/app/models"
+ end
+
+ test "initializing an application allows to load code on lib path inside application class definition" do
+ app_file "lib/foo.rb", <<-RUBY
+ module Foo; end
+ RUBY
+
+ add_to_config <<-RUBY
+ require "foo"
+ raise "Expected Foo to be defined" unless defined?(Foo)
+ RUBY
+
+ assert_nothing_raised do
+ require "#{app_path}/config/environment"
+ end
+
+ assert_includes $:, "#{app_path}/lib"
+ end
+
+ test "initializing an application eager load any path under app" do
+ app_file "app/anything/foo.rb", <<-RUBY
+ module Foo; end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert Foo
+ end
+
+ test "eager loading loads parent classes before children" do
+ app_file "lib/zoo.rb", <<-ZOO
+ class Zoo ; include ReptileHouse ; end
+ ZOO
+
+ app_file "lib/zoo/reptile_house.rb", <<-ZOO
+ module Zoo::ReptileHouse ; end
+ ZOO
+
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.eager_load_paths << "#{app_path}/lib"
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert Zoo
+ end
+
+ test "eager loading accepts Pathnames" do
+ app_file "lib/foo.rb", <<-RUBY
+ module Foo; end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.eager_load_paths << Pathname.new("#{app_path}/lib")
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert Foo
+ end
+
+ test "load environment with global" do
+ $initialize_test_set_from_env = nil
+ app_file "config/environments/development.rb", <<-RUBY
+ $initialize_test_set_from_env = 'success'
+ Rails.application.configure do
+ config.cache_classes = true
+ config.time_zone = "Brasilia"
+ end
+ RUBY
+
+ assert_nil $initialize_test_set_from_env
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.time_zone = "UTC"
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_equal "success", $initialize_test_set_from_env
+ assert Rails.application.config.cache_classes
+ assert_equal "Brasilia", Rails.application.config.time_zone
+ end
+ end
+end
diff --git a/railties/test/application/initializers/notifications_test.rb b/railties/test/application/initializers/notifications_test.rb
new file mode 100644
index 0000000000..b847ac6b36
--- /dev/null
+++ b/railties/test/application/initializers/notifications_test.rb
@@ -0,0 +1,55 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class NotificationsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def instrument(*args, &block)
+ ActiveSupport::Notifications.instrument(*args, &block)
+ end
+
+ def wait
+ ActiveSupport::Notifications.notifier.wait
+ end
+
+ test "rails log_subscribers are added" do
+ add_to_config <<-RUBY
+ config.colorize_logging = false
+ RUBY
+
+ require "#{app_path}/config/environment"
+ require "active_support/log_subscriber/test_helper"
+
+ logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
+ ActiveRecord::Base.logger = logger
+
+ # Mimic Active Record notifications
+ instrument "sql.active_record", name: "SQL", sql: "SHOW tables"
+ wait
+
+ assert_equal 1, logger.logged(:debug).size
+ assert_match(/SHOW tables/, logger.logged(:debug).last)
+ end
+
+ test "rails load_config_initializer event is instrumented" do
+ app_file "config/initializers/foo.rb", ""
+
+ events = []
+ callback = ->(*_) { events << _ }
+ ActiveSupport::Notifications.subscribed(callback, "load_config_initializer.railties") do
+ app
+ end
+
+ assert_equal %w[load_config_initializer.railties], events.map(&:first)
+ assert_includes events.first.last[:initializer], "config/initializers/foo.rb"
+ end
+ end
+end
diff --git a/railties/test/application/integration_test_case_test.rb b/railties/test/application/integration_test_case_test.rb
new file mode 100644
index 0000000000..1118e5037a
--- /dev/null
+++ b/railties/test/application/integration_test_case_test.rb
@@ -0,0 +1,73 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class IntegrationTestCaseTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ setup do
+ build_app
+ end
+
+ teardown do
+ teardown_app
+ end
+
+ test "resets Action Mailer test deliveries" do
+ script("generate mailer BaseMailer welcome")
+
+ app_file "test/integration/mailer_integration_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class MailerIntegrationTest < ActionDispatch::IntegrationTest
+ setup do
+ @old_delivery_method = ActionMailer::Base.delivery_method
+ ActionMailer::Base.delivery_method = :test
+ end
+
+ teardown do
+ ActionMailer::Base.delivery_method = @old_delivery_method
+ end
+
+ 2.times do |i|
+ define_method "test_resets_deliveries_\#{i}" do
+ BaseMailer.welcome.deliver_now
+ assert_equal 1, ActionMailer::Base.deliveries.count
+ end
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rails test 2>&1` }
+ assert_equal 0, $?.to_i, output
+ assert_match(/0 failures, 0 errors/, output)
+ end
+ end
+
+ class IntegrationTestDefaultApp < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ setup do
+ build_app
+ end
+
+ teardown do
+ teardown_app
+ end
+
+ test "app method of integration tests returns test_app by default" do
+ app_file "test/integration/default_app_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class DefaultAppIntegrationTest < ActionDispatch::IntegrationTest
+ def test_app_returns_action_dispatch_test_app_by_default
+ assert_equal ActionDispatch.test_app, app
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rails test 2>&1` }
+ assert_equal 0, $?.to_i, output
+ assert_match(/0 failures, 0 errors/, output)
+ end
+ end
+end
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
new file mode 100644
index 0000000000..c75a25bc6f
--- /dev/null
+++ b/railties/test/application/loading_test.rb
@@ -0,0 +1,371 @@
+require "isolation/abstract_unit"
+
+class LoadingTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ test "constants in app are autoloaded" do
+ app_file "app/models/post.rb", <<-MODEL
+ class Post < ActiveRecord::Base
+ validates_acceptance_of :title, accept: "omg"
+ end
+ MODEL
+
+ require "#{rails_root}/config/environment"
+ setup_ar!
+
+ p = Post.create(title: "omg")
+ assert_equal 1, Post.count
+ assert_equal "omg", p.title
+ p = Post.first
+ assert_equal "omg", p.title
+ end
+
+ test "concerns in app are autoloaded" do
+ app_file "app/controllers/concerns/trackable.rb", <<-CONCERN
+ module Trackable
+ end
+ CONCERN
+
+ app_file "app/mailers/concerns/email_loggable.rb", <<-CONCERN
+ module EmailLoggable
+ end
+ CONCERN
+
+ app_file "app/models/concerns/orderable.rb", <<-CONCERN
+ module Orderable
+ end
+ CONCERN
+
+ app_file "app/validators/concerns/matchable.rb", <<-CONCERN
+ module Matchable
+ end
+ CONCERN
+
+ require "#{rails_root}/config/environment"
+
+ assert_nothing_raised { Trackable }
+ assert_nothing_raised { EmailLoggable }
+ assert_nothing_raised { Orderable }
+ assert_nothing_raised { Matchable }
+ end
+
+ test "models without table do not panic on scope definitions when loaded" do
+ app_file "app/models/user.rb", <<-MODEL
+ class User < ActiveRecord::Base
+ default_scope { where(published: true) }
+ end
+ MODEL
+
+ require "#{rails_root}/config/environment"
+ setup_ar!
+
+ User
+ end
+
+ test "load config/environments/environment before Bootstrap initializers" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.development_environment_loaded = true
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.before_initialize do
+ config.loaded = config.development_environment_loaded
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert ::Rails.application.config.loaded
+ end
+
+ test "descendants loaded after framework initialization are cleaned on each request without cache classes" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ config.reload_classes_only_on_change = false
+ RUBY
+
+ app_file "app/models/post.rb", <<-MODEL
+ class Post < ActiveRecord::Base
+ end
+ MODEL
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/load', to: lambda { |env| [200, {}, Post.all] }
+ get '/unload', to: lambda { |env| [200, {}, []] }
+ end
+ RUBY
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+ setup_ar!
+
+ assert_equal [ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata], ActiveRecord::Base.descendants
+ get "/load"
+ assert_equal [ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata, Post], ActiveRecord::Base.descendants
+ get "/unload"
+ assert_equal [ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata], ActiveRecord::Base.descendants
+ end
+
+ test "initialize cant be called twice" do
+ require "#{app_path}/config/environment"
+ assert_raise(RuntimeError) { Rails.application.initialize! }
+ end
+
+ test "reload constants on development" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/c', to: lambda { |env| [200, {"Content-Type" => "text/plain"}, [User.counter.to_s]] }
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.counter; 1; end
+ end
+ MODEL
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+
+ get "/c"
+ assert_equal "1", last_response.body
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.counter; 2; end
+ end
+ MODEL
+
+ get "/c"
+ assert_equal "2", last_response.body
+ end
+
+ test "does not reload constants on development if custom file watcher always returns false" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ config.file_watcher = Class.new do
+ def initialize(*); end
+ def updated?; false; end
+ def execute; end
+ def execute_if_updated; false; end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/c', to: lambda { |env| [200, {"Content-Type" => "text/plain"}, [User.counter.to_s]] }
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.counter; 1; end
+ end
+ MODEL
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+
+ get "/c"
+ assert_equal "1", last_response.body
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.counter; 2; end
+ end
+ MODEL
+
+ get "/c"
+ assert_equal "1", last_response.body
+ end
+
+ test "added files (like db/schema.rb) also trigger reloading" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ $counter ||= 0
+ Rails.application.routes.draw do
+ get '/c', to: lambda { |env| User.name; [200, {"Content-Type" => "text/plain"}, [$counter.to_s]] }
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ $counter += 1
+ end
+ MODEL
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+
+ get "/c"
+ assert_equal "1", last_response.body
+
+ app_file "db/schema.rb", ""
+
+ get "/c"
+ assert_equal "2", last_response.body
+ end
+
+ test "dependencies reloading is followed by routes reloading" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ $counter ||= 1
+ $counter *= 2
+ Rails.application.routes.draw do
+ get '/c', to: lambda { |env| User.name; [200, {"Content-Type" => "text/plain"}, [$counter.to_s]] }
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ $counter += 1
+ end
+ MODEL
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+
+ get "/c"
+ assert_equal "3", last_response.body
+
+ app_file "db/schema.rb", ""
+
+ get "/c"
+ assert_equal "7", last_response.body
+ end
+
+ test "columns migrations also trigger reloading" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/title', to: lambda { |env| [200, {"Content-Type" => "text/plain"}, [Post.new.title]] }
+ get '/body', to: lambda { |env| [200, {"Content-Type" => "text/plain"}, [Post.new.body]] }
+ end
+ RUBY
+
+ app_file "app/models/post.rb", <<-MODEL
+ class Post < ActiveRecord::Base
+ end
+ MODEL
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ app_file "db/migrate/1_create_posts.rb", <<-MIGRATION
+ class CreatePosts < ActiveRecord::Migration::Current
+ def change
+ create_table :posts do |t|
+ t.string :title, default: "TITLE"
+ end
+ end
+ end
+ MIGRATION
+
+ Dir.chdir(app_path) { `rake db:migrate` }
+ require "#{rails_root}/config/environment"
+
+ get "/title"
+ assert_equal "TITLE", last_response.body
+
+ app_file "db/migrate/2_add_body_to_posts.rb", <<-MIGRATION
+ class AddBodyToPosts < ActiveRecord::Migration::Current
+ def change
+ add_column :posts, :body, :text, default: "BODY"
+ end
+ end
+ MIGRATION
+
+ Dir.chdir(app_path) { `rake db:migrate` }
+
+ get "/body"
+ assert_equal "BODY", last_response.body
+ end
+
+ test "AC load hooks can be used with metal" do
+ app_file "app/controllers/omg_controller.rb", <<-RUBY
+ begin
+ class OmgController < ActionController::Metal
+ ActiveSupport.run_load_hooks(:action_controller, self)
+ def show
+ self.response_body = ["OK"]
+ 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
+
+ require "#{rails_root}/config/environment"
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ get "/omg/show"
+ assert_equal "OK", last_response.body
+ end
+
+ def test_initialize_can_be_called_at_any_time
+ require "#{app_path}/config/application"
+
+ assert !Rails.initialized?
+ assert !Rails.application.initialized?
+ Rails.initialize!
+ assert Rails.initialized?
+ assert Rails.application.initialized?
+ end
+
+ private
+
+ def setup_ar!
+ 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
+ end
+end
diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb
new file mode 100644
index 0000000000..f5c013dab6
--- /dev/null
+++ b/railties/test/application/mailer_previews_test.rb
@@ -0,0 +1,785 @@
+require "isolation/abstract_unit"
+require "rack/test"
+require "base64"
+
+module ApplicationTests
+ class MailerPreviewsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "/rails/mailers is accessible in development" do
+ app("development")
+ get "/rails/mailers"
+ assert_equal 200, last_response.status
+ end
+
+ test "/rails/mailers is not accessible in production" do
+ app("production")
+ get "/rails/mailers"
+ assert_equal 404, last_response.status
+ end
+
+ test "/rails/mailers is accessible with correct configuration" do
+ add_to_config "config.action_mailer.show_previews = true"
+ app("production")
+ get "/rails/mailers", {}, "REMOTE_ADDR" => "4.2.42.42"
+ assert_equal 200, last_response.status
+ end
+
+ test "/rails/mailers is not accessible with show_previews = false" do
+ add_to_config "config.action_mailer.show_previews = false"
+ app("development")
+ get "/rails/mailers"
+ assert_equal 404, last_response.status
+ end
+
+ test "/rails/mailers is accessible with globbing route present" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '*foo', to: 'foo#index'
+ end
+ RUBY
+ app("development")
+ get "/rails/mailers"
+ assert_equal 200, last_response.status
+ end
+
+ test "mailer previews are loaded from the default preview_path" do
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ end
+
+ test "mailer previews are loaded from a custom preview_path" do
+ add_to_config "config.action_mailer.preview_path = '#{app_path}/lib/mailer_previews'"
+
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ app_file "lib/mailer_previews/notifier_preview.rb", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ end
+
+ test "mailer previews are reloaded across requests" do
+ app("development")
+
+ get "/rails/mailers"
+ assert_no_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+
+ remove_file "test/mailers/previews/notifier_preview.rb"
+ sleep(1)
+
+ get "/rails/mailers"
+ assert_no_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ end
+
+ test "mailer preview actions are added and removed" do
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ assert_no_match '<li><a href="/rails/mailers/notifier/bar">bar</a></li>', last_response.body
+
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+
+ def bar
+ mail to: "to@example.net"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ text_template "notifier/bar", <<-RUBY
+ Goodbye, World!
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+
+ def bar
+ Notifier.bar
+ end
+ end
+ RUBY
+
+ sleep(1)
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ assert_match '<li><a href="/rails/mailers/notifier/bar">bar</a></li>', last_response.body
+
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ remove_file "app/views/notifier/bar.text.erb"
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ sleep(1)
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ assert_no_match '<li><a href="/rails/mailers/notifier/bar">bar</a></li>', last_response.body
+ end
+
+ test "mailer previews are reloaded from a custom preview_path" do
+ add_to_config "config.action_mailer.preview_path = '#{app_path}/lib/mailer_previews'"
+
+ app("development")
+
+ get "/rails/mailers"
+ assert_no_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ app_file "lib/mailer_previews/notifier_preview.rb", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+
+ remove_file "lib/mailer_previews/notifier_preview.rb"
+ sleep(1)
+
+ get "/rails/mailers"
+ assert_no_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ end
+
+ test "mailer preview not found" do
+ app("development")
+ get "/rails/mailers/notifier"
+ assert last_response.not_found?
+ assert_match "Mailer preview &#39;notifier&#39; not found", last_response.body
+ end
+
+ test "mailer preview email not found" do
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers/notifier/bar"
+ assert last_response.not_found?
+ assert_match "Email &#39;bar&#39; not found in NotifierPreview", last_response.body
+ end
+
+ test "mailer preview NullMail" do
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ # does not call +mail+
+ end
+ end
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers/notifier/foo"
+ assert_match "You are trying to preview an email that does not have any content.", last_response.body
+ assert_match "notifier#foo", last_response.body
+ end
+
+ test "mailer preview email part not found" do
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers/notifier/foo?part=text%2Fhtml"
+ assert last_response.not_found?
+ assert_match "Email part &#39;text/html&#39; not found in NotifierPreview#foo", last_response.body
+ end
+
+ test "message header uses full display names" do
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "Ruby on Rails <core@rubyonrails.org>"
+
+ def foo
+ mail to: "Andrew White <andyw@pixeltrix.co.uk>",
+ cc: "David Heinemeier Hansson <david@heinemeierhansson.com>"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers/notifier/foo"
+ assert_equal 200, last_response.status
+ assert_match "Ruby on Rails &lt;core@rubyonrails.org&gt;", last_response.body
+ assert_match "Andrew White &lt;andyw@pixeltrix.co.uk&gt;", last_response.body
+ assert_match "David Heinemeier Hansson &lt;david@heinemeierhansson.com&gt;", last_response.body
+ end
+
+ test "part menu selects correct option" do
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ html_template "notifier/foo", <<-RUBY
+ <p>Hello, World!</p>
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers/notifier/foo.html"
+ assert_equal 200, last_response.status
+ assert_match '<option selected value="?part=text%2Fhtml">View as HTML email</option>', last_response.body
+
+ get "/rails/mailers/notifier/foo.txt"
+ assert_equal 200, last_response.status
+ assert_match '<option selected value="?part=text%2Fplain">View as plain-text email</option>', last_response.body
+ end
+
+ test "mailer previews create correct links when loaded on a subdirectory" do
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers", {}, "SCRIPT_NAME" => "/my_app"
+ assert_match '<h3><a href="/my_app/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/my_app/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ end
+
+ test "mailer preview receives query params" do
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo(name)
+ @name = name
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ html_template "notifier/foo", <<-RUBY
+ <p>Hello, <%= @name %>!</p>
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, <%= @name %>!
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo(params[:name] || "World")
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers/notifier/foo.txt"
+ assert_equal 200, last_response.status
+ assert_match '<iframe seamless name="messageBody" src="?part=text%2Fplain">', last_response.body
+ assert_match '<option selected value="?part=text%2Fplain">', last_response.body
+ assert_match '<option value="?part=text%2Fhtml">', last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text%2Fplain"
+ assert_equal 200, last_response.status
+ assert_match %r[Hello, World!], last_response.body
+
+ get "/rails/mailers/notifier/foo.html?name=Ruby"
+ assert_equal 200, last_response.status
+ assert_match '<iframe seamless name="messageBody" src="?name=Ruby&amp;part=text%2Fhtml">', last_response.body
+ assert_match '<option selected value="?name=Ruby&amp;part=text%2Fhtml">', last_response.body
+ assert_match '<option value="?name=Ruby&amp;part=text%2Fplain">', last_response.body
+
+ get "/rails/mailers/notifier/foo?name=Ruby&part=text%2Fhtml"
+ assert_equal 200, last_response.status
+ assert_match %r[<p>Hello, Ruby!</p>], last_response.body
+ end
+
+ test "plain text mailer preview with attachment" do
+ image_file "pixel.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEWzIioca/JlAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJgggo="
+
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ attachments['pixel.png'] = File.read("#{app_path}/public/images/pixel.png", mode: 'rb')
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers/notifier/foo"
+ assert_equal 200, last_response.status
+ assert_match %r[<iframe seamless name="messageBody"], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/plain"
+ assert_equal 200, last_response.status
+ assert_match %r[Hello, World!], last_response.body
+ end
+
+ test "multipart mailer preview with attachment" do
+ image_file "pixel.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEWzIioca/JlAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJgggo="
+
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ attachments['pixel.png'] = File.read("#{app_path}/public/images/pixel.png", mode: 'rb')
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ html_template "notifier/foo", <<-RUBY
+ <p>Hello, World!</p>
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers/notifier/foo"
+ assert_equal 200, last_response.status
+ assert_match %r[<iframe seamless name="messageBody"], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/plain"
+ assert_equal 200, last_response.status
+ assert_match %r[Hello, World!], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/html"
+ assert_equal 200, last_response.status
+ assert_match %r[<p>Hello, World!</p>], last_response.body
+ end
+
+ test "multipart mailer preview with inline attachment" do
+ image_file "pixel.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEWzIioca/JlAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJgggo="
+
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ attachments['pixel.png'] = File.read("#{app_path}/public/images/pixel.png", mode: 'rb')
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ html_template "notifier/foo", <<-RUBY
+ <p>Hello, World!</p>
+ <%= image_tag attachments['pixel.png'].url %>
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers/notifier/foo"
+ assert_equal 200, last_response.status
+ assert_match %r[<iframe seamless name="messageBody"], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/plain"
+ assert_equal 200, last_response.status
+ assert_match %r[Hello, World!], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/html"
+ assert_equal 200, last_response.status
+ assert_match %r[<p>Hello, World!</p>], last_response.body
+ assert_match %r[src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEWzIioca/JlAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJgggo="], last_response.body
+ end
+
+ test "multipart mailer preview with attached email" do
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ message = ::Mail.new do
+ from 'foo@example.com'
+ to 'bar@example.com'
+ subject 'Important Message'
+
+ text_part do
+ body 'Goodbye, World!'
+ end
+
+ html_part do
+ body '<p>Goodbye, World!</p>'
+ end
+ end
+
+ attachments['message.eml'] = message.to_s
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ Hello, World!
+ RUBY
+
+ html_template "notifier/foo", <<-RUBY
+ <p>Hello, World!</p>
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers/notifier/foo"
+ assert_equal 200, last_response.status
+ assert_match %r[<iframe seamless name="messageBody"], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/plain"
+ assert_equal 200, last_response.status
+ assert_match %r[Hello, World!], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/html"
+ assert_equal 200, last_response.status
+ assert_match %r[<p>Hello, World!</p>], last_response.body
+ end
+
+ test "multipart mailer preview with empty parts" do
+ mailer "notifier", <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template "notifier/foo", <<-RUBY
+ RUBY
+
+ html_template "notifier/foo", <<-RUBY
+ RUBY
+
+ mailer_preview "notifier", <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app("development")
+
+ get "/rails/mailers/notifier/foo?part=text/plain"
+ assert_equal 200, last_response.status
+
+ get "/rails/mailers/notifier/foo?part=text/html"
+ assert_equal 200, last_response.status
+ end
+
+ private
+ def build_app
+ super
+ app_file "config/routes.rb", "Rails.application.routes.draw do; end"
+ end
+
+ def mailer(name, contents)
+ app_file("app/mailers/#{name}.rb", contents)
+ end
+
+ def mailer_preview(name, contents)
+ app_file("test/mailers/previews/#{name}_preview.rb", contents)
+ end
+
+ def html_template(name, contents)
+ app_file("app/views/#{name}.html.erb", contents)
+ end
+
+ def text_template(name, contents)
+ app_file("app/views/#{name}.text.erb", contents)
+ end
+
+ def image_file(name, contents)
+ app_file("public/images/#{name}", Base64.strict_decode64(contents), "wb")
+ end
+ end
+end
diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb
new file mode 100644
index 0000000000..93b5263bf7
--- /dev/null
+++ b/railties/test/application/middleware/cache_test.rb
@@ -0,0 +1,179 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class CacheTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ require "rack/test"
+ extend Rack::Test::Methods
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def simple_controller
+ controller :expires, <<-RUBY
+ class ExpiresController < ApplicationController
+ def expires_header
+ expires_in 10, public: !params[:private]
+ render plain: SecureRandom.hex(16)
+ end
+
+ def expires_etag
+ render_conditionally(etag: "1")
+ end
+
+ def expires_last_modified
+ $last_modified ||= Time.now.utc
+ render_conditionally(last_modified: $last_modified)
+ end
+
+ def keeps_if_modified_since
+ render plain: request.headers['If-Modified-Since']
+ end
+ private
+ def render_conditionally(headers)
+ if stale?(headers.merge(public: !params[:private]))
+ render plain: SecureRandom.hex(16)
+ end
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ end
+ RUBY
+ end
+
+ def test_cache_keeps_if_modified_since
+ simple_controller
+ expected = "Wed, 30 May 1984 19:43:31 GMT"
+
+ get "/expires/keeps_if_modified_since", {}, "HTTP_IF_MODIFIED_SINCE" => expected
+
+ assert_equal 200, last_response.status
+ assert_equal expected, last_response.body, "cache should have kept If-Modified-Since"
+ end
+
+ def test_cache_is_disabled_in_dev_mode
+ simple_controller
+ app("development")
+
+ get "/expires/expires_header"
+ assert_nil last_response.headers["X-Rack-Cache"]
+
+ body = last_response.body
+
+ get "/expires/expires_header"
+ assert_nil last_response.headers["X-Rack-Cache"]
+ assert_not_equal body, last_response.body
+ end
+
+ def test_cache_works_with_expires
+ simple_controller
+
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_header"
+ assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
+ assert_equal "max-age=10, public", last_response.headers["Cache-Control"]
+
+ body = last_response.body
+
+ get "/expires/expires_header"
+
+ assert_equal "fresh", last_response.headers["X-Rack-Cache"]
+
+ assert_equal body, last_response.body
+ end
+
+ def test_cache_works_with_expires_private
+ simple_controller
+
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_header", private: true
+ assert_equal "miss", last_response.headers["X-Rack-Cache"]
+ assert_equal "private, max-age=10", last_response.headers["Cache-Control"]
+
+ body = last_response.body
+
+ get "/expires/expires_header", private: true
+ assert_equal "miss", last_response.headers["X-Rack-Cache"]
+ assert_not_equal body, last_response.body
+ end
+
+ def test_cache_works_with_etags
+ simple_controller
+
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_etag"
+ assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
+ assert_equal "public", last_response.headers["Cache-Control"]
+
+ etag = last_response.headers["ETag"]
+
+ get "/expires/expires_etag", {}, "HTTP_IF_NONE_MATCH" => etag
+ assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"]
+ assert_equal 304, last_response.status
+ assert_equal "", last_response.body
+ end
+
+ def test_cache_works_with_etags_private
+ simple_controller
+
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_etag", private: true
+ assert_equal "miss", last_response.headers["X-Rack-Cache"]
+ assert_equal "must-revalidate, private, max-age=0", last_response.headers["Cache-Control"]
+
+ body = last_response.body
+ etag = last_response.headers["ETag"]
+
+ get "/expires/expires_etag", { private: true }, "HTTP_IF_NONE_MATCH" => etag
+ assert_equal "miss", last_response.headers["X-Rack-Cache"]
+ assert_not_equal body, last_response.body
+ end
+
+ def test_cache_works_with_last_modified
+ simple_controller
+
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_last_modified"
+ assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
+ assert_equal "public", last_response.headers["Cache-Control"]
+
+ last = last_response.headers["Last-Modified"]
+
+ get "/expires/expires_last_modified", {}, "HTTP_IF_MODIFIED_SINCE" => last
+ assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"]
+ assert_equal 304, last_response.status
+ assert_equal "", last_response.body
+ end
+
+ def test_cache_works_with_last_modified_private
+ simple_controller
+
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_last_modified", private: true
+ assert_equal "miss", last_response.headers["X-Rack-Cache"]
+ assert_equal "must-revalidate, private, max-age=0", last_response.headers["Cache-Control"]
+
+ body = last_response.body
+ last = last_response.headers["Last-Modified"]
+
+ get "/expires/expires_last_modified", { private: true }, "HTTP_IF_MODIFIED_SINCE" => last
+ assert_equal "miss", last_response.headers["X-Rack-Cache"]
+ assert_not_equal body, last_response.body
+ end
+ end
+end
diff --git a/railties/test/application/middleware/cookies_test.rb b/railties/test/application/middleware/cookies_test.rb
new file mode 100644
index 0000000000..1e4b5d086c
--- /dev/null
+++ b/railties/test/application/middleware/cookies_test.rb
@@ -0,0 +1,46 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class CookiesTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def new_app
+ File.expand_path("#{app_path}/../new_app")
+ end
+
+ def setup
+ build_app
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ FileUtils.rm_rf(new_app) if File.directory?(new_app)
+ end
+
+ test "always_write_cookie is true by default in development" do
+ require "rails"
+ Rails.env = "development"
+ require "#{app_path}/config/environment"
+ assert_equal true, ActionDispatch::Cookies::CookieJar.always_write_cookie
+ end
+
+ test "always_write_cookie is false by default in production" do
+ require "rails"
+ Rails.env = "production"
+ require "#{app_path}/config/environment"
+ assert_equal false, ActionDispatch::Cookies::CookieJar.always_write_cookie
+ end
+
+ test "always_write_cookie can be overridden" do
+ add_to_config <<-RUBY
+ config.action_dispatch.always_write_cookie = false
+ RUBY
+
+ require "rails"
+ Rails.env = "development"
+ require "#{app_path}/config/environment"
+ assert_equal false, ActionDispatch::Cookies::CookieJar.always_write_cookie
+ end
+ end
+end
diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb
new file mode 100644
index 0000000000..fe07ad3cbe
--- /dev/null
+++ b/railties/test/application/middleware/exceptions_test.rb
@@ -0,0 +1,138 @@
+require "isolation/abstract_unit"
+require "rack/test"
+
+module ApplicationTests
+ class MiddlewareExceptionsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "show exceptions middleware filter backtrace before logging" do
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ raise 'oops'
+ end
+ end
+ RUBY
+
+ get "/foo"
+ assert_equal 500, last_response.status
+
+ log = File.read(Rails.application.config.paths["log"].first)
+ assert_no_match(/action_dispatch/, log, log)
+ assert_match(/oops/, log, log)
+ end
+
+ test "renders active record exceptions as 404" do
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ raise ActiveRecord::RecordNotFound
+ end
+ end
+ RUBY
+
+ get "/foo"
+ assert_equal 404, last_response.status
+ end
+
+ test "uses custom exceptions app" do
+ add_to_config <<-RUBY
+ config.exceptions_app = lambda do |env|
+ [404, { "Content-Type" => "text/plain" }, ["YOU FAILED"]]
+ end
+ RUBY
+
+ app.config.action_dispatch.show_exceptions = true
+
+ get "/foo"
+ assert_equal 404, last_response.status
+ assert_equal "YOU FAILED", last_response.body
+ end
+
+ test "url generation error when action_dispatch.show_exceptions is set raises an exception" do
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ raise ActionController::UrlGenerationError
+ end
+ end
+ RUBY
+
+ app.config.action_dispatch.show_exceptions = true
+
+ get "/foo"
+ assert_equal 500, last_response.status
+ end
+
+ test "unspecified route when action_dispatch.show_exceptions is not set raises an exception" do
+ app.config.action_dispatch.show_exceptions = false
+
+ assert_raise(ActionController::RoutingError) do
+ get "/foo"
+ end
+ end
+
+ test "unspecified route when action_dispatch.show_exceptions is set shows 404" do
+ app.config.action_dispatch.show_exceptions = true
+
+ assert_nothing_raised do
+ get "/foo"
+ assert_match "The page you were looking for doesn't exist.", last_response.body
+ end
+ end
+
+ test "unspecified route when action_dispatch.show_exceptions and consider_all_requests_local are set shows diagnostics" do
+ app.config.action_dispatch.show_exceptions = true
+ app.config.consider_all_requests_local = true
+
+ assert_nothing_raised do
+ get "/foo"
+ assert_match "No route matches", last_response.body
+ end
+ end
+
+ test "routing to an nonexistent controller when action_dispatch.show_exceptions and consider_all_requests_local are set shows diagnostics" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ resources :articles
+ end
+ RUBY
+
+ app.config.action_dispatch.show_exceptions = true
+ app.config.consider_all_requests_local = true
+
+ get "/articles"
+ assert_match "<title>Action Controller: Exception caught</title>", last_response.body
+ end
+
+ test "displays diagnostics message when exception raised in template that contains UTF-8" do
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ end
+ end
+ RUBY
+
+ app.config.action_dispatch.show_exceptions = true
+ app.config.consider_all_requests_local = true
+
+ app_file "app/views/foo/index.html.erb", <<-ERB
+ <% raise 'boooom' %>
+ ✓測試テスト시험
+ ERB
+
+ get "/foo", utf8: "✓"
+ assert_match(/boooom/, last_response.body)
+ assert_match(/測試テスト시험/, last_response.body)
+ end
+ end
+end
diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb
new file mode 100644
index 0000000000..c34d9d6ee7
--- /dev/null
+++ b/railties/test/application/middleware/remote_ip_test.rb
@@ -0,0 +1,78 @@
+require "ipaddr"
+require "isolation/abstract_unit"
+require "active_support/key_generator"
+
+module ApplicationTests
+ class RemoteIpTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def remote_ip(env = {})
+ remote_ip = nil
+ env = Rack::MockRequest.env_for("/").merge(env).merge!(
+ "action_dispatch.show_exceptions" => false,
+ "action_dispatch.key_generator" => ActiveSupport::LegacyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33")
+ )
+
+ endpoint = Proc.new do |e|
+ remote_ip = ActionDispatch::Request.new(e).remote_ip
+ [200, {}, ["Hello"]]
+ end
+
+ Rails.application.middleware.build(endpoint).call(env)
+ remote_ip
+ end
+
+ test "remote_ip works" do
+ make_basic_app
+ assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "1.1.1.1")
+ end
+
+ test "checks IP spoofing by default" do
+ make_basic_app
+ assert_raises(ActionDispatch::RemoteIp::IpSpoofAttackError) do
+ remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1", "HTTP_CLIENT_IP" => "1.1.1.2")
+ end
+ end
+
+ test "works with both headers individually" do
+ make_basic_app
+ assert_nothing_raised do
+ assert_equal "1.1.1.1", remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1")
+ end
+ assert_nothing_raised do
+ assert_equal "1.1.1.2", remote_ip("HTTP_CLIENT_IP" => "1.1.1.2")
+ end
+ end
+
+ test "can disable IP spoofing check" do
+ make_basic_app do |app|
+ app.config.action_dispatch.ip_spoofing_check = false
+ end
+
+ assert_nothing_raised do
+ assert_equal "1.1.1.1", remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1", "HTTP_CLIENT_IP" => "1.1.1.2")
+ end
+ end
+
+ test "remote_ip works with HTTP_X_FORWARDED_FOR" do
+ make_basic_app
+ assert_equal "4.2.42.42", remote_ip("REMOTE_ADDR" => "1.1.1.1", "HTTP_X_FORWARDED_FOR" => "4.2.42.42")
+ end
+
+ test "the user can set trusted proxies" do
+ make_basic_app do |app|
+ app.config.action_dispatch.trusted_proxies = /^4\.2\.42\.42$/
+ end
+
+ assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "1.1.1.1", "HTTP_X_FORWARDED_FOR" => "4.2.42.42")
+ end
+
+ test "the user can set trusted proxies with an IPAddr argument" do
+ make_basic_app do |app|
+ app.config.action_dispatch.trusted_proxies = IPAddr.new("4.2.42.0/24")
+ end
+
+ assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "1.1.1.1", "HTTP_X_FORWARDED_FOR" => "10.0.0.0,4.2.42.42")
+ end
+ end
+end
diff --git a/railties/test/application/middleware/sendfile_test.rb b/railties/test/application/middleware/sendfile_test.rb
new file mode 100644
index 0000000000..4938402fdc
--- /dev/null
+++ b/railties/test/application/middleware/sendfile_test.rb
@@ -0,0 +1,73 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class SendfileTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ define_method :simple_controller do
+ class ::OmgController < ActionController::Base
+ def index
+ send_file __FILE__
+ end
+ end
+ end
+
+ # x_sendfile_header middleware
+ test "config.action_dispatch.x_sendfile_header defaults to nil" do
+ make_basic_app
+ simple_controller
+
+ get "/"
+ assert !last_response.headers["X-Sendfile"]
+ assert_equal File.read(__FILE__), last_response.body
+ end
+
+ test "config.action_dispatch.x_sendfile_header can be set" do
+ make_basic_app do |app|
+ app.config.action_dispatch.x_sendfile_header = "X-Sendfile"
+ end
+
+ simple_controller
+
+ get "/"
+ assert_equal File.expand_path(__FILE__), last_response.headers["X-Sendfile"]
+ end
+
+ test "config.action_dispatch.x_sendfile_header is sent to Rack::Sendfile" do
+ make_basic_app do |app|
+ app.config.action_dispatch.x_sendfile_header = "X-Lighttpd-Send-File"
+ end
+
+ simple_controller
+
+ get "/"
+ assert_equal File.expand_path(__FILE__), last_response.headers["X-Lighttpd-Send-File"]
+ end
+
+ test "files handled by ActionDispatch::Static are handled by Rack::Sendfile" do
+ make_basic_app do |app|
+ app.config.action_dispatch.x_sendfile_header = "X-Sendfile"
+ app.config.public_file_server.enabled = true
+ app.paths["public"] = File.join(rails_root, "public")
+ end
+
+ app_file "public/foo.txt", "foo"
+
+ get "/foo.txt", "HTTP_X_SENDFILE_TYPE" => "X-Sendfile"
+ assert_equal File.join(rails_root, "public/foo.txt"), last_response.headers["X-Sendfile"]
+ end
+ end
+end
diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb
new file mode 100644
index 0000000000..a14ea589ed
--- /dev/null
+++ b/railties/test/application/middleware/session_test.rb
@@ -0,0 +1,457 @@
+require "isolation/abstract_unit"
+require "rack/test"
+
+module ApplicationTests
+ class MiddlewareSessionTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ test "config.force_ssl sets cookie to secure only by default" do
+ add_to_config "config.force_ssl = true"
+ require "#{app_path}/config/environment"
+ assert app.config.session_options[:secure], "Expected session to be marked as secure"
+ end
+
+ test "config.force_ssl doesn't set cookie to secure only when changed from default" do
+ add_to_config "config.force_ssl = true"
+ add_to_config "config.ssl_options = { secure_cookies: false }"
+ require "#{app_path}/config/environment"
+ assert !app.config.session_options[:secure]
+ end
+
+ test "session is not loaded if it's not used" do
+ make_basic_app
+
+ class ::OmgController < ActionController::Base
+ def index
+ if params[:flash]
+ flash[:notice] = "notice"
+ end
+
+ head :ok
+ end
+ end
+
+ get "/?flash=true"
+ get "/"
+
+ assert last_request.env["HTTP_COOKIE"]
+ assert !last_response.headers["Set-Cookie"]
+ end
+
+ test "session is empty and isn't saved on unverified request when using :null_session protect method" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ post ':controller(/:action)'
+ end
+ RUBY
+
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ protect_from_forgery with: :null_session
+
+ def write_session
+ session[:foo] = 1
+ head :ok
+ end
+
+ def read_session
+ render plain: session[:foo].inspect
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.action_controller.allow_forgery_protection = true
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ get "/foo/write_session"
+ get "/foo/read_session"
+ assert_equal "1", last_response.body
+
+ post "/foo/read_session" # Read session using POST request without CSRF token
+ assert_equal "nil", last_response.body # Stored value shouldn't be accessible
+
+ post "/foo/write_session" # Write session using POST request without CSRF token
+ get "/foo/read_session" # Session shouldn't be changed
+ assert_equal "1", last_response.body
+ end
+
+ test "cookie jar is empty and isn't saved on unverified request when using :null_session protect method" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ post ':controller(/:action)'
+ end
+ RUBY
+
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ protect_from_forgery with: :null_session
+
+ def write_cookie
+ cookies[:foo] = '1'
+ head :ok
+ end
+
+ def read_cookie
+ render plain: cookies[:foo].inspect
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.action_controller.allow_forgery_protection = true
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ get "/foo/write_cookie"
+ get "/foo/read_cookie"
+ assert_equal '"1"', last_response.body
+
+ post "/foo/read_cookie" # Read cookie using POST request without CSRF token
+ assert_equal "nil", last_response.body # Stored value shouldn't be accessible
+
+ post "/foo/write_cookie" # Write cookie using POST request without CSRF token
+ get "/foo/read_cookie" # Cookie shouldn't be changed
+ assert_equal '"1"', last_response.body
+ end
+
+ test "session using encrypted cookie store" 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_session
+ 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
+ # Enable AEAD cookies
+ config.action_dispatch.use_authenticated_cookie_encryption = true
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ get "/foo/write_session"
+ get "/foo/read_session"
+ assert_equal "1", last_response.body
+
+ get "/foo/read_encrypted_cookie"
+ assert_equal "1", 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 1, encryptor.decrypt_and_verify(last_response.body)["foo"]
+ end
+
+ test "session upgrading signature to encryption cookie store works the same way as encrypted cookie store" 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_session
+ 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_session"
+ get "/foo/read_session"
+ assert_equal "1", last_response.body
+
+ get "/foo/read_encrypted_cookie"
+ assert_equal "1", 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 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"]
+ end
+
+ test "session upgrading from AES-CBC-HMAC encryption to AES-GCM encryption" 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
+ # AES-256-CBC with SHA1 HMAC
+ # {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1}
+ cookies[:_myapp_session] = "TlgrdS85aUpDd1R2cDlPWlR6K0FJeGExckwySjZ2Z0pkR3d2QnRObGxZT25aalJWYWVvbFVLcHF4d0VQVDdSaFF2QjFPbG9MVjJzeWp3YjcyRUlKUUU2ZlR4bXlSNG9ZUkJPRUtld0E3dVU9LS0xNDZXbGpRZ3NjdW43N2haUEZJSUNRPT0=--3639b5ce54c09495cfeaae928cd5634e0c4b2e96"
+ 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
+ # Use a static key
+ secrets.secret_key_base = "known key base"
+
+ # 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"]
+ 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"
+ secrets.secret_key_base = nil
+ 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_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"]
+ end
+
+ test "calling reset_session on request does not trigger an error for API apps" do
+ add_to_config "config.api_only = true"
+
+ controller :test, <<-RUBY
+ class TestController < ApplicationController
+ def dump_flash
+ request.reset_session
+ render plain: 'It worked!'
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/dump_flash' => "test#dump_flash"
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ get "/dump_flash"
+
+ assert_equal 200, last_response.status
+ assert_equal "It worked!", last_response.body
+
+ assert_not_includes Rails.application.middleware, ActionDispatch::Flash
+ end
+
+ test "cookie_only is set to true even if user tries to overwrite it" do
+ add_to_config "config.session_store :cookie_store, key: '_myapp_session', cookie_only: false"
+ require "#{app_path}/config/environment"
+ assert app.config.session_options[:cookie_only], "Expected cookie_only to be set to true"
+ end
+ end
+end
diff --git a/railties/test/application/middleware/static_test.rb b/railties/test/application/middleware/static_test.rb
new file mode 100644
index 0000000000..5cd3e4325e
--- /dev/null
+++ b/railties/test/application/middleware/static_test.rb
@@ -0,0 +1,68 @@
+require "isolation/abstract_unit"
+require "rack/test"
+
+module ApplicationTests
+ class MiddlewareStaticTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ # Regression test to #8907
+ # See https://github.com/rails/rails/commit/9cc82b77196d21a5c7021f6dca59ab9b2b158a45#commitcomment-2416514
+ test "doesn't set Cache-Control header when it is nil" do
+ app_file "public/foo.html", "static"
+
+ require "#{app_path}/config/environment"
+
+ get "foo"
+
+ assert_not last_response.headers.has_key?("Cache-Control"), "Cache-Control should not be set"
+ end
+
+ test "headers for static files are configurable" do
+ app_file "public/about.html", "static"
+ add_to_config <<-CONFIG
+ config.public_file_server.headers = {
+ "Access-Control-Allow-Origin" => "http://rubyonrails.org",
+ "Cache-Control" => "public, max-age=60"
+ }
+ CONFIG
+
+ require "#{app_path}/config/environment"
+
+ get "/about.html"
+
+ assert_equal "http://rubyonrails.org", last_response.headers["Access-Control-Allow-Origin"]
+ assert_equal "public, max-age=60", last_response.headers["Cache-Control"]
+ end
+
+ test "public_file_server.index_name defaults to 'index'" do
+ app_file "public/index.html", "/index.html"
+
+ require "#{app_path}/config/environment"
+
+ get "/"
+
+ assert_equal "/index.html\n", last_response.body
+ end
+
+ test "public_file_server.index_name configurable" do
+ app_file "public/other-index.html", "/other-index.html"
+ add_to_config "config.public_file_server.index_name = 'other-index'"
+
+ require "#{app_path}/config/environment"
+
+ get "/"
+
+ assert_equal "/other-index.html\n", last_response.body
+ end
+ end
+end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
new file mode 100644
index 0000000000..0a6e5b52e9
--- /dev/null
+++ b/railties/test/application/middleware_test.rb
@@ -0,0 +1,310 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class MiddlewareTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ test "default middleware stack" do
+ add_to_config "config.active_record.migration_error = :page_load"
+
+ boot!
+
+ assert_equal [
+ "Rack::Sendfile",
+ "ActionDispatch::Static",
+ "ActionDispatch::Executor",
+ "ActiveSupport::Cache::Strategy::LocalCache",
+ "Rack::Runtime",
+ "Rack::MethodOverride",
+ "ActionDispatch::RequestId",
+ "ActionDispatch::RemoteIp",
+ "Rails::Rack::Logger",
+ "ActionDispatch::ShowExceptions",
+ "ActionDispatch::DebugExceptions",
+ "ActionDispatch::Reloader",
+ "ActionDispatch::Callbacks",
+ "ActiveRecord::Migration::CheckPending",
+ "ActionDispatch::Cookies",
+ "ActionDispatch::Session::CookieStore",
+ "ActionDispatch::Flash",
+ "Rack::Head",
+ "Rack::ConditionalGet",
+ "Rack::ETag"
+ ], middleware
+ end
+
+ test "api middleware stack" do
+ add_to_config "config.api_only = true"
+
+ boot!
+
+ assert_equal [
+ "Rack::Sendfile",
+ "ActionDispatch::Static",
+ "ActionDispatch::Executor",
+ "ActiveSupport::Cache::Strategy::LocalCache",
+ "Rack::Runtime",
+ "ActionDispatch::RequestId",
+ "ActionDispatch::RemoteIp",
+ "Rails::Rack::Logger",
+ "ActionDispatch::ShowExceptions",
+ "ActionDispatch::DebugExceptions",
+ "ActionDispatch::Reloader",
+ "ActionDispatch::Callbacks",
+ "Rack::Head",
+ "Rack::ConditionalGet",
+ "Rack::ETag"
+ ], middleware
+ end
+
+ test "middleware dependencies" do
+ boot!
+
+ # The following array-of-arrays describes dependencies between
+ # middlewares: the first item in each list depends on the
+ # remaining items (and therefore must occur later in the
+ # middleware stack).
+
+ dependencies = [
+ # Logger needs a fully "corrected" request environment
+ %w(Rails::Rack::Logger Rack::MethodOverride ActionDispatch::RequestId ActionDispatch::RemoteIp),
+
+ # Serving public/ doesn't invoke user code, so it should skip
+ # locks etc
+ %w(ActionDispatch::Executor ActionDispatch::Static),
+
+ # Errors during reload must be reported
+ %w(ActionDispatch::Reloader ActionDispatch::ShowExceptions ActionDispatch::DebugExceptions),
+
+ # Outright dependencies
+ %w(ActionDispatch::Static Rack::Sendfile),
+ %w(ActionDispatch::Flash ActionDispatch::Session::CookieStore),
+ %w(ActionDispatch::Session::CookieStore ActionDispatch::Cookies),
+ ]
+
+ require "tsort"
+ sorted = TSort.tsort((middleware | dependencies.flatten).method(:each),
+ lambda { |n, &b| dependencies.each { |m, *ds| ds.each(&b) if m == n } })
+ assert_equal sorted, middleware
+ end
+
+ test "Rack::Cache is not included by default" do
+ boot!
+
+ assert_not_includes middleware, "Rack::Cache", "Rack::Cache is not included in the default stack unless you set config.action_dispatch.rack_cache"
+ end
+
+ test "Rack::Cache is present when action_dispatch.rack_cache is set" do
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ boot!
+
+ assert_includes middleware, "Rack::Cache"
+ end
+
+ test "ActiveRecord::Migration::CheckPending is present when active_record.migration_error is set to :page_load" do
+ add_to_config "config.active_record.migration_error = :page_load"
+
+ boot!
+
+ assert_includes middleware, "ActiveRecord::Migration::CheckPending"
+ end
+
+ test "ActionDispatch::SSL is present when force_ssl is set" do
+ add_to_config "config.force_ssl = true"
+ boot!
+ assert_includes middleware, "ActionDispatch::SSL"
+ end
+
+ test "ActionDispatch::SSL is configured with options when given" do
+ add_to_config "config.force_ssl = true"
+ add_to_config "config.ssl_options = { redirect: { host: 'example.com' } }"
+ boot!
+
+ assert_equal [{ redirect: { host: "example.com" } }], Rails.application.middleware.first.args
+ end
+
+ test "removing Active Record omits its middleware" do
+ use_frameworks []
+ boot!
+ assert_not_includes middleware, "ActiveRecord::Migration::CheckPending"
+ end
+
+ test "includes executor" do
+ boot!
+ assert_includes middleware, "ActionDispatch::Executor"
+ end
+
+ test "does not include lock if cache_classes is set and so is eager_load" do
+ add_to_config "config.cache_classes = true"
+ add_to_config "config.eager_load = true"
+ boot!
+ assert_not_includes middleware, "Rack::Lock"
+ end
+
+ test "does not include lock if allow_concurrency is set to :unsafe" do
+ add_to_config "config.allow_concurrency = :unsafe"
+ boot!
+ assert_not_includes middleware, "Rack::Lock"
+ end
+
+ test "includes lock if allow_concurrency is disabled" do
+ add_to_config "config.allow_concurrency = false"
+ boot!
+ assert_includes middleware, "Rack::Lock"
+ end
+
+ test "removes static asset server if public_file_server.enabled is disabled" do
+ add_to_config "config.public_file_server.enabled = false"
+ boot!
+ assert_not_includes middleware, "ActionDispatch::Static"
+ end
+
+ test "can delete a middleware from the stack" do
+ add_to_config "config.middleware.delete ActionDispatch::Static"
+ boot!
+ assert_not_includes middleware, "ActionDispatch::Static"
+ end
+
+ test "can delete a middleware from the stack even if insert_before is added after delete" do
+ add_to_config "config.middleware.delete Rack::Runtime"
+ add_to_config "config.middleware.insert_before(Rack::Runtime, Rack::Config)"
+ boot!
+ assert_includes middleware, "Rack::Config"
+ assert_not middleware.include?("Rack::Runtime")
+ end
+
+ test "can delete a middleware from the stack even if insert_after is added after delete" do
+ add_to_config "config.middleware.delete Rack::Runtime"
+ add_to_config "config.middleware.insert_after(Rack::Runtime, Rack::Config)"
+ boot!
+ assert_includes middleware, "Rack::Config"
+ assert_not middleware.include?("Rack::Runtime")
+ end
+
+ test "includes exceptions middlewares even if action_dispatch.show_exceptions is disabled" do
+ add_to_config "config.action_dispatch.show_exceptions = false"
+ boot!
+ assert_includes middleware, "ActionDispatch::ShowExceptions"
+ assert_includes middleware, "ActionDispatch::DebugExceptions"
+ end
+
+ test "removes ActionDispatch::Reloader if cache_classes is true" do
+ add_to_config "config.cache_classes = true"
+ boot!
+ assert_not_includes middleware, "ActionDispatch::Reloader"
+ end
+
+ test "use middleware" do
+ use_frameworks []
+ add_to_config "config.middleware.use Rack::Config"
+ boot!
+ assert_equal "Rack::Config", middleware.last
+ end
+
+ test "insert middleware after" do
+ add_to_config "config.middleware.insert_after Rack::Sendfile, Rack::Config"
+ boot!
+ assert_equal "Rack::Config", middleware.second
+ end
+
+ test "unshift middleware" do
+ add_to_config "config.middleware.unshift Rack::Config"
+ boot!
+ assert_equal "Rack::Config", middleware.first
+ 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
+ end
+
+ test "Rails.cache does respond to middleware" do
+ boot!
+ assert_equal "Rack::Runtime", middleware.fifth
+ end
+
+ test "insert middleware before" do
+ add_to_config "config.middleware.insert_before Rack::Sendfile, Rack::Config"
+ boot!
+ assert_equal "Rack::Config", middleware.first
+ end
+
+ test "can't change middleware after it's built" do
+ boot!
+ assert_raise RuntimeError do
+ app.config.middleware.use Rack::Config
+ end
+ end
+
+ # ConditionalGet + Etag
+ test "conditional get + etag middlewares handle http caching based on body" do
+ make_basic_app
+
+ class ::OmgController < ActionController::Base
+ def index
+ if params[:nothing]
+ render plain: ""
+ else
+ render plain: "OMG"
+ end
+ end
+ end
+
+ etag = "W/" + "c00862d1c6c1cf7c1b49388306e7b3c1".inspect
+
+ get "/"
+ assert_equal 200, last_response.status
+ assert_equal "OMG", last_response.body
+ assert_equal "text/plain; charset=utf-8", last_response.headers["Content-Type"]
+ assert_equal "max-age=0, private, must-revalidate", last_response.headers["Cache-Control"]
+ assert_equal etag, last_response.headers["Etag"]
+
+ get "/", {}, "HTTP_IF_NONE_MATCH" => etag
+ assert_equal 304, last_response.status
+ assert_equal "", last_response.body
+ assert_nil last_response.headers["Content-Type"]
+ assert_equal "max-age=0, private, must-revalidate", last_response.headers["Cache-Control"]
+ assert_equal etag, last_response.headers["Etag"]
+
+ get "/?nothing=true"
+ assert_equal 200, last_response.status
+ assert_equal "", last_response.body
+ assert_equal "text/plain; charset=utf-8", last_response.headers["Content-Type"]
+ assert_equal "no-cache", last_response.headers["Cache-Control"]
+ assert_nil last_response.headers["Etag"]
+ end
+
+ test "ORIGINAL_FULLPATH is passed to env" do
+ boot!
+ env = ::Rack::MockRequest.env_for("/foo/?something")
+ Rails.application.call(env)
+
+ assert_equal "/foo/?something", env["ORIGINAL_FULLPATH"]
+ end
+
+ private
+
+ def boot!
+ require "#{app_path}/config/environment"
+ end
+
+ def middleware
+ Rails.application.middleware.map(&:klass).map(&:name)
+ end
+ end
+end
diff --git a/railties/test/application/multiple_applications_test.rb b/railties/test/application/multiple_applications_test.rb
new file mode 100644
index 0000000000..26b810af73
--- /dev/null
+++ b/railties/test/application/multiple_applications_test.rb
@@ -0,0 +1,175 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class MultipleApplicationsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app(initializers: true)
+ require "#{rails_root}/config/environment"
+ Rails.application.config.some_setting = "something_or_other"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def test_cloning_an_application_makes_a_shallow_copy_of_config
+ clone = Rails.application.clone
+
+ assert_equal Rails.application.config, clone.config, "The cloned application should get a copy of the config"
+ assert_equal Rails.application.config.some_setting, clone.config.some_setting, "The some_setting on the config should be the same"
+ end
+
+ def test_inheriting_multiple_times_from_application
+ new_application_class = Class.new(Rails::Application)
+
+ assert_not_equal Rails.application.object_id, new_application_class.instance.object_id
+ end
+
+ def test_initialization_of_multiple_copies_of_same_application
+ application1 = AppTemplate::Application.new
+ application2 = AppTemplate::Application.new
+
+ assert_not_equal Rails.application.object_id, application1.object_id, "New applications should not be the same as the original application"
+ assert_not_equal Rails.application.object_id, application2.object_id, "New applications should not be the same as the original application"
+ end
+
+ def test_initialization_of_application_with_previous_config
+ application1 = AppTemplate::Application.create(config: Rails.application.config)
+ application2 = AppTemplate::Application.create
+
+ assert_equal Rails.application.config, application1.config, "Creating a new application while setting an initial config should result in the same config"
+ assert_not_equal Rails.application.config, application2.config, "New applications without setting an initial config should not have the same config"
+ end
+
+ def test_initialization_of_application_with_previous_railties
+ application1 = AppTemplate::Application.create(railties: Rails.application.railties)
+ application2 = AppTemplate::Application.create
+
+ assert_equal Rails.application.railties, application1.railties
+ assert_not_equal Rails.application.railties, application2.railties
+ end
+
+ def test_initialize_new_application_with_all_previous_initialization_variables
+ application1 = AppTemplate::Application.create(
+ config: Rails.application.config,
+ railties: Rails.application.railties,
+ routes_reloader: Rails.application.routes_reloader,
+ reloaders: Rails.application.reloaders,
+ routes: Rails.application.routes,
+ helpers: Rails.application.helpers,
+ app_env_config: Rails.application.env_config
+ )
+
+ assert_equal Rails.application.config, application1.config
+ assert_equal Rails.application.railties, application1.railties
+ assert_equal Rails.application.routes_reloader, application1.routes_reloader
+ assert_equal Rails.application.reloaders, application1.reloaders
+ assert_equal Rails.application.routes, application1.routes
+ assert_equal Rails.application.helpers, application1.helpers
+ assert_equal Rails.application.env_config, application1.env_config
+ end
+
+ def test_rake_tasks_defined_on_different_applications_go_to_the_same_class
+ run_count = 0
+
+ application1 = AppTemplate::Application.new
+ application1.rake_tasks do
+ run_count += 1
+ end
+
+ application2 = AppTemplate::Application.new
+ application2.rake_tasks do
+ run_count += 1
+ end
+
+ require "#{app_path}/config/environment"
+
+ assert_equal 0, run_count, "The count should stay at zero without any calls to the rake tasks"
+ require "rake"
+ require "rake/testtask"
+ require "rdoc/task"
+ Rails.application.load_tasks
+ assert_equal 2, run_count, "Calling a rake task should result in two increments to the count"
+ end
+
+ def test_multiple_applications_can_be_initialized
+ 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 }
+ AppTemplate::Application.new.console { run_count += 1 }
+
+ assert_equal 0, run_count, "Without loading the consoles, the count should be 0"
+ Rails.application.load_console
+ assert_equal 2, run_count, "There should have been two consoles that increment the count"
+ end
+
+ def test_generators_run_on_different_applications_go_to_the_same_class
+ run_count = 0
+ AppTemplate::Application.generators { run_count += 1 }
+ AppTemplate::Application.new.generators { run_count += 1 }
+
+ assert_equal 0, run_count, "Without loading the generators, the count should be 0"
+ Rails.application.load_generators
+ assert_equal 2, run_count, "There should have been two generators that increment the count"
+ end
+
+ def test_runners_run_on_different_applications_go_to_the_same_class
+ run_count = 0
+ AppTemplate::Application.runner { run_count += 1 }
+ AppTemplate::Application.new.runner { run_count += 1 }
+
+ assert_equal 0, run_count, "Without loading the runners, the count should be 0"
+ Rails.application.load_runner
+ assert_equal 2, run_count, "There should have been two runners that increment the count"
+ end
+
+ def test_isolate_namespace_on_an_application
+ assert_nil Rails.application.railtie_namespace, "Before isolating namespace, the railtie namespace should be nil"
+ Rails.application.isolate_namespace(AppTemplate)
+ assert_equal Rails.application.railtie_namespace, AppTemplate, "After isolating namespace, we should have a namespace"
+ end
+
+ def test_inserting_configuration_into_application
+ app = AppTemplate::Application.new(config: Rails.application.config)
+ 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.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 new_config, app.config, "The application's config should have changed to the new config."
+ end
+ end
+end
diff --git a/railties/test/application/paths_test.rb b/railties/test/application/paths_test.rb
new file mode 100644
index 0000000000..515205296c
--- /dev/null
+++ b/railties/test/application/paths_test.rb
@@ -0,0 +1,82 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class PathsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ app_file "config/environments/development.rb", ""
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.after_initialize do |app|
+ app.config.session_store nil
+ end
+ RUBY
+ require "#{app_path}/config/environment"
+ @paths = Rails.application.config.paths
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def root(*path)
+ app_path(*path).to_s
+ end
+
+ def assert_path(paths, *dir)
+ assert_equal [root(*dir)], paths.expanded
+ end
+
+ def assert_in_load_path(*path)
+ assert $:.any? { |p| File.expand_path(p) == root(*path) }, "Load path does not include '#{root(*path)}'. They are:\n-----\n #{$:.join("\n")}\n-----"
+ end
+
+ def assert_not_in_load_path(*path)
+ assert !$:.any? { |p| File.expand_path(p) == root(*path) }, "Load path includes '#{root(*path)}'. They are:\n-----\n #{$:.join("\n")}\n-----"
+ end
+
+ test "booting up Rails yields a valid paths object" do
+ assert_path @paths["app/models"], "app/models"
+ assert_path @paths["app/helpers"], "app/helpers"
+ assert_path @paths["app/views"], "app/views"
+ assert_path @paths["lib"], "lib"
+ assert_path @paths["vendor"], "vendor"
+ assert_path @paths["tmp"], "tmp"
+ assert_path @paths["config"], "config"
+ assert_path @paths["config/locales"], "config/locales/en.yml"
+ assert_path @paths["config/environment"], "config/environment.rb"
+ assert_path @paths["config/environments"], "config/environments/development.rb"
+
+ assert_equal root("app", "controllers"), @paths["app/controllers"].expanded.first
+ end
+
+ test "booting up Rails yields a list of paths that are eager" do
+ eager_load = @paths.eager_load
+ assert_includes eager_load, root("app/controllers")
+ assert_includes eager_load, root("app/helpers")
+ assert_includes eager_load, root("app/models")
+ end
+
+ test "environments has a glob equal to the current environment" do
+ assert_equal "#{Rails.env}.rb", @paths["config/environments"].glob
+ end
+
+ test "load path includes each of the paths in config.paths as long as the directories exist" do
+ assert_in_load_path "app", "controllers"
+ assert_in_load_path "app", "models"
+ assert_in_load_path "app", "helpers"
+ assert_in_load_path "lib"
+ assert_in_load_path "vendor"
+
+ assert_not_in_load_path "app", "views"
+ assert_not_in_load_path "config"
+ assert_not_in_load_path "config", "locales"
+ assert_not_in_load_path "config", "environments"
+ assert_not_in_load_path "tmp"
+ assert_not_in_load_path "tmp", "cache"
+ end
+ end
+end
diff --git a/railties/test/application/per_request_digest_cache_test.rb b/railties/test/application/per_request_digest_cache_test.rb
new file mode 100644
index 0000000000..6e6996a6ba
--- /dev/null
+++ b/railties/test/application/per_request_digest_cache_test.rb
@@ -0,0 +1,70 @@
+require "isolation/abstract_unit"
+require "rack/test"
+require "minitest/mock"
+
+require "action_view"
+require "active_support/testing/method_call_assertions"
+
+class PerRequestDigestCacheTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include ActiveSupport::Testing::MethodCallAssertions
+ include Rack::Test::Methods
+
+ setup do
+ build_app
+ add_to_config "config.consider_all_requests_local = true"
+
+ app_file "app/models/customer.rb", <<-RUBY
+ class Customer < Struct.new(:name, :id)
+ extend ActiveModel::Naming
+ include ActiveModel::Conversion
+
+ def cache_key
+ [ name, id ].join("/")
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ resources :customers, only: :index
+ end
+ RUBY
+
+ app_file "app/controllers/customers_controller.rb", <<-RUBY
+ class CustomersController < ApplicationController
+ self.perform_caching = true
+
+ def index
+ render [ Customer.new('david', 1), Customer.new('dingus', 2) ]
+ end
+ end
+ RUBY
+
+ app_file "app/views/customers/_customer.html.erb", <<-RUBY
+ <% cache customer do %>
+ <%= customer.name %>
+ <% end %>
+ RUBY
+
+ require "#{app_path}/config/environment"
+ end
+
+ teardown :teardown_app
+
+ test "digests are reused when rendering the same template twice" do
+ get "/customers"
+ assert_equal 200, last_response.status
+
+ values = ActionView::LookupContext::DetailsKey.digest_caches.first.values
+ assert_equal [ "8ba099b7749542fe765ff34a6824d548" ], values
+ assert_equal %w(david dingus), last_response.body.split.map(&:strip)
+ end
+
+ test "template digests are cleared before a request" do
+ assert_called(ActionView::LookupContext::DetailsKey, :clear) do
+ get "/customers"
+ assert_equal 200, last_response.status
+ end
+ end
+end
diff --git a/railties/test/application/rack/logger_test.rb b/railties/test/application/rack/logger_test.rb
new file mode 100644
index 0000000000..e71bcbc536
--- /dev/null
+++ b/railties/test/application/rack/logger_test.rb
@@ -0,0 +1,56 @@
+require "isolation/abstract_unit"
+require "active_support/log_subscriber/test_helper"
+require "rack/test"
+
+module ApplicationTests
+ module RackTests
+ class LoggerTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include ActiveSupport::LogSubscriber::TestHelper
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ add_to_config <<-RUBY
+ config.logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
+ RUBY
+
+ require "#{app_path}/config/environment"
+ super
+ end
+
+ def teardown
+ super
+ teardown_app
+ end
+
+ def logs
+ @logs ||= Rails.logger.logged(:info).join("\n")
+ end
+
+ test "logger logs proper HTTP GET verb and path" do
+ get "/blah"
+ wait
+ assert_match 'Started GET "/blah"', logs
+ end
+
+ test "logger logs proper HTTP HEAD verb and path" do
+ head "/blah"
+ wait
+ assert_match 'Started HEAD "/blah"', logs
+ end
+
+ test "logger logs HTTP verb override" do
+ post "/", _method: "put"
+ wait
+ assert_match 'Started PUT "/"', logs
+ end
+
+ test "logger logs HEAD requests" do
+ post "/", _method: "head"
+ wait
+ assert_match 'Started HEAD "/"', logs
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rackup_test.rb b/railties/test/application/rackup_test.rb
new file mode 100644
index 0000000000..2943e9ee5d
--- /dev/null
+++ b/railties/test/application/rackup_test.rb
@@ -0,0 +1,42 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class RackupTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def rackup
+ require "rack"
+ app, _ = Rack::Builder.parse_file("#{app_path}/config.ru")
+ app
+ end
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "Rails app is present" do
+ assert File.exist?(app_path("config"))
+ end
+
+ test "config.ru can be racked up" do
+ Dir.chdir app_path do
+ @app = rackup
+ assert_welcome get("/")
+ end
+ end
+
+ test "Rails.application is available after config.ru has been racked up" do
+ rackup
+ assert_kind_of Rails::Application, Rails.application
+ end
+
+ test "the config object is available on the application object" do
+ rackup
+ assert_equal "UTC", Rails.application.config.time_zone
+ end
+ end
+end
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
new file mode 100644
index 0000000000..3216121de3
--- /dev/null
+++ b/railties/test/application/rake/dbs_test.rb
@@ -0,0 +1,318 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class RakeDbsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def database_url_db_name
+ "db/database_url_db.sqlite3"
+ end
+
+ def set_database_url
+ ENV["DATABASE_URL"] = "sqlite3:#{database_url_db_name}"
+ # ensure it's using the DATABASE_URL
+ FileUtils.rm_rf("#{app_path}/config/database.yml")
+ end
+
+ def db_create_and_drop(expected_database)
+ Dir.chdir(app_path) do
+ output = `bin/rails db:create`
+ assert_match(/Created database/, output)
+ assert File.exist?(expected_database)
+ assert_equal expected_database, ActiveRecord::Base.connection_config[:database]
+ output = `bin/rails db:drop`
+ assert_match(/Dropped database/, output)
+ assert !File.exist?(expected_database)
+ end
+ end
+
+ 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
+ require "#{app_path}/config/environment"
+ set_database_url
+ db_create_and_drop database_url_db_name
+ end
+
+ def with_database_existing
+ Dir.chdir(app_path) do
+ set_database_url
+ `bin/rails db:create`
+ yield
+ `bin/rails db:drop`
+ end
+ end
+
+ test "db:create failure because database exists" do
+ with_database_existing do
+ output = `bin/rails db:create 2>&1`
+ assert_match(/already exists/, output)
+ assert_equal 0, $?.exitstatus
+ end
+ end
+
+ def with_bad_permissions
+ Dir.chdir(app_path) do
+ set_database_url
+ FileUtils.chmod("-w", "db")
+ yield
+ FileUtils.chmod("+w", "db")
+ end
+ end
+
+ test "db:create failure because bad permissions" do
+ with_bad_permissions do
+ output = `bin/rails db:create 2>&1`
+ assert_match(/Couldn't create database/, output)
+ assert_equal 1, $?.exitstatus
+ end
+ end
+
+ test "db:drop failure because database does not exist" do
+ Dir.chdir(app_path) do
+ output = `bin/rails db:drop:_unsafe --trace 2>&1`
+ assert_match(/does not exist/, output)
+ assert_equal 0, $?.exitstatus
+ end
+ end
+
+ test "db:drop failure because bad permissions" do
+ with_database_existing do
+ with_bad_permissions do
+ output = `bin/rails db:drop 2>&1`
+ assert_match(/Couldn't drop/, output)
+ assert_equal 1, $?.exitstatus
+ end
+ end
+ end
+
+ def db_migrate_and_status(expected_database)
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string;
+ bin/rails db:migrate`
+ output = `bin/rails db:migrate:status`
+ assert_match(%r{database:\s+\S*#{Regexp.escape(expected_database)}}, output)
+ assert_match(/up\s+\d{14}\s+Create books/, output)
+ end
+ end
+
+ test "db:migrate and db:migrate:status without database_url" do
+ require "#{app_path}/config/environment"
+ db_migrate_and_status ActiveRecord::Base.configurations[Rails.env]["database"]
+ end
+
+ test "db:migrate and db:migrate:status with database_url" do
+ require "#{app_path}/config/environment"
+ set_database_url
+ db_migrate_and_status database_url_db_name
+ end
+
+ def db_schema_dump
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string;
+ bin/rails db:migrate db:schema:dump`
+ schema_dump = File.read("db/schema.rb")
+ assert_match(/create_table \"books\"/, schema_dump)
+ end
+ end
+
+ test "db:schema:dump without database_url" do
+ db_schema_dump
+ end
+
+ test "db:schema:dump with database_url" do
+ set_database_url
+ db_schema_dump
+ end
+
+ def db_fixtures_load(expected_database)
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string;
+ bin/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
+
+ test "db:fixtures:load without database_url" do
+ require "#{app_path}/config/environment"
+ db_fixtures_load ActiveRecord::Base.configurations[Rails.env]["database"]
+ end
+
+ test "db:fixtures:load with database_url" do
+ require "#{app_path}/config/environment"
+ set_database_url
+ db_fixtures_load database_url_db_name
+ end
+
+ test "db:fixtures:load with namespaced fixture" do
+ require "#{app_path}/config/environment"
+ Dir.chdir(app_path) do
+ `bin/rails generate model admin::book title:string;
+ bin/rails db:migrate db:fixtures:load`
+ require "#{app_path}/app/models/admin/book"
+ assert_equal 2, Admin::Book.count
+ end
+ end
+
+ def db_structure_dump_and_load(expected_database)
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string;
+ bin/rails db:migrate db:structure:dump`
+ structure_dump = File.read("db/structure.sql")
+ assert_match(/CREATE TABLE (?:IF NOT EXISTS )?\"books\"/, structure_dump)
+ `bin/rails environment db:drop db:structure:load`
+ assert_match expected_database, ActiveRecord::Base.connection_config[:database]
+ require "#{app_path}/app/models/book"
+ #if structure is not loaded correctly, exception would be raised
+ assert_equal 0, Book.count
+ end
+ end
+
+ test "db:structure:dump and db:structure:load without database_url" do
+ require "#{app_path}/config/environment"
+ db_structure_dump_and_load ActiveRecord::Base.configurations[Rails.env]["database"]
+ end
+
+ test "db:structure:dump and db:structure:load with database_url" do
+ require "#{app_path}/config/environment"
+ set_database_url
+ db_structure_dump_and_load database_url_db_name
+ end
+
+ test "db:structure:dump does not dump schema information when no migrations are used" do
+ Dir.chdir(app_path) do
+ # create table without migrations
+ `bin/rails runner 'ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }'`
+
+ stderr_output = capture(:stderr) { `bin/rails db:structure:dump` }
+ assert_empty stderr_output
+ structure_dump = File.read("db/structure.sql")
+ assert_match(/CREATE TABLE (?:IF NOT EXISTS )?\"posts\"/, structure_dump)
+ end
+ end
+
+ test "db:schema:load and db:structure:load do not purge the existing database" do
+ Dir.chdir(app_path) do
+ `bin/rails runner 'ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }'`
+
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: 20140423102712) do
+ create_table(:comments) {}
+ end
+ RUBY
+
+ list_tables = lambda { `bin/rails runner 'p ActiveRecord::Base.connection.tables'`.strip }
+
+ assert_equal '["posts"]', list_tables[]
+ `bin/rails db:schema:load`
+ assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata"]', list_tables[]
+
+ app_file "db/structure.sql", <<-SQL
+ CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255));
+ SQL
+
+ `bin/rails db:structure:load`
+ assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata", "users"]', list_tables[]
+ end
+ end
+
+ test "db:schema:load with inflections" do
+ Dir.chdir(app_path) do
+ app_file "config/initializers/inflection.rb", <<-RUBY
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.irregular 'goose', 'geese'
+ end
+ RUBY
+ app_file "config/initializers/primary_key_table_name.rb", <<-RUBY
+ ActiveRecord::Base.primary_key_prefix_type = :table_name
+ RUBY
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: 20140423102712) do
+ create_table("goose".pluralize) do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
+ `bin/rails db:schema:load`
+
+ tables = `bin/rails runner 'p ActiveRecord::Base.connection.tables'`.strip
+ assert_match(/"geese"/, tables)
+
+ columns = `bin/rails runner 'p ActiveRecord::Base.connection.columns("geese").map(&:name)'`.strip
+ assert_equal columns, '["gooseid", "name"]'
+ end
+ end
+
+ test "db:schema:load fails if schema.rb doesn't exist yet" do
+ Dir.chdir(app_path) do
+ stderr_output = capture(:stderr) { `bin/rails db:schema:load` }
+ assert_match(/Run `rails db:migrate` to create it/, stderr_output)
+ end
+ end
+
+ def db_test_load_structure
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string;
+ bin/rails db:migrate db:structure:dump db:test:load_structure`
+ ActiveRecord::Base.configurations = Rails.application.config.database_configuration
+ ActiveRecord::Base.establish_connection :test
+ require "#{app_path}/app/models/book"
+ #if structure is not loaded correctly, exception would be raised
+ assert_equal 0, Book.count
+ assert_match ActiveRecord::Base.configurations["test"]["database"],
+ ActiveRecord::Base.connection_config[:database]
+ end
+ end
+
+ test "db:test:load_structure without database_url" do
+ require "#{app_path}/config/environment"
+ db_test_load_structure
+ 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"
+
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: "1") do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
+ app_file "db/seeds.rb", <<-RUBY
+ puts ActiveRecord::Base.connection_config[:database]
+ RUBY
+
+ Dir.chdir(app_path) do
+ database_path = `bin/rails db:setup`
+ assert_equal "development.sqlite3", File.basename(database_path.strip)
+ end
+ ensure
+ ENV["RAILS_ENV"] = @old_rails_env
+ ENV["RACK_ENV"] = @old_rack_env
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/dev_test.rb b/railties/test/application/rake/dev_test.rb
new file mode 100644
index 0000000000..4f992d9c8d
--- /dev/null
+++ b/railties/test/application/rake/dev_test.rb
@@ -0,0 +1,43 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class RakeDevTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ 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)
+ 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)
+ end
+ end
+
+ test "dev:cache removes server.pid also" do
+ Dir.chdir(app_path) do
+ FileUtils.mkdir_p("tmp/pids")
+ FileUtils.touch("tmp/pids/server.pid")
+ `rails dev:cache`
+ assert_not File.exist?("tmp/pids/server.pid")
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/framework_test.rb b/railties/test/application/rake/framework_test.rb
new file mode 100644
index 0000000000..40488a6aab
--- /dev/null
+++ b/railties/test/application/rake/framework_test.rb
@@ -0,0 +1,46 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class FrameworkTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def load_tasks
+ require "rake"
+ require "rdoc/task"
+ require "rake/testtask"
+
+ Rails.application.load_tasks
+ end
+
+ test "requiring the rake task should not define method .app_generator on Object" do
+ require "#{app_path}/config/environment"
+
+ load_tasks
+
+ assert_raise NameError do
+ Object.method(:app_generator)
+ end
+ end
+
+ test "requiring the rake task should not define method .invoke_from_app_generator on Object" do
+ require "#{app_path}/config/environment"
+
+ load_tasks
+
+ assert_raise NameError do
+ Object.method(:invoke_from_app_generator)
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/log_test.rb b/railties/test/application/rake/log_test.rb
new file mode 100644
index 0000000000..fdd3c71fe8
--- /dev/null
+++ b/railties/test/application/rake/log_test.rb
@@ -0,0 +1,33 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class LogTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "log:clear clear all environments log files by default" do
+ Dir.chdir(app_path) do
+ File.open("config/environments/staging.rb", "w")
+
+ File.write("log/staging.log", "staging")
+ File.write("log/test.log", "test")
+ File.write("log/dummy.log", "dummy")
+
+ `rails log:clear`
+
+ assert_equal 0, File.size("log/test.log")
+ assert_equal 0, File.size("log/staging.log")
+ assert_equal 5, File.size("log/dummy.log")
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb
new file mode 100644
index 0000000000..51dfe2ef98
--- /dev/null
+++ b/railties/test/application/rake/migrations_test.rb
@@ -0,0 +1,304 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class RakeMigrationsTest < ActiveSupport::TestCase
+ def setup
+ build_app
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "running migrations with given scope" do
+ Dir.chdir(app_path) do
+ `bin/rails generate model user username:string password:string`
+
+ app_file "db/migrate/01_a_migration.bukkits.rb", <<-MIGRATION
+ class AMigration < ActiveRecord::Migration::Current
+ end
+ MIGRATION
+
+ output = `bin/rails db:migrate SCOPE=bukkits`
+ assert_no_match(/create_table\(:users\)/, output)
+ assert_no_match(/CreateUsers/, output)
+ assert_no_match(/add_column\(:users, :email, :string\)/, output)
+
+ assert_match(/AMigration: migrated/, output)
+
+ output = `bin/rails db:migrate SCOPE=bukkits VERSION=0`
+ assert_no_match(/drop_table\(:users\)/, output)
+ assert_no_match(/CreateUsers/, output)
+ assert_no_match(/remove_column\(:users, :email\)/, output)
+
+ assert_match(/AMigration: reverted/, output)
+ end
+ end
+
+ test "migration with empty version" do
+ Dir.chdir(app_path) do
+ output = `bin/rails db:migrate VERSION= 2>&1`
+ assert_match(/Empty VERSION provided/, output)
+
+ output = `bin/rails db:migrate:redo VERSION= 2>&1`
+ assert_match(/Empty VERSION provided/, output)
+
+ output = `bin/rails db:migrate:up VERSION= 2>&1`
+ assert_match(/VERSION is required/, output)
+
+ output = `bin/rails db:migrate:up 2>&1`
+ assert_match(/VERSION is required/, output)
+
+ output = `bin/rails db:migrate:down VERSION= 2>&1`
+ assert_match(/VERSION is required - To go down one migration, use db:rollback/, output)
+
+ output = `bin/rails db:migrate:down 2>&1`
+ assert_match(/VERSION is required - To go down one migration, use db:rollback/, output)
+ end
+ end
+
+ test "model and migration generator with change syntax" do
+ Dir.chdir(app_path) do
+ `bin/rails generate model user username:string password:string;
+ bin/rails generate migration add_email_to_users email:string`
+
+ output = `bin/rails db:migrate`
+ assert_match(/create_table\(:users\)/, output)
+ assert_match(/CreateUsers: migrated/, output)
+ assert_match(/add_column\(:users, :email, :string\)/, output)
+ assert_match(/AddEmailToUsers: migrated/, output)
+
+ output = `bin/rails db:rollback STEP=2`
+ assert_match(/drop_table\(:users\)/, output)
+ assert_match(/CreateUsers: reverted/, output)
+ assert_match(/remove_column\(:users, :email, :string\)/, output)
+ assert_match(/AddEmailToUsers: reverted/, output)
+ end
+ end
+
+ test "migration status when schema migrations table is not present" do
+ output = Dir.chdir(app_path) { `bin/rails db:migrate:status 2>&1` }
+ assert_equal "Schema migrations table does not exist yet.\n", output
+ end
+
+ test "migration status" do
+ Dir.chdir(app_path) do
+ `bin/rails generate model user username:string password:string;
+ bin/rails generate migration add_email_to_users email:string;
+ bin/rails db:migrate`
+
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+Add email to users/, output)
+
+ `bin/rails db:rollback STEP=1`
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/down\s+\d{14}\s+Add email to users/, output)
+ end
+ end
+
+ test "migration status without timestamps" do
+ add_to_config("config.active_record.timestamped_migrations = false")
+
+ Dir.chdir(app_path) do
+ `bin/rails generate model user username:string password:string;
+ bin/rails generate migration add_email_to_users email:string;
+ bin/rails db:migrate`
+
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/up\s+\d{3,}\s+Create users/, output)
+ assert_match(/up\s+\d{3,}\s+Add email to users/, output)
+
+ `bin/rails db:rollback STEP=1`
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/up\s+\d{3,}\s+Create users/, output)
+ assert_match(/down\s+\d{3,}\s+Add email to users/, output)
+ end
+ end
+
+ test "migration status after rollback and redo" do
+ Dir.chdir(app_path) do
+ `bin/rails generate model user username:string password:string;
+ bin/rails generate migration add_email_to_users email:string;
+ bin/rails db:migrate`
+
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+Add email to users/, output)
+
+ `bin/rails db:rollback STEP=2`
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/down\s+\d{14}\s+Create users/, output)
+ assert_match(/down\s+\d{14}\s+Add email to users/, output)
+
+ `bin/rails db:migrate:redo`
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+Add email to users/, output)
+ end
+ end
+
+ test "migration status after rollback and forward" do
+ Dir.chdir(app_path) do
+ `bin/rails generate model user username:string password:string;
+ bin/rails generate migration add_email_to_users email:string;
+ bin/rails db:migrate`
+
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+Add email to users/, output)
+
+ `bin/rails db:rollback STEP=2`
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/down\s+\d{14}\s+Create users/, output)
+ assert_match(/down\s+\d{14}\s+Add email to users/, output)
+
+ `bin/rails db:forward STEP=2`
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+Add email to users/, output)
+ end
+ end
+
+ test "raise error on any move when current migration does not exist" do
+ Dir.chdir(app_path) do
+ `bin/rails generate model user username:string password:string;
+ bin/rails generate migration add_email_to_users email:string;
+ bin/rails db:migrate
+ rm db/migrate/*email*.rb`
+
+ output = `bin/rails db:migrate:status`
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+\** NO FILE \**/, output)
+
+ output = `bin/rails db:rollback 2>&1`
+ assert_match(/rails aborted!/, output)
+ assert_match(/ActiveRecord::UnknownMigrationVersionError:/, output)
+ assert_match(/No migration with version number\s\d{14}\./, output)
+
+ output = `bin/rails db:migrate:status`
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+\** NO FILE \**/, output)
+
+ output = `bin/rails db:forward 2>&1`
+ assert_match(/rails aborted!/, output)
+ assert_match(/ActiveRecord::UnknownMigrationVersionError:/, output)
+ assert_match(/No migration with version number\s\d{14}\./, output)
+
+ output = `bin/rails db:migrate:status`
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+\** NO FILE \**/, output)
+ end
+ end
+
+ test "migration status after rollback and redo without timestamps" do
+ add_to_config("config.active_record.timestamped_migrations = false")
+
+ Dir.chdir(app_path) do
+ `bin/rails generate model user username:string password:string;
+ bin/rails generate migration add_email_to_users email:string;
+ bin/rails db:migrate`
+
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/up\s+\d{3,}\s+Create users/, output)
+ assert_match(/up\s+\d{3,}\s+Add email to users/, output)
+
+ `bin/rails db:rollback STEP=2`
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/down\s+\d{3,}\s+Create users/, output)
+ assert_match(/down\s+\d{3,}\s+Add email to users/, output)
+
+ `bin/rails db:migrate:redo`
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/up\s+\d{3,}\s+Create users/, output)
+ assert_match(/up\s+\d{3,}\s+Add email to users/, output)
+ end
+ end
+
+ test "running migrations with not timestamp head migration files" do
+ Dir.chdir(app_path) do
+
+ app_file "db/migrate/1_one_migration.rb", <<-MIGRATION
+ class OneMigration < ActiveRecord::Migration::Current
+ end
+ MIGRATION
+
+ app_file "db/migrate/02_two_migration.rb", <<-MIGRATION
+ class TwoMigration < ActiveRecord::Migration::Current
+ end
+ MIGRATION
+
+ `bin/rails db:migrate`
+
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/up\s+001\s+One migration/, output)
+ assert_match(/up\s+002\s+Two migration/, output)
+ end
+ end
+
+ test "schema generation when dump_schema_after_migration is set" do
+ add_to_config("config.active_record.dump_schema_after_migration = false")
+
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string`
+ output = `bin/rails generate model author name:string`
+ version = output =~ %r{[^/]+db/migrate/(\d+)_create_authors\.rb} && $1
+
+ `bin/rails db:migrate db:rollback db:forward db:migrate:up db:migrate:down VERSION=#{version}`
+ assert !File.exist?("db/schema.rb"), "should not dump schema when configured not to"
+ end
+
+ add_to_config("config.active_record.dump_schema_after_migration = true")
+
+ Dir.chdir(app_path) do
+ `bin/rails generate model reviews book_id:integer`
+ `bin/rails db:migrate`
+
+ structure_dump = File.read("db/schema.rb")
+ assert_match(/create_table "reviews"/, structure_dump)
+ end
+ end
+
+ test "default schema generation after migration" do
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string;
+ bin/rails db:migrate`
+
+ structure_dump = File.read("db/schema.rb")
+ assert_match(/create_table "books"/, structure_dump)
+ end
+ end
+
+ test "migration status migrated file is deleted" do
+ Dir.chdir(app_path) do
+ `bin/rails generate model user username:string password:string;
+ bin/rails generate migration add_email_to_users email:string;
+ bin/rails db:migrate
+ rm db/migrate/*email*.rb`
+
+ output = `bin/rails db:migrate:status`
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+\** NO FILE \**/, output)
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
new file mode 100644
index 0000000000..e7ffea2e71
--- /dev/null
+++ b/railties/test/application/rake/notes_test.rb
@@ -0,0 +1,168 @@
+require "isolation/abstract_unit"
+require "rails/source_annotation_extractor"
+
+module ApplicationTests
+ module RakeTests
+ class RakeNotesTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ require "rails/all"
+ super
+ end
+
+ def teardown
+ super
+ teardown_app
+ end
+
+ test "notes finds notes for certain file_types" do
+ app_file "app/views/home/index.html.erb", "<% # TODO: note in erb %>"
+ app_file "app/assets/javascripts/application.js", "// TODO: note in js"
+ app_file "app/assets/stylesheets/application.css", "// TODO: note in css"
+ app_file "app/controllers/application_controller.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in ruby"
+ app_file "lib/tasks/task.rake", "# TODO: note in rake"
+ app_file "app/views/home/index.html.builder", "# TODO: note in builder"
+ app_file "config/locales/en.yml", "# TODO: note in yml"
+ app_file "config/locales/en.yaml", "# TODO: note in yaml"
+ app_file "app/views/home/index.ruby", "# TODO: note in ruby"
+
+ run_rake_notes do |output, lines|
+ assert_match(/note in erb/, output)
+ assert_match(/note in js/, output)
+ assert_match(/note in css/, output)
+ assert_match(/note in rake/, output)
+ assert_match(/note in builder/, output)
+ assert_match(/note in yml/, output)
+ assert_match(/note in yaml/, output)
+ assert_match(/note in ruby/, output)
+
+ assert_equal 9, lines.size
+ assert_equal [4], lines.map(&:size).uniq
+ end
+ end
+
+ test "notes finds notes in default directories" do
+ app_file "app/controllers/some_controller.rb", "# TODO: note in app directory"
+ app_file "config/initializers/some_initializer.rb", "# TODO: note in config directory"
+ app_file "db/some_seeds.rb", "# TODO: note in db directory"
+ app_file "lib/some_file.rb", "# TODO: note in lib directory"
+ app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory"
+
+ app_file "some_other_dir/blah.rb", "# TODO: note in some_other directory"
+
+ run_rake_notes do |output, lines|
+ assert_match(/note in app directory/, output)
+ assert_match(/note in config directory/, output)
+ assert_match(/note in db directory/, output)
+ assert_match(/note in lib directory/, output)
+ assert_match(/note in test directory/, output)
+ assert_no_match(/note in some_other directory/, output)
+
+ assert_equal 5, lines.size
+ assert_equal [4], lines.map(&:size).uniq
+ end
+ end
+
+ test "notes finds notes in custom directories" do
+ app_file "app/controllers/some_controller.rb", "# TODO: note in app directory"
+ app_file "config/initializers/some_initializer.rb", "# TODO: note in config directory"
+ app_file "db/some_seeds.rb", "# TODO: note in db directory"
+ app_file "lib/some_file.rb", "# TODO: note in lib directory"
+ app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory"
+
+ app_file "some_other_dir/blah.rb", "# TODO: note in some_other directory"
+
+ run_rake_notes "SOURCE_ANNOTATION_DIRECTORIES='some_other_dir' bin/rails notes" do |output, lines|
+ assert_match(/note in app directory/, output)
+ assert_match(/note in config directory/, output)
+ assert_match(/note in db directory/, output)
+ assert_match(/note in lib directory/, output)
+ assert_match(/note in test directory/, output)
+
+ assert_match(/note in some_other directory/, output)
+
+ assert_equal 6, lines.size
+ assert_equal [4], lines.map(&:size).uniq
+ end
+ end
+
+ test "custom rake task finds specific notes in specific directories" do
+ app_file "app/controllers/some_controller.rb", "# TODO: note in app directory"
+ app_file "lib/some_file.rb", "# OPTIMIZE: note in lib directory\n" << "# FIXME: note in lib directory"
+ app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory"
+
+ app_file "lib/tasks/notes_custom.rake", <<-EOS
+ require 'rails/source_annotation_extractor'
+ task :notes_custom do
+ tags = 'TODO|FIXME'
+ opts = { dirs: %w(lib test), tag: true }
+ SourceAnnotationExtractor.enumerate(tags, opts)
+ end
+ EOS
+
+ run_rake_notes "bin/rails notes_custom" do |output, lines|
+ assert_match(/\[FIXME\] note in lib directory/, output)
+ assert_match(/\[TODO\] note in test directory/, output)
+ assert_no_match(/OPTIMIZE/, output)
+ assert_no_match(/note in app directory/, output)
+
+ assert_equal 2, lines.size
+ assert_equal [4], lines.map(&:size).uniq
+ end
+ end
+
+ test "register a new extension" do
+ add_to_config "config.assets.precompile = []"
+ add_to_config %q{ config.annotations.register_extensions("scss", "sass") { |annotation| /\/\/\s*(#{annotation}):?\s*(.*)$/ } }
+ app_file "app/assets/stylesheets/application.css.scss", "// TODO: note in scss"
+ app_file "app/assets/stylesheets/application.css.sass", "// TODO: note in sass"
+
+ run_rake_notes do |output, lines|
+ assert_match(/note in scss/, output)
+ assert_match(/note in sass/, output)
+ assert_equal 2, lines.size
+ end
+ end
+
+ test "register additional directories" do
+ app_file "spec/spec_helper.rb", "# TODO: note in spec"
+ app_file "spec/models/user_spec.rb", "# TODO: note in model spec"
+ add_to_config ' config.annotations.register_directories("spec") '
+
+ run_rake_notes do |output, lines|
+ assert_match(/note in spec/, output)
+ assert_match(/note in model spec/, output)
+ assert_equal 2, lines.size
+ end
+ end
+
+ private
+
+ def run_rake_notes(command = "bin/rails notes")
+ boot_rails
+ load_tasks
+
+ Dir.chdir(app_path) do
+ output = `#{command}`
+ lines = output.scan(/\[([0-9\s]+)\]\s/).flatten
+
+ yield output, lines
+ end
+ end
+
+ def load_tasks
+ require "rake"
+ require "rdoc/task"
+ require "rake/testtask"
+
+ Rails.application.load_tasks
+ end
+
+ def boot_rails
+ require "#{app_path}/config/environment"
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/restart_test.rb b/railties/test/application/rake/restart_test.rb
new file mode 100644
index 0000000000..6ebd2d5461
--- /dev/null
+++ b/railties/test/application/rake/restart_test.rb
@@ -0,0 +1,47 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class RakeRestartTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "rails restart touches tmp/restart.txt" do
+ Dir.chdir(app_path) do
+ `bin/rails restart`
+ assert File.exist?("tmp/restart.txt")
+
+ prev_mtime = File.mtime("tmp/restart.txt")
+ sleep(1)
+ `bin/rails restart`
+ curr_mtime = File.mtime("tmp/restart.txt")
+ assert_not_equal prev_mtime, curr_mtime
+ end
+ end
+
+ test "rails restart should work even if tmp folder does not exist" do
+ Dir.chdir(app_path) do
+ FileUtils.remove_dir("tmp")
+ `bin/rails restart`
+ assert File.exist?("tmp/restart.txt")
+ end
+ end
+
+ test "rails restart removes server.pid also" do
+ Dir.chdir(app_path) do
+ FileUtils.mkdir_p("tmp/pids")
+ FileUtils.touch("tmp/pids/server.pid")
+ `bin/rails restart`
+ assert_not File.exist?("tmp/pids/server.pid")
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/tmp_test.rb b/railties/test/application/rake/tmp_test.rb
new file mode 100644
index 0000000000..8423a98f84
--- /dev/null
+++ b/railties/test/application/rake/tmp_test.rb
@@ -0,0 +1,43 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class TmpTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "tmp:clear clear cache, socket and screenshot files" do
+ Dir.chdir(app_path) do
+ FileUtils.mkdir_p("tmp/cache")
+ FileUtils.touch("tmp/cache/cache_file")
+
+ FileUtils.mkdir_p("tmp/sockets")
+ FileUtils.touch("tmp/sockets/socket_file")
+
+ FileUtils.mkdir_p("tmp/screenshots")
+ FileUtils.touch("tmp/screenshots/fail.png")
+
+ `rails tmp:clear`
+
+ assert_not File.exist?("tmp/cache/cache_file")
+ assert_not File.exist?("tmp/sockets/socket_file")
+ assert_not File.exist?("tmp/screenshots/fail.png")
+ end
+ end
+
+ test "tmp:clear should work if folder missing" do
+ FileUtils.remove_dir("#{app_path}/tmp")
+ errormsg = Dir.chdir(app_path) { `bin/rails tmp:clear` }
+ assert_predicate $?, :success?
+ assert_empty errormsg
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
new file mode 100644
index 0000000000..134106812d
--- /dev/null
+++ b/railties/test/application/rake_test.rb
@@ -0,0 +1,395 @@
+require "isolation/abstract_unit"
+require "active_support/core_ext/string/strip"
+
+module ApplicationTests
+ class RakeTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def test_gems_tasks_are_loaded_first_than_application_ones
+ app_file "lib/tasks/app.rake", <<-RUBY
+ $task_loaded = Rake::Task.task_defined?("db:create:all")
+ RUBY
+
+ require "#{app_path}/config/environment"
+ ::Rails.application.load_tasks
+ assert $task_loaded
+ end
+
+ test "task is protected when previous migration was production" do
+ Dir.chdir(app_path) do
+ output = `bin/rails generate model product name:string;
+ env RAILS_ENV=production bin/rails db:create db:migrate;
+ env RAILS_ENV=production bin/rails db:test:prepare test 2>&1`
+
+ assert_match(/ActiveRecord::ProtectedEnvironmentError/, output)
+ end
+ end
+
+ def test_not_protected_when_previous_migration_was_not_production
+ Dir.chdir(app_path) do
+ output = `bin/rails generate model product name:string;
+ env RAILS_ENV=test bin/rails db:create db:migrate;
+ env RAILS_ENV=test bin/rails db:test:prepare test 2>&1`
+
+ refute_match(/ActiveRecord::ProtectedEnvironmentError/, output)
+ end
+ end
+
+ def test_environment_is_required_in_rake_tasks
+ app_file "config/environment.rb", <<-RUBY
+ SuperMiddleware = Struct.new(:app)
+
+ Rails.application.configure do
+ config.middleware.use SuperMiddleware
+ end
+
+ Rails.application.initialize!
+ RUBY
+
+ assert_match("SuperMiddleware", Dir.chdir(app_path) { `bin/rails middleware` })
+ end
+
+ def test_initializers_are_executed_in_rake_tasks
+ add_to_config <<-RUBY
+ initializer "do_something" do
+ puts "Doing something..."
+ end
+
+ rake_tasks do
+ task do_nothing: :environment do
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rails do_nothing` }
+ assert_match "Doing something...", output
+ end
+
+ def test_does_not_explode_when_accessing_a_model
+ add_to_config <<-RUBY
+ rake_tasks do
+ task do_nothing: :environment do
+ Hello.new.world
+ end
+ end
+ RUBY
+
+ app_file "app/models/hello.rb", <<-RUBY
+ class Hello
+ def world
+ puts 'Hello world'
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rails do_nothing` }
+ assert_match "Hello world", output
+ end
+
+ def test_should_not_eager_load_model_for_rake
+ add_to_config <<-RUBY
+ rake_tasks do
+ task do_nothing: :environment do
+ end
+ end
+ RUBY
+
+ add_to_env_config "production", <<-RUBY
+ config.eager_load = true
+ RUBY
+
+ app_file "app/models/hello.rb", <<-RUBY
+ raise 'should not be pre-required for rake even eager_load=true'
+ RUBY
+
+ Dir.chdir(app_path) do
+ assert system("bin/rails do_nothing RAILS_ENV=production"),
+ "should not be pre-required for rake even eager_load=true"
+ end
+ end
+
+ def test_code_statistics_sanity
+ assert_match "Code LOC: 25 Test LOC: 0 Code to Test Ratio: 1:0.0",
+ Dir.chdir(app_path) { `bin/rails stats` }
+ end
+
+ def test_rails_routes_calls_the_route_inspector
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/cart', to: 'cart#show'
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rails routes` }
+ assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+ end
+
+ def test_singular_resource_output_in_rake_routes
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ resource :post
+ end
+ RUBY
+
+ expected_output = [" Prefix Verb URI Pattern Controller#Action",
+ " new_post GET /post/new(.:format) posts#new",
+ "edit_post GET /post/edit(.:format) posts#edit",
+ " post GET /post(.:format) posts#show",
+ " PATCH /post(.:format) posts#update",
+ " PUT /post(.:format) posts#update",
+ " DELETE /post(.:format) posts#destroy",
+ " POST /post(.:format) posts#create\n"].join("\n")
+
+ output = Dir.chdir(app_path) { `bin/rails routes -c PostController` }
+ assert_equal expected_output, output
+ end
+
+ def test_rails_routes_with_global_search_key
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/cart', to: 'cart#show'
+ post '/cart', to: 'cart#create'
+ get '/basketballs', to: 'basketball#index'
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rails routes -g show` }
+ assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+
+ output = Dir.chdir(app_path) { `bin/rails routes -g POST` }
+ assert_equal "Prefix Verb URI Pattern Controller#Action\n POST /cart(.:format) cart#create\n", output
+
+ output = Dir.chdir(app_path) { `bin/rails routes -g basketballs` }
+ assert_equal " Prefix Verb URI Pattern Controller#Action\n" \
+ "basketballs GET /basketballs(.:format) basketball#index\n", output
+ end
+
+ def test_rails_routes_with_controller_search_key
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/cart', to: 'cart#show'
+ get '/basketball', to: 'basketball#index'
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rails routes -c cart` }
+ assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+
+ output = Dir.chdir(app_path) { `bin/rails routes -c Cart` }
+ assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+
+ output = Dir.chdir(app_path) { `bin/rails routes -c CartController` }
+ assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+ end
+
+ def test_rails_routes_with_namespaced_controller_search_key
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ namespace :admin do
+ resource :post
+ end
+ end
+ RUBY
+ expected_output = [" Prefix Verb URI Pattern Controller#Action",
+ " new_admin_post GET /admin/post/new(.:format) admin/posts#new",
+ "edit_admin_post GET /admin/post/edit(.:format) admin/posts#edit",
+ " admin_post GET /admin/post(.:format) admin/posts#show",
+ " PATCH /admin/post(.:format) admin/posts#update",
+ " PUT /admin/post(.:format) admin/posts#update",
+ " DELETE /admin/post(.:format) admin/posts#destroy",
+ " POST /admin/post(.:format) admin/posts#create\n"].join("\n")
+
+ output = Dir.chdir(app_path) { `bin/rails routes -c Admin::PostController` }
+ assert_equal expected_output, output
+
+ output = Dir.chdir(app_path) { `bin/rails routes -c PostController` }
+ assert_equal expected_output, output
+ end
+
+ def test_rails_routes_displays_message_when_no_routes_are_defined
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ end
+ RUBY
+
+ assert_equal <<-MESSAGE.strip_heredoc, Dir.chdir(app_path) { `bin/rails routes` }
+ You don't have any routes defined!
+
+ Please add some routes in config/routes.rb.
+
+ For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html.
+ MESSAGE
+ end
+
+ def test_rake_routes_with_rake_options
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/cart', to: 'cart#show'
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rake --rakefile Rakefile routes` }
+ assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+ end
+
+ def test_logger_is_flushed_when_exiting_production_rake_tasks
+ add_to_config <<-RUBY
+ rake_tasks do
+ task log_something: :environment do
+ Rails.logger.error("Sample log message")
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rails log_something RAILS_ENV=production && cat log/production.log` }
+ assert_match "Sample log message", output
+ end
+
+ def test_loading_specific_fixtures
+ Dir.chdir(app_path) do
+ `bin/rails generate model user username:string password:string;
+ bin/rails generate model product name:string;
+ bin/rails db:migrate`
+ end
+
+ require "#{rails_root}/config/environment"
+
+ # loading a specific fixture
+ errormsg = Dir.chdir(app_path) { `bin/rails db:fixtures:load FIXTURES=products` }
+ assert $?.success?, errormsg
+
+ assert_equal 2, ::AppTemplate::Application::Product.count
+ assert_equal 0, ::AppTemplate::Application::User.count
+ end
+
+ def test_loading_only_yml_fixtures
+ Dir.chdir(app_path) do
+ `bin/rails db:migrate`
+ end
+
+ app_file "test/fixtures/products.csv", ""
+
+ require "#{rails_root}/config/environment"
+ errormsg = Dir.chdir(app_path) { `bin/rails db:fixtures:load` }
+ assert $?.success?, errormsg
+ end
+
+ def test_scaffold_tests_pass_by_default
+ output = Dir.chdir(app_path) do
+ `bin/rails generate scaffold user username:string password:string;
+ RAILS_ENV=test bin/rails db:migrate test`
+ end
+
+ assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output)
+ assert_no_match(/Errors running/, output)
+ end
+
+ def test_api_scaffold_tests_pass_by_default
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+
+ app_file "app/controllers/application_controller.rb", <<-RUBY
+ class ApplicationController < ActionController::API
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) do
+ `bin/rails generate scaffold user username:string password:string;
+ RAILS_ENV=test bin/rails db:migrate test`
+ end
+
+ assert_match(/5 runs, 7 assertions, 0 failures, 0 errors/, output)
+ assert_no_match(/Errors running/, output)
+ end
+
+ def test_scaffold_with_references_columns_tests_pass_by_default
+ output = Dir.chdir(app_path) do
+ `bin/rails generate model Product;
+ bin/rails generate model Cart;
+ bin/rails generate scaffold LineItems product:references cart:belongs_to;
+ RAILS_ENV=test bin/rails db:migrate test`
+ end
+
+ assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output)
+ assert_no_match(/Errors running/, output)
+ end
+
+ def test_db_test_prepare_when_using_sql_format
+ add_to_config "config.active_record.schema_format = :sql"
+ output = Dir.chdir(app_path) do
+ `bin/rails generate scaffold user username:string;
+ bin/rails db:migrate;
+ bin/rails db:test:prepare 2>&1 --trace`
+ end
+ assert_match(/Execute db:test:load_structure/, output)
+ end
+
+ def test_rake_dump_structure_should_respect_db_structure_env_variable
+ Dir.chdir(app_path) do
+ # ensure we have a schema_migrations table to dump
+ `bin/rails db:migrate db:structure:dump SCHEMA=db/my_structure.sql`
+ end
+ assert File.exist?(File.join(app_path, "db", "my_structure.sql"))
+ end
+
+ def test_rake_dump_structure_should_be_called_twice_when_migrate_redo
+ add_to_config "config.active_record.schema_format = :sql"
+
+ output = Dir.chdir(app_path) do
+ `bin/rails g model post title:string;
+ bin/rails db:migrate:redo 2>&1 --trace;`
+ end
+
+ # expect only Invoke db:structure:dump (first_time)
+ assert_no_match(/^\*\* Invoke db:structure:dump\s+$/, output)
+ end
+
+ def test_rake_dump_schema_cache
+ Dir.chdir(app_path) do
+ `bin/rails generate model post title:string;
+ bin/rails generate model product name:string;
+ bin/rails db:migrate db:schema:cache:dump`
+ end
+ assert File.exist?(File.join(app_path, "db", "schema_cache.yml"))
+ end
+
+ def test_rake_clear_schema_cache
+ Dir.chdir(app_path) do
+ `bin/rails db:schema:cache:dump db:schema:cache:clear`
+ end
+ assert !File.exist?(File.join(app_path, "db", "schema_cache.yml"))
+ end
+
+ def test_copy_templates
+ Dir.chdir(app_path) do
+ `bin/rails app:templates:copy`
+ %w(controller mailer scaffold).each do |dir|
+ assert File.exist?(File.join(app_path, "lib", "templates", "erb", dir))
+ end
+ %w(controller helper scaffold_controller assets).each do |dir|
+ assert File.exist?(File.join(app_path, "lib", "templates", "rails", dir))
+ end
+ end
+ end
+
+ def test_template_load_initializers
+ app_file "config/initializers/dummy.rb", "puts 'Hello, World!'"
+ app_file "template.rb", ""
+
+ output = Dir.chdir(app_path) do
+ `bin/rails app:template LOCATION=template.rb`
+ end
+
+ assert_match(/Hello, World!/, output)
+ end
+ end
+end
diff --git a/railties/test/application/rendering_test.rb b/railties/test/application/rendering_test.rb
new file mode 100644
index 0000000000..ccafc5b6f1
--- /dev/null
+++ b/railties/test/application/rendering_test.rb
@@ -0,0 +1,44 @@
+require "isolation/abstract_unit"
+require "rack/test"
+
+module ApplicationTests
+ class RoutingTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "Unknown format falls back to HTML template" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'pages/:id', to: 'pages#show'
+ end
+ RUBY
+
+ app_file "app/controllers/pages_controller.rb", <<-RUBY
+ class PagesController < ApplicationController
+ layout false
+
+ def show
+ end
+ end
+ RUBY
+
+ app_file "app/views/pages/show.html.erb", <<-RUBY
+ <%= params[:id] %>
+ RUBY
+
+ get "/pages/foo"
+ assert_equal 200, last_response.status
+
+ get "/pages/foo.bar"
+ assert_equal 200, last_response.status
+ end
+ end
+end
diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb
new file mode 100644
index 0000000000..bc7580d6f4
--- /dev/null
+++ b/railties/test/application/routing_test.rb
@@ -0,0 +1,680 @@
+require "isolation/abstract_unit"
+require "rack/test"
+
+module ApplicationTests
+ class RoutingTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "rails/welcome in development" do
+ app("development")
+ get "/"
+ assert_equal 200, last_response.status
+ end
+
+ test "rails/info in development" do
+ app("development")
+ get "/rails/info"
+ assert_equal 302, last_response.status
+ end
+
+ test "rails/info/routes in development" do
+ app("development")
+ get "/rails/info/routes"
+ assert_equal 200, last_response.status
+ end
+
+ test "rails/info/properties in development" do
+ app("development")
+ get "/rails/info/properties"
+ assert_equal 200, last_response.status
+ end
+
+ test "/rails/info routes are accessible with globbing route present" do
+ app("development")
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '*foo', to: 'foo#index'
+ end
+ RUBY
+
+ get "/rails/info"
+ assert_equal 302, last_response.status
+
+ get "rails/info/routes"
+ assert_equal 200, last_response.status
+
+ get "rails/info/properties"
+ assert_equal 200, last_response.status
+ end
+
+ test "root takes precedence over internal welcome controller" do
+ app("development")
+
+ assert_welcome get("/")
+
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render plain: "foo"
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ root to: "foo#index"
+ end
+ RUBY
+
+ get "/"
+ assert_equal "foo", last_response.body
+ end
+
+ test "rails/welcome in production" do
+ app("production")
+ get "/"
+ assert_equal 404, last_response.status
+ end
+
+ test "rails/info in production" do
+ app("production")
+ get "/rails/info"
+ assert_equal 404, last_response.status
+ end
+
+ test "rails/info/routes in production" do
+ app("production")
+ get "/rails/info/routes"
+ assert_equal 404, last_response.status
+ end
+
+ test "rails/info/properties in production" do
+ app("production")
+ get "/rails/info/properties"
+ assert_equal 404, last_response.status
+ end
+
+ test "simple controller" do
+ simple_controller
+
+ get "/foo"
+ assert_equal "foo", last_response.body
+ end
+
+ test "simple controller with helper" do
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render inline: "<%= foo_or_bar? %>"
+ end
+ end
+ RUBY
+
+ app_file "app/helpers/bar_helper.rb", <<-RUBY
+ module BarHelper
+ def foo_or_bar?
+ "bar"
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ end
+ RUBY
+
+ get "/foo"
+ assert_equal "bar", last_response.body
+ end
+
+ test "mount rack app" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ mount lambda { |env| [200, {}, [env["PATH_INFO"]]] }, at: "/blog"
+ # The line below is required because mount sometimes
+ # fails when a resource route is added.
+ resource :user
+ end
+ RUBY
+
+ get "/blog/archives"
+ assert_equal "/archives", last_response.body
+ end
+
+ test "mount named rack app" do
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render plain: my_blog_path
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ mount lambda { |env| [200, {}, [env["PATH_INFO"]]] }, at: "/blog", as: "my_blog"
+ get '/foo' => 'foo#index'
+ end
+ RUBY
+
+ get "/foo"
+ assert_equal "/blog", last_response.body
+ end
+
+ test "multiple controllers" do
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render plain: "foo"
+ end
+ end
+ RUBY
+
+ controller :bar, <<-RUBY
+ class BarController < ActionController::Base
+ def index
+ render plain: "bar"
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ end
+ RUBY
+
+ get "/foo"
+ assert_equal "foo", last_response.body
+
+ get "/bar"
+ assert_equal "bar", last_response.body
+ end
+
+ test "nested controller" do
+ controller "foo", <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render plain: "foo"
+ end
+ end
+ RUBY
+
+ controller "admin/foo", <<-RUBY
+ module Admin
+ class FooController < ApplicationController
+ def index
+ render plain: "admin::foo"
+ end
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'admin/foo', to: 'admin/foo#index'
+ get 'foo', to: 'foo#index'
+ end
+ RUBY
+
+ get "/foo"
+ assert_equal "foo", last_response.body
+
+ get "/admin/foo"
+ assert_equal "admin::foo", last_response.body
+ end
+
+ test "routes appending blocks" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller/:action'
+ end
+ RUBY
+
+ add_to_config <<-R
+ routes.append do
+ get '/win' => lambda { |e| [200, {'Content-Type'=>'text/plain'}, ['WIN']] }
+ end
+ R
+
+ app "development"
+
+ get "/win"
+ assert_equal "WIN", last_response.body
+
+ app_file "config/routes.rb", <<-R
+ Rails.application.routes.draw do
+ get 'lol' => 'hello#index'
+ end
+ R
+
+ get "/win"
+ assert_equal "WIN", last_response.body
+ end
+
+ {
+ "development" => ["baz", "http://www.apple.com", "/dashboard"],
+ "production" => ["bar", "http://www.microsoft.com", "/profile"]
+ }.each do |mode, (expected_action, expected_url, expected_mapping)|
+ test "reloads routes when configuration is changed in #{mode}" do
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def bar
+ render plain: "bar"
+ end
+
+ def baz
+ render plain: "baz"
+ end
+
+ def custom
+ render plain: custom_url
+ end
+
+ def mapping
+ render plain: url_for(User.new)
+ end
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-RUBY
+ class User
+ extend ActiveModel::Naming
+ include ActiveModel::Conversion
+
+ def self.model_name
+ @_model_name ||= ActiveModel::Name.new(self.class, nil, "User")
+ end
+
+ def persisted?
+ false
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#bar'
+ get 'custom', to: 'foo#custom'
+ get 'mapping', to: 'foo#mapping'
+
+ direct(:custom) { "http://www.microsoft.com" }
+ resolve("User") { "/profile" }
+ end
+ RUBY
+
+ app(mode)
+
+ get "/foo"
+ assert_equal "bar", last_response.body
+
+ get "/custom"
+ assert_equal "http://www.microsoft.com", last_response.body
+
+ get "/mapping"
+ assert_equal "/profile", last_response.body
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#baz'
+ get 'custom', to: 'foo#custom'
+ get 'mapping', to: 'foo#mapping'
+
+ direct(:custom) { "http://www.apple.com" }
+ resolve("User") { "/dashboard" }
+ end
+ RUBY
+
+ sleep 0.1
+
+ get "/foo"
+ assert_equal expected_action, last_response.body
+
+ get "/custom"
+ assert_equal expected_url, last_response.body
+
+ get "/mapping"
+ assert_equal expected_mapping, last_response.body
+ end
+ end
+
+ test "routes are loaded just after initialization" do
+ require "#{app_path}/config/application"
+
+ # Create the rack app just inside after initialize callback
+ ActiveSupport.on_load(:after_initialize) do
+ ::InitializeRackApp = lambda { |env| [200, {}, ["InitializeRackApp"]] }
+ end
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: ::InitializeRackApp
+ end
+ RUBY
+
+ get "/foo"
+ assert_equal "InitializeRackApp", last_response.body
+ end
+
+ test "reload_routes! is part of Rails.application API" do
+ app("development")
+ assert_nothing_raised do
+ Rails.application.reload_routes!
+ end
+ end
+
+ def test_root_path
+ app("development")
+
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render plain: "foo"
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', :to => 'foo#index'
+ root :to => 'foo#index'
+ end
+ RUBY
+
+ remove_file "public/index.html"
+
+ get "/"
+ assert_equal "foo", last_response.body
+ end
+
+ test "routes are added and removed when reloading" do
+ app("development")
+
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render plain: "foo"
+ end
+
+ def custom
+ render plain: custom_url
+ end
+
+ def mapping
+ render plain: url_for(User.new)
+ end
+ end
+ RUBY
+
+ controller :bar, <<-RUBY
+ class BarController < ApplicationController
+ def index
+ render plain: "bar"
+ end
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-RUBY
+ class User
+ extend ActiveModel::Naming
+ include ActiveModel::Conversion
+
+ def self.model_name
+ @_model_name ||= ActiveModel::Name.new(self.class, nil, "User")
+ end
+
+ def persisted?
+ false
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#index'
+ end
+ RUBY
+
+ get "/foo"
+ assert_equal "foo", last_response.body
+ assert_equal "/foo", Rails.application.routes.url_helpers.foo_path
+
+ get "/bar"
+ assert_equal 404, last_response.status
+ assert_raises NoMethodError do
+ assert_equal "/bar", Rails.application.routes.url_helpers.bar_path
+ end
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#index'
+ get 'bar', to: 'bar#index'
+
+ get 'custom', to: 'foo#custom'
+ direct(:custom) { 'http://www.apple.com' }
+
+ get 'mapping', to: 'foo#mapping'
+ resolve('User') { '/profile' }
+ end
+ RUBY
+
+ Rails.application.reload_routes!
+
+ get "/foo"
+ assert_equal "foo", last_response.body
+ assert_equal "/foo", Rails.application.routes.url_helpers.foo_path
+
+ get "/bar"
+ assert_equal "bar", last_response.body
+ assert_equal "/bar", Rails.application.routes.url_helpers.bar_path
+
+ get "/custom"
+ assert_equal "http://www.apple.com", last_response.body
+ assert_equal "http://www.apple.com", Rails.application.routes.url_helpers.custom_url
+
+ get "/mapping"
+ assert_equal "/profile", last_response.body
+ assert_equal "/profile", Rails.application.routes.url_helpers.polymorphic_path(User.new)
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#index'
+ end
+ RUBY
+
+ Rails.application.reload_routes!
+
+ get "/foo"
+ assert_equal "foo", last_response.body
+ assert_equal "/foo", Rails.application.routes.url_helpers.foo_path
+
+ get "/bar"
+ assert_equal 404, last_response.status
+ assert_raises NoMethodError do
+ assert_equal "/bar", Rails.application.routes.url_helpers.bar_path
+ end
+
+ get "/custom"
+ assert_equal 404, last_response.status
+ assert_raises NoMethodError do
+ assert_equal "http://www.apple.com", Rails.application.routes.url_helpers.custom_url
+ end
+
+ get "/mapping"
+ assert_equal 404, last_response.status
+ assert_raises NoMethodError do
+ assert_equal "/profile", Rails.application.routes.url_helpers.polymorphic_path(User.new)
+ end
+ end
+
+ test "named routes are cleared when reloading" do
+ app("development")
+
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render plain: "foo"
+ end
+ end
+ RUBY
+
+ controller :bar, <<-RUBY
+ class BarController < ApplicationController
+ def index
+ render plain: "bar"
+ end
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-RUBY
+ class User
+ extend ActiveModel::Naming
+ include ActiveModel::Conversion
+
+ def self.model_name
+ @_model_name ||= ActiveModel::Name.new(self.class, nil, "User")
+ end
+
+ def persisted?
+ false
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get ':locale/foo', to: 'foo#index', as: 'foo'
+ get 'users', to: 'foo#users', as: 'users'
+ direct(:microsoft) { 'http://www.microsoft.com' }
+ resolve('User') { '/profile' }
+ end
+ RUBY
+
+ get "/en/foo"
+ assert_equal "foo", last_response.body
+ assert_equal "/en/foo", Rails.application.routes.url_helpers.foo_path(locale: "en")
+ assert_equal "http://www.microsoft.com", Rails.application.routes.url_helpers.microsoft_url
+ assert_equal "/profile", Rails.application.routes.url_helpers.polymorphic_path(User.new)
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get ':locale/bar', to: 'bar#index', as: 'foo'
+ get 'users', to: 'foo#users', as: 'users'
+ direct(:apple) { 'http://www.apple.com' }
+ end
+ RUBY
+
+ Rails.application.reload_routes!
+
+ get "/en/foo"
+ assert_equal 404, last_response.status
+
+ get "/en/bar"
+ assert_equal "bar", last_response.body
+ assert_equal "/en/bar", Rails.application.routes.url_helpers.foo_path(locale: "en")
+ assert_equal "http://www.apple.com", Rails.application.routes.url_helpers.apple_url
+ assert_equal "/users", Rails.application.routes.url_helpers.polymorphic_path(User.new)
+
+ assert_raises NoMethodError do
+ assert_equal "http://www.microsoft.com", Rails.application.routes.url_helpers.microsoft_url
+ end
+ end
+
+ test "resource routing with irregular inflection" do
+ app_file "config/initializers/inflection.rb", <<-RUBY
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.irregular 'yazi', 'yazilar'
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ resources :yazilar
+ end
+ RUBY
+
+ controller "yazilar", <<-RUBY
+ class YazilarController < ApplicationController
+ def index
+ render plain: 'yazilar#index'
+ end
+ end
+ RUBY
+
+ get "/yazilars"
+ assert_equal 404, last_response.status
+
+ get "/yazilar"
+ assert_equal 200, last_response.status
+ end
+
+ test "reloading routes removes methods and doesn't undefine them" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/url', to: 'url#index'
+ end
+ RUBY
+
+ app_file "app/models/url_helpers.rb", <<-RUBY
+ module UrlHelpers
+ def foo_path
+ "/foo"
+ end
+ end
+ RUBY
+
+ app_file "app/models/context.rb", <<-RUBY
+ class Context
+ include UrlHelpers
+ include Rails.application.routes.url_helpers
+ end
+ RUBY
+
+ controller "url", <<-RUBY
+ class UrlController < ApplicationController
+ def index
+ context = Context.new
+ render plain: context.foo_path
+ end
+ end
+ RUBY
+
+ get "/url"
+ assert_equal "/foo", last_response.body
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/url', to: 'url#index'
+ get '/bar', to: 'foo#index', as: 'foo'
+ end
+ RUBY
+
+ Rails.application.reload_routes!
+
+ get "/url"
+ assert_equal "/bar", last_response.body
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/url', to: 'url#index'
+ end
+ RUBY
+
+ Rails.application.reload_routes!
+
+ get "/url"
+ assert_equal "/foo", last_response.body
+ end
+ end
+end
diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb
new file mode 100644
index 0000000000..81f717b2c3
--- /dev/null
+++ b/railties/test/application/runner_test.rb
@@ -0,0 +1,133 @@
+require "isolation/abstract_unit"
+require "env_helpers"
+
+module ApplicationTests
+ class RunnerTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include EnvHelpers
+
+ def setup
+ build_app
+
+ # Lets create a model so we have something to play with
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.count
+ 42
+ end
+ end
+ MODEL
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def test_should_include_runner_in_shebang_line_in_help_without_option
+ assert_match "/rails runner", Dir.chdir(app_path) { `bin/rails runner` }
+ end
+
+ def test_should_include_runner_in_shebang_line_in_help
+ assert_match "/rails runner", Dir.chdir(app_path) { `bin/rails runner --help` }
+ end
+
+ def test_should_run_ruby_statement
+ assert_match "42", Dir.chdir(app_path) { `bin/rails runner "puts User.count"` }
+ end
+
+ def test_should_set_argv_when_running_code
+ output = Dir.chdir(app_path) {
+ # Both long and short args, at start and end of ARGV
+ `bin/rails runner "puts ARGV.join(',')" --foo a1 -b a2 a3 --moo`
+ }
+ assert_equal "--foo,a1,-b,a2,a3,--moo", output.chomp
+ end
+
+ def test_should_run_file
+ app_file "bin/count_users.rb", <<-SCRIPT
+ puts User.count
+ SCRIPT
+
+ assert_match "42", Dir.chdir(app_path) { `bin/rails runner "bin/count_users.rb"` }
+ end
+
+ def test_no_minitest_loaded_in_production_mode
+ app_file "bin/print_features.rb", <<-SCRIPT
+ p $LOADED_FEATURES.grep(/minitest/)
+ SCRIPT
+ assert_match "[]", Dir.chdir(app_path) {
+ `RAILS_ENV=production bin/rails runner "bin/print_features.rb"`
+ }
+ end
+
+ def test_should_set_dollar_0_to_file
+ app_file "bin/dollar0.rb", <<-SCRIPT
+ puts $0
+ SCRIPT
+
+ assert_match "bin/dollar0.rb", Dir.chdir(app_path) { `bin/rails runner "bin/dollar0.rb"` }
+ end
+
+ def test_should_set_dollar_program_name_to_file
+ app_file "bin/program_name.rb", <<-SCRIPT
+ puts $PROGRAM_NAME
+ SCRIPT
+
+ assert_match "bin/program_name.rb", Dir.chdir(app_path) { `bin/rails runner "bin/program_name.rb"` }
+ end
+
+ def test_passes_extra_args_to_file
+ app_file "bin/program_name.rb", <<-SCRIPT
+ p ARGV
+ SCRIPT
+
+ assert_match %w( a b ).to_s, Dir.chdir(app_path) { `bin/rails runner "bin/program_name.rb" a b` }
+ end
+
+ def test_should_run_stdin
+ app_file "bin/count_users.rb", <<-SCRIPT
+ puts User.count
+ SCRIPT
+
+ assert_match "42", Dir.chdir(app_path) { `cat bin/count_users.rb | bin/rails runner -` }
+ end
+
+ def test_with_hook
+ add_to_config <<-RUBY
+ runner do |app|
+ app.config.ran = true
+ end
+ RUBY
+
+ assert_match "true", Dir.chdir(app_path) { `bin/rails runner "puts Rails.application.config.ran"` }
+ end
+
+ def test_default_environment
+ assert_match "development", Dir.chdir(app_path) { `bin/rails runner "puts Rails.env"` }
+ end
+
+ def test_runner_detects_syntax_errors
+ output = Dir.chdir(app_path) { `bin/rails runner "puts 'hello world" 2>&1` }
+ assert_not $?.success?
+ assert_match "unterminated string meets end of file", output
+ end
+
+ def test_runner_detects_bad_script_name
+ output = Dir.chdir(app_path) { `bin/rails runner "iuiqwiourowe" 2>&1` }
+ assert_not $?.success?
+ assert_match "undefined local variable or method `iuiqwiourowe' for", output
+ end
+
+ def test_environment_with_rails_env
+ with_rails_env "production" do
+ assert_match "production", Dir.chdir(app_path) { `bin/rails runner "puts Rails.env"` }
+ end
+ end
+
+ def test_environment_with_rack_env
+ with_rack_env "production" do
+ assert_match "production", Dir.chdir(app_path) { `bin/rails runner "puts Rails.env"` }
+ end
+ end
+ end
+end
diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb
new file mode 100644
index 0000000000..bcd311c461
--- /dev/null
+++ b/railties/test/application/test_runner_test.rb
@@ -0,0 +1,769 @@
+require "isolation/abstract_unit"
+require "active_support/core_ext/string/strip"
+require "env_helpers"
+
+module ApplicationTests
+ class TestRunnerTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation, EnvHelpers
+
+ def setup
+ build_app
+ create_schema
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def test_run_via_backwardscompatibility
+ require "minitest/rails_plugin"
+
+ assert_nothing_raised do
+ Minitest.run_via[:ruby] = true
+ end
+
+ assert Minitest.run_via[:ruby]
+ end
+
+ def test_run_single_file
+ create_test_file :models, "foo"
+ create_test_file :models, "bar"
+ assert_match "1 runs, 1 assertions, 0 failures", run_test_command("test/models/foo_test.rb")
+ end
+
+ def test_run_single_file_with_absolute_path
+ create_test_file :models, "foo"
+ create_test_file :models, "bar"
+ assert_match "1 runs, 1 assertions, 0 failures", run_test_command("#{app_path}/test/models/foo_test.rb")
+ end
+
+ def test_run_multiple_files
+ create_test_file :models, "foo"
+ create_test_file :models, "bar"
+ assert_match "2 runs, 2 assertions, 0 failures", run_test_command("test/models/foo_test.rb test/models/bar_test.rb")
+ end
+
+ def test_run_multiple_files_with_absolute_paths
+ create_test_file :models, "foo"
+ create_test_file :controllers, "foobar_controller"
+ create_test_file :models, "bar"
+
+ assert_match "2 runs, 2 assertions, 0 failures", run_test_command("#{app_path}/test/models/foo_test.rb #{app_path}/test/controllers/foobar_controller_test.rb")
+ end
+
+ def test_run_file_with_syntax_error
+ app_file "test/models/error_test.rb", <<-RUBY
+ require 'test_helper'
+ def; end
+ RUBY
+
+ error = capture(:stderr) { run_test_command("test/models/error_test.rb") }
+ assert_match "syntax error", error
+ end
+
+ def test_run_models
+ create_test_file :models, "foo"
+ create_test_file :models, "bar"
+ create_test_file :controllers, "foobar_controller"
+ run_test_command("test/models").tap do |output|
+ assert_match "FooTest", output
+ assert_match "BarTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_helpers
+ create_test_file :helpers, "foo_helper"
+ create_test_file :helpers, "bar_helper"
+ create_test_file :controllers, "foobar_controller"
+ run_test_command("test/helpers").tap do |output|
+ assert_match "FooHelperTest", output
+ assert_match "BarHelperTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_units
+ create_test_file :models, "foo"
+ create_test_file :helpers, "bar_helper"
+ create_test_file :unit, "baz_unit"
+ create_test_file :controllers, "foobar_controller"
+
+ Dir.chdir(app_path) do
+ `bin/rails test:units`.tap do |output|
+ assert_match "FooTest", output
+ assert_match "BarHelperTest", output
+ assert_match "BazUnitTest", output
+ assert_match "3 runs, 3 assertions, 0 failures", output
+ end
+ end
+ end
+
+ def test_run_controllers
+ create_test_file :controllers, "foo_controller"
+ create_test_file :controllers, "bar_controller"
+ create_test_file :models, "foo"
+ run_test_command("test/controllers").tap do |output|
+ assert_match "FooControllerTest", output
+ assert_match "BarControllerTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_mailers
+ create_test_file :mailers, "foo_mailer"
+ create_test_file :mailers, "bar_mailer"
+ create_test_file :models, "foo"
+ run_test_command("test/mailers").tap do |output|
+ assert_match "FooMailerTest", output
+ assert_match "BarMailerTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_jobs
+ create_test_file :jobs, "foo_job"
+ create_test_file :jobs, "bar_job"
+ create_test_file :models, "foo"
+ run_test_command("test/jobs").tap do |output|
+ assert_match "FooJobTest", output
+ assert_match "BarJobTest", 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"
+ create_test_file :functional, "baz_functional"
+ create_test_file :models, "foo"
+
+ Dir.chdir(app_path) do
+ `bin/rails test:functionals`.tap do |output|
+ assert_match "FooMailerTest", output
+ assert_match "BarControllerTest", output
+ assert_match "BazFunctionalTest", output
+ assert_match "3 runs, 3 assertions, 0 failures", output
+ end
+ end
+ end
+
+ def test_run_integration
+ create_test_file :integration, "foo_integration"
+ create_test_file :models, "foo"
+ run_test_command("test/integration").tap do |output|
+ assert_match "FooIntegration", output
+ assert_match "1 runs, 1 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_all_suites
+ suites = [:models, :helpers, :unit, :controllers, :mailers, :functional, :integration, :jobs]
+ 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
+ end
+ end
+
+ def test_run_named_test
+ app_file "test/unit/chu_2_koi_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class Chu2KoiTest < ActiveSupport::TestCase
+ def test_rikka
+ puts 'Rikka'
+ end
+
+ def test_sanae
+ puts 'Sanae'
+ end
+ end
+ RUBY
+
+ run_test_command("-n test_rikka test/unit/chu_2_koi_test.rb").tap do |output|
+ assert_match "Rikka", output
+ assert_no_match "Sanae", output
+ end
+ end
+
+ def test_run_matched_test
+ app_file "test/unit/chu_2_koi_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class Chu2KoiTest < ActiveSupport::TestCase
+ def test_rikka
+ puts 'Rikka'
+ end
+
+ def test_sanae
+ puts 'Sanae'
+ end
+ end
+ RUBY
+
+ run_test_command("-n /rikka/ test/unit/chu_2_koi_test.rb").tap do |output|
+ assert_match "Rikka", output
+ assert_no_match "Sanae", output
+ end
+ end
+
+ def test_load_fixtures_when_running_test_suites
+ create_model_with_fixture
+ suites = [:models, :helpers, :controllers, :mailers, :integration]
+
+ suites.each do |suite, directory|
+ directory ||= suite
+ create_fixture_test directory
+ assert_match "3 users", run_test_command("test/#{suite}")
+ Dir.chdir(app_path) { FileUtils.rm_f "test/#{directory}" }
+ end
+ end
+
+ def test_run_with_model
+ skip "These feel a bit odd. Not sure we should keep supporting them."
+ create_model_with_fixture
+ create_fixture_test "models", "user"
+ assert_match "3 users", run_task(["test models/user"])
+ assert_match "3 users", run_task(["test app/models/user.rb"])
+ end
+
+ def test_run_different_environment_using_env_var
+ skip "no longer possible. Running tests in a different environment should be explicit"
+ app_file "test/unit/env_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class EnvTest < ActiveSupport::TestCase
+ def test_env
+ puts Rails.env
+ end
+ end
+ RUBY
+
+ ENV["RAILS_ENV"] = "development"
+ assert_match "development", run_test_command("test/unit/env_test.rb")
+ end
+
+ def test_run_in_test_environment_by_default
+ create_env_test
+
+ assert_match "Current Environment: test", run_test_command("test/unit/env_test.rb")
+ end
+
+ def test_run_different_environment
+ create_env_test
+
+ assert_match "Current Environment: development",
+ run_test_command("-e development test/unit/env_test.rb")
+ end
+
+ def test_generated_scaffold_works_with_rails_test
+ create_scaffold
+ assert_match "0 failures, 0 errors, 0 skips", run_test_command("")
+ end
+
+ def test_generated_controller_works_with_rails_test
+ create_controller
+ assert_match "0 failures, 0 errors, 0 skips", run_test_command("")
+ end
+
+ def test_run_multiple_folders
+ create_test_file :models, "account"
+ create_test_file :controllers, "accounts_controller"
+
+ run_test_command("test/models test/controllers").tap do |output|
+ assert_match "AccountTest", output
+ assert_match "AccountsControllerTest", output
+ assert_match "2 runs, 2 assertions, 0 failures, 0 errors, 0 skips", output
+ end
+ end
+
+ def test_run_multiple_folders_with_absolute_paths
+ create_test_file :models, "account"
+ create_test_file :controllers, "accounts_controller"
+ create_test_file :helpers, "foo_helper"
+
+ run_test_command("#{app_path}/test/models #{app_path}/test/controllers").tap do |output|
+ assert_match "AccountTest", output
+ assert_match "AccountsControllerTest", output
+ assert_match "2 runs, 2 assertions, 0 failures, 0 errors, 0 skips", output
+ end
+ end
+
+ def test_run_with_ruby_command
+ app_file "test/models/post_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class PostTest < ActiveSupport::TestCase
+ test 'declarative syntax works' do
+ puts 'PostTest'
+ assert true
+ end
+ end
+ RUBY
+
+ Dir.chdir(app_path) do
+ `ruby -Itest test/models/post_test.rb`.tap do |output|
+ assert_match "PostTest", output
+ assert_no_match "is already defined in", output
+ end
+ end
+ end
+
+ def test_mix_files_and_line_filters
+ create_test_file :models, "account"
+ app_file "test/models/post_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class PostTest < ActiveSupport::TestCase
+ def test_post
+ puts 'PostTest'
+ assert true
+ end
+
+ def test_line_filter_does_not_run_this
+ assert true
+ end
+ end
+ RUBY
+
+ run_test_command("test/models/account_test.rb test/models/post_test.rb:4").tap do |output|
+ assert_match "AccountTest", output
+ assert_match "PostTest", output
+ assert_match "2 runs, 2 assertions", output
+ end
+ end
+
+ def test_more_than_one_line_filter
+ app_file "test/models/post_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class PostTest < ActiveSupport::TestCase
+ test "first filter" do
+ puts 'PostTest:FirstFilter'
+ assert true
+ end
+
+ test "second filter" do
+ puts 'PostTest:SecondFilter'
+ assert true
+ end
+
+ test "line filter does not run this" do
+ assert true
+ end
+ end
+ RUBY
+
+ run_test_command("test/models/post_test.rb:4:9").tap do |output|
+ assert_match "PostTest:FirstFilter", output
+ assert_match "PostTest:SecondFilter", output
+ assert_match "2 runs, 2 assertions", output
+ end
+ end
+
+ def test_more_than_one_line_filter_with_multiple_files
+ app_file "test/models/account_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class AccountTest < ActiveSupport::TestCase
+ test "first filter" do
+ puts 'AccountTest:FirstFilter'
+ assert true
+ end
+
+ test "second filter" do
+ puts 'AccountTest:SecondFilter'
+ assert true
+ end
+
+ test "line filter does not run this" do
+ assert true
+ end
+ end
+ RUBY
+
+ app_file "test/models/post_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class PostTest < ActiveSupport::TestCase
+ test "first filter" do
+ puts 'PostTest:FirstFilter'
+ assert true
+ end
+
+ test "second filter" do
+ puts 'PostTest:SecondFilter'
+ assert true
+ end
+
+ test "line filter does not run this" do
+ assert true
+ end
+ end
+ RUBY
+
+ run_test_command("test/models/account_test.rb:4:9 test/models/post_test.rb:4:9").tap do |output|
+ assert_match "AccountTest:FirstFilter", output
+ assert_match "AccountTest:SecondFilter", output
+ assert_match "PostTest:FirstFilter", output
+ assert_match "PostTest:SecondFilter", output
+ assert_match "4 runs, 4 assertions", output
+ end
+ end
+
+ def test_multiple_line_filters
+ create_test_file :models, "account"
+ create_test_file :models, "post"
+
+ run_test_command("test/models/account_test.rb:4 test/models/post_test.rb:4").tap do |output|
+ assert_match "AccountTest", output
+ assert_match "PostTest", output
+ end
+ end
+
+ def test_line_filters_trigger_only_one_runnable
+ app_file "test/models/post_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class PostTest < ActiveSupport::TestCase
+ test 'truth' do
+ assert true
+ end
+ end
+
+ class SecondPostTest < ActiveSupport::TestCase
+ test 'truth' do
+ assert false, 'ran second runnable'
+ end
+ end
+ RUBY
+
+ # Pass seed guaranteeing failure.
+ run_test_command("test/models/post_test.rb:4 --seed 30410").tap do |output|
+ assert_no_match "ran second runnable", output
+ assert_match "1 runs, 1 assertions", output
+ end
+ end
+
+ def test_line_filter_with_minitest_string_filter
+ app_file "test/models/post_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class PostTest < ActiveSupport::TestCase
+ test 'by line' do
+ puts 'by line'
+ assert true
+ end
+
+ test 'by name' do
+ puts 'by name'
+ assert true
+ end
+ end
+ RUBY
+
+ run_test_command("test/models/post_test.rb:4 -n test_by_name").tap do |output|
+ assert_match "by line", output
+ assert_match "by name", output
+ assert_match "2 runs, 2 assertions", output
+ end
+ end
+
+ def test_shows_filtered_backtrace_by_default
+ create_backtrace_test
+
+ assert_match "Rails::BacktraceCleaner", run_test_command("test/unit/backtrace_test.rb")
+ end
+
+ def test_backtrace_option
+ create_backtrace_test
+
+ assert_match "Minitest::BacktraceFilter", run_test_command("test/unit/backtrace_test.rb -b")
+ assert_match "Minitest::BacktraceFilter",
+ run_test_command("test/unit/backtrace_test.rb --backtrace")
+ end
+
+ def test_show_full_backtrace_using_backtrace_environment_variable
+ create_backtrace_test
+
+ switch_env "BACKTRACE", "true" do
+ assert_match "Minitest::BacktraceFilter", run_test_command("test/unit/backtrace_test.rb")
+ end
+ end
+
+ def test_run_app_without_rails_loaded
+ # Simulate a real Rails app boot.
+ app_file "config/boot.rb", <<-RUBY
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
+
+ require 'bundler/setup' # Set up gems listed in the Gemfile.
+ RUBY
+
+ assert_match "0 runs, 0 assertions", run_test_command("")
+ end
+
+ def test_output_inline_by_default
+ create_test_file :models, "post", pass: false
+
+ output = run_test_command("test/models/post_test.rb")
+ expect = %r{Running:\n\nPostTest\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}
+ assert_match expect, output
+ end
+
+ def test_only_inline_failure_output
+ create_test_file :models, "post", pass: false
+
+ output = run_test_command("test/models/post_test.rb")
+ assert_match %r{Finished in.*\n\n1 runs, 1 assertions}, output
+ end
+
+ def test_fail_fast
+ create_test_file :models, "post", pass: false
+
+ assert_match(/Interrupt/,
+ capture(:stderr) { run_test_command("test/models/post_test.rb --fail-fast") })
+ end
+
+ def test_raise_error_when_specified_file_does_not_exist
+ error = capture(:stderr) { run_test_command("test/not_exists.rb") }
+ assert_match(%r{cannot load such file.+test/not_exists\.rb}, error)
+ end
+
+ def test_pass_TEST_env_on_rake_test
+ 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.
+ output = Dir.chdir(app_path) { `bin/rake test TEST=test/models/post_test.rb` }
+
+ assert_match "PostTest", output, "passing TEST= should run selected test"
+ assert_no_match "AccountTest", output, "passing TEST= should only run selected test"
+ assert_match "1 runs, 1 assertions", output
+ end
+
+ def test_pass_rake_options
+ create_test_file :models, "account"
+ output = Dir.chdir(app_path) { `bin/rake --rakefile Rakefile --trace=stdout test` }
+
+ assert_match "1 runs, 1 assertions", output
+ assert_match "Execute test", output
+ end
+
+ def test_rails_db_create_all_restores_db_connection
+ create_test_file :models, "account"
+ output = Dir.chdir(app_path) { `bin/rails db:create:all db:migrate && echo ".tables" | rails dbconsole` }
+ assert_match "ar_internal_metadata", output, "tables should be dumped"
+ end
+
+ def test_rails_db_create_all_restores_db_connection_after_drop
+ create_test_file :models, "account"
+ Dir.chdir(app_path) { `bin/rails db:create:all` } # create all to avoid warnings
+ output = Dir.chdir(app_path) { `bin/rails db:drop:all db:create:all db:migrate && echo ".tables" | rails dbconsole` }
+ assert_match "ar_internal_metadata", output, "tables should be dumped"
+ end
+
+ def test_rake_passes_TESTOPTS_to_minitest
+ create_test_file :models, "account"
+ output = Dir.chdir(app_path) { `bin/rake test TESTOPTS=-v` }
+ assert_match "AccountTest#test_truth", output, "passing TEST= should run selected test"
+ end
+
+ def test_rake_passes_multiple_TESTOPTS_to_minitest
+ create_test_file :models, "account"
+ output = Dir.chdir(app_path) { `bin/rake test TESTOPTS='-v --seed=1234'` }
+ assert_match "AccountTest#test_truth", output, "passing TEST= should run selected test"
+ assert_match "seed=1234", output, "passing TEST= should run selected test"
+ end
+
+ def test_rake_runs_multiple_test_tasks
+ create_test_file :models, "account"
+ create_test_file :controllers, "accounts_controller"
+ output = Dir.chdir(app_path) { `bin/rake test:models test:controllers TESTOPTS='-v'` }
+ assert_match "AccountTest#test_truth", output
+ assert_match "AccountsControllerTest#test_truth", output
+ end
+
+ def test_rake_db_and_test_tasks_parses_args_correctly
+ create_test_file :models, "account"
+ output = Dir.chdir(app_path) { `bin/rake db:migrate test:models TESTOPTS='-v' && echo ".tables" | rails dbconsole` }
+ assert_match "AccountTest#test_truth", output
+ assert_match "ar_internal_metadata", output
+ end
+
+ def test_warnings_option
+ app_file "test/models/warnings_test.rb", <<-RUBY
+ require 'test_helper'
+ def test_warnings
+ a = 1
+ end
+ RUBY
+ assert_match(/warning: assigned but unused variable/,
+ capture(:stderr) { run_test_command("test/models/warnings_test.rb -w") })
+ end
+
+ 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"
+
+ class ResetSessionBeforeRollbackTest < ApplicationSystemTestCase
+ def teardown_fixtures
+ puts "rollback"
+ super
+ end
+
+ Capybara.singleton_class.prepend(Module.new do
+ def reset_sessions!
+ puts "reset sessions"
+ super
+ end
+ end)
+
+ test "dummy" do
+ end
+ end
+ RUBY
+
+ run_test_command("test/system/reset_session_before_rollback_test.rb").tap do |output|
+ assert_match "reset sessions\nrollback", output
+ assert_match "1 runs, 0 assertions, 0 failures, 0 errors, 0 skips", output
+ end
+ 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"
+
+ class DummyTest < ApplicationSystemTestCase
+ test "something" do
+ assert true
+ end
+ end
+ RUBY
+
+ run_test_command("").tap do |output|
+ assert_match "0 runs, 0 assertions, 0 failures, 0 errors, 0 skips", output
+ end
+ end
+
+ def test_system_tests_are_not_run_through_rake_test
+ app_file "test/system/dummy_test.rb", <<-RUBY
+ require "application_system_test_case"
+
+ class DummyTest < ApplicationSystemTestCase
+ test "something" do
+ assert true
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rake test` }
+ assert_match "0 runs, 0 assertions, 0 failures, 0 errors, 0 skips", output
+ end
+
+ 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"
+
+ class DummyTest < ApplicationSystemTestCase
+ test "something" do
+ assert true
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rake test TEST=test/system/dummy_test.rb` }
+ assert_match "1 runs, 1 assertions, 0 failures, 0 errors, 0 skips", output
+ end
+
+ private
+ def run_test_command(arguments = "test/unit/test_test.rb")
+ Dir.chdir(app_path) { `bin/rails t #{arguments}` }
+ end
+
+ def create_model_with_fixture
+ script "generate model user name:string"
+
+ app_file "test/fixtures/users.yml", <<-YAML.strip_heredoc
+ vampire:
+ id: 1
+ name: Koyomi Araragi
+ crab:
+ id: 2
+ name: Senjougahara Hitagi
+ cat:
+ id: 3
+ name: Tsubasa Hanekawa
+ YAML
+
+ run_migration
+ end
+
+ def create_fixture_test(path = :unit, name = "test")
+ app_file "test/#{path}/#{name}_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class #{name.camelize}Test < ActiveSupport::TestCase
+ def test_fixture
+ puts "\#{User.count} users (\#{__FILE__})"
+ end
+ end
+ RUBY
+ end
+
+ def create_backtrace_test
+ app_file "test/unit/backtrace_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class BacktraceTest < ActiveSupport::TestCase
+ def test_backtrace
+ puts Minitest.backtrace_filter
+ end
+ end
+ RUBY
+ end
+
+ def create_schema
+ app_file "db/schema.rb", ""
+ end
+
+ def create_test_file(path = :unit, name = "test", pass: true)
+ app_file "test/#{path}/#{name}_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class #{name.camelize}Test < ActiveSupport::TestCase
+ def test_truth
+ puts "#{name.camelize}Test"
+ assert #{pass}, 'wups!'
+ end
+ end
+ RUBY
+ end
+
+ def create_env_test
+ app_file "test/unit/env_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class EnvTest < ActiveSupport::TestCase
+ def test_env
+ puts "Current Environment: \#{Rails.env}"
+ end
+ end
+ RUBY
+ end
+
+ def create_scaffold
+ script "generate scaffold user name:string"
+ Dir.chdir(app_path) { File.exist?("app/models/user.rb") }
+ run_migration
+ end
+
+ def create_controller
+ script "generate controller admin/dashboard index"
+ end
+
+ def run_migration
+ Dir.chdir(app_path) { `bin/rails db:migrate` }
+ end
+ end
+end
diff --git a/railties/test/application/test_test.rb b/railties/test/application/test_test.rb
new file mode 100644
index 0000000000..32d2a6857c
--- /dev/null
+++ b/railties/test/application/test_test.rb
@@ -0,0 +1,338 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class TestTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "simple successful test" do
+ app_file "test/unit/foo_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class FooTest < ActiveSupport::TestCase
+ def test_truth
+ assert true
+ end
+ end
+ RUBY
+
+ assert_successful_test_run "unit/foo_test.rb"
+ end
+
+ test "after_run" do
+ app_file "test/unit/foo_test.rb", <<-RUBY
+ require 'test_helper'
+
+ Minitest.after_run { puts "WORLD" }
+ Minitest.after_run { puts "HELLO" }
+
+ class FooTest < ActiveSupport::TestCase
+ def test_truth
+ assert true
+ end
+ end
+ RUBY
+
+ result = assert_successful_test_run "unit/foo_test.rb"
+ assert_equal ["HELLO", "WORLD"], result.scan(/HELLO|WORLD/) # only once and in correct order
+ end
+
+ test "simple failed test" do
+ app_file "test/unit/foo_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class FooTest < ActiveSupport::TestCase
+ def test_truth
+ assert false
+ end
+ end
+ RUBY
+
+ assert_unsuccessful_run "unit/foo_test.rb", "Failed assertion"
+ end
+
+ test "integration test" do
+ controller "posts", <<-RUBY
+ class PostsController < ActionController::Base
+ end
+ RUBY
+
+ app_file "app/views/posts/index.html.erb", <<-HTML
+ Posts#index
+ HTML
+
+ app_file "test/integration/posts_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class PostsTest < ActionDispatch::IntegrationTest
+ def test_index
+ get '/posts'
+ assert_response :success
+ assert_includes @response.body, 'Posts#index'
+ end
+ end
+ RUBY
+
+ assert_successful_test_run "integration/posts_test.rb"
+ end
+
+ test "enable full backtraces on test failures" do
+ app_file "test/unit/failing_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class FailingTest < ActiveSupport::TestCase
+ def test_failure
+ raise "fail"
+ end
+ end
+ RUBY
+
+ output = run_test_file("unit/failing_test.rb", env: { "BACKTRACE" => "1" })
+ assert_match %r{test/unit/failing_test\.rb}, output
+ assert_match %r{test/unit/failing_test\.rb:4}, output
+ end
+
+ test "ruby schema migrations" do
+ output = script("generate model user name:string")
+ version = output.match(/(\d+)_create_users\.rb/)[1]
+
+ app_file "test/models/user_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class UserTest < ActiveSupport::TestCase
+ test "user" do
+ User.create! name: "Jon"
+ end
+ end
+ RUBY
+ app_file "db/schema.rb", ""
+
+ assert_unsuccessful_run "models/user_test.rb", "Migrations are pending"
+
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: #{version}) do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
+ app_file "config/initializers/disable_maintain_test_schema.rb", <<-RUBY
+ Rails.application.config.active_record.maintain_test_schema = false
+ RUBY
+
+ assert_unsuccessful_run "models/user_test.rb", "Could not find table 'users'"
+
+ File.delete "#{app_path}/config/initializers/disable_maintain_test_schema.rb"
+
+ result = assert_successful_test_run("models/user_test.rb")
+ assert_not_includes result, "create_table(:users)"
+ end
+
+ test "sql structure migrations" do
+ output = script("generate model user name:string")
+ version = output.match(/(\d+)_create_users\.rb/)[1]
+
+ app_file "test/models/user_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class UserTest < ActiveSupport::TestCase
+ test "user" do
+ User.create! name: "Jon"
+ end
+ end
+ RUBY
+
+ app_file "db/structure.sql", ""
+ app_file "config/initializers/enable_sql_schema_format.rb", <<-RUBY
+ Rails.application.config.active_record.schema_format = :sql
+ RUBY
+
+ assert_unsuccessful_run "models/user_test.rb", "Migrations are pending"
+
+ app_file "db/structure.sql", <<-SQL
+ CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
+ CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
+ CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255));
+ INSERT INTO schema_migrations (version) VALUES ('#{version}');
+ SQL
+
+ app_file "config/initializers/disable_maintain_test_schema.rb", <<-RUBY
+ Rails.application.config.active_record.maintain_test_schema = false
+ RUBY
+
+ assert_unsuccessful_run "models/user_test.rb", "Could not find table 'users'"
+
+ File.delete "#{app_path}/config/initializers/disable_maintain_test_schema.rb"
+
+ assert_successful_test_run("models/user_test.rb")
+ end
+
+ test "sql structure migrations when adding column to existing table" do
+ output_1 = script("generate model user name:string")
+ version_1 = output_1.match(/(\d+)_create_users\.rb/)[1]
+
+ app_file "test/models/user_test.rb", <<-RUBY
+ require 'test_helper'
+ class UserTest < ActiveSupport::TestCase
+ test "user" do
+ User.create! name: "Jon"
+ end
+ end
+ RUBY
+
+ app_file "config/initializers/enable_sql_schema_format.rb", <<-RUBY
+ Rails.application.config.active_record.schema_format = :sql
+ RUBY
+
+ app_file "db/structure.sql", <<-SQL
+ CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
+ CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
+ CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255));
+ INSERT INTO schema_migrations (version) VALUES ('#{version_1}');
+ SQL
+
+ assert_successful_test_run("models/user_test.rb")
+
+ output_2 = script("generate migration add_email_to_users")
+ version_2 = output_2.match(/(\d+)_add_email_to_users\.rb/)[1]
+
+ app_file "test/models/user_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class UserTest < ActiveSupport::TestCase
+ test "user" do
+ User.create! name: "Jon", email: "jon@doe.com"
+ end
+ end
+ RUBY
+
+ app_file "db/structure.sql", <<-SQL
+ CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
+ CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
+ CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "email" varchar(255));
+ INSERT INTO schema_migrations (version) VALUES ('#{version_1}');
+ INSERT INTO schema_migrations (version) VALUES ('#{version_2}');
+ SQL
+
+ assert_successful_test_run("models/user_test.rb")
+ end
+
+ # TODO: would be nice if we could detect the schema change automatically.
+ # For now, the user has to synchronize the schema manually.
+ # This test-case serves as a reminder for this use-case.
+ test "manually synchronize test schema after rollback" do
+ output = script("generate model user name:string")
+ version = output.match(/(\d+)_create_users\.rb/)[1]
+
+ app_file "test/models/user_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class UserTest < ActiveSupport::TestCase
+ test "user" do
+ assert_equal ["id", "name"], User.columns_hash.keys
+ end
+ end
+ RUBY
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: #{version}) do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
+ assert_successful_test_run "models/user_test.rb"
+
+ # Simulate `db:rollback` + edit of the migration file + `db:migrate`
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: #{version}) do
+ create_table :users do |t|
+ t.string :name
+ t.integer :age
+ end
+ end
+ RUBY
+
+ assert_successful_test_run "models/user_test.rb"
+
+ Dir.chdir(app_path) { `bin/rails db:test:prepare` }
+
+ assert_unsuccessful_run "models/user_test.rb", <<-ASSERTION
+Expected: ["id", "name"]
+ Actual: ["id", "name", "age"]
+ ASSERTION
+ end
+
+ test "hooks for plugins" do
+ output = script("generate model user name:string")
+ version = output.match(/(\d+)_create_users\.rb/)[1]
+
+ app_file "lib/tasks/hooks.rake", <<-RUBY
+ task :before_hook do
+ has_user_table = ActiveRecord::Base.connection.table_exists?('users')
+ puts "before: " + has_user_table.to_s
+ end
+
+ task :after_hook do
+ has_user_table = ActiveRecord::Base.connection.table_exists?('users')
+ puts "after: " + has_user_table.to_s
+ end
+
+ Rake::Task["db:test:prepare"].enhance [:before_hook] do
+ Rake::Task[:after_hook].invoke
+ end
+ RUBY
+ app_file "test/models/user_test.rb", <<-RUBY
+ require 'test_helper'
+ class UserTest < ActiveSupport::TestCase
+ test "user" do
+ User.create! name: "Jon"
+ end
+ end
+ RUBY
+
+ # Simulate `db:migrate`
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: #{version}) do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
+ output = assert_successful_test_run "models/user_test.rb"
+ assert_includes output, "before: false\nafter: true"
+
+ # running tests again won't trigger a schema update
+ output = assert_successful_test_run "models/user_test.rb"
+ assert_not_includes output, "before:"
+ assert_not_includes output, "after:"
+ end
+
+ private
+ def assert_unsuccessful_run(name, message)
+ result = run_test_file(name)
+ assert_not_equal 0, $?.to_i
+ assert_includes result, message
+ result
+ end
+
+ def assert_successful_test_run(name)
+ result = run_test_file(name)
+ assert_equal 0, $?.to_i, result
+ result
+ end
+
+ def run_test_file(name, options = {})
+ Dir.chdir(app_path) { `bin/rails test "#{app_path}/test/#{name}" 2>&1` }
+ end
+ end
+end
diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb
new file mode 100644
index 0000000000..37f129475c
--- /dev/null
+++ b/railties/test/application/url_generation_test.rb
@@ -0,0 +1,57 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class UrlGenerationTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def app
+ Rails.application
+ end
+
+ test "it works" do
+ require "rails"
+ require "action_controller/railtie"
+ require "action_view/railtie"
+
+ class MyApp < Rails::Application
+ secrets.secret_key_base = "3b7cd727ee24e8444053437c36cc66c4"
+ config.session_store :cookie_store, key: "_myapp_session"
+ config.active_support.deprecation = :log
+ config.eager_load = false
+ end
+
+ Rails.application.initialize!
+
+ class ::ApplicationController < ActionController::Base
+ end
+
+ class ::OmgController < ::ApplicationController
+ def index
+ render plain: omg_path
+ end
+ end
+
+ MyApp.routes.draw do
+ get "/" => "omg#index", as: :omg
+ end
+
+ require "rack/test"
+ extend Rack::Test::Methods
+
+ get "/"
+ assert_equal "/", last_response.body
+ end
+
+ def test_routes_know_the_relative_root
+ require "rails"
+ require "action_controller/railtie"
+ require "action_view/railtie"
+
+ relative_url = "/hello"
+ ENV["RAILS_RELATIVE_URL_ROOT"] = relative_url
+ app = Class.new(Rails::Application)
+ assert_equal relative_url, app.routes.relative_url_root
+ ENV["RAILS_RELATIVE_URL_ROOT"] = nil
+ end
+ end
+end
diff --git a/railties/test/application/version_test.rb b/railties/test/application/version_test.rb
new file mode 100644
index 0000000000..6b419ae7ae
--- /dev/null
+++ b/railties/test/application/version_test.rb
@@ -0,0 +1,24 @@
+require "isolation/abstract_unit"
+require "rails/gem_version"
+
+class VersionTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "command works" do
+ output = Dir.chdir(app_path) { `bin/rails version` }
+ assert_equal "Rails #{Rails.gem_version}\n", output
+ end
+
+ test "short-cut alias works" do
+ output = Dir.chdir(app_path) { `bin/rails -v` }
+ assert_equal "Rails #{Rails.gem_version}\n", output
+ end
+end