rails/railties/test/application/middleware_test.rb
Matthew Draper 6b126ffdcd Enforce middleware ordering with a test, instead of comments
We want the actual order to be very predictable, so it's rightly defined
in code -- not with an on-the-fly tsort.

But we can do the tsort here, and then verify that it matches the
implemented ordering. This way we don't leave future readers guessing
which parts of the ordering are deliberate and which are arbitrary.
2016-12-31 08:40:01 +10:30

311 lines
9.8 KiB
Ruby

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