render_test.rb 14.0 KB
Newer Older
1
require 'abstract_unit'
2
require 'controller/fake_models'
J
Jeremy Kemper 已提交
3
require 'pathname'
D
Initial  
David Heinemeier Hansson 已提交
4

5 6 7 8 9 10 11 12
class TestControllerWithExtraEtags < ActionController::Base
  etag { nil  }
  etag { 'ab' }
  etag { :cde }
  etag { [:f] }
  etag { nil  }

  def fresh
13
    render text: "stale" if stale?(etag: '123', template: false)
14
  end
15 16

  def array
17 18 19 20 21 22 23
    render text: "stale" if stale?(etag: %w(1 2 3), template: false)
  end

  def with_template
    if stale? template: 'test/hello_world'
      render text: 'stale'
    end
24
  end
25 26
end

27
class TestController < ActionController::Base
P
Pratik Naik 已提交
28 29
  protect_from_forgery

30
  before_action :set_variable_for_layout
L
lest 已提交
31

J
Joshua Peek 已提交
32 33 34
  class LabellingFormBuilder < ActionView::Helpers::FormBuilder
  end

35
  layout :determine_layout
D
Initial  
David Heinemeier Hansson 已提交
36

37 38 39 40 41 42 43
  def name
    nil
  end

  private :name
  helper_method :name

44 45
  def hello_world
  end
D
Initial  
David Heinemeier Hansson 已提交
46

47
  def conditional_hello
48
    if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123])
49 50
      render :action => 'hello_world'
    end
51
  end
J
Joshua Peek 已提交
52

53
  def conditional_hello_with_record
54
    record = Struct.new(:updated_at, :cache_key).new(Time.now.utc.beginning_of_day, "foo/123")
55

56
    if stale?(record)
Ł
Łukasz Strzałkowski 已提交
57
      render :action => 'hello_world'
J
Joshua Peek 已提交
58 59 60
    end
  end

Ł
Łukasz Strzałkowski 已提交
61 62 63
  def conditional_hello_with_expires_in
    expires_in 60.1.seconds
    render :action => 'hello_world'
J
Joshua Peek 已提交
64 65
  end

Ł
Łukasz Strzałkowski 已提交
66 67 68
  def conditional_hello_with_expires_in_with_public
    expires_in 1.minute, :public => true
    render :action => 'hello_world'
J
Joshua Peek 已提交
69 70
  end

Ł
Łukasz Strzałkowski 已提交
71 72 73
  def conditional_hello_with_expires_in_with_must_revalidate
    expires_in 1.minute, :must_revalidate => true
    render :action => 'hello_world'
74 75
  end

Ł
Łukasz Strzałkowski 已提交
76 77 78
  def conditional_hello_with_expires_in_with_public_and_must_revalidate
    expires_in 1.minute, :public => true, :must_revalidate => true
    render :action => 'hello_world'
79 80
  end

Ł
Łukasz Strzałkowski 已提交
81 82 83
  def conditional_hello_with_expires_in_with_public_with_more_keys
    expires_in 1.minute, :public => true, 's-maxage' => 5.hours
    render :action => 'hello_world'
84 85
  end

Ł
Łukasz Strzałkowski 已提交
86 87 88
  def conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax
    expires_in 1.minute, :public => true, :private => nil, 's-maxage' => 5.hours
    render :action => 'hello_world'
J
Joshua Peek 已提交
89 90
  end

Ł
Łukasz Strzałkowski 已提交
91 92 93
  def conditional_hello_with_expires_now
    expires_now
    render :action => 'hello_world'
94 95
  end

Ł
Łukasz Strzałkowski 已提交
96 97 98 99
  def conditional_hello_with_cache_control_headers
    response.headers['Cache-Control'] = 'no-transform'
    expires_now
    render :action => 'hello_world'
100 101
  end

Ł
Łukasz Strzałkowski 已提交
102 103
  def conditional_hello_with_bangs
    render :action => 'hello_world'
104
  end
Ł
Łukasz Strzałkowski 已提交
105
  before_action :handle_last_modified_and_etags, :only=>:conditional_hello_with_bangs
106

Ł
Łukasz Strzałkowski 已提交
107 108
  def handle_last_modified_and_etags
    fresh_when(:last_modified => Time.now.utc.beginning_of_day, :etag => [ :foo, 123 ])
J
Joshua Peek 已提交
109 110
  end

Ł
Łukasz Strzałkowski 已提交
111 112
  def head_created
    head :created
