1
require 'abstract_unit'
3
# FIXME: crashes Ruby 1.9
4
class FilterTest < Test::Unit::TestCase
5
class TestController < ActionController::Base
6
before_filter :ensure_login
10
render :inline => "ran action"
16
@ran_filter << "ensure_login"
20
@ran_after_filter ||= []
21
@ran_after_filter << "clean_up"
25
class ChangingTheRequirementsController < TestController
26
before_filter :ensure_login, :except => [:go_wild]
29
render :text => "gobble"
33
class TestMultipleFiltersController < ActionController::Base
39
define_method "fail_#{i}" do
40
render :text => i.to_s
46
define_method "try_#{i}" do
47
instance_variable_set :@try, i
48
if action_name == "fail_#{i}"
55
class RenderingController < ActionController::Base
56
before_filter :render_something_else
60
render :inline => "ran action"
64
def render_something_else
65
render :inline => "something else"
69
class ConditionalFilterController < ActionController::Base
71
render :inline => "ran action"
75
render :inline => "ran action"
78
def show_without_filter
79
render :inline => "ran action without filter"
85
@ran_filter << "ensure_login"
90
@ran_filter << "clean_up_tmp"
93
def rescue_action(e) raise(e) end
96
class ConditionalCollectionFilterController < ConditionalFilterController
97
before_filter :ensure_login, :except => [ :show_without_filter, :another_action ]
100
class OnlyConditionSymController < ConditionalFilterController
101
before_filter :ensure_login, :only => :show
104
class ExceptConditionSymController < ConditionalFilterController
105
before_filter :ensure_login, :except => :show_without_filter
108
class BeforeAndAfterConditionController < ConditionalFilterController
109
before_filter :ensure_login, :only => :show
110
after_filter :clean_up_tmp, :only => :show
113
class OnlyConditionProcController < ConditionalFilterController
114
before_filter(:only => :show) {|c| c.instance_variable_set(:"@ran_proc_filter", true) }
117
class ExceptConditionProcController < ConditionalFilterController
118
before_filter(:except => :show_without_filter) {|c| c.instance_variable_set(:"@ran_proc_filter", true) }
121
class ConditionalClassFilter
122
def self.filter(controller) controller.instance_variable_set(:"@ran_class_filter", true) end
125
class OnlyConditionClassController < ConditionalFilterController
126
before_filter ConditionalClassFilter, :only => :show
129
class ExceptConditionClassController < ConditionalFilterController
130
before_filter ConditionalClassFilter, :except => :show_without_filter
133
class AnomolousYetValidConditionController < ConditionalFilterController
134
before_filter(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.instance_variable_set(:"@ran_proc_filter1", true)}, :except => :show_without_filter) { |c| c.instance_variable_set(:"@ran_proc_filter2", true)}
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 }
142
class EmptyFilterChainController < TestController
143
self.filter_chain.clear
145
@action_executed = true
146
render :text => "yawp!"
150
class PrependingController < TestController
151
prepend_before_filter :wonderful_life
152
# skip_before_filter :fire_flash
157
@ran_filter << "wonderful_life"
161
class SkippingAndLimitedController < TestController
162
skip_before_filter :ensure_login
163
before_filter :ensure_login, :only => :index
173
class SkippingAndReorderingController < TestController
174
skip_before_filter :ensure_login
175
before_filter :find_record
176
before_filter :ensure_login
181
@ran_filter << "find_record"
185
class ConditionalSkippingController < TestController
186
skip_before_filter :ensure_login, :only => [ :login ]
187
skip_after_filter :clean_up, :only => [ :login ]
189
before_filter :find_user, :only => [ :change_password ]
192
render :inline => "ran action"
196
render :inline => "ran action"
202
@ran_filter << "find_user"
206
class ConditionalParentOfConditionalSkippingController < ConditionalFilterController
207
before_filter :conditional_in_parent, :only => [:show, :another_action]
208
after_filter :conditional_in_parent, :only => [:show, :another_action]
212
def conditional_in_parent
214
@ran_filter << 'conditional_in_parent'
218
class ChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController
219
skip_before_filter :conditional_in_parent, :only => :another_action
220
skip_after_filter :conditional_in_parent, :only => :another_action
223
class AnotherChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController
224
skip_before_filter :conditional_in_parent, :only => :show
227
class ProcController < PrependingController
228
before_filter(proc { |c| c.instance_variable_set(:"@ran_proc_filter", true) })
231
class ImplicitProcController < PrependingController
232
before_filter { |c| c.instance_variable_set(:"@ran_proc_filter", true) }
236
def self.filter(controller)
237
controller.instance_variable_set(:"@was_audited", true)
242
def before(controller)
243
@execution_log = "before"
244
controller.class.execution_log << " before aroundfilter " if controller.respond_to? :execution_log
245
controller.instance_variable_set(:"@before_ran", true)
248
def after(controller)
249
controller.instance_variable_set(:"@execution_log", @execution_log + " and after")
250
controller.instance_variable_set(:"@after_ran", true)
251
controller.class.execution_log << " after aroundfilter " if controller.respond_to? :execution_log
255
class AppendedAroundFilter
256
def before(controller)
257
controller.class.execution_log << " before appended aroundfilter "
260
def after(controller)
261
controller.class.execution_log << " after appended aroundfilter "
265
class AuditController < ActionController::Base
266
before_filter(AuditFilter)
269
render :text => "hello"
273
class AroundFilterController < PrependingController
274
around_filter AroundFilter.new
277
class BeforeAfterClassFilterController < PrependingController
279
filter = AroundFilter.new
285
class MixedFilterController < PrependingController
286
cattr_accessor :execution_log
292
before_filter { |c| c.class.execution_log << " before procfilter " }
293
prepend_around_filter AroundFilter.new
295
after_filter { |c| c.class.execution_log << " after procfilter " }
296
append_around_filter AppendedAroundFilter.new
299
class MixedSpecializationController < ActionController::Base
300
class OutOfOrder < StandardError; end
303
before_filter :second, :only => :foo
306
render :text => 'foo'
310
render :text => 'bar'
319
raise OutOfOrder unless @first
323
class DynamicDispatchController < ActionController::Base
324
before_filter :choose
326
%w(foo bar baz).each do |action|
327
define_method(action) { render :text => action }
332
self.action_name = params[:choose]
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
343
@ran_filter << 'before_all'
348
@ran_filter << 'after_all'
351
def between_before_all_and_after_all
353
@ran_filter << 'between_before_all_and_after_all'
356
render :text => 'hello'
360
class ErrorToRescue < Exception; end
362
class RescuingAroundFilterWithBlock
363
def filter(controller)
366
rescue ErrorToRescue => ex
367
controller.__send__ :render, :text => "I rescued this: #{ex.inspect}"
372
class RescuedController < ActionController::Base
373
around_filter RescuingAroundFilterWithBlock.new
376
raise ErrorToRescue.new("Something made the bad noise.")
380
def rescue_action(exception)
385
class NonYieldingAroundFilterController < ActionController::Base
387
before_filter :filter_one
388
around_filter :non_yielding_filter
389
before_filter :filter_two
390
after_filter :filter_three
393
render :inline => "index"
396
#make sure the controller complains
397
def rescue_action(e); raise e; end
403
@filters << "filter_one"
407
@filters << "filter_two"
410
def non_yielding_filter
411
@filters << "zomg it didn't yield"
416
@filters << "filter_three"
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")
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")
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']
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']
451
def test_empty_filter_chain
452
assert_equal 0, EmptyFilterChainController.filter_chain.size
453
assert test_process(EmptyFilterChainController).template.assigns['action_executed']
456
def test_added_filter_to_inheritance_graph
457
assert_equal [ :ensure_login ], TestController.before_filters
460
def test_base_class_in_isolation
461
assert_equal [ ], ActionController::Base.before_filters
464
def test_prepending_filter
465
assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_filters
468
def test_running_filters
469
assert_equal %w( wonderful_life ensure_login ), test_process(PrependingController).template.assigns["ran_filter"]
472
def test_running_filters_with_proc
473
assert test_process(ProcController).template.assigns["ran_proc_filter"]
476
def test_running_filters_with_implicit_proc
477
assert test_process(ImplicitProcController).template.assigns["ran_proc_filter"]
480
def test_running_filters_with_class
481
assert test_process(AuditController).template.assigns["was_audited"]
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"]
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"]
498
def test_running_conditional_options
499
response = test_process(ConditionalOptionsFilter)
500
assert_equal %w( ensure_login ), response.template.assigns["ran_filter"]
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"]
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"]
513
assert test_process(OnlyConditionProcController).template.assigns["ran_proc_filter"]
514
assert !test_process(OnlyConditionProcController, "show_without_filter").template.assigns["ran_proc_filter"]
516
assert test_process(OnlyConditionClassController).template.assigns["ran_class_filter"]
517
assert !test_process(OnlyConditionClassController, "show_without_filter").template.assigns["ran_class_filter"]
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"]
524
assert test_process(ExceptConditionProcController).template.assigns["ran_proc_filter"]
525
assert !test_process(ExceptConditionProcController, "show_without_filter").template.assigns["ran_proc_filter"]
527
assert test_process(ExceptConditionClassController).template.assigns["ran_class_filter"]
528
assert !test_process(ExceptConditionClassController, "show_without_filter").template.assigns["ran_class_filter"]
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"]
536
def test_around_filter
537
controller = test_process(AroundFilterController)
538
assert controller.template.assigns["before_ran"]
539
assert controller.template.assigns["after_ran"]
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"]
548
def test_having_properties_in_around_filter
549
controller = test_process(AroundFilterController)
550
assert_equal "before and after", controller.template.assigns["execution_log"]
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
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"]
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
572
assert_nothing_raised do
573
response = test_process(MixedSpecializationController, 'foo')
574
assert_equal 'foo', response.body
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
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"]
593
def test_skipping_and_limiting_controller
594
assert_equal %w( ensure_login ), test_process(SkippingAndLimitedController, "index").template.assigns["ran_filter"]
595
assert_nil test_process(SkippingAndLimitedController, "public").template.assigns["ran_filter"]
598
def test_skipping_and_reordering_controller
599
assert_equal %w( find_record ensure_login ), test_process(SkippingAndReorderingController, "index").template.assigns["ran_filter"]
602
def test_conditional_skipping_of_filters
603
assert_nil test_process(ConditionalSkippingController, "login").template.assigns["ran_filter"]
604
assert_equal %w( ensure_login find_user ), test_process(ConditionalSkippingController, "change_password").template.assigns["ran_filter"]
606
assert_nil test_process(ConditionalSkippingController, "login").template.controller.instance_variable_get("@ran_after_filter")
607
assert_equal %w( clean_up ), test_process(ConditionalSkippingController, "change_password").template.controller.instance_variable_get("@ran_after_filter")
610
def test_conditional_skipping_of_filters_when_parent_filter_is_also_conditional
611
assert_equal %w( conditional_in_parent conditional_in_parent ), test_process(ChildOfConditionalParentController).template.assigns['ran_filter']
612
assert_nil test_process(ChildOfConditionalParentController, 'another_action').template.assigns['ran_filter']
615
def test_condition_skipping_of_filters_when_siblings_also_have_conditions
616
assert_equal %w( conditional_in_parent conditional_in_parent ), test_process(ChildOfConditionalParentController).template.assigns['ran_filter'], "1"
617
assert_equal nil, test_process(AnotherChildOfConditionalParentController).template.assigns['ran_filter']
618
assert_equal %w( conditional_in_parent conditional_in_parent ), test_process(ChildOfConditionalParentController).template.assigns['ran_filter']
621
def test_changing_the_requirements
622
assert_equal nil, test_process(ChangingTheRequirementsController, "go_wild").template.assigns['ran_filter']
625
def test_a_rescuing_around_filter
627
assert_nothing_raised do
628
response = test_process(RescuedController)
631
assert response.success?
632
assert_equal("I rescued this: #<FilterTest::ErrorToRescue: Something made the bad noise.>", response.body)
636
def test_process(controller, action = "show")
637
ActionController::Base.class_eval { include ActionController::ProcessWithTest } unless ActionController::Base < ActionController::ProcessWithTest
638
request = ActionController::TestRequest.new
639
request.action = action
640
controller = controller.new if controller.is_a?(Class)
641
controller.process_with_test(request, ActionController::TestResponse.new)
647
class PostsController < ActionController::Base
648
def rescue_action(e); raise e; end
650
module AroundExceptions
651
class Error < StandardError ; end
652
class Before < Error ; end
653
class After < Error ; end
655
include AroundExceptions
658
include AroundExceptions
661
module_eval %w(raises_before raises_after raises_both no_raise no_filter).map { |action| "def #{action}; default_action end" }.join("\n")
665
render :inline => "#{action_name} called"
669
class ControllerWithSymbolAsFilter < PostsController
670
around_filter :raise_before, :only => :raises_before
671
around_filter :raise_after, :only => :raises_after
672
around_filter :without_exception, :only => :no_raise
685
def without_exception
696
class ControllerWithFilterClass < PostsController
697
class YieldingFilter < DefaultFilter
698
def self.filter(controller)
704
around_filter YieldingFilter, :only => :raises_after
707
class ControllerWithFilterInstance < PostsController
708
class YieldingFilter < DefaultFilter
709
def filter(controller)
715
around_filter YieldingFilter.new, :only => :raises_after
718
class ControllerWithFilterMethod < PostsController
719
class YieldingFilter < DefaultFilter
720
def filter(controller)
726
around_filter YieldingFilter.new.method(:filter), :only => :raises_after
729
class ControllerWithProcFilter < PostsController
730
around_filter(:only => :no_raise) do |c,b|
731
c.instance_variable_set(:"@before", true)
733
c.instance_variable_set(:"@after", true)
737
class ControllerWithNestedFilters < ControllerWithSymbolAsFilter
738
around_filter :raise_before, :raise_after, :without_exception, :only => :raises_both
741
class ControllerWithAllTypesOfFilters < PostsController
742
before_filter :before
743
around_filter :around
745
around_filter :around_again
750
@ran_filter << 'before'
754
@ran_filter << 'around (before yield)'
756
@ran_filter << 'around (after yield)'
760
@ran_filter << 'after'
764
@ran_filter << 'around_again (before yield)'
766
@ran_filter << 'around_again (after yield)'
770
class ControllerWithTwoLessFilters < ControllerWithAllTypesOfFilters
771
skip_filter :around_again
775
class YieldingAroundFiltersTest < Test::Unit::TestCase
776
include PostsController::AroundExceptions
778
def test_filters_registering
779
assert_equal 1, ControllerWithFilterMethod.filter_chain.size
780
assert_equal 1, ControllerWithFilterClass.filter_chain.size
781
assert_equal 1, ControllerWithFilterInstance.filter_chain.size
782
assert_equal 3, ControllerWithSymbolAsFilter.filter_chain.size
783
assert_equal 6, ControllerWithNestedFilters.filter_chain.size
784
assert_equal 4, ControllerWithAllTypesOfFilters.filter_chain.size
788
controller = PostsController
789
assert_nothing_raised { test_process(controller,'no_raise') }
790
assert_nothing_raised { test_process(controller,'raises_before') }
791
assert_nothing_raised { test_process(controller,'raises_after') }
792
assert_nothing_raised { test_process(controller,'no_filter') }
796
controller = ControllerWithSymbolAsFilter
797
assert_nothing_raised { test_process(controller,'no_raise') }
798
assert_raise(Before) { test_process(controller,'raises_before') }
799
assert_raise(After) { test_process(controller,'raises_after') }
800
assert_nothing_raised { test_process(controller,'no_raise') }
804
controller = ControllerWithFilterClass
805
assert_nothing_raised { test_process(controller,'no_raise') }
806
assert_raise(After) { test_process(controller,'raises_after') }
809
def test_with_instance
810
controller = ControllerWithFilterInstance
811
assert_nothing_raised { test_process(controller,'no_raise') }
812
assert_raise(After) { test_process(controller,'raises_after') }
816
controller = ControllerWithFilterMethod
817
assert_nothing_raised { test_process(controller,'no_raise') }
818
assert_raise(After) { test_process(controller,'raises_after') }
822
controller = test_process(ControllerWithProcFilter,'no_raise')
823
assert controller.template.assigns['before']
824
assert controller.template.assigns['after']
827
def test_nested_filters
828
controller = ControllerWithNestedFilters
829
assert_nothing_raised do
831
test_process(controller,'raises_both')
835
assert_raise Before do
837
test_process(controller,'raises_both')
843
def test_filter_order_with_all_filter_types
844
controller = test_process(ControllerWithAllTypesOfFilters,'no_raise')
845
assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) around (after yield) after',controller.template.assigns['ran_filter'].join(' ')
848
def test_filter_order_with_skip_filter_method
849
controller = test_process(ControllerWithTwoLessFilters,'no_raise')
850
assert_equal 'before around (before yield) around (after yield)',controller.template.assigns['ran_filter'].join(' ')
853
def test_first_filter_in_multiple_before_filter_chain_halts
854
controller = ::FilterTest::TestMultipleFiltersController.new
855
response = test_process(controller, 'fail_1')
856
assert_equal ' ', response.body
857
assert_equal 1, controller.instance_variable_get(:@try)
858
assert controller.instance_variable_get(:@before_filter_chain_aborted)
861
def test_second_filter_in_multiple_before_filter_chain_halts
862
controller = ::FilterTest::TestMultipleFiltersController.new
863
response = test_process(controller, 'fail_2')
864
assert_equal ' ', response.body
865
assert_equal 2, controller.instance_variable_get(:@try)
866
assert controller.instance_variable_get(:@before_filter_chain_aborted)
869
def test_last_filter_in_multiple_before_filter_chain_halts
870
controller = ::FilterTest::TestMultipleFiltersController.new
871
response = test_process(controller, 'fail_3')
872
assert_equal ' ', response.body
873
assert_equal 3, controller.instance_variable_get(:@try)
874
assert controller.instance_variable_get(:@before_filter_chain_aborted)
878
def test_process(controller, action = "show")
879
ActionController::Base.class_eval { include ActionController::ProcessWithTest } unless ActionController::Base < ActionController::ProcessWithTest
880
request = ActionController::TestRequest.new
881
request.action = action
882
controller = controller.new if controller.is_a?(Class)
883
controller.process_with_test(request, ActionController::TestResponse.new)