48bc3fa59b86086bc4d4423dffe2be84e8c3a869
[yaffs-website] / vendor / symfony-cmf / routing / Tests / Routing / ChainRouterTest.php
1 <?php
2
3 /*
4  * This file is part of the Symfony CMF package.
5  *
6  * (c) 2011-2015 Symfony CMF
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Cmf\Component\Routing\Tests\Routing;
13
14 use Symfony\Cmf\Component\Routing\VersatileGeneratorInterface;
15 use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
16 use Symfony\Component\Routing\Exception\MethodNotAllowedException;
17 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
18 use Symfony\Component\Routing\Exception\RouteNotFoundException;
19 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
20 use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
21 use Symfony\Component\Routing\RequestContext;
22 use Symfony\Component\Routing\RouteCollection;
23 use Symfony\Component\HttpFoundation\Request;
24 use Symfony\Cmf\Component\Routing\ChainRouter;
25 use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
26 use Symfony\Component\Routing\RouterInterface;
27
28 class ChainRouterTest extends CmfUnitTestCase
29 {
30     /**
31      * @var ChainRouter
32      */
33     private $router;
34     /**
35      * @var RequestContext|\PHPUnit_Framework_MockObject_MockObject
36      */
37     private $context;
38
39     public function setUp()
40     {
41         $this->router = new ChainRouter($this->getMock('Psr\Log\LoggerInterface'));
42         $this->context = $this->getMock('Symfony\Component\Routing\RequestContext');
43     }
44
45     public function testPriority()
46     {
47         $this->assertEquals(array(), $this->router->all());
48
49         list($low, $high) = $this->createRouterMocks();
50
51         $this->router->add($low, 10);
52         $this->router->add($high, 100);
53
54         $this->assertEquals(array(
55             $high,
56             $low,
57         ), $this->router->all());
58     }
59
60     public function testHasRouters()
61     {
62         $this->assertEquals(array(), $this->router->all());
63         $this->assertFalse($this->router->hasRouters());
64
65         list($low, $high) = $this->createRouterMocks();
66
67         $this->router->add($low, 10);
68         $this->router->add($high, 100);
69
70         $this->assertTrue($this->router->hasRouters());
71     }
72
73     /**
74      * Routers are supposed to be sorted only once.
75      * This test will check that by trying to get all routers several times.
76      *
77      * @covers \Symfony\Cmf\Component\Routing\ChainRouter::sortRouters
78      * @covers \Symfony\Cmf\Component\Routing\ChainRouter::all
79      */
80     public function testSortRouters()
81     {
82         list($low, $medium, $high) = $this->createRouterMocks();
83         // We're using a mock here and not $this->router because we need to ensure that the sorting operation is done only once.
84         /** @var $router ChainRouter|\PHPUnit_Framework_MockObject_MockObject */
85         $router = $this->buildMock('Symfony\Cmf\Component\Routing\ChainRouter', array('sortRouters'));
86         $router
87             ->expects($this->once())
88             ->method('sortRouters')
89             ->will(
90                 $this->returnValue(
91                     array($high, $medium, $low)
92                 )
93             )
94         ;
95
96         $router->add($low, 10);
97         $router->add($medium, 50);
98         $router->add($high, 100);
99         $expectedSortedRouters = array($high, $medium, $low);
100         // Let's get all routers 5 times, we should only sort once.
101         for ($i = 0; $i < 5; ++$i) {
102             $this->assertSame($expectedSortedRouters, $router->all());
103         }
104     }
105
106     /**
107      * This test ensures that if a router is being added on the fly, the sorting is reset.
108      *
109      * @covers \Symfony\Cmf\Component\Routing\ChainRouter::sortRouters
110      * @covers \Symfony\Cmf\Component\Routing\ChainRouter::all
111      * @covers \Symfony\Cmf\Component\Routing\ChainRouter::add
112      */
113     public function testReSortRouters()
114     {
115         list($low, $medium, $high) = $this->createRouterMocks();
116         $highest = clone $high;
117         // We're using a mock here and not $this->router because we need to ensure that the sorting operation is done only once.
118         /** @var $router ChainRouter|\PHPUnit_Framework_MockObject_MockObject */
119         $router = $this->buildMock('Symfony\Cmf\Component\Routing\ChainRouter', array('sortRouters'));
120         $router
121             ->expects($this->at(0))
122             ->method('sortRouters')
123             ->will(
124                 $this->returnValue(
125                     array($high, $medium, $low)
126                 )
127             )
128         ;
129         // The second time sortRouters() is called, we're supposed to get the newly added router ($highest)
130         $router
131             ->expects($this->at(1))
132             ->method('sortRouters')
133             ->will(
134                 $this->returnValue(
135                     array($highest, $high, $medium, $low)
136                 )
137             )
138         ;
139
140         $router->add($low, 10);
141         $router->add($medium, 50);
142         $router->add($high, 100);
143         $this->assertSame(array($high, $medium, $low), $router->all());
144
145         // Now adding another router on the fly, sorting must have been reset
146         $router->add($highest, 101);
147         $this->assertSame(array($highest, $high, $medium, $low), $router->all());
148     }
149
150     /**
151      * context must be propagated to chained routers and be stored locally.
152      */
153     public function testContext()
154     {
155         list($low, $high) = $this->createRouterMocks();
156
157         $low
158             ->expects($this->once())
159             ->method('setContext')
160             ->with($this->context)
161         ;
162
163         $high
164             ->expects($this->once())
165             ->method('setContext')
166             ->with($this->context)
167         ;
168
169         $this->router->add($low, 10);
170         $this->router->add($high, 100);
171
172         $this->router->setContext($this->context);
173         $this->assertSame($this->context, $this->router->getContext());
174     }
175
176     /**
177      * context must be propagated also when routers are added after context is set.
178      */
179     public function testContextOrder()
180     {
181         list($low, $high) = $this->createRouterMocks();
182
183         $low
184             ->expects($this->once())
185             ->method('setContext')
186             ->with($this->context)
187         ;
188
189         $high
190             ->expects($this->once())
191             ->method('setContext')
192             ->with($this->context)
193         ;
194
195         $this->router->setContext($this->context);
196
197         $this->router->add($low, 10);
198         $this->router->add($high, 100);
199
200         $this->router->all();
201
202         $this->assertSame($this->context, $this->router->getContext());
203     }
204
205     /**
206      * The first usable match is used, no further routers are queried once a match is found.
207      */
208     public function testMatch()
209     {
210         $url = '/test';
211         list($lower, $low, $high) = $this->createRouterMocks();
212
213         $high
214             ->expects($this->once())
215             ->method('match')
216             ->with($url)
217             ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
218         ;
219         $low
220             ->expects($this->once())
221             ->method('match')
222             ->with($url)
223             ->will($this->returnValue(array('test')))
224         ;
225         $lower
226             ->expects($this->never())
227             ->method('match');
228         $this->router->add($lower, 5);
229         $this->router->add($low, 10);
230         $this->router->add($high, 100);
231
232         $result = $this->router->match('/test');
233         $this->assertEquals(array('test'), $result);
234     }
235
236     /**
237      * The first usable match is used, no further routers are queried once a match is found.
238      */
239     public function testMatchRequest()
240     {
241         $url = '/test';
242         list($lower, $low, $high) = $this->createRouterMocks();
243
244         $highest = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\RequestMatcher');
245
246         $request = Request::create('/test');
247
248         $highest
249             ->expects($this->once())
250             ->method('matchRequest')
251             ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
252         ;
253         $high
254             ->expects($this->once())
255             ->method('match')
256             ->with($url)
257             ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
258         ;
259         $low
260             ->expects($this->once())
261             ->method('match')
262             ->with($url)
263             ->will($this->returnValue(array('test')))
264         ;
265         $lower
266             ->expects($this->never())
267             ->method('match')
268         ;
269
270         $this->router->add($lower, 5);
271         $this->router->add($low, 10);
272         $this->router->add($high, 100);
273         $this->router->add($highest, 200);
274
275         $result = $this->router->matchRequest($request);
276         $this->assertEquals(array('test'), $result);
277     }
278
279     /**
280      * Call match on ChainRouter that has RequestMatcher in the chain.
281      */
282     public function testMatchWithRequestMatchers()
283     {
284         $url = '/test';
285
286         list($low) = $this->createRouterMocks();
287
288         $high = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\RequestMatcher');
289
290         $high
291             ->expects($this->once())
292             ->method('matchRequest')
293             ->with($this->callback(function (Request $r) use ($url) {
294                 return $r->getPathInfo() === $url;
295             }))
296             ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
297         ;
298         $low
299             ->expects($this->once())
300             ->method('match')
301             ->with($url)
302             ->will($this->returnValue(array('test')))
303         ;
304
305         $this->router->add($low, 10);
306         $this->router->add($high, 20);
307
308         $result = $this->router->match($url);
309         $this->assertEquals(array('test'), $result);
310     }
311
312     public function provideBaseUrl()
313     {
314         return array(
315             array(''),
316             array('/web'),
317         );
318     }
319
320     /**
321      * Call match on ChainRouter that has RequestMatcher in the chain.
322      *
323      * @dataProvider provideBaseUrl
324      */
325     public function testMatchWithRequestMatchersAndContext($baseUrl)
326     {
327         $url = '//test';
328
329         list($low) = $this->createRouterMocks();
330
331         $high = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\RequestMatcher');
332
333         $high
334             ->expects($this->once())
335             ->method('matchRequest')
336             ->with($this->callback(function (Request $r) use ($url, $baseUrl) {
337                 return true === $r->isSecure()
338                     && 'foobar.com' === $r->getHost()
339                     && 4433 === $r->getPort()
340                     && $baseUrl === $r->getBaseUrl()
341                     && $url === $r->getPathInfo()
342                 ;
343             }))
344             ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
345         ;
346         $low
347             ->expects($this->once())
348             ->method('match')
349             ->with($url)
350             ->will($this->returnValue(array('test')))
351         ;
352
353         $this->router->add($low, 10);
354         $this->router->add($high, 20);
355
356         $requestContext = new RequestContext();
357         $requestContext->setScheme('https');
358         $requestContext->setHost('foobar.com');
359         $requestContext->setHttpsPort(4433);
360         $requestContext->setBaseUrl($baseUrl);
361         $this->router->setContext($requestContext);
362
363         $result = $this->router->match($url);
364         $this->assertEquals(array('test'), $result);
365     }
366
367     /**
368      * If there is a method not allowed but another router matches, that one is used.
369      */
370     public function testMatchAndNotAllowed()
371     {
372         $url = '/test';
373         list($low, $high) = $this->createRouterMocks();
374
375         $high
376             ->expects($this->once())
377             ->method('match')
378             ->with($url)
379             ->will($this->throwException(new \Symfony\Component\Routing\Exception\MethodNotAllowedException(array())))
380         ;
381         $low
382             ->expects($this->once())
383             ->method('match')
384             ->with($url)
385             ->will($this->returnValue(array('test')))
386         ;
387         $this->router->add($low, 10);
388         $this->router->add($high, 100);
389
390         $result = $this->router->match('/test');
391         $this->assertEquals(array('test'), $result);
392     }
393
394     /**
395      * If there is a method not allowed but another router matches, that one is used.
396      */
397     public function testMatchRequestAndNotAllowed()
398     {
399         $url = '/test';
400         list($low, $high) = $this->createRouterMocks();
401
402         $high
403             ->expects($this->once())
404             ->method('match')
405             ->with($url)
406             ->will($this->throwException(new \Symfony\Component\Routing\Exception\MethodNotAllowedException(array())))
407         ;
408         $low
409             ->expects($this->once())
410             ->method('match')
411             ->with($url)
412             ->will($this->returnValue(array('test')))
413         ;
414         $this->router->add($low, 10);
415         $this->router->add($high, 100);
416
417         $result = $this->router->matchRequest(Request::create('/test'));
418         $this->assertEquals(array('test'), $result);
419     }
420
421     /**
422      * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
423      */
424     public function testMatchNotFound()
425     {
426         $url = '/test';
427         list($low, $high) = $this->createRouterMocks();
428
429         $high
430             ->expects($this->once())
431             ->method('match')
432             ->with($url)
433             ->will($this->throwException(new ResourceNotFoundException()))
434         ;
435         $low
436             ->expects($this->once())
437             ->method('match')
438             ->with($url)
439             ->will($this->throwException(new ResourceNotFoundException()))
440         ;
441         $this->router->add($low, 10);
442         $this->router->add($high, 100);
443
444         $this->router->match('/test');
445     }
446
447     /**
448      * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
449      */
450     public function testMatchRequestNotFound()
451     {
452         $url = '/test';
453         list($low, $high) = $this->createRouterMocks();
454
455         $high
456             ->expects($this->once())
457             ->method('match')
458             ->with($url)
459             ->will($this->throwException(new ResourceNotFoundException()))
460         ;
461         $low
462             ->expects($this->once())
463             ->method('match')
464             ->with($url)
465             ->will($this->throwException(new ResourceNotFoundException()))
466         ;
467         $this->router->add($low, 10);
468         $this->router->add($high, 100);
469
470         $this->router->matchRequest(Request::create('/test'));
471     }
472
473     /**
474      * Call match on ChainRouter that has RequestMatcher in the chain.
475      *
476      * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
477      * @expectedExceptionMessage None of the routers in the chain matched url '/test'
478      */
479     public function testMatchWithRequestMatchersNotFound()
480     {
481         $url = '/test';
482         $request = Request::create('/test');
483
484         $high = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\RequestMatcher');
485
486         $high
487             ->expects($this->once())
488             ->method('matchRequest')
489             ->with($request)
490             ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
491         ;
492
493         $this->router->add($high, 20);
494
495         $this->router->match($url);
496     }
497
498     /**
499      * If any of the routers throws a not allowed exception and no other matches, we need to see this.
500      *
501      * @expectedException \Symfony\Component\Routing\Exception\MethodNotAllowedException
502      */
503     public function testMatchMethodNotAllowed()
504     {
505         $url = '/test';
506         list($low, $high) = $this->createRouterMocks();
507
508         $high
509             ->expects($this->once())
510             ->method('match')
511             ->with($url)
512             ->will($this->throwException(new MethodNotAllowedException(array())))
513         ;
514         $low
515             ->expects($this->once())
516             ->method('match')
517             ->with($url)
518             ->will($this->throwException(new ResourceNotFoundException()))
519         ;
520         $this->router->add($low, 10);
521         $this->router->add($high, 100);
522
523         $this->router->match('/test');
524     }
525
526     /**
527      * If any of the routers throws a not allowed exception and no other matches, we need to see this.
528      *
529      * @expectedException \Symfony\Component\Routing\Exception\MethodNotAllowedException
530      */
531     public function testMatchRequestMethodNotAllowed()
532     {
533         $url = '/test';
534         list($low, $high) = $this->createRouterMocks();
535
536         $high
537             ->expects($this->once())
538             ->method('match')
539             ->with($url)
540             ->will($this->throwException(new MethodNotAllowedException(array())))
541         ;
542         $low
543             ->expects($this->once())
544             ->method('match')
545             ->with($url)
546             ->will($this->throwException(new ResourceNotFoundException()))
547         ;
548         $this->router->add($low, 10);
549         $this->router->add($high, 100);
550
551         $this->router->matchRequest(Request::create('/test'));
552     }
553
554     public function testGenerate()
555     {
556         $url = '/test';
557         $name = 'test';
558         $parameters = array('test' => 'value');
559         list($lower, $low, $high) = $this->createRouterMocks();
560
561         $high
562             ->expects($this->once())
563             ->method('generate')
564             ->with($name, $parameters, UrlGeneratorInterface::ABSOLUTE_PATH)
565             ->will($this->throwException(new RouteNotFoundException()))
566         ;
567         $low
568             ->expects($this->once())
569             ->method('generate')
570             ->with($name, $parameters, UrlGeneratorInterface::ABSOLUTE_PATH)
571             ->will($this->returnValue($url))
572         ;
573         $lower
574             ->expects($this->never())
575             ->method('generate')
576         ;
577
578         $this->router->add($low, 10);
579         $this->router->add($high, 100);
580
581         $result = $this->router->generate($name, $parameters);
582         $this->assertEquals($url, $result);
583     }
584
585     /**
586      * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
587      */
588     public function testGenerateNotFound()
589     {
590         $name = 'test';
591         $parameters = array('test' => 'value');
592         list($low, $high) = $this->createRouterMocks();
593
594         $high
595             ->expects($this->once())
596             ->method('generate')
597             ->with($name, $parameters, UrlGeneratorInterface::ABSOLUTE_PATH)
598             ->will($this->throwException(new RouteNotFoundException()))
599         ;
600         $low->expects($this->once())
601             ->method('generate')
602             ->with($name, $parameters, UrlGeneratorInterface::ABSOLUTE_PATH)
603             ->will($this->throwException(new RouteNotFoundException()))
604         ;
605         $this->router->add($low, 10);
606         $this->router->add($high, 100);
607
608         $this->router->generate($name, $parameters);
609     }
610
611     /**
612      * Route is an object but no versatile generator around to do the debug message.
613      *
614      * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
615      */
616     public function testGenerateObjectNotFound()
617     {
618         $name = new \stdClass();
619         $parameters = array('test' => 'value');
620
621         $defaultRouter = $this->getMock('Symfony\Component\Routing\RouterInterface');
622
623         $defaultRouter
624             ->expects($this->never())
625             ->method('generate')
626         ;
627
628         $this->router->add($defaultRouter, 200);
629
630         $this->router->generate($name, $parameters);
631     }
632
633     /**
634      * A versatile router will generate the debug message.
635      *
636      * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
637      */
638     public function testGenerateObjectNotFoundVersatile()
639     {
640         $name = new \stdClass();
641         $parameters = array('test' => 'value');
642
643         $chainedRouter = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\VersatileRouter');
644         $chainedRouter
645             ->expects($this->once())
646             ->method('supports')
647             ->will($this->returnValue(true))
648         ;
649         $chainedRouter->expects($this->once())
650             ->method('generate')
651             ->with($name, $parameters, UrlGeneratorInterface::ABSOLUTE_PATH)
652             ->will($this->throwException(new RouteNotFoundException()))
653         ;
654         $chainedRouter->expects($this->once())
655             ->method('getRouteDebugMessage')
656             ->with($name, $parameters)
657             ->will($this->returnValue('message'))
658         ;
659
660         $this->router->add($chainedRouter, 10);
661
662         $this->router->generate($name, $parameters);
663     }
664
665     public function testGenerateObjectName()
666     {
667         $name = new \stdClass();
668         $parameters = array('test' => 'value');
669
670         $defaultRouter = $this->getMock('Symfony\Component\Routing\RouterInterface');
671         $chainedRouter = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\VersatileRouter');
672
673         $defaultRouter
674             ->expects($this->never())
675             ->method('generate')
676         ;
677         $chainedRouter
678             ->expects($this->once())
679             ->method('supports')
680             ->will($this->returnValue(true))
681         ;
682         $chainedRouter
683             ->expects($this->once())
684             ->method('generate')
685             ->with($name, $parameters, UrlGeneratorInterface::ABSOLUTE_PATH)
686             ->will($this->returnValue($name))
687         ;
688
689         $this->router->add($defaultRouter, 200);
690         $this->router->add($chainedRouter, 100);
691
692         $result = $this->router->generate($name, $parameters);
693         $this->assertEquals($name, $result);
694     }
695
696     public function testWarmup()
697     {
698         $dir = 'test_dir';
699         list($low) = $this->createRouterMocks();
700
701         $low
702             ->expects($this->never())
703             ->method('warmUp')
704         ;
705         $high = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\WarmableRouterMock');
706         $high
707             ->expects($this->once())
708             ->method('warmUp')
709             ->with($dir)
710         ;
711
712         $this->router->add($low, 10);
713         $this->router->add($high, 100);
714
715         $this->router->warmUp($dir);
716     }
717
718     public function testRouteCollection()
719     {
720         list($low, $high) = $this->createRouterMocks();
721         $lowcol = new RouteCollection();
722         $lowcol->add('low', $this->buildMock('Symfony\Component\Routing\Route'));
723         $highcol = new RouteCollection();
724         $highcol->add('high', $this->buildMock('Symfony\Component\Routing\Route'));
725
726         $low
727             ->expects($this->once())
728             ->method('getRouteCollection')
729             ->will($this->returnValue($lowcol))
730         ;
731         $high
732             ->expects($this->once())
733             ->method('getRouteCollection')
734             ->will($this->returnValue($highcol))
735         ;
736
737         $this->router->add($low, 10);
738         $this->router->add($high, 100);
739
740         $collection = $this->router->getRouteCollection();
741         $this->assertInstanceOf('Symfony\Component\Routing\RouteCollection', $collection);
742
743         $names = array();
744         foreach ($collection->all() as $name => $route) {
745             $this->assertInstanceOf('Symfony\Component\Routing\Route', $route);
746             $names[] = $name;
747         }
748         $this->assertEquals(array('high', 'low'), $names);
749     }
750
751     /**
752      * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
753      */
754     public function testSupport()
755     {
756         $router = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\VersatileRouter');
757         $router
758             ->expects($this->once())
759             ->method('supports')
760             ->will($this->returnValue(false))
761         ;
762
763         $router
764             ->expects($this->never())
765             ->method('generate')
766             ->will($this->returnValue(false))
767         ;
768
769         $this->router->add($router);
770
771         $this->router->generate('foobar');
772     }
773
774     /**
775      * @return RouterInterface[]|\PHPUnit_Framework_MockObject_MockObject[]
776      */
777     protected function createRouterMocks()
778     {
779         return array(
780             $this->getMock('Symfony\Component\Routing\RouterInterface'),
781             $this->getMock('Symfony\Component\Routing\RouterInterface'),
782             $this->getMock('Symfony\Component\Routing\RouterInterface'),
783         );
784     }
785 }
786
787 abstract class WarmableRouterMock implements RouterInterface, WarmableInterface
788 {
789 }
790
791 abstract class RequestMatcher implements RouterInterface, RequestMatcherInterface
792 {
793 }
794
795 abstract class VersatileRouter implements VersatileGeneratorInterface, RequestMatcherInterface
796 {
797 }