render_test.rb 17.2 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

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

Ł
Łukasz Strzałkowski 已提交
87 88 89
  def conditional_hello_with_expires_in_with_public
    expires_in 1.minute, :public => true
    render :action => 'hello_world'
J
Joshua Peek 已提交
90 91
  end

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

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

Ł
Łukasz Strzałkowski 已提交
102 103 104
  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'
105 106
  end

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

Ł
Łukasz Strzałkowski 已提交
112 113 114
  def conditional_hello_with_expires_now
    expires_now
    render :action => 'hello_world'
115 116
  end

Ł
Łukasz Strzałkowski 已提交
117 118 119 120
  def conditional_hello_with_cache_control_headers
    response.headers['Cache-Control'] = 'no-transform'
    expires_now
    render :action => 'hello_world'
121 122
  end

Ł
Łukasz Strzałkowski 已提交
123 124
  def conditional_hello_with_bangs
    render :action => 'hello_world'
125
  end
Ł
Łukasz Strzałkowski 已提交
126
  before_action :handle_last_modified_and_etags, :only=>:conditional_hello_with_bangs
127

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

Ł
Łukasz Strzałkowski 已提交
132 133
  def head_created
    head :created
J
Joshua Peek 已提交
134 135
  end

Ł
Łukasz Strzałkowski 已提交
136 137
  def head_created_with_application_json_content_type
    head :created, :content_type => "application/json"
138 139
  end

Ł
Łukasz Strzałkowski 已提交
140 141
  def head_ok_with_image_png_content_type
    head :ok, :content_type => "image/png"
J
Joshua Peek 已提交
142 143
  end

Ł
Łukasz Strzałkowski 已提交
144 145
  def head_with_location_header
    head :location => "/foo"
146 147
  end

Ł
Łukasz Strzałkowski 已提交
148 149
  def head_with_location_object
    head :location => Customer.new("david", 1)
J
Joshua Peek 已提交
150 151
  end

Ł
Łukasz Strzałkowski 已提交
152 153
  def head_with_symbolic_status
    head :status => params[:status].intern
J
Joshua Peek 已提交
154 155
  end

Ł
Łukasz Strzałkowski 已提交
156 157
  def head_with_integer_status
    head :status => params[:status].to_i
J
Joshua Peek 已提交
158 159
  end

Ł
Łukasz Strzałkowski 已提交
160 161
  def head_with_string_status
    head :status => params[:status]
J
Joshua Peek 已提交
162 163
  end

Ł
Łukasz Strzałkowski 已提交
164 165
  def head_with_custom_header
    head :x_custom_header => "something"
166 167
  end

Ł
Łukasz Strzałkowski 已提交
168 169
  def head_with_www_authenticate_header
    head 'WWW-Authenticate' => 'something'
170 171
  end

Ł
Łukasz Strzałkowski 已提交
172 173
  def head_with_status_code_first
    head :forbidden, :x_custom_header => "something"
J
Joshua Peek 已提交
174 175
  end

176 177 178 179 180 181 182 183 184
  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 已提交
185
  private
J
Joshua Peek 已提交
186

Ł
Łukasz Strzałkowski 已提交
187 188
    def set_variable_for_layout
      @variable_for_layout = nil
J
Joshua Peek 已提交
189 190
    end

Ł
Łukasz Strzałkowski 已提交
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
    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 已提交
216 217
  include ActionController::RackDelegation

J
Joshua Peek 已提交
218

Ł
Łukasz Strzałkowski 已提交
219 220
  def accessing_logger_in_template
    render :inline =>  "<%= logger.class %>"
221
  end
222 223
end

224 225 226 227 228 229 230
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 已提交
231

232
  def test_expires_in_header_with_public
233 234 235
    get :conditional_hello_with_expires_in_with_public
    assert_equal "max-age=60, public", @response.headers["Cache-Control"]
  end
J
Joshua Peek 已提交
236

237 238 239 240 241 242 243 244 245 246
  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

247 248
  def test_expires_in_header_with_additional_headers
    get :conditional_hello_with_expires_in_with_public_with_more_keys
249
    assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"]
250
  end
J
Joshua Peek 已提交
251

252 253
  def test_expires_in_old_syntax
    get :conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax
254
    assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"]
