*/ class RoutesConfigTest extends ContainerAwareTestCase { /** * Path to the routing files XSD. * @var string */ protected static $routingXSDPath; /** * Path to the core (admin + api) routing files. * @var string */ protected static $routingFilesPathCore; /** * Path to the front routing files. * @var string */ protected static $routingFilesPathFront; /** * Routing files paths => [routing files names]. * @var array */ protected static $routingFiles; /** * Routing files paths => file locator for that path. * @var FileLocatorInterface[] */ protected $routerFileLocators; /** * Routing files paths => file loader for that path. * @var LoaderInterface[] */ protected $routerFileLoaders; protected function buildContainer(ContainerBuilder $container) { } public static function setUpBeforeClass() { self::$routingXSDPath = THELIA_VENDOR . DS . 'symfony' . DS . 'routing' . DS . 'Loader' . DS . XmlFileLoader::SCHEME_PATH; // core files self::$routingFilesPathCore = THELIA_LIB . DS . 'Config' . DS . 'Resources' . DS . 'routing'; self::$routingFiles[self::$routingFilesPathCore] = [ 'admin.xml', 'api.xml', ]; // front files self::$routingFilesPathFront = THELIA_MODULE_DIR . DS . 'Front' . DS . 'Config'; self::$routingFiles[self::$routingFilesPathFront] = [ 'front.xml' ]; } public function setUp() { foreach (static::$routingFiles as $filePath => $fileNames) { $this->routerFileLocators[$filePath] = new FileLocator($filePath); $this->routerFileLoaders[$filePath] = new XmlFileLoader($this->routerFileLocators[$filePath]); } } /** * Check that there are no duplicate route ids. */ public function testNoDuplicateIds() { $existingIds = []; foreach (static::$routingFiles as $filePath => $fileNames) { $routerFileLocator = $this->routerFileLocators[$filePath]; foreach ($fileNames as $fileName) { $xml = XmlUtils::loadFile( $routerFileLocator->locate($fileName), static::$routingXSDPath ); foreach ($xml->documentElement->childNodes as $node) { if (!$node instanceof \DOMElement) { continue; } if ($node->localName != 'route') { continue; } $id = $node->getAttribute('id'); $this->assertFalse(in_array($id, $existingIds), "Duplicate route id '$id'."); $existingIds[] = $id; } } } } /** * Check that there are no obvious duplicate route methods + paths. * Will not catch all duplicates, but should catch most of the common errors. * Can catch some routes that looks like they are conflicting but are not due to disjoint arguments requirements. */ public function testNoDuplicatePaths() { // a map of existing paths => [existing methods for this path] $existingPaths = []; foreach (static::$routingFiles as $filePath => $fileNames) { $routerFileLoader = $this->routerFileLoaders[$filePath]; foreach ($fileNames as $fileName) { $router = new Router($routerFileLoader, $fileName); /** @var Route $route */ foreach ($router->getRouteCollection() as $route) { $routeMethods = $route->getMethods(); if (empty($routeMethods)) { $routeMethods = ['*']; } // check for path collision, then method collision $pathsCollide = in_array($route->getPath(), array_keys($existingPaths)); if ($pathsCollide) { $methodsIntersection = array_intersect($routeMethods, $existingPaths[$route->getPath()]); } else { $methodsIntersection = []; } $methodsCollide = !empty($methodsIntersection); $this->assertFalse( $pathsCollide && $methodsCollide, "Potentially duplicate route path '" . implode('|', $methodsIntersection) . " " . $route->getPath() . "'." ); if ($pathsCollide) { $existingPaths[$route->getPath()] = array_merge($existingPaths[$route->getPath()], $routeMethods); } else { $existingPaths[$route->getPath()] = $routeMethods; } } } } } /** * Check that controller methods for the routes are callable. */ public function testTargetControllerMethodsAreCallable() { $controllerResolver = new ControllerResolver($this->getContainer()); foreach (static::$routingFiles as $filePath => $fileNames) { $routerFileLoader = $this->routerFileLoaders[$filePath]; foreach ($fileNames as $fileName) { $router = new Router($routerFileLoader, $fileName); /** @var Route $route */ foreach ($router->getRouteCollection() as $route) { // prepare a dummy request to the controller so that we can check it using the ControllerResolver $request = new Request(); $request->attributes->set('_controller', $route->getDefault('_controller')); // will throw an exception if the controller method is not callable $controllerResolver->getController($request); } } } } /** * Check that views for the front routes exists. */ public function testTargetFrontViewsExists() { /** @var RequestStack $requestStack */ $requestStack = $this->getContainer()->get('request_stack'); /** @var EventDispatcherInterface $eventDispatcher */ $eventDispatcher = $this->getContainer()->get('event_dispatcher'); // we are not going to do any actual rendering, so a mock ParserContext should be enough /** @var ParserContext $parserContext */ $parserContext = $this ->getMockBuilder('Thelia\Core\Template\ParserContext') ->disableOriginalConstructor() ->getMock(); $templateHelper = new TheliaTemplateHelper(); $parser = new SmartyParser( $requestStack, $eventDispatcher, $parserContext, $templateHelper ); $parser->setTemplateDefinition($templateHelper->getActiveFrontTemplate()); $frontRouterFileLoader = $this->routerFileLoaders[static::$routingFilesPathFront]; foreach (static::$routingFiles[static::$routingFilesPathFront] as $fileName) { $router = new Router($frontRouterFileLoader, $fileName); /** @var Route $route */ foreach ($router->getRouteCollection() as $route) { if (null === $view = $route->getDefault('_view')) { continue; } $this->assertTrue( $parser->templateExists($view . '.html'), "Front view '$view' does not exist." ); } } } }