J
Joshua Peek 已提交
113 114
  end

Ł
Łukasz Strzałkowski 已提交
115 116
  def head_created_with_application_json_content_type
    head :created, :content_type => "application/json"
117 118
  end

Ł
Łukasz Strzałkowski 已提交
119 120
  def head_ok_with_image_png_content_type
    head :ok, :content_type => "image/png"
J
Joshua Peek 已提交
121 122
  end

Ł
Łukasz Strzałkowski 已提交
123 124
  def head_with_location_header
    head :location => "/foo"
125 126
  end

Ł
Łukasz Strzałkowski 已提交
127 128
  def head_with_location_object
    head :location => Customer.new("david", 1)
J
Joshua Peek 已提交
129 130
  end

Ł
Łukasz Strzałkowski 已提交
131 132
  def head_with_symbolic_status
    head :status => params[:status].intern
J
Joshua Peek 已提交
133 134
  end

Ł
Łukasz Strzałkowski 已提交
135 136
  def head_with_integer_status
    head :status => params[:status].to_i
J
Joshua Peek 已提交
137 138
  end

Ł
Łukasz Strzałkowski 已提交
139 140
  def head_with_string_status
    head :status => params[:status]
J
Joshua Peek 已提交
141 142
  end

Ł
Łukasz Strzałkowski 已提交
143 144
  def head_with_custom_header
    head :x_custom_header => "something"
145 146
  end

Ł
Łukasz Strzałkowski 已提交
147 148
  def head_with_www_authenticate_header
    head 'WWW-Authenticate' => 'something'
149 150
  end

Ł
Łukasz Strzałkowski 已提交
151 152
  def head_with_status_code_first
    head :forbidden, :x_custom_header => "something"
J
Joshua Peek 已提交
153 154
  end

155 156 157 158 159 160 161 162 163
  def head_with_no_content
    # Fill in the headers with dummy data to make
    # sure they get removed during the testing
    response.headers["Content-Type"] = "dummy"
    response.headers["Content-Length"] = 42

    head 204
  end

Ł
Łukasz Strzałkowski 已提交
164
  private
J
Joshua Peek 已提交
165

Ł
Łukasz Strzałkowski 已提交
166 167
    def set_variable_for_layout
      @variable_for_layout = nil
J
Joshua Peek 已提交
168 169
    end

Ł
Łukasz Strzałkowski 已提交
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
    def determine_layout
      case action_name
        when "hello_world", "layout_test", "rendering_without_layout",
             "rendering_nothing_on_layout", "render_text_hello_world",
             "render_text_hello_world_with_layout",
             "hello_world_with_layout_false",
             "partial_only", "accessing_params_in_template",
             "accessing_params_in_template_with_layout",
             "render_with_explicit_template",
             "render_with_explicit_string_template",
             "update_page", "update_page_with_instance_variables"

          "layouts/standard"
        when "action_talk_to_layout", "layout_overriding_layout"
          "layouts/talk_from_action"
        when "render_implicit_html_template_from_xhr_request"
          (request.xhr? ? 'layouts/xhr' : 'layouts/standard')
      end
    end
end

class MetalTestController < ActionController::Metal
  include AbstractController::Rendering
  include ActionView::Rendering
  include ActionController::Rendering
G
Guo Xiang Tan 已提交
195 196
  include ActionController::RackDelegation

J
Joshua Peek 已提交
197

Ł
Łukasz Strzałkowski 已提交
198 199
  def accessing_logger_in_template
    render :inline =>  "<%= logger.class %>"
200
  end
201 202
end

203 204 205 206 207 208 209
class ExpiresInRenderTest < ActionController::TestCase
  tests TestController

  def test_expires_in_header
    get :conditional_hello_with_expires_in
    assert_equal "max-age=60, private", @response.headers["Cache-Control"]
  end
J
Joshua Peek 已提交
210

211
  def test_expires_in_header_with_public
212 213 214
    get :conditional_hello_with_expires_in_with_public
    assert_equal "max-age=60, public", @response.headers["Cache-Control"]
  end
J
Joshua Peek 已提交
215

216 217 218 219 220 221 222 223 224 225
  def test_expires_in_header_with_must_revalidate
    get :conditional_hello_with_expires_in_with_must_revalidate
    assert_equal "max-age=60, private, must-revalidate", @response.headers["Cache-Control"]
  end

  def test_expires_in_header_with_public_and_must_revalidate
    get :conditional_hello_with_expires_in_with_public_and_must_revalidate
    assert_equal "max-age=60, public, must-revalidate", @response.headers["Cache-Control"]
  end

