render_test.rb 18.3 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 plain: "stale" if stale?(etag: '123', template: false)
14
  end
15 16

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

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

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

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

35
  before_action :set_variable_for_layout
L
lest 已提交
36

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

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

42 43 44 45 46 47 48
  def name
    nil
  end

  private :name
  helper_method :name

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

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

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

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

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
  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 已提交
87 88 89
  def conditional_hello_with_expires_in
    expires_in 60.1.seconds
    render :action => 'hello_world'
J
Joshua Peek 已提交
90 91
  end

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

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

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

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

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

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

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

128 129 130 131
  def respond_with_empty_body
    render nothing: true
  end

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

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

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

  def head_with_hash_does_not_include_status
    head warning: :deprecated
  end

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

J
Joshua Peek 已提交
240

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

246 247 248 249 250 251 252
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 已提交
253

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

259 260 261 262 263 264 265 266 267 268
  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

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

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

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

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

290 291 292 293 294 295
  def test_render_nothing_deprecated
    assert_deprecated do
      get :respond_with_empty_body
    end
  end

296 297 298 299 300 301
  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
302 303
end

304 305
class LastModifiedRenderTest < ActionController::TestCase
  tests TestController
306

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

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

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

325 326 327 328 329 330 331
  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

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

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

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

397 398 399 400 401
  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
402

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

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

416 417 418 419
class EtagRenderTest < ActionController::TestCase
  tests TestControllerWithExtraEtags

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

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

  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

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

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

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

479 480 481 482 483 484 485 486 487
class ImplicitRenderTest < ActionController::TestCase
  tests ImplicitRenderTestController

  def test_implicit_no_content_response
    get :empty_action
    assert_response :no_content
  end
end

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

501 502 503 504 505 506 507 508 509 510 511 512 513
  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 已提交
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 562 563
  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
564
    get :head_with_symbolic_status, params: { status: "ok" }
Ł
Łukasz Strzałkowski 已提交
565 566 567
    assert_equal 200, @response.status
    assert_response :ok

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

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

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

  def test_head_returns_truthy_value
    assert_nothing_raised do
      get :head_and_return
    end
  end
619
end
620 621 622 623 624

class HttpCacheForeverTest < ActionController::TestCase
  class HttpCacheForeverController < ActionController::Base
    def cache_me_forever
      http_cache_forever(public: params[:public], version: params[:version] || 'v1') do
625
        render plain: 'hello'
626 627 628 629 630 631 632 633 634 635 636 637 638 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 671 672
      end
    end
  end

  tests HttpCacheForeverController

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

  def test_cache_with_private
    get :cache_me_forever
    assert_equal "max-age=#{100.years.to_i}, private", @response.headers["Cache-Control"]
    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