--- /dev/null
+require 'fileutils'
+require 'abstract_unit'
+
+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
+
+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.stubs(: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
+ get :ok, :format=>'json'
+ 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? }, :expires_in => 1.hour
+ 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
+ def cache_format; nil 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
+ old_use_accept_header = ActionController::Base.use_accept_header
+ ActionController::Base.use_accept_header = true
+ @request.env['HTTP_ACCEPT'] = 'application/json'
+ get :index
+ assert !fragment_exist?('hostname.com/action_caching_test')
+ ActionController::Base.use_accept_header = old_use_accept_header
+ end
+
+ uses_mocha 'test action cache' do
+ def test_action_cache_with_store_options
+ MockTime.expects(:now).returns(12345).once
+ @controller.expects(:read_fragment).with('hostname.com/action_caching_test', :expires_in => 1.hour).once
+ @controller.expects(:write_fragment).with('hostname.com/action_caching_test', '12345.0', :expires_in => 1.hour).once
+ get :index
+ end
+ 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!
+
+ 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(buffer, 'expensive') { fragment_computed = true }
+
+ 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(buffer, 'expensive') { fragment_computed = true }
+
+ assert !fragment_computed
+ assert_equal 'generated till now -> fragment content', buffer
+ 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 formatted_fragment_cached
+ respond_to do |format|
+ format.html
+ format.xml
+ format.js
+ end
+ end
+
+ def rescue_action(e)
+ raise e
+ end
+end
+
+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_render_inline_before_fragment_caching
+ get :inline_fragment_cached
+ assert_response :success
+ assert_match /Some inline content/, @response.body
+ assert_match /Some cached content/, @response.body
+ assert_match "Some cached content", @store.read('views/test.host/functional_caching/inline_fragment_cached')
+ 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
+
+ def test_html_formatted_fragment_caching
+ get :formatted_fragment_cached, :format => "html"
+ assert_response :success
+ expected_body = "<body>\n<p>ERB</p>\n</body>"
+
+ assert_equal expected_body, @response.body
+
+ assert_equal "<p>ERB</p>", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
+ end
+
+ def test_xml_formatted_fragment_caching
+ get :formatted_fragment_cached, :format => "xml"
+ assert_response :success
+ expected_body = "<body>\n <p>Builder</p>\n</body>\n"
+
+ assert_equal expected_body, @response.body
+
+ assert_equal " <p>Builder</p>\n", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
+ end
+
+ def test_js_formatted_fragment_caching
+ get :formatted_fragment_cached, :format => "js"
+ assert_response :success
+ expected_body = %(title = "Hey";\n$("element_1").visualEffect("highlight");\n) +
+ %($("element_2").visualEffect("highlight");\nfooter = "Bye";)
+ assert_equal expected_body, @response.body
+
+ assert_equal ['$("element_1").visualEffect("highlight");', '$("element_2").visualEffect("highlight");'],
+ @store.read('views/test.host/functional_caching/formatted_fragment_cached')
+ end
+end