1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/railties/test/application/middleware_test.rb
Xavier Noria 112077c255 inject Rack::Lock if config.eager_load is false
If code is not eager loaded constants are loaded on demand. Constant
autoloading is not thread-safe, so if eager loading is not enabled
multi-threading should not be allowed.

This showed up in certain Capybara scenarios: Most Capybara drivers
other than Rack::Test need a web server. In particular, drivers for
JavaScript support. Capybara launches WEBrick in its own thread for
those but that per se is fine, because the spec thread and the server
thread are coordinated.

Problem comes if the page being served in the spec makes Ajax calls.
Those may hit WEBrick in parallel, and since WEBrick is multi-threaded
and allow_concurrency? returns true in the test environment before
this patch, threads are spawned to serve those parallel requests. On
the other hand, since eager_load is false by default in the test
environment, constants are not preloaded.

So the suite is autoloading constants in a multi-threaded set. That's
a receipt for paracetamol. The symptom is random obscure errors whose
messages point somehow to constant autoloading.

As a consequence of this fix for allow_concurrency? WEBrick in
Capybara scenarios no longer runs in multi-threaded mode.

Fixes #15089.
2014-09-18 23:04:08 +02:00

240 lines
7.5 KiB
Ruby

require 'isolation/abstract_unit'
module ApplicationTests
class MiddlewareTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
build_app
boot_rails
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",
"Rack::Lock",
"ActiveSupport::Cache::Strategy::LocalCache",
"Rack::Runtime",
"Rack::MethodOverride",
"ActionDispatch::RequestId",
"Rails::Rack::Logger", # must come after Rack::MethodOverride to properly log overridden methods
"ActionDispatch::ShowExceptions",
"ActionDispatch::DebugExceptions",
"ActionDispatch::RemoteIp",
"ActionDispatch::Reloader",
"ActionDispatch::Callbacks",
"ActiveRecord::Migration::CheckPending",
"ActiveRecord::ConnectionAdapters::ConnectionManagement",
"ActiveRecord::QueryCache",
"ActionDispatch::Cookies",
"ActionDispatch::Session::CookieStore",
"ActionDispatch::Flash",
"ActionDispatch::ParamsParser",
"Rack::Head",
"Rack::ConditionalGet",
"Rack::ETag"
], middleware
end
test "Rack::Cache is not included by default" do
boot!
assert !middleware.include?("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 middleware.include?("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 middleware.include?("ActiveRecord::Migration::CheckPending")
end
test "ActionDispatch::SSL is present when force_ssl is set" do
add_to_config "config.force_ssl = true"
boot!
assert middleware.include?("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 = { host: 'example.com' }"
boot!
assert_equal [{host: 'example.com'}], Rails.application.middleware.first.args
end
test "removing Active Record omits its middleware" do
use_frameworks []
boot!
assert !middleware.include?("ActiveRecord::ConnectionAdapters::ConnectionManagement")
assert !middleware.include?("ActiveRecord::QueryCache")
assert !middleware.include?("ActiveRecord::Migration::CheckPending")
end
test "includes lock if cache_classes is set but eager_load is not" do
add_to_config "config.cache_classes = true"
boot!
assert middleware.include?("Rack::Lock")
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 !middleware.include?("Rack::Lock")
end
test "does not include lock if allow_concurrency is set" do
add_to_config "config.allow_concurrency = true"
boot!
assert !middleware.include?("Rack::Lock")
end
test "removes static asset server if serve_static_assets is disabled" do
add_to_config "config.serve_static_assets = false"
boot!
assert !middleware.include?("ActionDispatch::Static")
end
test "can delete a middleware from the stack" do
add_to_config "config.middleware.delete ActionDispatch::Static"
boot!
assert !middleware.include?("ActionDispatch::Static")
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 middleware.include?("ActionDispatch::ShowExceptions")
assert middleware.include?("ActionDispatch::DebugExceptions")
end
test "removes ActionDispatch::Reloader if cache_classes is true" do
add_to_config "config.cache_classes = true"
boot!
assert !middleware.include?("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 text: ""
else
render text: "OMG"
end
end
end
etag = "W/" + "5af83e3196bf99f440f31f2e1a6c9afe".inspect
get "/"
assert_equal 200, last_response.status
assert_equal "OMG", last_response.body
assert_equal "text/html; 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_equal 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/html; charset=utf-8", last_response.headers["Content-Type"]
assert_equal "no-cache", last_response.headers["Cache-Control"]
assert_equal 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