aboutsummaryrefslogtreecommitdiffstats
path: root/railties/test/application/middleware_test.rb
blob: c5a0d02fe1f40a85025d26676be757f93cbc8b10 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
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.action_dispatch.x_sendfile_header = 'X-Sendfile'"

      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::ConnectionAdapters::ConnectionManagement",
        "ActiveRecord::QueryCache",
        "ActionDispatch::Cookies",
        "ActionDispatch::Session::CookieStore",
        "ActionDispatch::Flash",
        "ActionDispatch::ParamsParser",
        "Rack::Head",
        "Rack::ConditionalGet",
        "Rack::ETag",
        "ActionDispatch::BestStandardsSupport"
      ], middleware
    end

    test "Rack::Sendfile is not included by default" do
      boot!

      assert !middleware.include?("Rack::Sendfile"), "Rack::Sendfile is not included in the default stack unless you set config.action_dispatch.x_sendfile_header"
    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_equal "Rack::Cache", middleware.first
    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 AppTemplate::Application.middleware.first.args, [{host: 'example.com'}]
    end

    test "removing Active Record omits its middleware" do
      use_frameworks []
      boot!
      assert !middleware.include?("ActiveRecord::ConnectionAdapters::ConnectionManagement")
      assert !middleware.include?("ActiveRecord::QueryCache")
    end

    test "removes lock if cache classes is set" do
      add_to_config "config.cache_classes = 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 ActionDispatch::Static, Rack::Config"
      boot!
      assert_equal "Rack::Config", middleware.second
    end

    test "Rails.cache does not respond to middleware" do
      add_to_config "config.cache_store = :memory_store"
      boot!
      assert_equal "Rack::Runtime", middleware.third
    end

    test "Rails.cache does respond to middleware" do
      boot!
      assert_equal "Rack::Runtime", middleware.fourth
    end

    test "insert middleware before" do
      add_to_config "config.middleware.insert_before ActionDispatch::Static, 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 = "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
        AppTemplate::Application.middleware.map(&:klass).map(&:name)
      end
  end
end