render_test.rb 18.2 KB
Newer Older
1
require 'abstract_unit'
2
require 'controller/fake_models'
D
Initial  
David Heinemeier Hansson 已提交
3

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

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

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

  def with_template
    if stale? template: 'test/hello_world'
21
      render plain: 'stale'
22
    end
23
  end
24 25
end

26 27 28 29 30
class ImplicitRenderTestController < ActionController::Base
  def empty_action
  end
end

31
class TestController < ActionController::Base
P
Pratik Naik 已提交
32 33
  protect_from_forgery

34
  before_action :set_variable_for_layout
L
lest 已提交
35

J
Joshua Peek 已提交
36 37 38
  class LabellingFormBuilder < ActionView::Helpers::FormBuilder
  end

39
  layout :determine_layout
D
Initial  
David Heinemeier Hansson 已提交
40

41 42 43 44 45 46 47
  def name
    nil
  end

  private :name
  helper_method :name

48 49
  def hello_world
  end
D
Initial  
David Heinemeier Hansson 已提交
50

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

57
  def conditional_hello_with_record
58
    record = Struct.new(:updated_at, :cache_key).new(Time.now.utc.beginning_of_day, "foo/123")
59

60
    if stale?(record)
Ł
Łukasz Strzałkowski 已提交
61
      render :action => 'hello_world'
J
Joshua Peek 已提交
62 63 64
    end
  end

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
  class Collection
    def initialize(records)
      @records = records
    end

    def maximum(attribute)
      @records.max_by(&attribute).public_send(attribute)
    end
  end

  def conditional_hello_with_collection_of_records
    ts = Time.now.utc.beginning_of_day

    record = Struct.new(:updated_at, :cache_key).new(ts, "foo/123")
    old_record = Struct.new(:updated_at, :cache_key).new(ts - 1.day, "bar/123")

    if stale?(Collection.new([record, old_record]))
      render action: 'hello_world'
    end
  end

Ł
Łukasz Strzałkowski 已提交
86 87 88
  def conditional_hello_with_expires_in
    expires_in 60.1.seconds
    render :action => 'hello_world'
J
Joshua Peek 已提交
89 90
  end

Ł
Łukasz Strzałkowski 已提交
91 92 93
  def conditional_hello_with_expires_in_with_public
    expires_in 1.minute, :public => true
    render :action => 'hello_world'
J
Joshua Peek 已提交
94 95
  end

Ł
Łukasz Strzałkowski 已提交
96 97 98
  def conditional_hello_with_expires_in_with_must_revalidate
    expires_in 1.minute, :must_revalidate => true
    render :action => 'hello_world'
99 100
  end

Ł
Łukasz Strzałkowski 已提交
101 102 103
  def conditional_hello_with_expires_in_with_public_and_must_revalidate
    expires_in 1.minute, :public => true, :must_revalidate => true
    render :action => 'hello_world'
104 105
  end

Ł
Łukasz Strzałkowski 已提交
106 107 108
  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'
109 110
  end

Ł
Łukasz Strzałkowski 已提交
111 112 113
  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 已提交
114 115
  end

Ł
Łukasz Strzałkowski 已提交
116 117 118
  def conditional_hello_with_expires_now
    expires_now
    render :action => 'hello_world'
119 120
  end

Ł
Łukasz Strzałkowski 已提交
121 122 123 124
  def conditional_hello_with_cache_control_headers
    response.headers['Cache-Control'] = 'no-transform'
    expires_now
    render :action => 'hello_world'
125 126
  end

127 128 129 130
  def respond_with_empty_body
    render nothing: true
  end

Ł
Łukasz Strzałkowski 已提交
131 132
  def conditional_hello_with_bangs
    render :action => 'hello_world'
133
  end
Ł
Łukasz Strzałkowski 已提交
134
  before_action :handle_last_modified_and_etags, :only=>:conditional_hello_with_bangs
135

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

140 141 142 143 144 145 146 147
  def head_with_status_hash
    head status: :created
  end

  def head_with_hash_does_not_include_status
    head warning: :deprecated
  end

Ł
Łukasz Strzałkowski 已提交
148 149
  def head_created
    head :created
J
Joshua Peek 已提交
150 151
  end

Ł
Łukasz Strzałkowski 已提交
152 153
  def head_created_with_application_json_content_type
    head :created, :content_type => "application/json"
154 155
  end

Ł
Łukasz Strzałkowski 已提交
156 157
  def head_ok_with_image_png_content_type
    head :ok, :content_type => "image/png"
J
Joshua Peek 已提交
158 159
  end

Ł
Łukasz Strzałkowski 已提交
160
  def head_with_location_header
161
    head :ok, :location => "/foo"
162 163
  end

Ł
Łukasz Strzałkowski 已提交
164
  def head_with_location_object
165
    head :ok, :location => Customer.new("david", 1)
J
Joshua Peek 已提交
166 167
  end

Ł
Łukasz Strzałkowski 已提交
168
  def head_with_symbolic_status
169
    head params[:status].intern
