Updated README.rdoc again
[feedcatcher.git] / vendor / rails / actionpack / test / controller / caching_test.rb
1 require 'fileutils'
2 require 'abstract_unit'
3
4 CACHE_DIR = 'test_cache'
5 # Don't change '/../temp/' cavalierly or you might hose something you don't want hosed
6 FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR)
7 ActionController::Base.page_cache_directory = FILE_STORE_PATH
8 ActionController::Base.cache_store = :file_store, FILE_STORE_PATH
9
10 class PageCachingTestController < ActionController::Base
11 caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? }
12 caches_page :found, :not_found
13
14 def ok
15 head :ok
16 end
17
18 def no_content
19 head :no_content
20 end
21
22 def found
23 redirect_to :action => 'ok'
24 end
25
26 def not_found
27 head :not_found
28 end
29
30 def custom_path
31 render :text => "Super soaker"
32 cache_page("Super soaker", "/index.html")
33 end
34
35 def expire_custom_path
36 expire_page("/index.html")
37 head :ok
38 end
39
40 def trailing_slash
41 render :text => "Sneak attack"
42 end
43 end
44
45 class PageCachingTest < ActionController::TestCase
46 def setup
47 ActionController::Base.perform_caching = true
48
49 ActionController::Routing::Routes.draw do |map|
50 map.main '', :controller => 'posts'
51 map.formatted_posts 'posts.:format', :controller => 'posts'
52 map.resources :posts
53 map.connect ':controller/:action/:id'
54 end
55
56 @request = ActionController::TestRequest.new
57 @request.host = 'hostname.com'
58
59 @response = ActionController::TestResponse.new
60 @controller = PageCachingTestController.new
61
62 @params = {:controller => 'posts', :action => 'index', :only_path => true, :skip_relative_url_root => true}
63 @rewriter = ActionController::UrlRewriter.new(@request, @params)
64
65 FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
66 FileUtils.mkdir_p(FILE_STORE_PATH)
67 end
68
69 def teardown
70 FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
71 ActionController::Routing::Routes.clear!
72 ActionController::Base.perform_caching = false
73 end
74
75 def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route
76 @params[:format] = 'rss'
77 assert_equal '/posts.rss', @rewriter.rewrite(@params)
78 @params[:format] = nil
79 assert_equal '/', @rewriter.rewrite(@params)
80 end
81
82 def test_should_cache_get_with_ok_status
83 get :ok
84 assert_response :ok
85 assert_page_cached :ok, "get with ok status should have been cached"
86 end
87
88 def test_should_cache_with_custom_path
89 get :custom_path
90 assert File.exist?("#{FILE_STORE_PATH}/index.html")
91 end
92
93 def test_should_expire_cache_with_custom_path
94 get :custom_path
95 assert File.exist?("#{FILE_STORE_PATH}/index.html")
96
97 get :expire_custom_path
98 assert !File.exist?("#{FILE_STORE_PATH}/index.html")
99 end
100
101 def test_should_cache_without_trailing_slash_on_url
102 @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash'
103 assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
104 end
105
106 def test_should_cache_with_trailing_slash_on_url
107 @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash/'
108 assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
109 end
110
111 def test_should_cache_ok_at_custom_path
112 @request.stubs(:path).returns("/index.html")
113 get :ok
114 assert_response :ok
115 assert File.exist?("#{FILE_STORE_PATH}/index.html")
116 end
117
118 [:ok, :no_content, :found, :not_found].each do |status|
119 [:get, :post, :put, :delete].each do |method|
120 unless method == :get and status == :ok
121 define_method "test_shouldnt_cache_#{method}_with_#{status}_status" do
122 send(method, status)
123 assert_response status
124 assert_page_not_cached status, "#{method} with #{status} status shouldn't have been cached"
125 end
126 end
127 end
128 end
129
130 def test_page_caching_conditional_options
131 get :ok, :format=>'json'
132 assert_page_not_cached :ok
133 end
134
135 private
136 def assert_page_cached(action, message = "#{action} should have been cached")
137 assert page_cached?(action), message
138 end
139
140 def assert_page_not_cached(action, message = "#{action} shouldn't have been cached")
141 assert !page_cached?(action), message
142 end
143
144 def page_cached?(action)
145 File.exist? "#{FILE_STORE_PATH}/page_caching_test/#{action}.html"
146 end
147 end
148
149 class ActionCachingTestController < ActionController::Base
150 caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour
151 caches_action :show, :cache_path => 'http://test.host/custom/show'
152 caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" }
153 caches_action :with_layout
154 caches_action :layout_false, :layout => false
155
156 layout 'talk_from_action.erb'
157
158 def index
159 @cache_this = MockTime.now.to_f.to_s
160 render :text => @cache_this
161 end
162
163 def redirected
164 redirect_to :action => 'index'
165 end
166
167 def forbidden
168 render :text => "Forbidden"
169 response.status = "403 Forbidden"
170 end
171
172 def with_layout
173 @cache_this = MockTime.now.to_f.to_s
174 render :text => @cache_this, :layout => true
175 end
176
177 alias_method :show, :index
178 alias_method :edit, :index
179 alias_method :destroy, :index
180 alias_method :layout_false, :with_layout
181
182 def expire
183 expire_action :controller => 'action_caching_test', :action => 'index'
184 render :nothing => true
185 end
186
187 def expire_xml
188 expire_action :controller => 'action_caching_test', :action => 'index', :format => 'xml'
189 render :nothing => true
190 end
191 end
192
193 class MockTime < Time
194 # Let Time spicy to assure that Time.now != Time.now
195 def to_f
196 super+rand
197 end
198 end
199
200 class ActionCachingMockController
201 attr_accessor :mock_url_for
202 attr_accessor :mock_path
203
204 def initialize
205 yield self if block_given?
206 end
207
208 def url_for(*args)
209 @mock_url_for
210 end
211
212 def request
213 mocked_path = @mock_path
214 Object.new.instance_eval(<<-EVAL)
215 def path; '#{@mock_path}' end
216 def format; 'all' end
217 def cache_format; nil end
218 self
219 EVAL
220 end
221 end
222
223 class ActionCacheTest < ActionController::TestCase
224 def setup
225 reset!
226 FileUtils.mkdir_p(FILE_STORE_PATH)
227 @path_class = ActionController::Caching::Actions::ActionCachePath
228 @mock_controller = ActionCachingMockController.new
229 end
230
231 def teardown
232 FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
233 end
234
235 def test_simple_action_cache
236 get :index
237 cached_time = content_to_cache
238 assert_equal cached_time, @response.body
239 assert fragment_exist?('hostname.com/action_caching_test')
240 reset!
241
242 get :index
243 assert_equal cached_time, @response.body
244 end
245
246 def test_simple_action_not_cached
247 get :destroy
248 cached_time = content_to_cache
249 assert_equal cached_time, @response.body
250 assert !fragment_exist?('hostname.com/action_caching_test/destroy')
251 reset!
252
253 get :destroy
254 assert_not_equal cached_time, @response.body
255 end
256
257 def test_action_cache_with_layout
258 get :with_layout
259 cached_time = content_to_cache
260 assert_not_equal cached_time, @response.body
261 assert fragment_exist?('hostname.com/action_caching_test/with_layout')
262 reset!
263
264 get :with_layout
265 assert_not_equal cached_time, @response.body
266
267 assert_equal @response.body, read_fragment('hostname.com/action_caching_test/with_layout')
268 end
269
270 def test_action_cache_with_layout_and_layout_cache_false
271 get :layout_false
272 cached_time = content_to_cache
273 assert_not_equal cached_time, @response.body
274 assert fragment_exist?('hostname.com/action_caching_test/layout_false')
275 reset!
276
277 get :layout_false
278 assert_not_equal cached_time, @response.body
279
280 assert_equal cached_time, read_fragment('hostname.com/action_caching_test/layout_false')
281 end
282
283 def test_action_cache_conditional_options
284 old_use_accept_header = ActionController::Base.use_accept_header
285 ActionController::Base.use_accept_header = true
286 @request.env['HTTP_ACCEPT'] = 'application/json'
287 get :index
288 assert !fragment_exist?('hostname.com/action_caching_test')
289 ActionController::Base.use_accept_header = old_use_accept_header
290 end
291
292 def test_action_cache_with_store_options
293 MockTime.expects(:now).returns(12345).once
294 @controller.expects(:read_fragment).with('hostname.com/action_caching_test', :expires_in => 1.hour).once
295 @controller.expects(:write_fragment).with('hostname.com/action_caching_test', '12345.0', :expires_in => 1.hour).once
296 get :index
297 end
298
299 def test_action_cache_with_custom_cache_path
300 get :show
301 cached_time = content_to_cache
302 assert_equal cached_time, @response.body
303 assert fragment_exist?('test.host/custom/show')
304 reset!
305
306 get :show
307 assert_equal cached_time, @response.body
308 end
309
310 def test_action_cache_with_custom_cache_path_in_block
311 get :edit
312 assert fragment_exist?('test.host/edit')
313 reset!
314
315 get :edit, :id => 1
316 assert fragment_exist?('test.host/1;edit')
317 end
318
319 def test_cache_expiration
320 get :index
321 cached_time = content_to_cache
322 reset!
323
324 get :index
325 assert_equal cached_time, @response.body
326 reset!
327
328 get :expire
329 reset!
330
331 get :index
332 new_cached_time = content_to_cache
333 assert_not_equal cached_time, @response.body
334 reset!
335
336 get :index
337 assert_response :success
338 assert_equal new_cached_time, @response.body
339 end
340
341 def test_cache_expiration_isnt_affected_by_request_format
342 get :index
343 cached_time = content_to_cache
344 reset!
345
346 @request.set_REQUEST_URI "/action_caching_test/expire.xml"
347 get :expire, :format => :xml
348 reset!
349
350 get :index
351 new_cached_time = content_to_cache
352 assert_not_equal cached_time, @response.body
353 end
354
355 def test_cache_is_scoped_by_subdomain
356 @request.host = 'jamis.hostname.com'
357 get :index
358 jamis_cache = content_to_cache
359
360 reset!
361
362 @request.host = 'david.hostname.com'
363 get :index
364 david_cache = content_to_cache
365 assert_not_equal jamis_cache, @response.body
366
367 reset!
368
369 @request.host = 'jamis.hostname.com'
370 get :index
371 assert_equal jamis_cache, @response.body
372
373 reset!
374
375 @request.host = 'david.hostname.com'
376 get :index
377 assert_equal david_cache, @response.body
378 end
379
380 def test_redirect_is_not_cached
381 get :redirected
382 assert_response :redirect
383 reset!
384
385 get :redirected
386 assert_response :redirect
387 end
388
389 def test_forbidden_is_not_cached
390 get :forbidden
391 assert_response :forbidden
392 reset!
393
394 get :forbidden
395 assert_response :forbidden
396 end
397
398 def test_xml_version_of_resource_is_treated_as_different_cache
399 with_routing do |set|
400 set.draw do |map|
401 map.connect ':controller/:action.:format'
402 map.connect ':controller/:action'
403 end
404
405 get :index, :format => 'xml'
406 cached_time = content_to_cache
407 assert_equal cached_time, @response.body
408 assert fragment_exist?('hostname.com/action_caching_test/index.xml')
409 reset!
410
411 get :index, :format => 'xml'
412 assert_equal cached_time, @response.body
413 assert_equal 'application/xml', @response.content_type
414 reset!
415
416 get :expire_xml
417 reset!
418
419 get :index, :format => 'xml'
420 assert_not_equal cached_time, @response.body
421 end
422 end
423
424 def test_correct_content_type_is_returned_for_cache_hit
425 # run it twice to cache it the first time
426 get :index, :id => 'content-type.xml'
427 get :index, :id => 'content-type.xml'
428 assert_equal 'application/xml', @response.content_type
429 end
430
431 def test_correct_content_type_is_returned_for_cache_hit_on_action_with_string_key
432 # run it twice to cache it the first time
433 get :show, :format => 'xml'
434 get :show, :format => 'xml'
435 assert_equal 'application/xml', @response.content_type
436 end
437
438 def test_correct_content_type_is_returned_for_cache_hit_on_action_with_string_key_from_proc
439 # run it twice to cache it the first time
440 get :edit, :id => 1, :format => 'xml'
441 get :edit, :id => 1, :format => 'xml'
442 assert_equal 'application/xml', @response.content_type
443 end
444
445 def test_empty_path_is_normalized
446 @mock_controller.mock_url_for = 'http://example.org/'
447 @mock_controller.mock_path = '/'
448
449 assert_equal 'example.org/index', @path_class.path_for(@mock_controller, {})
450 end
451
452 def test_file_extensions
453 get :index, :id => 'kitten.jpg'
454 get :index, :id => 'kitten.jpg'
455
456 assert_response :success
457 end
458
459 private
460 def content_to_cache
461 assigns(:cache_this)
462 end
463
464 def reset!
465 @request = ActionController::TestRequest.new
466 @response = ActionController::TestResponse.new
467 @controller = ActionCachingTestController.new
468 @request.host = 'hostname.com'
469 end
470
471 def fragment_exist?(path)
472 @controller.fragment_exist?(path)
473 end
474
475 def read_fragment(path)
476 @controller.read_fragment(path)
477 end
478 end
479
480 class FragmentCachingTestController < ActionController::Base
481 def some_action; end;
482 end
483
484 class FragmentCachingTest < ActionController::TestCase
485 def setup
486 ActionController::Base.perform_caching = true
487 @store = ActiveSupport::Cache::MemoryStore.new
488 ActionController::Base.cache_store = @store
489 @controller = FragmentCachingTestController.new
490 @params = {:controller => 'posts', :action => 'index'}
491 @request = ActionController::TestRequest.new
492 @response = ActionController::TestResponse.new
493 @controller.params = @params
494 @controller.request = @request
495 @controller.response = @response
496 @controller.send(:initialize_current_url)
497 @controller.send(:initialize_template_class, @response)
498 @controller.send(:assign_shortcuts, @request, @response)
499 end
500
501 def test_fragment_cache_key
502 assert_equal 'views/what a key', @controller.fragment_cache_key('what a key')
503 assert_equal "views/test.host/fragment_caching_test/some_action",
504 @controller.fragment_cache_key(:controller => 'fragment_caching_test',:action => 'some_action')
505 end
506
507 def test_read_fragment_with_caching_enabled
508 @store.write('views/name', 'value')
509 assert_equal 'value', @controller.read_fragment('name')
510 end
511
512 def test_read_fragment_with_caching_disabled
513 ActionController::Base.perform_caching = false
514 @store.write('views/name', 'value')
515 assert_nil @controller.read_fragment('name')
516 end
517
518 def test_fragment_exist_with_caching_enabled
519 @store.write('views/name', 'value')
520 assert @controller.fragment_exist?('name')
521 assert !@controller.fragment_exist?('other_name')
522 end
523
524 def test_fragment_exist_with_caching_disabled
525 ActionController::Base.perform_caching = false
526 @store.write('views/name', 'value')
527 assert !@controller.fragment_exist?('name')
528 assert !@controller.fragment_exist?('other_name')
529 end
530
531 def test_write_fragment_with_caching_enabled
532 assert_nil @store.read('views/name')
533 assert_equal 'value', @controller.write_fragment('name', 'value')
534 assert_equal 'value', @store.read('views/name')
535 end
536
537 def test_write_fragment_with_caching_disabled
538 assert_nil @store.read('views/name')
539 ActionController::Base.perform_caching = false
540 assert_equal 'value', @controller.write_fragment('name', 'value')
541 assert_nil @store.read('views/name')
542 end
543
544 def test_expire_fragment_with_simple_key
545 @store.write('views/name', 'value')
546 @controller.expire_fragment 'name'
547 assert_nil @store.read('views/name')
548 end
549
550 def test_expire_fragment_with_regexp
551 @store.write('views/name', 'value')
552 @store.write('views/another_name', 'another_value')
553 @store.write('views/primalgrasp', 'will not expire ;-)')
554
555 @controller.expire_fragment /name/
556
557 assert_nil @store.read('views/name')
558 assert_nil @store.read('views/another_name')
559 assert_equal 'will not expire ;-)', @store.read('views/primalgrasp')
560 end
561
562 def test_fragment_for_with_disabled_caching
563 ActionController::Base.perform_caching = false
564
565 @store.write('views/expensive', 'fragment content')
566 fragment_computed = false
567
568 buffer = 'generated till now -> '
569 @controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
570
571 assert fragment_computed
572 assert_equal 'generated till now -> ', buffer
573 end
574
575 def test_fragment_for
576 @store.write('views/expensive', 'fragment content')
577 fragment_computed = false
578
579 buffer = 'generated till now -> '
580 @controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
581
582 assert !fragment_computed
583 assert_equal 'generated till now -> fragment content', buffer
584 end
585 end
586
587 class FunctionalCachingController < ActionController::Base
588 def fragment_cached
589 end
590
591 def html_fragment_cached_with_partial
592 respond_to do |format|
593 format.html
594 end
595 end
596
597 def js_fragment_cached_with_partial
598 respond_to do |format|
599 format.js
600 end
601 end
602
603 def formatted_fragment_cached
604 respond_to do |format|
605 format.html
606 format.xml
607 format.js
608 end
609 end
610
611 def rescue_action(e)
612 raise e
613 end
614 end
615
616 class FunctionalFragmentCachingTest < ActionController::TestCase
617 def setup
618 ActionController::Base.perform_caching = true
619 @store = ActiveSupport::Cache::MemoryStore.new
620 ActionController::Base.cache_store = @store
621 @controller = FunctionalCachingController.new
622 @request = ActionController::TestRequest.new
623 @response = ActionController::TestResponse.new
624 end
625
626 def test_fragment_caching
627 get :fragment_cached
628 assert_response :success
629 expected_body = <<-CACHED
630 Hello
631 This bit's fragment cached
632 CACHED
633 assert_equal expected_body, @response.body
634
635 assert_equal "This bit's fragment cached", @store.read('views/test.host/functional_caching/fragment_cached')
636 end
637
638 def test_fragment_caching_in_partials
639 get :html_fragment_cached_with_partial
640 assert_response :success
641 assert_match /Fragment caching in a partial/, @response.body
642 assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/html_fragment_cached_with_partial')
643 end
644
645 def test_render_inline_before_fragment_caching
646 get :inline_fragment_cached
647 assert_response :success
648 assert_match /Some inline content/, @response.body
649 assert_match /Some cached content/, @response.body
650 assert_match "Some cached content", @store.read('views/test.host/functional_caching/inline_fragment_cached')
651 end
652
653 def test_fragment_caching_in_rjs_partials
654 xhr :get, :js_fragment_cached_with_partial
655 assert_response :success
656 assert_match /Fragment caching in a partial/, @response.body
657 assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/js_fragment_cached_with_partial')
658 end
659
660 def test_html_formatted_fragment_caching
661 get :formatted_fragment_cached, :format => "html"
662 assert_response :success
663 expected_body = "<body>\n<p>ERB</p>\n</body>"
664
665 assert_equal expected_body, @response.body
666
667 assert_equal "<p>ERB</p>", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
668 end
669
670 def test_xml_formatted_fragment_caching
671 get :formatted_fragment_cached, :format => "xml"
672 assert_response :success
673 expected_body = "<body>\n <p>Builder</p>\n</body>\n"
674
675 assert_equal expected_body, @response.body
676
677 assert_equal " <p>Builder</p>\n", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
678 end
679
680 def test_js_formatted_fragment_caching
681 get :formatted_fragment_cached, :format => "js"
682 assert_response :success
683 expected_body = %(title = "Hey";\n$("element_1").visualEffect("highlight");\n) +
684 %($("element_2").visualEffect("highlight");\nfooter = "Bye";)
685 assert_equal expected_body, @response.body
686
687 assert_equal ['$("element_1").visualEffect("highlight");', '$("element_2").visualEffect("highlight");'],
688 @store.read('views/test.host/functional_caching/formatted_fragment_cached')
689 end
690 end