226 227
  def test_expires_in_header_with_additional_headers
    get :conditional_hello_with_expires_in_with_public_with_more_keys
228
    assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"]
229
  end
J
Joshua Peek 已提交
230

231 232
  def test_expires_in_old_syntax
    get :conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax
233
    assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"]
234
  end
235 236 237 238 239

  def test_expires_now
    get :conditional_hello_with_expires_now
    assert_equal "no-cache", @response.headers["Cache-Control"]
  end
240

A
Arun Agrawal 已提交
241
  def test_expires_now_with_cache_control_headers
242
    get :conditional_hello_with_cache_control_headers
A
Arun Agrawal 已提交
243 244
    assert_match(/no-cache/, @response.headers["Cache-Control"])
    assert_match(/no-transform/, @response.headers["Cache-Control"])
245 246
  end

247 248 249 250 251 252
  def test_date_header_when_expires_in
    time = Time.mktime(2011,10,30)
    Time.stubs(:now).returns(time)
    get :conditional_hello_with_expires_in
    assert_equal Time.now.httpdate, @response.headers["Date"]
  end
253 254
end

255 256
class LastModifiedRenderTest < ActionController::TestCase
  tests TestController
257

258
  def setup
259
    super
260
    @last_modified = Time.now.utc.beginning_of_day.httpdate
261 262
  end

263 264 265
  def test_responds_with_last_modified
    get :conditional_hello
    assert_equal @last_modified, @response.headers['Last-Modified']
266 267
  end

268
  def test_request_not_modified
269
    @request.if_modified_since = @last_modified
270
    get :conditional_hello
271
    assert_equal 304, @response.status.to_i
272
    assert @response.body.blank?
273
    assert_equal @last_modified, @response.headers['Last-Modified']
274 275
  end

276 277 278 279 280 281 282
  def test_request_not_modified_but_etag_differs
    @request.if_modified_since = @last_modified
    @request.if_none_match = "234"
    get :conditional_hello
    assert_response :success
  end

283
  def test_request_modified
284
    @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
285
    get :conditional_hello
286
    assert_equal 200, @response.status.to_i
287
    assert @response.body.present?
288
    assert_equal @last_modified, @response.headers['Last-Modified']
289
  end
290

291 292 293 294 295 296 297 298 299 300

  def test_responds_with_last_modified_with_record
    get :conditional_hello_with_record
    assert_equal @last_modified, @response.headers['Last-Modified']
  end

  def test_request_not_modified_with_record
    @request.if_modified_since = @last_modified
    get :conditional_hello_with_record
    assert_equal 304, @response.status.to_i
301
    assert @response.body.blank?
302 303 304 305 306 307 308 309 310 311 312 313 314 315
    assert_equal @last_modified, @response.headers['Last-Modified']
  end

  def test_request_not_modified_but_etag_differs_with_record
    @request.if_modified_since = @last_modified
    @request.if_none_match = "234"
    get :conditional_hello_with_record
    assert_response :success
  end

  def test_request_modified_with_record
    @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
    get :conditional_hello_with_record
    assert_equal 200, @response.status.to_i
316
    assert @response.body.present?
317 318 319
    assert_equal @last_modified, @response.headers['Last-Modified']
  end

320 321 322 323 324
  def test_request_with_bang_gets_last_modified
    get :conditional_hello_with_bangs
    assert_equal @last_modified, @response.headers['Last-Modified']
    assert_response :success
  end
325

326 327 328 329 330
  def test_request_with_bang_obeys_last_modified
    @request.if_modified_since = @last_modified
    get :conditional_hello_with_bangs
    assert_response :not_modified
  end
331 332 333 334

  def test_last_modified_works_with_less_than_too
    @request.if_modified_since = 5.years.ago.httpdate
    get :conditional_hello_with_bangs
335
    assert_response :success
336
  end
337
end
338

339 340 341 342
class EtagRenderTest < ActionController::TestCase
  tests TestControllerWithExtraEtags

  def test_multiple_etags
343
    @request.if_none_match = etag(["123", 'ab', :cde, [:f]])
344 345 346 347 348 349 350
    get :fresh
    assert_response :not_modified

    @request.if_none_match = %("nomatch")
    get :fresh
    assert_response :success
  end