J
Joshua Peek 已提交
170 171
  end

Ł
Łukasz Strzałkowski 已提交
172
  def head_with_integer_status
173
    head params[:status].to_i
J
Joshua Peek 已提交
174 175
  end

Ł
Łukasz Strzałkowski 已提交
176
  def head_with_string_status
177
    head params[:status]
J
Joshua Peek 已提交
178 179
  end

Ł
Łukasz Strzałkowski 已提交
180
  def head_with_custom_header
181
    head :ok, :x_custom_header => "something"
182 183
  end

Ł
Łukasz Strzałkowski 已提交
184
  def head_with_www_authenticate_header
185
    head :ok, 'WWW-Authenticate' => 'something'
186 187
  end

Ł
Łukasz Strzałkowski 已提交
188 189
  def head_with_status_code_first
    head :forbidden, :x_custom_header => "something"
J
Joshua Peek 已提交
190 191
  end

J
Joel Hayhurst 已提交
192 193 194 195 196
  def head_and_return
    head :ok and return
    raise 'should not reach this line'
  end

197 198 199 200 201 202 203 204 205
  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 已提交
206
  private
J
Joshua Peek 已提交
207

Ł
Łukasz Strzałkowski 已提交
208 209
    def set_variable_for_layout
      @variable_for_layout = nil
J
Joshua Peek 已提交
210 211
    end

Ł
Łukasz Strzałkowski 已提交
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
    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
J
Joshua Peek 已提交
237

Ł
Łukasz Strzałkowski 已提交
238 239
  def accessing_logger_in_template
    render :inline =>  "<%= logger.class %>"
240
  end
241 242
end

243 244 245 246 247 248 249
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 已提交
250

251
  def test_expires_in_header_with_public
252 253 254
    get :conditional_hello_with_expires_in_with_public
    assert_equal "max-age=60, public", @response.headers["Cache-Control"]
  end
J
Joshua Peek 已提交
255

256 257 258 259 260 261 262 263 264 265
  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

266 267
  def test_expires_in_header_with_additional_headers
    get :conditional_hello_with_expires_in_with_public_with_more_keys
268
    assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"]
269
  end
J
Joshua Peek 已提交
270

271 272
  def test_expires_in_old_syntax
    get :conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax
273
    assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"]
274
  end
275 276 277 278 279

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

A
Arun Agrawal 已提交
281
  def test_expires_now_with_cache_control_headers
282
    get :conditional_hello_with_cache_control_headers
A
Arun Agrawal 已提交
283 284
    assert_match(/no-cache/, @response.headers["Cache-Control"])
    assert_match(/no-transform/, @response.headers["Cache-Control"])
285 286
  end

287 288 289 290 291 292
  def test_render_nothing_deprecated
    assert_deprecated do
      get :respond_with_empty_body
    end
  end

293 294
  def test_date_header_when_expires_in
    time = Time.mktime(2011,10,30)
295 296 297 298
    Time.stub :now, time do
      get :conditional_hello_with_expires_in
      assert_equal Time.now.httpdate, @response.headers["Date"]
    end
299
  end
300 301
end

302 303
class LastModifiedRenderTest < ActionController::TestCase
  tests TestController
304

305
  def setup
306
    super
307
    @last_modified = Time.now.utc.beginning_of_day.httpdate
308 309
  end

310 311 312
  def test_responds_with_last_modified
    get :conditional_hello
    assert_equal @last_modified, @response.headers['Last-Modified']
313 314
  end

315
  def test_request_not_modified
316
    @request.if_modified_since = @last_modified
317
    get :conditional_hello
318
    assert_equal 304, @response.status.to_i
319
    assert @response.body.blank?
320
    assert_equal @last_modified, @response.headers['Last-Modified']
321 322
  end

323 324 325 326 327 328 329
  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

330
  def test_request_modified
331
    @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
332
    get :conditional_hello
333
    assert_equal 200, @response.status.to_i
334
    assert @response.body.present?
335
    assert_equal @last_modified, @response.headers['Last-Modified']
336
  end
337

338 339 340 341 342 343 344 345 346
  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
347
    assert @response.body.blank?
C
claudiob 已提交
348
    assert_not_nil @response.etag
349 350 351 352 353 354 355 356 357 358 359 360 361 362
    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
363
    assert @response.body.present?
364 365 366
    assert_equal @last_modified, @response.headers['Last-Modified']
  end

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
  def test_responds_with_last_modified_with_collection_of_records
    get :conditional_hello_with_collection_of_records
    assert_equal @last_modified, @response.headers['Last-Modified']
  end

  def test_request_not_modified_with_collection_of_records
    @request.if_modified_since = @last_modified
    get :conditional_hello_with_collection_of_records
    assert_equal 304, @response.status.to_i
    assert @response.body.blank?
    assert_equal @last_modified, @response.headers['Last-Modified']
  end

  def test_request_not_modified_but_etag_differs_with_collection_of_records
    @request.if_modified_since = @last_modified
    @request.if_none_match = "234"
    get :conditional_hello_with_collection_of_records
    assert_response :success
  end

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

