5 use lib "$FindBin::Bin/../lib";
9 BEGIN { $iters = $ENV{CAT_BENCH_ITERS} || 1; }
14 use Catalyst::Test 'TestApp';
16 if ( $ENV{CAT_BENCHMARK} ) {
18 Benchmark::timethis( $iters, \&run_tests );
27 my ($run_number) = @_;
30 # This is a simple test where the parent and child actions are
31 # within the same controller.
35 TestApp::Controller::Action::Chained->begin
36 TestApp::Controller::Action::Chained->foo
37 TestApp::Controller::Action::Chained->endpoint
38 TestApp::Controller::Action::Chained->end
41 my $expected = join( ", ", @expected );
43 ok( my $response = request('http://localhost/chained/foo/1/end/2'), 'chained + local endpoint' );
44 is( $response->header('X-Catalyst-Executed'),
45 $expected, 'Executed actions' );
46 is( $response->content, '1; 2', 'Content OK' );
50 # This makes sure the above isn't found if the argument for the
51 # end action isn't supplied.
56 ok( my $response = request('http://localhost/chained/foo/1/end'),
57 'chained + local endpoint; missing last argument' );
58 is( $response->header('X-Catalyst-Executed'),
59 $expected, 'Executed actions' );
60 is( $response->code, 500, 'Status OK' );
64 # Tests the case when the child action is placed in a subcontroller.
68 TestApp::Controller::Action::Chained->begin
69 TestApp::Controller::Action::Chained->foo
70 TestApp::Controller::Action::Chained::Foo->spoon
71 TestApp::Controller::Action::Chained->end
74 my $expected = join( ", ", @expected );
76 ok( my $response = request('http://localhost/chained/foo/1/spoon'), 'chained + subcontroller endpoint' );
77 is( $response->header('X-Catalyst-Executed'),
78 $expected, 'Executed actions' );
79 is( $response->content, '1; ', 'Content OK' );
83 # Tests if the relative specification (e.g.: Chained('bar') ) works
88 TestApp::Controller::Action::Chained->begin
89 TestApp::Controller::Action::Chained->bar
90 TestApp::Controller::Action::Chained->finale
91 TestApp::Controller::Action::Chained->end
94 my $expected = join( ", ", @expected );
96 ok( my $response = request('http://localhost/chained/bar/1/spoon'), 'chained + relative endpoint' );
97 is( $response->header('X-Catalyst-Executed'),
98 $expected, 'Executed actions' );
99 is( $response->content, '; 1, spoon', 'Content OK' );
103 # Just a test for multiple arguments.
107 TestApp::Controller::Action::Chained->begin
108 TestApp::Controller::Action::Chained->foo2
109 TestApp::Controller::Action::Chained->endpoint2
110 TestApp::Controller::Action::Chained->end
113 my $expected = join( ", ", @expected );
115 ok( my $response = request('http://localhost/chained/foo2/10/20/end2/15/25'),
116 'chained + local (2 args each)' );
117 is( $response->header('X-Catalyst-Executed'),
118 $expected, 'Executed actions' );
119 is( $response->content, '10, 20; 15, 25', 'Content OK' );
123 # The first three-chain test tries to call the action with :Args(1)
124 # specification. There's also a one action with a :CaptureArgs(1)
125 # attribute, that should not be dispatched to.
129 TestApp::Controller::Action::Chained->begin
130 TestApp::Controller::Action::Chained->one_end
131 TestApp::Controller::Action::Chained->end
134 my $expected = join( ", ", @expected );
136 ok( my $response = request('http://localhost/chained/one/23'),
137 'three-chain (only first)' );
138 is( $response->header('X-Catalyst-Executed'),
139 $expected, 'Executed actions' );
140 is( $response->content, '; 23', 'Content OK' );
144 # This is the second three-chain test, it goes for the action that
145 # handles "/one/$cap/two/$arg1/$arg2" paths. Should be the two action
146 # having :Args(2), not the one having :CaptureArgs(2).
150 TestApp::Controller::Action::Chained->begin
151 TestApp::Controller::Action::Chained->one
152 TestApp::Controller::Action::Chained->two_end
153 TestApp::Controller::Action::Chained->end
156 my $expected = join( ", ", @expected );
158 ok( my $response = request('http://localhost/chained/one/23/two/23/46'),
159 'three-chain (up to second)' );
160 is( $response->header('X-Catalyst-Executed'),
161 $expected, 'Executed actions' );
162 is( $response->content, '23; 23, 46', 'Content OK' );
166 # Last of the three-chain tests. Has no concurrent action with :CaptureArgs
167 # and is more thought to simply test the chain as a whole and the 'two'
168 # action specifying :CaptureArgs.
172 TestApp::Controller::Action::Chained->begin
173 TestApp::Controller::Action::Chained->one
174 TestApp::Controller::Action::Chained->two
175 TestApp::Controller::Action::Chained->three_end
176 TestApp::Controller::Action::Chained->end
179 my $expected = join( ", ", @expected );
181 ok( my $response = request('http://localhost/chained/one/23/two/23/46/three/1/2/3'),
182 'three-chain (all three)' );
183 is( $response->header('X-Catalyst-Executed'),
184 $expected, 'Executed actions' );
185 is( $response->content, '23, 23, 46; 1, 2, 3', 'Content OK' );
189 # Tests dispatching on number of arguments for :Args. This should be
190 # dispatched to the action expecting one argument.
194 TestApp::Controller::Action::Chained->begin
195 TestApp::Controller::Action::Chained->multi1
196 TestApp::Controller::Action::Chained->end
199 my $expected = join( ", ", @expected );
201 ok( my $response = request('http://localhost/chained/multi/23'),
202 'multi-action (one arg)' );
203 is( $response->header('X-Catalyst-Executed'),
204 $expected, 'Executed actions' );
205 is( $response->content, '; 23', 'Content OK' );
209 # Belongs to the former test and goes for the action expecting two arguments.
213 TestApp::Controller::Action::Chained->begin
214 TestApp::Controller::Action::Chained->multi2
215 TestApp::Controller::Action::Chained->end
218 my $expected = join( ", ", @expected );
220 ok( my $response = request('http://localhost/chained/multi/23/46'),
221 'multi-action (two args)' );
222 is( $response->header('X-Catalyst-Executed'),
223 $expected, 'Executed actions' );
224 is( $response->content, '; 23, 46', 'Content OK' );
228 # Dispatching on argument count again, this time we provide too many
229 # arguments, so dispatching should fail.
232 my $expected = undef;
234 ok( my $response = request('http://localhost/chained/multi/23/46/67'),
235 'multi-action (three args, should lead to error)' );
236 is( $response->header('X-Catalyst-Executed'),
237 $expected, 'Executed actions' );
238 is( $response->code, 500, 'Status OK' );
242 # This tests the case when an action says it's the child of an action in
247 TestApp::Controller::Action::Chained->begin
248 TestApp::Controller::Action::Chained::Foo->higher_root
249 TestApp::Controller::Action::Chained->higher_root
250 TestApp::Controller::Action::Chained->end
253 my $expected = join( ", ", @expected );
255 ok( my $response = request('http://localhost/chained/higher_root/23/bar/11'),
256 'root higher than child' );
257 is( $response->header('X-Catalyst-Executed'),
258 $expected, 'Executed actions' );
259 is( $response->content, '23; 11', 'Content OK' );
263 # Just a more complex version of the former test. It tests if a controller ->
264 # subcontroller -> controller dispatch works.
268 TestApp::Controller::Action::Chained->begin
269 TestApp::Controller::Action::Chained->pcp1
270 TestApp::Controller::Action::Chained::Foo->pcp2
271 TestApp::Controller::Action::Chained->pcp3
272 TestApp::Controller::Action::Chained->end
275 my $expected = join( ", ", @expected );
277 ok( my $response = request('http://localhost/chained/pcp1/1/pcp2/2/pcp3/3'),
278 'parent -> child -> parent' );
279 is( $response->header('X-Catalyst-Executed'),
280 $expected, 'Executed actions' );
281 is( $response->content, '1, 2; 3', 'Content OK' );
285 # Tests dispatch on capture number. This test is for a one capture action.
289 TestApp::Controller::Action::Chained->begin
290 TestApp::Controller::Action::Chained->multi_cap1
291 TestApp::Controller::Action::Chained->multi_cap_end1
292 TestApp::Controller::Action::Chained->end
295 my $expected = join( ", ", @expected );
297 ok( my $response = request('http://localhost/chained/multi_cap/1/baz'),
298 'dispatch on capture num 1' );
299 is( $response->header('X-Catalyst-Executed'),
300 $expected, 'Executed actions' );
301 is( $response->content, '1; ', 'Content OK' );
305 # Belongs to the former test. This one goes for the action expecting two
310 TestApp::Controller::Action::Chained->begin
311 TestApp::Controller::Action::Chained->multi_cap2
312 TestApp::Controller::Action::Chained->multi_cap_end2
313 TestApp::Controller::Action::Chained->end
316 my $expected = join( ", ", @expected );
318 ok( my $response = request('http://localhost/chained/multi_cap/1/2/baz'),
319 'dispatch on capture num 2' );
320 is( $response->header('X-Catalyst-Executed'),
321 $expected, 'Executed actions' );
322 is( $response->content, '1, 2; ', 'Content OK' );
326 # Tests the priority of a slurpy arguments action (with :Args) against
327 # two actions chained together. The two actions should win.
331 TestApp::Controller::Action::Chained->begin
332 TestApp::Controller::Action::Chained->priority_a2
333 TestApp::Controller::Action::Chained->priority_a2_end
334 TestApp::Controller::Action::Chained->end
337 my $expected = join( ", ", @expected );
339 ok( my $response = request('http://localhost/chained/priority_a/1/end/2'),
340 'priority - slurpy args vs. parent/child' );
341 is( $response->header('X-Catalyst-Executed'),
342 $expected, 'Executed actions' );
343 is( $response->content, '1; 2', 'Content OK' );
347 # This belongs to the former test but tests if two chained actions have
348 # priority over an action with the exact arguments.
352 TestApp::Controller::Action::Chained->begin
353 TestApp::Controller::Action::Chained->priority_b2
354 TestApp::Controller::Action::Chained->priority_b2_end
355 TestApp::Controller::Action::Chained->end
358 my $expected = join( ", ", @expected );
360 ok( my $response = request('http://localhost/chained/priority_b/1/end/2'),
361 'priority - fixed args vs. parent/child' );
362 is( $response->header('X-Catalyst-Executed'),
363 $expected, 'Executed actions' );
364 is( $response->content, '1; 2', 'Content OK' );
368 # This belongs to the former test but tests if two chained actions have
369 # priority over an action with one child action not having the Args() attr set.
373 TestApp::Controller::Action::Chained->begin
374 TestApp::Controller::Action::Chained->priority_c1
375 TestApp::Controller::Action::Chained->priority_c2_xyz
376 TestApp::Controller::Action::Chained->end
379 my $expected = join( ", ", @expected );
381 ok( my $response = request('http://localhost/chained/priority_c/1/xyz/'),
382 'priority - no Args() order mismatch' );
383 is( $response->header('X-Catalyst-Executed'),
384 $expected, 'Executed actions' );
385 is( $response->content, '1; ', 'Content OK' );
389 # Test dispatching between two controllers that are on the same level and
390 # therefor have no parent/child relationship.
394 TestApp::Controller::Action::Chained->begin
395 TestApp::Controller::Action::Chained::Bar->cross1
396 TestApp::Controller::Action::Chained::Foo->cross2
397 TestApp::Controller::Action::Chained->end
400 my $expected = join( ", ", @expected );
402 ok( my $response = request('http://localhost/chained/cross/1/end/2'),
403 'cross controller w/o par/child relation' );
404 is( $response->header('X-Catalyst-Executed'),
405 $expected, 'Executed actions' );
406 is( $response->content, '1; 2', 'Content OK' );
410 # This is for testing if the arguments got passed to the actions
415 TestApp::Controller::Action::Chained->begin
416 TestApp::Controller::Action::Chained::PassedArgs->first
417 TestApp::Controller::Action::Chained::PassedArgs->second
418 TestApp::Controller::Action::Chained::PassedArgs->third
419 TestApp::Controller::Action::Chained::PassedArgs->end
422 my $expected = join( ", ", @expected );
424 ok( my $response = request('http://localhost/chained/passedargs/a/1/b/2/c/3'),
425 'Correct arguments passed to actions' );
426 is( $response->header('X-Catalyst-Executed'),
427 $expected, 'Executed actions' );
428 is( $response->content, '1; 2; 3', 'Content OK' );
432 # The :Args attribute is optional, we check the action not specifying
433 # it with these tests.
437 TestApp::Controller::Action::Chained->begin
438 TestApp::Controller::Action::Chained->opt_args
439 TestApp::Controller::Action::Chained->end
442 my $expected = join( ", ", @expected );
444 ok( my $response = request('http://localhost/chained/opt_args/1/2/3'),
445 'Optional :Args attribute working' );
446 is( $response->header('X-Catalyst-Executed'),
447 $expected, 'Executed actions' );
448 is( $response->content, '; 1, 2, 3', 'Content OK' );
452 # Tests for optional PathPart attribute.
456 TestApp::Controller::Action::Chained->begin
457 TestApp::Controller::Action::Chained->opt_pp_start
458 TestApp::Controller::Action::Chained->opt_pathpart
459 TestApp::Controller::Action::Chained->end
462 my $expected = join( ", ", @expected );
464 ok( my $response = request('http://localhost/chained/optpp/1/opt_pathpart/2'),
465 'Optional :PathName attribute working' );
466 is( $response->header('X-Catalyst-Executed'),
467 $expected, 'Executed actions' );
468 is( $response->content, '1; 2', 'Content OK' );
472 # Tests for optional PathPart *and* Args attributes.
476 TestApp::Controller::Action::Chained->begin
477 TestApp::Controller::Action::Chained->opt_all_start
478 TestApp::Controller::Action::Chained->oa
479 TestApp::Controller::Action::Chained->end
482 my $expected = join( ", ", @expected );
484 ok( my $response = request('http://localhost/chained/optall/1/oa/2/3'),
485 'Optional :PathName *and* :Args attributes working' );
486 is( $response->header('X-Catalyst-Executed'),
487 $expected, 'Executed actions' );
488 is( $response->content, '1; 2, 3', 'Content OK' );
492 # Test if :Chained is the same as :Chained('/')
496 TestApp::Controller::Action::Chained->begin
497 TestApp::Controller::Action::Chained->rootdef
498 TestApp::Controller::Action::Chained->end
501 my $expected = join( ", ", @expected );
503 ok( my $response = request('http://localhost/chained/rootdef/23'),
504 ":Chained is the same as :Chained('/')" );
505 is( $response->header('X-Catalyst-Executed'),
506 $expected, 'Executed actions' );
507 is( $response->content, '; 23', 'Content OK' );
511 # Test if :Chained('.') is working
515 TestApp::Controller::Action::Chained->begin
516 TestApp::Controller::Action::Chained->parentchain
517 TestApp::Controller::Action::Chained::ParentChain->child
518 TestApp::Controller::Action::Chained->end
521 my $expected = join( ", ", @expected );
523 ok( my $response = request('http://localhost/chained/parentchain/1/child/2'),
524 ":Chained('.') chains to parent controller action" );
525 is( $response->header('X-Catalyst-Executed'),
526 $expected, 'Executed actions' );
527 is( $response->content, '1; 2', 'Content OK' );
531 # Test if :Chained('../act') is working
535 TestApp::Controller::Action::Chained->begin
536 TestApp::Controller::Action::Chained->one
537 TestApp::Controller::Action::Chained::ParentChain->chained_rel
538 TestApp::Controller::Action::Chained->end
541 my $expected = join( ", ", @expected );
543 ok( my $response = request('http://localhost/chained/one/1/chained_rel/3/2'),
544 ":Chained('../action') chains to correct action" );
545 is( $response->header('X-Catalyst-Executed'),
546 $expected, 'Executed actions' );
547 is( $response->content, '1; 3, 2', 'Content OK' );
551 # Test if ../ works to go up more than one level
555 TestApp::Controller::Action::Chained->begin
556 TestApp::Controller::Action::Chained->one
557 TestApp::Controller::Action::Chained::ParentChain::Relative->chained_rel_two
558 TestApp::Controller::Action::Chained->end
561 my $expected = join( ", ", @expected );
563 ok( my $response = request('http://localhost/chained/one/1/chained_rel_two/42/23'),
564 "../ works to go up more than one level" );
565 is( $response->header('X-Catalyst-Executed'),
566 $expected, 'Executed actions' );
567 is( $response->content, '1; 42, 23', 'Content OK' );
571 # Test if :ChainedParent is working
575 TestApp::Controller::Action::Chained->begin
576 TestApp::Controller::Action::Chained->loose
577 TestApp::Controller::Action::Chained::ParentChain->loose
578 TestApp::Controller::Action::Chained->end
581 my $expected = join( ", ", @expected );
583 ok( my $response = request('http://localhost/chained/loose/4/loose/a/b'),
584 ":Chained('../action') chains to correct action" );
585 is( $response->header('X-Catalyst-Executed'),
586 $expected, 'Executed actions' );
587 is( $response->content, '4; a, b', 'Content OK' );
591 # Test if :Chained('../name/act') is working
595 TestApp::Controller::Action::Chained->begin
596 TestApp::Controller::Action::Chained::Bar->cross1
597 TestApp::Controller::Action::Chained::ParentChain->up_down
598 TestApp::Controller::Action::Chained->end
601 my $expected = join( ", ", @expected );
603 ok( my $response = request('http://localhost/chained/cross/4/up_down/5'),
604 ":Chained('../action') chains to correct action" );
605 is( $response->header('X-Catalyst-Executed'),
606 $expected, 'Executed actions' );
607 is( $response->content, '4; 5', 'Content OK' );
611 # Test behaviour of auto actions returning '1' for the chain.
615 TestApp::Controller::Action::Chained->begin
616 TestApp::Controller::Action::Chained::Auto->auto
617 TestApp::Controller::Action::Chained::Auto::Foo->auto
618 TestApp::Controller::Action::Chained::Auto->foo
619 TestApp::Controller::Action::Chained::Auto::Foo->fooend
620 TestApp::Controller::Action::Chained->end
623 my $expected = join( ", ", @expected );
625 ok( my $response = request('http://localhost/chained/autochain1/1/fooend/2'),
626 "Behaviour when auto returns 1 correct" );
627 is( $response->header('X-Catalyst-Executed'),
628 $expected, 'Executed actions' );
629 is( $response->content, '1; 2', 'Content OK' );
633 # Test behaviour of auto actions returning '0' for the chain.
637 TestApp::Controller::Action::Chained->begin
638 TestApp::Controller::Action::Chained::Auto->auto
639 TestApp::Controller::Action::Chained::Auto::Bar->auto
640 TestApp::Controller::Action::Chained->end
643 my $expected = join( ", ", @expected );
645 ok( my $response = request('http://localhost/chained/autochain2/1/barend/2'),
646 "Behaviour when auto returns 0 correct" );
647 is( $response->header('X-Catalyst-Executed'),
648 $expected, 'Executed actions' );
649 is( $response->content, '1; 2', 'Content OK' );
653 # Test what auto actions are run when namespaces are changed
658 TestApp::Controller::Action::Chained->begin
659 TestApp::Controller::Action::Chained::Auto->auto
660 TestApp::Controller::Action::Chained::Auto::Foo->auto
661 TestApp::Controller::Action::Chained::Auto::Bar->crossloose
662 TestApp::Controller::Action::Chained::Auto::Foo->crossend
663 TestApp::Controller::Action::Chained->end
666 my $expected = join( ", ", @expected );
668 ok( my $response = request('http://localhost/chained/auto_cross/1/crossend/2'),
669 "Correct auto actions are run on cross controller dispatch" );
670 is( $response->header('X-Catalyst-Executed'),
671 $expected, 'Executed actions' );
672 is( $response->content, '1; 2', 'Content OK' );
676 # Test forwarding from auto action in chain dispatch.
680 TestApp::Controller::Action::Chained->begin
681 TestApp::Controller::Action::Chained::Auto->auto
682 TestApp::Controller::Action::Chained::Auto::Forward->auto
683 TestApp::Controller::Action::Chained::Auto->fw3
684 TestApp::Controller::Action::Chained::Auto->fw1
685 TestApp::Controller::Action::Chained::Auto::Forward->forwardend
686 TestApp::Controller::Action::Chained->end
689 my $expected = join( ", ", @expected );
691 ok( my $response = request('http://localhost/chained/auto_forward/1/forwardend/2'),
692 "Forwarding out of auto in chain" );
693 is( $response->header('X-Catalyst-Executed'),
694 $expected, 'Executed actions' );
695 is( $response->content, '1; 2', 'Content OK' );
699 # Detaching out of the auto action of a chain.
703 TestApp::Controller::Action::Chained->begin
704 TestApp::Controller::Action::Chained::Auto->auto
705 TestApp::Controller::Action::Chained::Auto::Detach->auto
706 TestApp::Controller::Action::Chained::Auto->fw3
707 TestApp::Controller::Action::Chained->end
710 my $expected = join( ", ", @expected );
712 ok( my $response = request('http://localhost/chained/auto_detach/1/detachend/2'),
713 "Detaching out of auto in chain" );
714 is( $response->header('X-Catalyst-Executed'),
715 $expected, 'Executed actions' );
716 is( $response->content, '1; 2', 'Content OK' );
720 # Test forwarding from auto action in chain dispatch.
723 my $expected = undef;
725 ok( my $response = request('http://localhost/chained/loose/23'),
726 "Loose end is not callable" );
727 is( $response->header('X-Catalyst-Executed'),
728 $expected, 'Executed actions' );
729 is( $response->code, 500, 'Status OK' );
733 # Test forwarding out of a chain.
737 TestApp::Controller::Action::Chained->begin
738 TestApp::Controller::Action::Chained->chain_fw_a
739 TestApp::Controller::Action::Chained->fw_dt_target
740 TestApp::Controller::Action::Chained->chain_fw_b
741 TestApp::Controller::Action::Chained->end
744 my $expected = join( ", ", @expected );
746 ok( my $response = request('http://localhost/chained/chain_fw/1/end/2'),
747 "Forwarding out a chain" );
748 is( $response->header('X-Catalyst-Executed'),
749 $expected, 'Executed actions' );
750 is( $response->content, '1; 2', 'Content OK' );
754 # Test detaching out of a chain.
758 TestApp::Controller::Action::Chained->begin
759 TestApp::Controller::Action::Chained->chain_dt_a
760 TestApp::Controller::Action::Chained->fw_dt_target
761 TestApp::Controller::Action::Chained->end
764 my $expected = join( ", ", @expected );
766 ok( my $response = request('http://localhost/chained/chain_dt/1/end/2'),
767 "Forwarding out a chain" );
768 is( $response->header('X-Catalyst-Executed'),
769 $expected, 'Executed actions' );
770 is( $response->content, '1; 2', 'Content OK' );
774 # Test throwing an error in the middle of a chain.
778 TestApp::Controller::Action::Chained->begin
779 TestApp::Controller::Action::Chained->chain_error_a
780 TestApp::Controller::Action::Chained->end
783 my $expected = join( ", ", @expected );
785 ok( my $response = request('http://localhost/chained/chain_error/1/end/2'),
786 "Break a chain in the middle" );
787 is( $response->header('X-Catalyst-Executed'),
788 $expected, 'Executed actions' );
789 is( $response->content, 'FATAL ERROR: break in the middle of a chain', 'Content OK' );
793 # Test dieing in the middle of a chain.
797 TestApp::Controller::Action::Chained->begin
798 TestApp::Controller::Action::Chained->chain_die_a
799 TestApp::Controller::Action::Chained->end
802 my $expected = join( ", ", @expected );
804 ok( my $response = request('http://localhost/chained/chain_die/1/end/2'),
805 "Break a chain in the middle" );
806 is( $response->header('X-Catalyst-Executed'),
807 $expected, 'Executed actions' );
808 is( $response->content, 'FATAL ERROR: Caught exception in TestApp::Controller::Action::Chained->chain_die_a "die in the middle of a chain"', 'Content OK' );
812 # Tests that an uri_for to a chained root index action
813 # returns the right value.
816 ok( my $response = request(
817 'http://localhost/action/chained/to_root' ),
818 'uri_for with chained root action as arg' );
819 like( $response->content,
820 qr(URI:https?://[^/]+/),
821 'Correct URI generated' );
825 # Test interception of recursive chains. This test was added because at
826 # one point during the :Chained development, Catalyst used to hang on
830 eval { require 'TestAppChainedRecursive.pm' };
831 if ($run_number == 1) {
832 ok( ! $@, "Interception of recursive chains" );
834 else { pass( "Interception of recursive chains already tested" ) }
838 # Test failure of absolute path part arguments.
841 eval { require 'TestAppChainedAbsolutePathPart.pm' };
842 if ($run_number == 1) {
843 like( $@, qr(foo/foo),
844 "Usage of absolute path part argument emits error" );
846 else { pass( "Error on absolute path part arguments already tested" ) }
850 # Test chained actions in the root controller
854 TestApp::Controller::Action::Chained::Root->rootsub
855 TestApp::Controller::Action::Chained::Root->endpointsub
856 TestApp::Controller::Root->end
859 my $expected = join( ", ", @expected );
861 ok( my $response = request('http://localhost/rootsub/1/endpointsub/2'), 'chained in root namespace' );
862 is( $response->header('X-Catalyst-Executed'),
863 $expected, 'Executed actions' );
864 is( $response->content, '', 'Content OK' );
868 # Complex path with multiple empty pathparts
872 TestApp::Controller::Action::Chained->begin
873 TestApp::Controller::Action::Chained->mult_nopp_base
874 TestApp::Controller::Action::Chained->mult_nopp_all
875 TestApp::Controller::Action::Chained->end
878 my $expected = join( ", ", @expected );
880 ok( my $response = request('http://localhost/chained/mult_nopp'),
881 "Complex path with multiple empty pathparts" );
882 is( $response->header('X-Catalyst-Executed'),
883 $expected, 'Executed actions' );
884 is( $response->content, '; ', 'Content OK' );
888 # Complex path with multiple non-capturing pathparts
889 # PathPart('') CaptureArgs(0), PathPart('foo') CaptureArgs(0), PathPart('') Args(0)
890 # should win over PathPart('') CaptureArgs(1), PathPart('') Args(0)
894 TestApp::Controller::Action::Chained->begin
895 TestApp::Controller::Action::Chained->mult_nopp2_base
896 TestApp::Controller::Action::Chained->mult_nopp2_nocap
897 TestApp::Controller::Action::Chained->mult_nopp2_action
898 TestApp::Controller::Action::Chained->mult_nopp2_action_default
899 TestApp::Controller::Action::Chained->end
902 my $expected = join( ", ", @expected );
904 ok( my $response = request('http://localhost/chained/mult_nopp2/action'),
905 "Complex path with multiple non-capturing pathparts" );
906 is( $response->header('X-Catalyst-Executed'),
907 $expected, 'Executed actions' );
908 is( $response->content, '; ', 'Content OK' );
912 # Higher Args() hiding more specific CaptureArgs chains sections
916 TestApp::Controller::Action::Chained->begin
917 TestApp::Controller::Action::Chained->cc_base
918 TestApp::Controller::Action::Chained->cc_link
919 TestApp::Controller::Action::Chained->cc_anchor
920 TestApp::Controller::Action::Chained->end
923 my $expected = join ', ', @expected;
925 ok( my $response = request('http://localhost/chained/choose_capture/anchor.html'),
926 'Choose between an early Args() and a later more ideal chain' );
927 is( $response->header('X-Catalyst-Executed') => $expected, 'Executed actions');
928 is( $response->content => '; ', 'Content OK' );
932 # Less specific chain not being seen correctly due to earlier looser capture
936 TestApp::Controller::Action::Chained->begin
937 TestApp::Controller::Action::Chained->cc_base
938 TestApp::Controller::Action::Chained->cc_b
939 TestApp::Controller::Action::Chained->cc_b_link
940 TestApp::Controller::Action::Chained->cc_b_anchor
941 TestApp::Controller::Action::Chained->end
944 my $expected = join ', ', @expected;
946 ok( my $response = request('http://localhost/chained/choose_capture/b/a/anchor.html'),
947 'Choose between a more specific chain and an earlier looser one' );
948 is( $response->header('X-Catalyst-Executed') => $expected, 'Executed actions');
949 is( $response->content => 'a; ', 'Content OK' );
953 # Check we get the looser one when it's the correct match
957 TestApp::Controller::Action::Chained->begin
958 TestApp::Controller::Action::Chained->cc_base
959 TestApp::Controller::Action::Chained->cc_a
960 TestApp::Controller::Action::Chained->cc_a_link
961 TestApp::Controller::Action::Chained->cc_a_anchor
962 TestApp::Controller::Action::Chained->end
965 my $expected = join ', ', @expected;
967 ok( my $response = request('http://localhost/chained/choose_capture/a/a/anchor.html'),
968 'Choose between a more specific chain and an earlier looser one' );
969 is( $response->header('X-Catalyst-Executed') => $expected, 'Executed actions');
970 is( $response->content => 'a; anchor.html', 'Content OK' );
973 # CaptureArgs(1) PathPart('...') should win over CaptureArgs(2) PathPart('')
976 TestApp::Controller::Action::Chained->begin
977 TestApp::Controller::Action::Chained::CaptureArgs->base
978 TestApp::Controller::Action::Chained::CaptureArgs->one_arg
979 TestApp::Controller::Action::Chained::CaptureArgs->edit_one_arg
980 TestApp::Controller::Action::Chained::CaptureArgs->end
983 my $expected = join( ", ", @expected );
985 # should dispatch to /base/one_args/edit_one_arg
986 ok( my $response = request('http://localhost/captureargs/one/edit'),
987 'Correct arg order ran' );
989 local $TODO = 'Known bug';
990 is( $response->header('X-Catalyst-Executed'),
991 $expected, 'Executed actions' );
992 is( $response->content, 'base; one_arg; edit_one_arg', 'Content OK' );
996 # PathPart('...') Args(1) should win over CaptureArgs(2) PathPart('')
999 TestApp::Controller::Action::Chained->begin
1000 TestApp::Controller::Action::Chained::CaptureArgs->base
1001 TestApp::Controller::Action::Chained::CaptureArgs->test_one_arg
1002 TestApp::Controller::Action::Chained::CaptureArgs->end
1005 my $expected = join( ", ", @expected );
1007 # should dispatch to /base/test_one_arg
1008 ok( my $response = request('http://localhost/captureargs/test/one'),
1009 'Correct pathpart/arg ran' );
1011 local $TODO = 'Known bug';
1012 is( $response->header('X-Catalyst-Executed'),
1013 $expected, 'Executed actions' );
1014 is( $response->content, 'base; test_plus_arg; one;', 'Content OK' );
1019 # Args(0) should win over Args() if we actually have no arguments.
1022 TestApp::Controller::Action::Chained->begin
1023 TestApp::Controller::Action::Chained::ArgsOrder->base
1024 TestApp::Controller::Action::Chained::ArgsOrder->index
1025 TestApp::Controller::Action::Chained::ArgsOrder->end
1028 my $expected = join( ", ", @expected );
1030 # With no args, we should run "index"
1031 ok( my $response = request('http://localhost/argsorder/'),
1032 'Correct arg order ran' );
1033 is( $response->header('X-Catalyst-Executed'),
1034 $expected, 'Executed actions' );
1035 is( $response->content, 'base; ; index; ', 'Content OK' );
1037 # With args given, run "all"
1038 ok( $response = request('http://localhost/argsorder/X'),
1039 'Correct arg order ran' );
1040 is( $response->header('X-Catalyst-Executed'),
1043 TestApp::Controller::Action::Chained->begin
1044 TestApp::Controller::Action::Chained::ArgsOrder->base
1045 TestApp::Controller::Action::Chained::ArgsOrder->all
1046 TestApp::Controller::Action::Chained::ArgsOrder->end
1049 is( $response->content, 'base; ; all; X', 'Content OK' );
1058 TestApp::Controller::Action::Chained->begin
1059 TestApp::Controller::Action::Chained::PathPrefix->instance
1060 TestApp::Controller::Action::Chained->end
1063 my $expected = join( ", ", @expected );
1065 ok( my $response = request('http://localhost/action/chained/pathprefix/1'),
1066 "PathPrefix (as an endpoint)" );
1067 is( $response->header('X-Catalyst-Executed'),
1068 $expected, 'Executed actions' );
1069 is( $response->content, '; 1', 'Content OK' );
1073 # static paths vs. captures
1077 TestApp::Controller::Action::Chained->begin
1078 TestApp::Controller::Action::Chained->apan
1079 TestApp::Controller::Action::Chained->korv
1080 TestApp::Controller::Action::Chained->static_end
1081 TestApp::Controller::Action::Chained->end
1084 my $expected = join( ", ", @expected );
1086 ok( my $response = request('http://localhost/action/chained/static_end'),
1087 "static paths are prefered over captures" );
1088 is( $response->header('X-Catalyst-Executed'),
1089 $expected, 'Executed actions' );
1096 # request for doc/search should end up in doc/*
1099 TestApp::Controller::Action::Chained->begin
1100 TestApp::Controller::Action::Chained->doc_star
1101 TestApp::Controller::Action::Chained->end
1104 my $expected = join( ", ", @expected );
1106 ok( my $response = request('http://localhost/chained/doc/search'),
1107 "we prefer static path parts earlier in the chain" );
1109 local $TODO = 'gbjk never got off his ass and fixed this';
1110 is( $response->header('X-Catalyst-Executed'),
1111 $expected, 'Executed actions' );
1117 get('http://localhost/chained/capture%2Farg%3B/return_arg/foo%2Fbar%3B'),
1118 'request with URI-encoded arg' );
1119 like( $content, qr{foo/bar;\z}, 'args decoded' );
1120 like( $content, qr{capture/arg;}, 'captureargs decoded' );
1124 get('http://localhost/chained/return_arg_decoded/foo%2Fbar%3B'),
1125 'request with URI-encoded arg' );
1126 like( $content, qr{foo/bar;\z}, 'args decoded' );
1129 # Test round tripping, specifically the / character %2F in uri_for:
1130 # not being able to feed it back action + captureargs and args into uri for
1131 # and result in the original request uri is a major piece of suck ;)
1134 ['foo%2Fbar', 'baz'],
1135 ['foo', 'bar%2Fbaz'],
1136 ['foo%2Fbar', 'baz%2Fquux'],
1137 ['foo%2Fbar', 'baz%2Fquux', { foo => 'bar', 'baz' => 'quux%2Ffrood'}],
1138 ['foo%2Fbar', 'baz%2Fquux', { foo => 'bar', 'baz%2Ffnoo' => 'quux%2Ffrood'}],
1139 ['h%C3%BCtte', 'h%C3%BCtte', { test => 'h%C3%BCtte' } ],
1141 my $path = '/chained/roundtrip_urifor/' .
1142 $thing->[0] . '/' . $thing->[1];
1143 $path .= '?' . join('&',
1144 map { $_ .'='. $thing->[2]->{$_}}
1145 sort keys %{$thing->[2]}) if $thing->[2];
1147 get('http://localhost/' . $path),
1148 'request ' . $path . ' ok');
1149 my $exp = URI->new('http://localhost:3000' . $path);
1150 my ($want) = $content =~ m{/chained/(.*)};
1151 my $got = URI->new('http://localhost:3000/chained/' . $want);
1152 # Just check that the path matches, as who the hell knows or cares
1153 # where the app is based (live tests etc)
1154 is $got->path, $exp->path, "uri $path can round trip through uri_for (path)"
1155 or diag("Expected $path, got $content");
1156 is_deeply $got->query_form_hash, $exp->query_form_hash, "uri $path can round trip through uri_for (query)"
1157 or diag("Expected $path, got $content");
1165 ok( my $response = request('http://localhost/chained/match_captures/foo/bar'), 'match_captures: falling through' );
1166 is($response->header('X-TestAppActionTestMatchCaptures'), 'fallthrough', 'match_captures: fell through');
1168 ok($response = request('http://localhost/chained/match_captures/force/bar'), 'match_captures: *not* falling through' );
1169 is($response->header('X-TestAppActionTestMatchCaptures'), 'forcing', 'match_captures: forced');
1170 is($response->header('X-TestAppActionTestMatchCapturesHasRan'), 'yes', 'match_captures: actually ran');