aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/test/controller/render_test.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/test/controller/render_test.rb')
-rw-r--r--actionpack/test/controller/render_test.rb843
1 files changed, 843 insertions, 0 deletions
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
new file mode 100644
index 0000000000..fc21543049
--- /dev/null
+++ b/actionpack/test/controller/render_test.rb
@@ -0,0 +1,843 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+require "controller/fake_models"
+
+class TestControllerWithExtraEtags < ActionController::Base
+ def self.controller_name; "test"; end
+ def self.controller_path; "test"; end
+
+ etag { nil }
+ etag { "ab" }
+ etag { :cde }
+ etag { [:f] }
+ etag { nil }
+
+ def fresh
+ render plain: "stale" if stale?(etag: "123", template: false)
+ end
+
+ def array
+ render plain: "stale" if stale?(etag: %w(1 2 3), template: false)
+ end
+
+ def strong
+ render plain: "stale" if stale?(strong_etag: "strong", template: false)
+ end
+
+ def with_template
+ if stale? template: "test/hello_world"
+ render plain: "stale"
+ end
+ end
+
+ def with_implicit_template
+ fresh_when(etag: "123")
+ end
+end
+
+class ImplicitRenderTestController < ActionController::Base
+ def empty_action
+ end
+
+ def empty_action_with_template
+ end
+end
+
+module Namespaced
+ class ImplicitRenderTestController < ActionController::Base
+ def hello_world
+ fresh_when(etag: "abc")
+ end
+ end
+end
+
+class TestController < ActionController::Base
+ protect_from_forgery
+
+ before_action :set_variable_for_layout
+
+ class LabellingFormBuilder < ActionView::Helpers::FormBuilder
+ end
+
+ layout :determine_layout
+
+ def name
+ nil
+ end
+
+ private :name
+ helper_method :name
+
+ def hello_world
+ end
+
+ def conditional_hello
+ if stale?(last_modified: Time.now.utc.beginning_of_day, etag: [:foo, 123])
+ render action: "hello_world"
+ end
+ end
+
+ def conditional_hello_with_record
+ record = Struct.new(:updated_at, :cache_key).new(Time.now.utc.beginning_of_day, "foo/123")
+
+ if stale?(record)
+ render action: "hello_world"
+ end
+ end
+
+ def dynamic_render
+ render params[:id] # => String, AC::Params
+ end
+
+ def dynamic_render_permit
+ render params[:id].permit(:file)
+ end
+
+ def dynamic_render_with_file
+ # This is extremely bad, but should be possible to do.
+ file = params[:id] # => String, AC::Params
+ render file: file
+ end
+
+ 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
+
+ def conditional_hello_with_expires_in
+ expires_in 60.1.seconds
+ render action: "hello_world"
+ end
+
+ def conditional_hello_with_expires_in_with_public
+ expires_in 1.minute, public: true
+ render action: "hello_world"
+ end
+
+ def conditional_hello_with_expires_in_with_must_revalidate
+ expires_in 1.minute, must_revalidate: true
+ render action: "hello_world"
+ end
+
+ def conditional_hello_with_expires_in_with_public_and_must_revalidate
+ expires_in 1.minute, public: true, must_revalidate: true
+ render action: "hello_world"
+ end
+
+ 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"
+ end
+
+ 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"
+ end
+
+ def conditional_hello_with_expires_now
+ expires_now
+ render action: "hello_world"
+ end
+
+ def conditional_hello_with_cache_control_headers
+ response.headers["Cache-Control"] = "no-transform"
+ expires_now
+ render action: "hello_world"
+ end
+
+ def conditional_hello_with_expires_and_confliciting_cache_control_headers
+ response.headers["Cache-Control"] = "no-cache, must-revalidate"
+ expires_now
+ render action: "hello_world"
+ end
+
+ def conditional_hello_without_expires_and_confliciting_cache_control_headers
+ response.headers["Cache-Control"] = "no-cache, must-revalidate"
+ render action: "hello_world"
+ end
+
+ def conditional_hello_with_bangs
+ render action: "hello_world"
+ end
+ before_action :handle_last_modified_and_etags, only: :conditional_hello_with_bangs
+
+ def handle_last_modified_and_etags
+ fresh_when(last_modified: Time.now.utc.beginning_of_day, etag: [ :foo, 123 ])
+ end
+
+ def head_created
+ head :created
+ end
+
+ def head_created_with_application_json_content_type
+ head :created, content_type: "application/json"
+ end
+
+ def head_ok_with_image_png_content_type
+ head :ok, content_type: "image/png"
+ end
+
+ def head_with_location_header
+ head :ok, location: "/foo"
+ end
+
+ def head_with_location_object
+ head :ok, location: Customer.new("david", 1)
+ end
+
+ def head_with_symbolic_status
+ head params[:status].intern
+ end
+
+ def head_with_integer_status
+ head params[:status].to_i
+ end
+
+ def head_with_string_status
+ head params[:status]
+ end
+
+ def head_with_custom_header
+ head :ok, x_custom_header: "something"
+ end
+
+ def head_with_www_authenticate_header
+ head :ok, "WWW-Authenticate" => "something"
+ end
+
+ def head_with_status_code_first
+ head :forbidden, x_custom_header: "something"
+ end
+
+ def head_and_return
+ head(:ok) && return
+ raise "should not reach this line"
+ end
+
+ 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
+
+ private
+
+ def set_variable_for_layout
+ @variable_for_layout = nil
+ end
+
+ 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
+
+module TemplateModificationHelper
+ private
+ def modify_template(name)
+ path = File.expand_path("../fixtures/#{name}.erb", __dir__)
+ original = File.read(path)
+ File.write(path, "#{original} Modified!")
+ ActionView::LookupContext::DetailsKey.clear
+ yield
+ ensure
+ File.write(path, original)
+ end
+end
+
+class MetalTestController < ActionController::Metal
+ include AbstractController::Rendering
+ include ActionView::Rendering
+ include ActionController::Rendering
+
+ def accessing_logger_in_template
+ render inline: "<%= logger.class %>"
+ end
+end
+
+class ExpiresInRenderTest < ActionController::TestCase
+ tests TestController
+
+ def setup
+ super
+ ActionController::Base.view_paths.paths.each(&:clear_cache)
+ end
+
+ def test_dynamic_render_with_file
+ # This is extremely bad, but should be possible to do.
+ assert File.exist?(File.expand_path("../../test/abstract_unit.rb", __dir__))
+ response = get :dynamic_render_with_file, params: { id: '../\\../test/abstract_unit.rb' }
+ assert_equal File.read(File.expand_path("../../test/abstract_unit.rb", __dir__)),
+ response.body
+ end
+
+ def test_dynamic_render_with_absolute_path
+ file = Tempfile.new("name")
+ file.write "secrets!"
+ file.flush
+ assert_raises ActionView::MissingTemplate do
+ get :dynamic_render, params: { id: file.path }
+ end
+ ensure
+ file.close
+ file.unlink
+ end
+
+ def test_dynamic_render
+ assert File.exist?(File.expand_path("../../test/abstract_unit.rb", __dir__))
+ assert_raises ActionView::MissingTemplate do
+ get :dynamic_render, params: { id: '../\\../test/abstract_unit.rb' }
+ end
+ end
+
+ def test_permitted_dynamic_render_file_hash
+ assert File.exist?(File.expand_path("../../test/abstract_unit.rb", __dir__))
+ response = get :dynamic_render_permit, params: { id: { file: '../\\../test/abstract_unit.rb' } }
+ assert_equal File.read(File.expand_path("../../test/abstract_unit.rb", __dir__)),
+ response.body
+ end
+
+ def test_dynamic_render_file_hash
+ assert_raises ArgumentError do
+ get :dynamic_render, params: { id: { file: '../\\../test/abstract_unit.rb' } }
+ end
+ end
+
+ def test_expires_in_header
+ get :conditional_hello_with_expires_in
+ assert_equal "max-age=60, private", @response.headers["Cache-Control"]
+ end
+
+ def test_expires_in_header_with_public
+ get :conditional_hello_with_expires_in_with_public
+ assert_equal "max-age=60, public", @response.headers["Cache-Control"]
+ end
+
+ 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
+
+ def test_expires_in_header_with_additional_headers
+ get :conditional_hello_with_expires_in_with_public_with_more_keys
+ assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"]
+ end
+
+ def test_expires_in_old_syntax
+ get :conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax
+ assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"]
+ end
+
+ def test_expires_now
+ get :conditional_hello_with_expires_now
+ assert_equal "no-cache", @response.headers["Cache-Control"]
+ end
+
+ def test_expires_now_with_cache_control_headers
+ get :conditional_hello_with_cache_control_headers
+ assert_match(/no-cache/, @response.headers["Cache-Control"])
+ assert_match(/no-transform/, @response.headers["Cache-Control"])
+ end
+
+ def test_expires_now_with_conflicting_cache_control_headers
+ get :conditional_hello_with_expires_and_confliciting_cache_control_headers
+ assert_equal "no-cache", @response.headers["Cache-Control"]
+ end
+
+ def test_no_expires_now_with_conflicting_cache_control_headers
+ get :conditional_hello_without_expires_and_confliciting_cache_control_headers
+ assert_equal "no-cache", @response.headers["Cache-Control"]
+ end
+
+ def test_date_header_when_expires_in
+ time = Time.mktime(2011, 10, 30)
+ Time.stub :now, time do
+ get :conditional_hello_with_expires_in
+ assert_equal Time.now.httpdate, @response.headers["Date"]
+ end
+ end
+end
+
+class LastModifiedRenderTest < ActionController::TestCase
+ tests TestController
+
+ def setup
+ super
+ @last_modified = Time.now.utc.beginning_of_day.httpdate
+ end
+
+ def test_responds_with_last_modified
+ get :conditional_hello
+ assert_equal @last_modified, @response.headers["Last-Modified"]
+ end
+
+ def test_request_not_modified
+ @request.if_modified_since = @last_modified
+ get :conditional_hello
+ assert_equal 304, @response.status.to_i
+ assert_predicate @response.body, :blank?
+ assert_equal @last_modified, @response.headers["Last-Modified"]
+ end
+
+ 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
+
+ def test_request_modified
+ @request.if_modified_since = "Thu, 16 Jul 2008 00:00:00 GMT"
+ get :conditional_hello
+ assert_equal 200, @response.status.to_i
+ assert_predicate @response.body, :present?
+ assert_equal @last_modified, @response.headers["Last-Modified"]
+ end
+
+ 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
+ assert_predicate @response.body, :blank?
+ assert_not_nil @response.etag
+ 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
+ assert_predicate @response.body, :present?
+ assert_equal @last_modified, @response.headers["Last-Modified"]
+ end
+
+ 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_predicate @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_predicate @response.body, :present?
+ assert_equal @last_modified, @response.headers["Last-Modified"]
+ end
+
+ 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
+
+ def test_request_with_bang_obeys_last_modified
+ @request.if_modified_since = @last_modified
+ get :conditional_hello_with_bangs
+ assert_response :not_modified
+ end
+
+ def test_last_modified_works_with_less_than_too
+ @request.if_modified_since = 5.years.ago.httpdate
+ get :conditional_hello_with_bangs
+ assert_response :success
+ end
+end
+
+class EtagRenderTest < ActionController::TestCase
+ tests TestControllerWithExtraEtags
+ include TemplateModificationHelper
+
+ def test_strong_etag
+ @request.if_none_match = strong_etag(["strong", "ab", :cde, [:f]])
+ get :strong
+ assert_response :not_modified
+
+ @request.if_none_match = "*"
+ get :strong
+ assert_response :not_modified
+
+ @request.if_none_match = '"strong"'
+ get :strong
+ assert_response :ok
+
+ @request.if_none_match = weak_etag(["strong", "ab", :cde, [:f]])
+ get :strong
+ assert_response :ok
+ end
+
+ def test_multiple_etags
+ @request.if_none_match = weak_etag(["123", "ab", :cde, [:f]])
+ get :fresh
+ assert_response :not_modified
+
+ @request.if_none_match = %("nomatch")
+ get :fresh
+ assert_response :success
+ end
+
+ def test_array
+ @request.if_none_match = weak_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
+
+ 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_template("test/hello_world") do
+ request.if_none_match = etag
+ get :with_template
+ assert_response :ok
+ assert_not_equal etag, @response.etag
+ end
+ end
+
+ def test_etag_reflects_implicit_template_digest
+ get :with_implicit_template
+ assert_response :ok
+ assert_not_nil etag = @response.etag
+
+ request.if_none_match = etag
+ get :with_implicit_template
+ assert_response :not_modified
+
+ modify_template("test/with_implicit_template") do
+ request.if_none_match = etag
+ get :with_implicit_template
+ assert_response :ok
+ assert_not_equal etag, @response.etag
+ end
+ end
+
+ private
+ def weak_etag(record)
+ "W/#{strong_etag record}"
+ end
+
+ def strong_etag(record)
+ %("#{ActiveSupport::Digest.hexdigest(ActiveSupport::Cache.expand_cache_key(record))}")
+ end
+end
+
+class NamespacedEtagRenderTest < ActionController::TestCase
+ tests Namespaced::ImplicitRenderTestController
+ include TemplateModificationHelper
+
+ def test_etag_reflects_template_digest
+ get :hello_world
+ assert_response :ok
+ assert_not_nil etag = @response.etag
+
+ request.if_none_match = etag
+ get :hello_world
+ assert_response :not_modified
+
+ modify_template("namespaced/implicit_render_test/hello_world") do
+ request.if_none_match = etag
+ get :hello_world
+ assert_response :ok
+ assert_not_equal etag, @response.etag
+ end
+ end
+end
+
+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
+
+class ActionControllerRenderTest < ActionController::TestCase
+ class MinimalController < ActionController::Metal
+ include AbstractController::Rendering
+ include ActionController::Rendering
+ end
+
+ def test_direct_render_to_string_with_body
+ mc = MinimalController.new
+ assert_equal "Hello world!", mc.render_to_string(body: ["Hello world!"])
+ end
+end
+
+class ActionControllerBaseRenderTest < ActionController::TestCase
+ def test_direct_render_to_string
+ ac = ActionController::Base.new()
+ assert_equal "Hello world!", ac.render_to_string(template: "test/hello_world")
+ end
+end
+
+class ImplicitRenderTest < ActionController::TestCase
+ tests ImplicitRenderTestController
+
+ def test_implicit_no_content_response_as_browser
+ assert_raises(ActionController::UnknownFormat) do
+ get :empty_action
+ end
+ end
+
+ def test_implicit_no_content_response_as_xhr
+ get :empty_action, xhr: true
+ assert_response :no_content
+ end
+
+ def test_implicit_success_response_with_right_format
+ get :empty_action_with_template
+ assert_equal "<h1>Empty action rendered this implicitly.</h1>\n", @response.body
+ assert_response :success
+ end
+
+ def test_implicit_unknown_format_response
+ assert_raises(ActionController::UnknownFormat) do
+ get :empty_action_with_template, format: "json"
+ end
+ end
+end
+
+class HeadRenderTest < ActionController::TestCase
+ tests TestController
+
+ def setup
+ @request.host = "www.nextangle.com"
+ end
+
+ def test_head_created
+ post :head_created
+ assert_predicate @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_predicate @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_predicate @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_predicate @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
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action"
+ end
+ end
+
+ get :head_with_location_object
+ assert_predicate @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_predicate @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_predicate @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, params: { status: "ok" }
+ assert_equal 200, @response.status
+ assert_response :ok
+
+ get :head_with_symbolic_status, params: { status: "not_found" }
+ assert_equal 404, @response.status
+ assert_response :not_found
+
+ get :head_with_symbolic_status, params: { status: "no_content" }
+ assert_equal 204, @response.status
+ assert_not_includes @response.headers, "Content-Length"
+ assert_response :no_content
+
+ Rack::Utils::SYMBOL_TO_STATUS_CODE.each do |status, code|
+ get :head_with_symbolic_status, params: { 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, params: { status: code.to_s }
+ assert_equal message, @response.message
+ end
+ end
+
+ 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
+
+ def test_head_with_string_status
+ get :head_with_string_status, params: { 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
+
+ def test_head_returns_truthy_value
+ assert_nothing_raised do
+ get :head_and_return
+ end
+ end
+end
+
+class HttpCacheForeverTest < ActionController::TestCase
+ class HttpCacheForeverController < ActionController::Base
+ def cache_me_forever
+ http_cache_forever(public: params[:public]) do
+ render plain: "hello"
+ end
+ end
+ end
+
+ tests HttpCacheForeverController
+
+ def test_cache_with_public
+ get :cache_me_forever, params: { public: true }
+ assert_response :ok
+ assert_equal "max-age=#{100.years}, public", @response.headers["Cache-Control"]
+ assert_not_nil @response.etag
+ assert_predicate @response, :weak_etag?
+ end
+
+ def test_cache_with_private
+ get :cache_me_forever
+ assert_response :ok
+ assert_equal "max-age=#{100.years}, private", @response.headers["Cache-Control"]
+ assert_not_nil @response.etag
+ assert_predicate @response, :weak_etag?
+ end
+
+ def test_cache_response_code_with_if_modified_since
+ get :cache_me_forever
+ assert_response :ok
+
+ @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 :ok
+
+ @request.if_none_match = @response.etag
+ get :cache_me_forever
+ assert_response :not_modified
+ end
+end