395 396 397 398 399
  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
400

401 402 403 404 405
  def test_request_with_bang_obeys_last_modified
    @request.if_modified_since = @last_modified
    get :conditional_hello_with_bangs
    assert_response :not_modified
  end
406 407 408 409

  def test_last_modified_works_with_less_than_too
    @request.if_modified_since = 5.years.ago.httpdate
    get :conditional_hello_with_bangs
410
    assert_response :success
411
  end
412
end
413

414 415 416 417
class EtagRenderTest < ActionController::TestCase
  tests TestControllerWithExtraEtags

  def test_multiple_etags
418
    @request.if_none_match = etag(["123", 'ab', :cde, [:f]])
419 420 421 422 423 424 425
    get :fresh
    assert_response :not_modified

    @request.if_none_match = %("nomatch")
    get :fresh
    assert_response :success
  end
426 427 428 429 430 431 432 433 434 435 436

  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

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
  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

463 464 465
  def etag(record)
    Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(record)).inspect
  end
466 467
end

468 469 470 471 472 473 474 475
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 已提交
476

477 478 479 480 481 482 483 484 485
class ImplicitRenderTest < ActionController::TestCase
  tests ImplicitRenderTestController

  def test_implicit_no_content_response
    get :empty_action
    assert_response :no_content
  end
end

Ł
Łukasz Strzałkowski 已提交
486 487 488 489 490 491 492 493 494 495 496 497 498
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

499 500 501 502 503 504 505 506 507 508 509 510 511
  def test_passing_hash_to_head_as_first_parameter_deprecated
    assert_deprecated do
      get :head_with_status_hash
    end
  end

  def test_head_with_default_value_is_deprecated
    assert_deprecated do
      get :head_with_hash_does_not_include_status
      assert_response :ok
    end
  end

Ł
Łukasz Strzałkowski 已提交
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
  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
562
    get :head_with_symbolic_status, params: { status: "ok" }
Ł
Łukasz Strzałkowski 已提交
563 564 565
    assert_equal 200, @response.status
    assert_response :ok

566
    get :head_with_symbolic_status, params: { status: "not_found" }
Ł
Łukasz Strzałkowski 已提交
567 568 569
    assert_equal 404, @response.status
    assert_response :not_found

570
    get :head_with_symbolic_status, params: { status: "no_content" }
Ł
Łukasz Strzałkowski 已提交
571 572 573 574 575
    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|
576
      get :head_with_symbolic_status, params: { status: status.to_s }
Ł
Łukasz Strzałkowski 已提交
577 578 579 580 581 582 583
      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|
584
      get :head_with_integer_status, params: { status: code.to_s }
Ł
Łukasz Strzałkowski 已提交
585 586 587 588
      assert_equal message, @response.message
    end
  end

589 590 591 592 593 594 595 596
  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 已提交
597
  def test_head_with_string_status
598
    get :head_with_string_status, params: { status: "404 Eat Dirt" }
Ł
Łukasz Strzałkowski 已提交
599 600 601 602 603 604 605 606 607 608 609 610
    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
J
Joel Hayhurst 已提交
611 612 613 614 615 616

  def test_head_returns_truthy_value
    assert_nothing_raised do
      get :head_and_return
    end
  end
617
end
618 619 620 621 622

class HttpCacheForeverTest < ActionController::TestCase
  class HttpCacheForeverController < ActionController::Base
    def cache_me_forever
      http_cache_forever(public: params[:public], version: params[:version] || 'v1') do
623
        render plain: 'hello'
624 625 626 627 628 629 630 631
      end
    end
  end

  tests HttpCacheForeverController

  def test_cache_with_public
    get :cache_me_forever, params: {public: true}
632
    assert_equal "max-age=#{100.years}, public", @response.headers["Cache-Control"]
633 634 635 636 637
    assert_not_nil @response.etag
  end

  def test_cache_with_private
    get :cache_me_forever
638
    assert_equal "max-age=#{100.years}, private", @response.headers["Cache-Control"]
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
    assert_not_nil @response.etag
    assert_response :success
  end

  def test_cache_response_code_with_if_modified_since
    get :cache_me_forever
    assert_response :success
    @request.if_modified_since = @response.headers['Last-Modified']
    get :cache_me_forever
    assert_response :not_modified
  end

  def test_cache_response_code_with_etag
    get :cache_me_forever
    assert_response :success
    @request.if_modified_since = @response.headers['Last-Modified']
    @request.if_none_match = @response.etag

    get :cache_me_forever
    assert_response :not_modified
    @request.if_modified_since = @response.headers['Last-Modified']
    @request.if_none_match = @response.etag

    get :cache_me_forever, params: {version: 'v2'}
    assert_response :success
    @request.if_modified_since = @response.headers['Last-Modified']
    @request.if_none_match = @response.etag

    get :cache_me_forever, params: {version: 'v2'}
    assert_response :not_modified
  end
end