mirror of
https://github.com/github/rails.git
synced 2026-02-04 11:15:08 -05:00
Make ActionCaching more aware of different mimetype formats. It will now use request.format to look up the cache type, in addition to the path extension. When expiring caches, the request format no longer affects which cache is expired. Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
672 lines
19 KiB
Ruby
672 lines
19 KiB
Ruby
require 'fileutils'
|
|
require 'abstract_unit'
|
|
require "active_support/cache/memory_store"
|
|
|
|
CACHE_DIR = 'test_cache'
|
|
# Don't change '/../temp/' cavalierly or you might hose something you don't want hosed
|
|
FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR)
|
|
ActionController::Base.page_cache_directory = FILE_STORE_PATH
|
|
ActionController::Base.cache_store = :file_store, FILE_STORE_PATH
|
|
ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/' ]
|
|
|
|
class PageCachingTestController < ActionController::Base
|
|
caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? }
|
|
caches_page :found, :not_found
|
|
|
|
def ok
|
|
head :ok
|
|
end
|
|
|
|
def no_content
|
|
head :no_content
|
|
end
|
|
|
|
def found
|
|
redirect_to :action => 'ok'
|
|
end
|
|
|
|
def not_found
|
|
head :not_found
|
|
end
|
|
|
|
def custom_path
|
|
render :text => "Super soaker"
|
|
cache_page("Super soaker", "/index.html")
|
|
end
|
|
|
|
def expire_custom_path
|
|
expire_page("/index.html")
|
|
head :ok
|
|
end
|
|
|
|
def trailing_slash
|
|
render :text => "Sneak attack"
|
|
end
|
|
end
|
|
|
|
class PageCachingTest < Test::Unit::TestCase
|
|
def setup
|
|
ActionController::Base.perform_caching = true
|
|
|
|
ActionController::Routing::Routes.draw do |map|
|
|
map.main '', :controller => 'posts'
|
|
map.resources :posts
|
|
map.connect ':controller/:action/:id'
|
|
end
|
|
|
|
@request = ActionController::TestRequest.new
|
|
@request.host = 'hostname.com'
|
|
|
|
@response = ActionController::TestResponse.new
|
|
@controller = PageCachingTestController.new
|
|
|
|
@params = {:controller => 'posts', :action => 'index', :only_path => true, :skip_relative_url_root => true}
|
|
@rewriter = ActionController::UrlRewriter.new(@request, @params)
|
|
|
|
FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
|
|
FileUtils.mkdir_p(FILE_STORE_PATH)
|
|
end
|
|
|
|
def teardown
|
|
FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
|
|
|
|
ActionController::Base.perform_caching = false
|
|
end
|
|
|
|
def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route
|
|
@params[:format] = 'rss'
|
|
assert_equal '/posts.rss', @rewriter.rewrite(@params)
|
|
@params[:format] = nil
|
|
assert_equal '/', @rewriter.rewrite(@params)
|
|
end
|
|
|
|
def test_should_cache_get_with_ok_status
|
|
get :ok
|
|
assert_response :ok
|
|
assert_page_cached :ok, "get with ok status should have been cached"
|
|
end
|
|
|
|
def test_should_cache_with_custom_path
|
|
get :custom_path
|
|
assert File.exist?("#{FILE_STORE_PATH}/index.html")
|
|
end
|
|
|
|
def test_should_expire_cache_with_custom_path
|
|
get :custom_path
|
|
assert File.exist?("#{FILE_STORE_PATH}/index.html")
|
|
|
|
get :expire_custom_path
|
|
assert !File.exist?("#{FILE_STORE_PATH}/index.html")
|
|
end
|
|
|
|
def test_should_cache_without_trailing_slash_on_url
|
|
@controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash'
|
|
assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
|
|
end
|
|
|
|
def test_should_cache_with_trailing_slash_on_url
|
|
@controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash/'
|
|
assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
|
|
end
|
|
|
|
uses_mocha("should_cache_ok_at_custom_path") do
|
|
def test_should_cache_ok_at_custom_path
|
|
@request.expects(:path).returns("/index.html")
|
|
get :ok
|
|
assert_response :ok
|
|
assert File.exist?("#{FILE_STORE_PATH}/index.html")
|
|
end
|
|
end
|
|
|
|
[:ok, :no_content, :found, :not_found].each do |status|
|
|
[:get, :post, :put, :delete].each do |method|
|
|
unless method == :get and status == :ok
|
|
define_method "test_shouldnt_cache_#{method}_with_#{status}_status" do
|
|
@request.env['REQUEST_METHOD'] = method.to_s.upcase
|
|
process status
|
|
assert_response status
|
|
assert_page_not_cached status, "#{method} with #{status} status shouldn't have been cached"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_page_caching_conditional_options
|
|
@request.env['HTTP_ACCEPT'] = 'application/json'
|
|
get :ok
|
|
assert_page_not_cached :ok
|
|
end
|
|
|
|
private
|
|
def assert_page_cached(action, message = "#{action} should have been cached")
|
|
assert page_cached?(action), message
|
|
end
|
|
|
|
def assert_page_not_cached(action, message = "#{action} shouldn't have been cached")
|
|
assert !page_cached?(action), message
|
|
end
|
|
|
|
def page_cached?(action)
|
|
File.exist? "#{FILE_STORE_PATH}/page_caching_test/#{action}.html"
|
|
end
|
|
end
|
|
|
|
|
|
class ActionCachingTestController < ActionController::Base
|
|
caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| !c.request.format.json? }
|
|
caches_action :show, :cache_path => 'http://test.host/custom/show'
|
|
caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" }
|
|
caches_action :with_layout
|
|
caches_action :layout_false, :layout => false
|
|
|
|
layout 'talk_from_action.erb'
|
|
|
|
def index
|
|
@cache_this = MockTime.now.to_f.to_s
|
|
render :text => @cache_this
|
|
end
|
|
|
|
def redirected
|
|
redirect_to :action => 'index'
|
|
end
|
|
|
|
def forbidden
|
|
render :text => "Forbidden"
|
|
headers["Status"] = "403 Forbidden"
|
|
end
|
|
|
|
def with_layout
|
|
@cache_this = MockTime.now.to_f.to_s
|
|
render :text => @cache_this, :layout => true
|
|
end
|
|
|
|
alias_method :show, :index
|
|
alias_method :edit, :index
|
|
alias_method :destroy, :index
|
|
alias_method :layout_false, :with_layout
|
|
|
|
def expire
|
|
expire_action :controller => 'action_caching_test', :action => 'index'
|
|
render :nothing => true
|
|
end
|
|
def expire_xml
|
|
expire_action :controller => 'action_caching_test', :action => 'index', :format => 'xml'
|
|
render :nothing => true
|
|
end
|
|
end
|
|
|
|
class MockTime < Time
|
|
# Let Time spicy to assure that Time.now != Time.now
|
|
def to_f
|
|
super+rand
|
|
end
|
|
end
|
|
|
|
class ActionCachingMockController
|
|
attr_accessor :mock_url_for
|
|
attr_accessor :mock_path
|
|
|
|
def initialize
|
|
yield self if block_given?
|
|
end
|
|
|
|
def url_for(*args)
|
|
@mock_url_for
|
|
end
|
|
|
|
def request
|
|
mocked_path = @mock_path
|
|
Object.new.instance_eval(<<-EVAL)
|
|
def path; '#{@mock_path}' end
|
|
def format; 'all' end
|
|
self
|
|
EVAL
|
|
end
|
|
end
|
|
|
|
class ActionCacheTest < Test::Unit::TestCase
|
|
def setup
|
|
reset!
|
|
FileUtils.mkdir_p(FILE_STORE_PATH)
|
|
@path_class = ActionController::Caching::Actions::ActionCachePath
|
|
@mock_controller = ActionCachingMockController.new
|
|
end
|
|
|
|
def teardown
|
|
FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
|
|
end
|
|
|
|
def test_simple_action_cache
|
|
get :index
|
|
cached_time = content_to_cache
|
|
assert_equal cached_time, @response.body
|
|
assert fragment_exist?('hostname.com/action_caching_test')
|
|
reset!
|
|
|
|
get :index
|
|
assert_equal cached_time, @response.body
|
|
end
|
|
|
|
def test_simple_action_not_cached
|
|
get :destroy
|
|
cached_time = content_to_cache
|
|
assert_equal cached_time, @response.body
|
|
assert !fragment_exist?('hostname.com/action_caching_test/destroy')
|
|
reset!
|
|
|
|
get :destroy
|
|
assert_not_equal cached_time, @response.body
|
|
end
|
|
|
|
def test_action_cache_with_layout
|
|
get :with_layout
|
|
cached_time = content_to_cache
|
|
assert_not_equal cached_time, @response.body
|
|
assert fragment_exist?('hostname.com/action_caching_test/with_layout')
|
|
reset!
|
|
|
|
get :with_layout
|
|
assert_not_equal cached_time, @response.body
|
|
|
|
assert_equal @response.body, read_fragment('hostname.com/action_caching_test/with_layout')
|
|
end
|
|
|
|
def test_action_cache_with_layout_and_layout_cache_false
|
|
get :layout_false
|
|
cached_time = content_to_cache
|
|
assert_not_equal cached_time, @response.body
|
|
assert fragment_exist?('hostname.com/action_caching_test/layout_false')
|
|
reset!
|
|
|
|
get :layout_false
|
|
assert_not_equal cached_time, @response.body
|
|
|
|
assert_equal cached_time, read_fragment('hostname.com/action_caching_test/layout_false')
|
|
end
|
|
|
|
def test_action_cache_conditional_options
|
|
@request.env['HTTP_ACCEPT'] = 'application/json'
|
|
get :index
|
|
assert !fragment_exist?('hostname.com/action_caching_test')
|
|
end
|
|
|
|
def test_action_cache_with_custom_cache_path
|
|
get :show
|
|
cached_time = content_to_cache
|
|
assert_equal cached_time, @response.body
|
|
assert fragment_exist?('test.host/custom/show')
|
|
reset!
|
|
|
|
get :show
|
|
assert_equal cached_time, @response.body
|
|
end
|
|
|
|
def test_action_cache_with_custom_cache_path_in_block
|
|
get :edit
|
|
assert fragment_exist?('test.host/edit')
|
|
reset!
|
|
|
|
get :edit, :id => 1
|
|
assert fragment_exist?('test.host/1;edit')
|
|
end
|
|
|
|
def test_cache_expiration
|
|
get :index
|
|
cached_time = content_to_cache
|
|
reset!
|
|
|
|
get :index
|
|
assert_equal cached_time, @response.body
|
|
reset!
|
|
|
|
get :expire
|
|
reset!
|
|
|
|
get :index
|
|
new_cached_time = content_to_cache
|
|
assert_not_equal cached_time, @response.body
|
|
reset!
|
|
|
|
get :index
|
|
assert_response :success
|
|
assert_equal new_cached_time, @response.body
|
|
end
|
|
|
|
def test_cache_expiration_isnt_affected_by_request_format
|
|
get :index
|
|
cached_time = content_to_cache
|
|
reset!
|
|
|
|
@request.set_REQUEST_URI "/action_caching_test/expire.xml"
|
|
get :expire, :format => :xml
|
|
reset!
|
|
|
|
get :index
|
|
new_cached_time = content_to_cache
|
|
assert_not_equal cached_time, @response.body
|
|
end
|
|
|
|
def test_cache_is_scoped_by_subdomain
|
|
@request.host = 'jamis.hostname.com'
|
|
get :index
|
|
jamis_cache = content_to_cache
|
|
|
|
reset!
|
|
|
|
@request.host = 'david.hostname.com'
|
|
get :index
|
|
david_cache = content_to_cache
|
|
assert_not_equal jamis_cache, @response.body
|
|
|
|
reset!
|
|
|
|
@request.host = 'jamis.hostname.com'
|
|
get :index
|
|
assert_equal jamis_cache, @response.body
|
|
|
|
reset!
|
|
|
|
@request.host = 'david.hostname.com'
|
|
get :index
|
|
assert_equal david_cache, @response.body
|
|
end
|
|
|
|
def test_redirect_is_not_cached
|
|
get :redirected
|
|
assert_response :redirect
|
|
reset!
|
|
|
|
get :redirected
|
|
assert_response :redirect
|
|
end
|
|
|
|
def test_forbidden_is_not_cached
|
|
get :forbidden
|
|
assert_response :forbidden
|
|
reset!
|
|
|
|
get :forbidden
|
|
assert_response :forbidden
|
|
end
|
|
|
|
def test_xml_version_of_resource_is_treated_as_different_cache
|
|
with_routing do |set|
|
|
ActionController::Routing::Routes.draw do |map|
|
|
map.connect ':controller/:action.:format'
|
|
map.connect ':controller/:action'
|
|
end
|
|
|
|
get :index, :format => 'xml'
|
|
cached_time = content_to_cache
|
|
assert_equal cached_time, @response.body
|
|
assert fragment_exist?('hostname.com/action_caching_test/index.xml')
|
|
reset!
|
|
|
|
get :index, :format => 'xml'
|
|
assert_equal cached_time, @response.body
|
|
assert_equal 'application/xml', @response.content_type
|
|
reset!
|
|
|
|
@request.env['HTTP_ACCEPT'] = "application/xml"
|
|
get :index
|
|
assert_equal cached_time, @response.body
|
|
assert_equal 'application/xml', @response.content_type
|
|
reset!
|
|
|
|
get :expire_xml
|
|
reset!
|
|
|
|
get :index, :format => 'xml'
|
|
assert_not_equal cached_time, @response.body
|
|
end
|
|
end
|
|
|
|
def test_correct_content_type_is_returned_for_cache_hit
|
|
# run it twice to cache it the first time
|
|
get :index, :id => 'content-type.xml'
|
|
get :index, :id => 'content-type.xml'
|
|
assert_equal 'application/xml', @response.content_type
|
|
end
|
|
|
|
def test_empty_path_is_normalized
|
|
@mock_controller.mock_url_for = 'http://example.org/'
|
|
@mock_controller.mock_path = '/'
|
|
|
|
assert_equal 'example.org/index', @path_class.path_for(@mock_controller, {})
|
|
end
|
|
|
|
def test_file_extensions
|
|
get :index, :id => 'kitten.jpg'
|
|
get :index, :id => 'kitten.jpg'
|
|
|
|
assert_response :success
|
|
end
|
|
|
|
private
|
|
def content_to_cache
|
|
assigns(:cache_this)
|
|
end
|
|
|
|
def reset!
|
|
@request = ActionController::TestRequest.new
|
|
@response = ActionController::TestResponse.new
|
|
@controller = ActionCachingTestController.new
|
|
@request.host = 'hostname.com'
|
|
end
|
|
|
|
def fragment_exist?(path)
|
|
@controller.fragment_exist?(path)
|
|
end
|
|
|
|
def read_fragment(path)
|
|
@controller.read_fragment(path)
|
|
end
|
|
end
|
|
|
|
class FragmentCachingTestController < ActionController::Base
|
|
def some_action; end;
|
|
end
|
|
|
|
class FragmentCachingTest < Test::Unit::TestCase
|
|
def setup
|
|
ActionController::Base.perform_caching = true
|
|
@store = ActiveSupport::Cache::MemoryStore.new
|
|
ActionController::Base.cache_store = @store
|
|
@controller = FragmentCachingTestController.new
|
|
@params = {:controller => 'posts', :action => 'index'}
|
|
@request = ActionController::TestRequest.new
|
|
@response = ActionController::TestResponse.new
|
|
@controller.params = @params
|
|
@controller.request = @request
|
|
@controller.response = @response
|
|
@controller.send(:initialize_current_url)
|
|
@controller.send(:initialize_template_class, @response)
|
|
@controller.send(:assign_shortcuts, @request, @response)
|
|
end
|
|
|
|
def test_fragment_cache_key
|
|
assert_equal 'views/what a key', @controller.fragment_cache_key('what a key')
|
|
assert_equal( "views/test.host/fragment_caching_test/some_action",
|
|
@controller.fragment_cache_key(:controller => 'fragment_caching_test',:action => 'some_action'))
|
|
end
|
|
|
|
def test_read_fragment__with_caching_enabled
|
|
@store.write('views/name', 'value')
|
|
assert_equal 'value', @controller.read_fragment('name')
|
|
end
|
|
|
|
def test_read_fragment__with_caching_disabled
|
|
ActionController::Base.perform_caching = false
|
|
@store.write('views/name', 'value')
|
|
assert_nil @controller.read_fragment('name')
|
|
end
|
|
|
|
def test_fragment_exist__with_caching_enabled
|
|
@store.write('views/name', 'value')
|
|
assert @controller.fragment_exist?('name')
|
|
assert !@controller.fragment_exist?('other_name')
|
|
end
|
|
|
|
def test_fragment_exist__with_caching_disabled
|
|
ActionController::Base.perform_caching = false
|
|
@store.write('views/name', 'value')
|
|
assert !@controller.fragment_exist?('name')
|
|
assert !@controller.fragment_exist?('other_name')
|
|
end
|
|
|
|
def test_write_fragment__with_caching_enabled
|
|
assert_nil @store.read('views/name')
|
|
assert_equal 'value', @controller.write_fragment('name', 'value')
|
|
assert_equal 'value', @store.read('views/name')
|
|
end
|
|
|
|
def test_write_fragment__with_caching_disabled
|
|
assert_nil @store.read('views/name')
|
|
ActionController::Base.perform_caching = false
|
|
assert_equal nil, @controller.write_fragment('name', 'value')
|
|
assert_nil @store.read('views/name')
|
|
end
|
|
|
|
def test_expire_fragment__with_simple_key
|
|
@store.write('views/name', 'value')
|
|
@controller.expire_fragment 'name'
|
|
assert_nil @store.read('views/name')
|
|
end
|
|
|
|
def test_expire_fragment__with__regexp
|
|
@store.write('views/name', 'value')
|
|
@store.write('views/another_name', 'another_value')
|
|
@store.write('views/primalgrasp', 'will not expire ;-)')
|
|
|
|
@controller.expire_fragment /name/
|
|
|
|
assert_nil @store.read('views/name')
|
|
assert_nil @store.read('views/another_name')
|
|
assert_equal 'will not expire ;-)', @store.read('views/primalgrasp')
|
|
end
|
|
|
|
def test_fragment_for__with_disabled_caching
|
|
ActionController::Base.perform_caching = false
|
|
|
|
@store.write('views/expensive', 'fragment content')
|
|
fragment_computed = false
|
|
|
|
buffer = 'generated till now -> '
|
|
@controller.fragment_for(Proc.new { fragment_computed = true }, 'expensive') { buffer }
|
|
|
|
assert fragment_computed
|
|
assert_equal 'generated till now -> ', buffer
|
|
end
|
|
|
|
def test_fragment_for
|
|
@store.write('views/expensive', 'fragment content')
|
|
fragment_computed = false
|
|
|
|
buffer = 'generated till now -> '
|
|
@controller.fragment_for(Proc.new { fragment_computed = true }, 'expensive') { buffer}
|
|
|
|
assert !fragment_computed
|
|
assert_equal 'generated till now -> fragment content', buffer
|
|
end
|
|
|
|
def test_cache_erb_fragment
|
|
@store.write('views/expensive', 'fragment content')
|
|
@controller.response.template.output_buffer = 'generated till now -> '
|
|
|
|
assert_equal( 'generated till now -> fragment content',
|
|
ActionView::TemplateHandlers::ERB.new(@controller).cache_fragment(Proc.new{ }, 'expensive'))
|
|
end
|
|
|
|
def test_cache_rxml_fragment
|
|
@store.write('views/expensive', 'fragment content')
|
|
xml = 'generated till now -> '
|
|
class << xml; def target!; to_s; end; end
|
|
|
|
assert_equal( 'generated till now -> fragment content',
|
|
ActionView::TemplateHandlers::Builder.new(@controller).cache_fragment(Proc.new{ }, 'expensive'))
|
|
end
|
|
|
|
def test_cache_rjs_fragment
|
|
@store.write('views/expensive', 'fragment content')
|
|
page = 'generated till now -> '
|
|
|
|
assert_equal( 'generated till now -> fragment content',
|
|
ActionView::TemplateHandlers::RJS.new(@controller).cache_fragment(Proc.new{ }, 'expensive'))
|
|
end
|
|
|
|
def test_cache_rjs_fragment_debug_mode_does_not_interfere
|
|
@store.write('views/expensive', 'fragment content')
|
|
page = 'generated till now -> '
|
|
|
|
begin
|
|
debug_mode, ActionView::Base.debug_rjs = ActionView::Base.debug_rjs, true
|
|
assert_equal( 'generated till now -> fragment content',
|
|
ActionView::TemplateHandlers::RJS.new(@controller).cache_fragment(Proc.new{ }, 'expensive'))
|
|
assert ActionView::Base.debug_rjs
|
|
ensure
|
|
ActionView::Base.debug_rjs = debug_mode
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
class FunctionalCachingController < ActionController::Base
|
|
def fragment_cached
|
|
end
|
|
|
|
def html_fragment_cached_with_partial
|
|
respond_to do |format|
|
|
format.html
|
|
end
|
|
end
|
|
|
|
def js_fragment_cached_with_partial
|
|
respond_to do |format|
|
|
format.js
|
|
end
|
|
end
|
|
|
|
|
|
def rescue_action(e)
|
|
raise e
|
|
end
|
|
end
|
|
|
|
FunctionalCachingController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
|
|
|
|
class FunctionalFragmentCachingTest < Test::Unit::TestCase
|
|
def setup
|
|
ActionController::Base.perform_caching = true
|
|
@store = ActiveSupport::Cache::MemoryStore.new
|
|
ActionController::Base.cache_store = @store
|
|
@controller = FunctionalCachingController.new
|
|
@request = ActionController::TestRequest.new
|
|
@response = ActionController::TestResponse.new
|
|
end
|
|
def test_fragment_caching
|
|
get :fragment_cached
|
|
assert_response :success
|
|
expected_body = <<-CACHED
|
|
Hello
|
|
This bit's fragment cached
|
|
CACHED
|
|
assert_equal expected_body, @response.body
|
|
|
|
assert_equal "This bit's fragment cached", @store.read('views/test.host/functional_caching/fragment_cached')
|
|
end
|
|
|
|
def test_fragment_caching_in_partials
|
|
get :html_fragment_cached_with_partial
|
|
assert_response :success
|
|
assert_match /Fragment caching in a partial/, @response.body
|
|
assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/html_fragment_cached_with_partial')
|
|
end
|
|
|
|
def test_fragment_caching_in_rjs_partials
|
|
xhr :get, :js_fragment_cached_with_partial
|
|
assert_response :success
|
|
assert_match /Fragment caching in a partial/, @response.body
|
|
assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/js_fragment_cached_with_partial')
|
|
end
|
|
end
|