351 352 353 354 355 356 357 358 359 360 361

  def test_array
    @request.if_none_match = etag([%w(1 2 3), 'ab', :cde, [:f]])
    get :array
    assert_response :not_modified

    @request.if_none_match = %("nomatch")
    get :array
    assert_response :success
  end

362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
  def test_etag_reflects_template_digest
    get :with_template
    assert_response :ok
    assert_not_nil etag = @response.etag

    request.if_none_match = etag
    get :with_template
    assert_response :not_modified

    # Modify the template digest
    path = File.expand_path('../../fixtures/test/hello_world.erb', __FILE__)
    old = File.read(path)

    begin
      File.write path, 'foo'
      ActionView::Digestor.cache.clear

      request.if_none_match = etag
      get :with_template
      assert_response :ok
      assert_not_equal etag, @response.etag
    ensure
      File.write path, old
    end
  end

388 389 390
  def etag(record)
    Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(record)).inspect
  end
391 392
end

393 394 395 396 397 398 399 400
class MetalRenderTest < ActionController::TestCase
  tests MetalTestController

  def test_access_to_logger_in_view
    get :accessing_logger_in_template
    assert_equal "NilClass", @response.body
  end
end
Ł
Łukasz Strzałkowski 已提交
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491

class HeadRenderTest < ActionController::TestCase
  tests TestController

  def setup
    @request.host = "www.nextangle.com"
  end

  def test_head_created
    post :head_created
    assert @response.body.blank?
    assert_response :created
  end

  def test_head_created_with_application_json_content_type
    post :head_created_with_application_json_content_type
    assert @response.body.blank?
    assert_equal "application/json", @response.header["Content-Type"]
    assert_response :created
  end

  def test_head_ok_with_image_png_content_type
    post :head_ok_with_image_png_content_type
    assert @response.body.blank?
    assert_equal "image/png", @response.header["Content-Type"]
    assert_response :ok
  end

  def test_head_with_location_header
    get :head_with_location_header
    assert @response.body.blank?
    assert_equal "/foo", @response.headers["Location"]
    assert_response :ok
  end

  def test_head_with_location_object
    with_routing do |set|
      set.draw do
        resources :customers
        get ':controller/:action'
      end

      get :head_with_location_object
      assert @response.body.blank?
      assert_equal "http://www.nextangle.com/customers/1", @response.headers["Location"]
      assert_response :ok
    end
  end

  def test_head_with_custom_header
    get :head_with_custom_header
    assert @response.body.blank?
    assert_equal "something", @response.headers["X-Custom-Header"]
    assert_response :ok
  end

  def test_head_with_www_authenticate_header
    get :head_with_www_authenticate_header
    assert @response.body.blank?
    assert_equal "something", @response.headers["WWW-Authenticate"]
    assert_response :ok
  end

  def test_head_with_symbolic_status
    get :head_with_symbolic_status, :status => "ok"
    assert_equal 200, @response.status
    assert_response :ok

    get :head_with_symbolic_status, :status => "not_found"
    assert_equal 404, @response.status
    assert_response :not_found

    get :head_with_symbolic_status, :status => "no_content"
    assert_equal 204, @response.status
    assert !@response.headers.include?('Content-Length')
    assert_response :no_content

    Rack::Utils::SYMBOL_TO_STATUS_CODE.each do |status, code|
      get :head_with_symbolic_status, :status => status.to_s
      assert_equal code, @response.response_code
      assert_response status
    end
  end

  def test_head_with_integer_status
    Rack::Utils::HTTP_STATUS_CODES.each do |code, message|
      get :head_with_integer_status, :status => code.to_s
      assert_equal message, @response.message
    end
  end

492 493 494 495 496 497 498 499
  def test_head_with_no_content
    get :head_with_no_content

    assert_equal 204, @response.status
    assert_nil @response.headers["Content-Type"]
    assert_nil @response.headers["Content-Length"]
  end

Ł
Łukasz Strzałkowski 已提交
500 501 502 503 504 505 506 507 508 509 510 511 512 513
  def test_head_with_string_status
    get :head_with_string_status, :status => "404 Eat Dirt"
    assert_equal 404, @response.response_code
    assert_equal "Not Found", @response.message
    assert_response :not_found
  end

  def test_head_with_status_code_first
    get :head_with_status_code_first
    assert_equal 403, @response.response_code
    assert_equal "Forbidden", @response.message
    assert_equal "something", @response.headers["X-Custom-Header"]
    assert_response :forbidden
  end
514
end