255
  end
256 257 258 259 260

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

A
Arun Agrawal 已提交
262
  def test_expires_now_with_cache_control_headers
263
    get :conditional_hello_with_cache_control_headers
A
Arun Agrawal 已提交
264 265
    assert_match(/no-cache/, @response.headers["Cache-Control"])
    assert_match(/no-transform/, @response.headers["Cache-Control"])
266 267
  end

268 269 270 271 272 273
  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
274 275
end

276 277
class LastModifiedRenderTest < ActionController::TestCase
  tests TestController
278

279
  def setup
280
    super
281
    @last_modified = Time.now.utc.beginning_of_day.httpdate
282 283
  end

284 285 286
  def test_responds_with_last_modified
    get :conditional_hello
    assert_equal @last_modified, @response.headers['Last-Modified']
287 288
  end

289
  def test_request_not_modified
290
    @request.if_modified_since = @last_modified
291
    get :conditional_hello
292
    assert_equal 304, @response.status.to_i
293
    assert @response.body.blank?
294
    assert_equal @last_modified, @response.headers['Last-Modified']
295 296
  end

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

304
  def test_request_modified
305
    @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
306
    get :conditional_hello
307
    assert_equal 200, @response.status.to_i
308
    assert @response.body.present?
309
    assert_equal @last_modified, @response.headers['Last-Modified']
310
  end
311

312 313 314 315 316 317 318 319 320
  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
321
    assert @response.body.blank?
C
claudiob 已提交
322
    assert_not_nil @response.etag
323 324 325 326 327 328 329 330 331 332 333 334 335 336
    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
337
    assert @response.body.present?
338 339 340
    assert_equal @last_modified, @response.headers['Last-Modified']
  end

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
  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

369 370 371 372 373
  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
374

375 376 377 378 379
  def test_request_with_bang_obeys_last_modified
    @request.if_modified_since = @last_modified
    get :conditional_hello_with_bangs
    assert_response :not_modified
  end
380 381 382 383

  def test_last_modified_works_with_less_than_too
    @request.if_modified_since = 5.years.ago.httpdate
    get :conditional_hello_with_bangs
384
    assert_response :success
385
  end
386
end
387

388 389 390 391
class EtagRenderTest < ActionController::TestCase
  tests TestControllerWithExtraEtags

  def test_multiple_etags
392
    @request.if_none_match = etag(["123", 'ab', :cde, [:f]])
393 394 395 396 397 398 399
    get :fresh
    assert_response :not_modified

    @request.if_none_match = %("nomatch")
    get :fresh
    assert_response :success
  end
400 401 402 403 404 405 406 407 408 409 410

  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

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

437 438 439
  def etag(record)
    Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(record)).inspect
  end
440 441
end

442 443 444 445 446 447 448 449
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 已提交
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 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513

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
514
    get :head_with_symbolic_status, params: { status: "ok" }
Ł
Łukasz Strzałkowski 已提交
515 516 517
    assert_equal 200, @response.status
    assert_response :ok

518
    get :head_with_symbolic_status, params: { status: "not_found" }
Ł
Łukasz Strzałkowski 已提交
519 520 521
    assert_equal 404, @response.status
    assert_response :not_found

522
    get :head_with_symbolic_status, params: { status: "no_content" }
Ł
Łukasz Strzałkowski 已提交
523 524 525 526 527
    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|
528
      get :head_with_symbolic_status, params: { status: status.to_s }
Ł
Łukasz Strzałkowski 已提交
529 530 531 532 533 534 535
      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|
536
      get :head_with_integer_status, params: { status: code.to_s }
Ł
Łukasz Strzałkowski 已提交
537 538 539 540
      assert_equal message, @response.message
    end
  end

541 542 543 544 545 546 547 548
  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 已提交
549
  def test_head_with_string_status
550
    get :head_with_string_status, params: { status: "404 Eat Dirt" }
Ł
Łukasz Strzałkowski 已提交
551 552 553 554 555 556 557 558 559 560 561 562
    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
563
end
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616

class HttpCacheForeverTest < ActionController::TestCase
  class HttpCacheForeverController < ActionController::Base
    def cache_me_forever
      http_cache_forever(public: params[:public], version: params[:version] || 'v1') do
        render text: 'hello'
      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