Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/trunk/actionpack/test/controller/filters_test.rb

Revision 9080, 26.5 kB (checked in by rick, 8 months ago)

Fix regression from filter refactoring where re-adding a skipped filter resulted in it being called twice. [rick]

Line 
1 require 'abstract_unit'
2
3 # FIXME: crashes Ruby 1.9
4 class FilterTest < Test::Unit::TestCase
5   class TestController < ActionController::Base
6     before_filter :ensure_login
7     after_filter  :clean_up
8
9     def show
10       render :inline => "ran action"
11     end
12
13     private
14       def ensure_login
15         @ran_filter ||= []
16         @ran_filter << "ensure_login"
17       end
18
19       def clean_up
20         @ran_after_filter ||= []
21         @ran_after_filter << "clean_up"
22       end
23   end
24
25   class ChangingTheRequirementsController < TestController
26     before_filter :ensure_login, :except => [:go_wild]
27
28     def go_wild
29       render :text => "gobble"
30     end
31   end
32
33   class TestMultipleFiltersController < ActionController::Base
34     before_filter :try_1
35     before_filter :try_2
36     before_filter :try_3
37
38     (1..3).each do |i|
39       define_method "fail_#{i}" do
40         render :text => i.to_s
41       end
42     end
43
44     protected
45     (1..3).each do |i|
46       define_method "try_#{i}" do
47         instance_variable_set :@try, i
48         if action_name == "fail_#{i}"
49           head(404)
50         end
51       end
52     end
53   end
54
55   class RenderingController < ActionController::Base
56     before_filter :render_something_else
57
58     def show
59       @ran_action = true
60       render :inline => "ran action"
61     end
62
63     private
64       def render_something_else
65         render :inline => "something else"
66       end
67   end
68
69   class ConditionalFilterController < ActionController::Base
70     def show
71       render :inline => "ran action"
72     end
73
74     def another_action
75       render :inline => "ran action"
76     end
77
78     def show_without_filter
79       render :inline => "ran action without filter"
80     end
81
82     private
83       def ensure_login
84         @ran_filter ||= []
85         @ran_filter << "ensure_login"
86       end
87
88       def clean_up_tmp
89         @ran_filter ||= []
90         @ran_filter << "clean_up_tmp"
91       end
92
93       def rescue_action(e) raise(e) end
94   end
95
96   class ConditionalCollectionFilterController < ConditionalFilterController
97     before_filter :ensure_login, :except => [ :show_without_filter, :another_action ]
98   end
99
100   class OnlyConditionSymController < ConditionalFilterController
101     before_filter :ensure_login, :only => :show
102   end
103
104   class ExceptConditionSymController < ConditionalFilterController
105     before_filter :ensure_login, :except => :show_without_filter
106   end
107
108   class BeforeAndAfterConditionController < ConditionalFilterController
109     before_filter :ensure_login, :only => :show
110     after_filter  :clean_up_tmp, :only => :show
111   end
112
113   class OnlyConditionProcController < ConditionalFilterController
114     before_filter(:only => :show) {|c| c.assigns["ran_proc_filter"] = true }
115   end
116
117   class ExceptConditionProcController < ConditionalFilterController
118     before_filter(:except => :show_without_filter) {|c| c.assigns["ran_proc_filter"] = true }
119   end
120
121   class ConditionalClassFilter
122     def self.filter(controller) controller.assigns["ran_class_filter"] = true end
123   end
124
125   class OnlyConditionClassController < ConditionalFilterController
126     before_filter ConditionalClassFilter, :only => :show
127   end
128
129   class ExceptConditionClassController < ConditionalFilterController
130     before_filter ConditionalClassFilter, :except => :show_without_filter
131   end
132
133   class AnomolousYetValidConditionController < ConditionalFilterController
134     before_filter(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.assigns["ran_proc_filter1"] = true }, :except => :show_without_filter) { |c| c.assigns["ran_proc_filter2"] = true}
135   end
136
137   class ConditionalOptionsFilter < ConditionalFilterController
138     before_filter :ensure_login, :if => Proc.new { |c| true }
139     before_filter :clean_up_tmp, :if => Proc.new { |c| false }
140   end
141
142   class EmptyFilterChainController < TestController
143     self.filter_chain.clear
144     def show
145       @action_executed = true
146       render :text => "yawp!"
147     end
148   end
149
150   class PrependingController < TestController
151     prepend_before_filter :wonderful_life
152     # skip_before_filter :fire_flash
153
154     private
155       def wonderful_life
156         @ran_filter ||= []
157         @ran_filter << "wonderful_life"
158       end
159   end
160
161   class SkippingAndLimitedController < TestController
162     skip_before_filter :ensure_login
163     before_filter :ensure_login, :only => :index
164
165     def index
166       render :text => 'ok'
167     end
168    
169     def public
170     end
171   end
172  
173   class SkippingAndReorderingController < TestController
174     skip_before_filter :ensure_login
175     before_filter :find_record
176     before_filter :ensure_login
177
178     private
179       def find_record
180         @ran_filter ||= []
181         @ran_filter << "find_record"
182       end
183   end
184
185   class ConditionalSkippingController < TestController
186     skip_before_filter :ensure_login, :only => [ :login ]
187     skip_after_filter  :clean_up,     :only => [ :login ]
188
189     before_filter :find_user, :only => [ :change_password ]
190
191     def login
192       render :inline => "ran action"
193     end
194
195     def change_password
196       render :inline => "ran action"
197     end
198
199     protected
200       def find_user
201         @ran_filter ||= []
202         @ran_filter << "find_user"
203       end
204   end
205
206   class ConditionalParentOfConditionalSkippingController < ConditionalFilterController
207     before_filter :conditional_in_parent, :only => [:show, :another_action]
208     after_filter  :conditional_in_parent, :only => [:show, :another_action]
209
210     private
211
212       def conditional_in_parent
213         @ran_filter ||= []
214         @ran_filter << 'conditional_in_parent'
215       end
216   end
217
218   class ChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController
219     skip_before_filter :conditional_in_parent, :only => :another_action
220     skip_after_filter  :conditional_in_parent, :only => :another_action
221   end
222
223   class AnotherChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController
224     skip_before_filter :conditional_in_parent, :only => :show
225   end
226
227   class ProcController < PrependingController
228     before_filter(proc { |c| c.assigns["ran_proc_filter"] = true })
229   end
230
231   class ImplicitProcController < PrependingController
232     before_filter { |c| c.assigns["ran_proc_filter"] = true }
233   end
234
235   class AuditFilter
236     def self.filter(controller)
237       controller.assigns["was_audited"] = true
238     end
239   end
240
241   class AroundFilter
242     def before(controller)
243       @execution_log = "before"
244       controller.class.execution_log << " before aroundfilter " if controller.respond_to? :execution_log
245       controller.assigns["before_ran"] = true
246     end
247
248     def after(controller)
249       controller.assigns["execution_log"] = @execution_log + " and after"
250       controller.assigns["after_ran"] = true
251       controller.class.execution_log << " after aroundfilter " if controller.respond_to? :execution_log
252     end
253   end
254
255   class AppendedAroundFilter
256     def before(controller)
257       controller.class.execution_log << " before appended aroundfilter "
258     end
259
260     def after(controller)
261       controller.class.execution_log << " after appended aroundfilter "
262     end
263   end
264
265   class AuditController < ActionController::Base
266     before_filter(AuditFilter)
267
268     def show
269       render :text => "hello"
270     end
271   end
272
273   class AroundFilterController < PrependingController
274     around_filter AroundFilter.new
275   end
276
277   class BeforeAfterClassFilterController < PrependingController
278     begin
279       filter = AroundFilter.new
280       before_filter filter
281       after_filter filter
282     end
283   end
284
285   class MixedFilterController < PrependingController
286     cattr_accessor :execution_log
287
288     def initialize
289       @@execution_log = ""
290     end
291
292     before_filter { |c| c.class.execution_log << " before procfilter "  }
293     prepend_around_filter AroundFilter.new
294
295     after_filter  { |c| c.class.execution_log << " after procfilter " }
296     append_around_filter AppendedAroundFilter.new
297   end
298
299   class MixedSpecializationController < ActionController::Base
300     class OutOfOrder < StandardError; end
301
302     before_filter :first
303     before_filter :second, :only => :foo
304
305     def foo
306       render :text => 'foo'
307     end
308
309     def bar
310       render :text => 'bar'
311     end
312
313     protected
314       def first
315         @first = true
316       end
317
318       def second
319         raise OutOfOrder unless @first
320       end
321   end
322
323   class DynamicDispatchController < ActionController::Base
324     before_filter :choose
325
326     %w(foo bar baz).each do |action|
327       define_method(action) { render :text => action }
328     end
329
330     private
331       def choose
332         self.action_name = params[:choose]
333       end
334   end
335
336   class PrependingBeforeAndAfterController < ActionController::Base
337     prepend_before_filter :before_all
338     prepend_after_filter :after_all
339     before_filter :between_before_all_and_after_all
340
341     def before_all
342       @ran_filter ||= []
343       @ran_filter << 'before_all'
344     end
345
346     def after_all
347       @ran_filter ||= []
348       @ran_filter << 'after_all'
349     end
350
351     def between_before_all_and_after_all
352       @ran_filter ||= []
353       @ran_filter << 'between_before_all_and_after_all'
354     end
355     def show
356       render :text => 'hello'
357     end
358   end
359
360   class ErrorToRescue < Exception; end
361
362   class RescuingAroundFilterWithBlock
363     def filter(controller)
364       begin
365         yield
366       rescue ErrorToRescue => ex
367         controller.send! :render, :text => "I rescued this: #{ex.inspect}"
368       end
369     end
370   end
371
372   class RescuedController < ActionController::Base
373     around_filter RescuingAroundFilterWithBlock.new
374
375     def show
376       raise ErrorToRescue.new("Something made the bad noise.")
377     end
378
379   private
380     def rescue_action(exception)
381       raise exception
382     end
383   end
384
385   class NonYieldingAroundFilterController < ActionController::Base
386
387     before_filter :filter_one
388     around_filter :non_yielding_filter
389     before_filter :filter_two
390     after_filter :filter_three
391
392     def index
393       render :inline => "index"
394     end
395
396     #make sure the controller complains
397     def rescue_action(e); raise e; end
398
399     private
400
401       def filter_one
402         @filters  ||= []
403         @filters  << "filter_one"
404       end
405
406       def filter_two
407         @filters  << "filter_two"
408       end
409
410       def non_yielding_filter
411         @filters  << "zomg it didn't yield"
412         @filter_return_value
413       end
414
415       def filter_three
416         @filters  << "filter_three"
417       end
418
419   end
420
421   def test_non_yielding_around_filters_not_returning_false_do_not_raise
422     controller = NonYieldingAroundFilterController.new
423     controller.instance_variable_set "@filter_return_value", true
424     assert_nothing_raised do
425       test_process(controller, "index")
426     end
427   end
428
429   def test_non_yielding_around_filters_returning_false_do_not_raise
430     controller = NonYieldingAroundFilterController.new
431     controller.instance_variable_set "@filter_return_value", false
432     assert_nothing_raised do
433       test_process(controller, "index")
434     end
435   end
436
437   def test_after_filters_are_not_run_if_around_filter_returns_false
438     controller = NonYieldingAroundFilterController.new
439     controller.instance_variable_set "@filter_return_value", false
440     test_process(controller, "index")
441     assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters']
442   end
443
444   def test_after_filters_are_not_run_if_around_filter_does_not_yield
445     controller = NonYieldingAroundFilterController.new
446     controller.instance_variable_set "@filter_return_value", true
447     test_process(controller, "index")
448     assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters']
449   end
450
451   def test_empty_filter_chain
452     assert_equal 0, EmptyFilterChainController.filter_chain.size
453     assert test_process(EmptyFilterChainController).template.assigns['action_executed']
454   end
455
456   def test_added_filter_to_inheritance_graph
457     assert_equal [ :ensure_login ], TestController.before_filters
458   end
459
460   def test_base_class_in_isolation
461     assert_equal [ ], ActionController::Base.before_filters
462   end
463
464   def test_prepending_filter
465     assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_filters
466   end
467
468   def test_running_filters
469     assert_equal %w( wonderful_life ensure_login ), test_process(PrependingController).template.assigns["ran_filter"]
470   end
471
472   def test_running_filters_with_proc
473     assert test_process(ProcController).template.assigns["ran_proc_filter"]
474   end
475
476   def test_running_filters_with_implicit_proc
477     assert test_process(ImplicitProcController).template.assigns["ran_proc_filter"]
478   end
479
480   def test_running_filters_with_class
481     assert test_process(AuditController).template.assigns["was_audited"]
482   end
483
484   def test_running_anomolous_yet_valid_condition_filters
485     response = test_process(AnomolousYetValidConditionController)
486     assert_equal %w( ensure_login ), response.template.assigns["ran_filter"]
487     assert response.template.assigns["ran_class_filter"]
488     assert response.template.assigns["ran_proc_filter1"]
489     assert response.template.assigns["ran_proc_filter2"]
490
491     response = test_process(AnomolousYetValidConditionController, "show_without_filter")
492     assert_equal nil, response.template.assigns["ran_filter"]
493     assert !response.template.assigns["ran_class_filter"]
494     assert !response.template.assigns["ran_proc_filter1"]
495     assert !response.template.assigns["ran_proc_filter2"]
496   end
497
498   def test_running_conditional_options
499     response = test_process(ConditionalOptionsFilter)
500     assert_equal %w( ensure_login ), response.template.assigns["ran_filter"]
501   end
502
503   def test_running_collection_condition_filters
504     assert_equal %w( ensure_login ), test_process(ConditionalCollectionFilterController).template.assigns["ran_filter"]
505     assert_equal nil, test_process(ConditionalCollectionFilterController, "show_without_filter").template.assigns["ran_filter"]
506     assert_equal nil, test_process(ConditionalCollectionFilterController, "another_action").template.assigns["ran_filter"]
507   end
508
509   def test_running_only_condition_filters
510     assert_equal %w( ensure_login ), test_process(OnlyConditionSymController).template.assigns["ran_filter"]
511     assert_equal nil, test_process(OnlyConditionSymController, "show_without_filter").template.assigns["ran_filter"]
512
513     assert test_process(OnlyConditionProcController).template.assigns["ran_proc_filter"]
514     assert !test_process(OnlyConditionProcController, "show_without_filter").template.assigns["ran_proc_filter"]
515
516     assert test_process(OnlyConditionClassController).template.assigns["ran_class_filter"]
517     assert !test_process(OnlyConditionClassController, "show_without_filter").template.assigns["ran_class_filter"]
518   end
519
520   def test_running_except_condition_filters
521     assert_equal %w( ensure_login ), test_process(ExceptConditionSymController).template.assigns["ran_filter"]
522     assert_equal nil, test_process(ExceptConditionSymController, "show_without_filter").template.assigns["ran_filter"]
523
524     assert test_process(ExceptConditionProcController).template.assigns["ran_proc_filter"]
525     assert !test_process(ExceptConditionProcController, "show_without_filter").template.assigns["ran_proc_filter"]
526
527     assert test_process(ExceptConditionClassController).template.assigns["ran_class_filter"]
528     assert !test_process(ExceptConditionClassController, "show_without_filter").template.assigns["ran_class_filter"]
529   end
530
531   def test_running_before_and_after_condition_filters
532     assert_equal %w( ensure_login clean_up_tmp), test_process(BeforeAndAfterConditionController).template.assigns["ran_filter"]
533     assert_equal nil, test_process(BeforeAndAfterConditionController, "show_without_filter").template.assigns["ran_filter"]
534   end
535
536   def test_around_filter
537     controller = test_process(AroundFilterController)
538     assert controller.template.assigns["before_ran"]
539     assert controller.template.assigns["after_ran"]
540   end
541
542   def test_before_after_class_filter
543     controller = test_process(BeforeAfterClassFilterController)
544     assert controller.template.assigns["before_ran"]
545     assert controller.template.assigns["after_ran"]
546   end
547
548   def test_having_properties_in_around_filter
549     controller = test_process(AroundFilterController)
550     assert_equal "before and after", controller.template.assigns["execution_log"]
551   end
552
553   def test_prepending_and_appending_around_filter
554     controller = test_process(MixedFilterController)
555     assert_equal " before aroundfilter  before procfilter  before appended aroundfilter " +
556                  " after appended aroundfilter  after aroundfilter  after procfilter ",
557                  MixedFilterController.execution_log
558   end
559
560   def test_rendering_breaks_filtering_chain
561     response = test_process(RenderingController)
562     assert_equal "something else", response.body
563     assert !response.template.assigns["ran_action"]
564   end
565
566   def test_filters_with_mixed_specialization_run_in_order
567     assert_nothing_raised do
568       response = test_process(MixedSpecializationController, 'bar')
569       assert_equal 'bar', response.body
570     end
571
572     assert_nothing_raised do
573       response = test_process(MixedSpecializationController, 'foo')
574       assert_equal 'foo', response.body
575     end
576   end
577
578   def test_dynamic_dispatch
579     %w(foo bar baz).each do |action|
580       request = ActionController::TestRequest.new
581       request.query_parameters[:choose] = action
582       response = DynamicDispatchController.process(request, ActionController::TestResponse.new)
583       assert_equal action, response.body
584     end
585   end
586
587   def test_running_prepended_before_and_after_filter
588     assert_equal 3, PrependingBeforeAndAfterController.filter_chain.length
589     response = test_process(PrependingBeforeAndAfterController)
590     assert_equal %w( before_all between_before_all_and_after_all after_all ), response.template.assigns["ran_filter"]
591   end
592  
593   def test_skipping_and_limiting_controller
594     assert_equal %w( ensure_login ), test_process(SkippingAndLimitedController, "index").template.assigns["ran_filter"]
595   &nbs