Skip to content

Commit

Permalink
[TASK] Add test coverage for RouteDispatcher
Browse files Browse the repository at this point in the history
The backend route dispatcher with its different controller
incarnations benefits from a basic code coverage.

Resolves: #85191
Related: #84196
Releases: master
Change-Id: I0ff1efae64c41e7fbeba8fb3569c5601cb005669
Reviewed-on: https://review.typo3.org/56095
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
  • Loading branch information
lolli42 authored and maddy2101 committed Jun 9, 2018
1 parent 815a15a commit 0970664
Show file tree
Hide file tree
Showing 6 changed files with 380 additions and 2 deletions.
2 changes: 0 additions & 2 deletions typo3/sysext/backend/Classes/Http/RouteDispatcher.php
Expand Up @@ -42,9 +42,7 @@ class RouteDispatcher extends Dispatcher
*/
public function dispatch(ServerRequestInterface $request, ResponseInterface $response)
{
/** @var Router $router */
$router = GeneralUtility::makeInstance(Router::class);
/** @var Route $route */
$route = $router->matchRequest($request);
$request = $request->withAttribute('route', $route);
$request = $request->withAttribute('target', $route->getOption('target'));
Expand Down
@@ -0,0 +1,31 @@
<?php
namespace TYPO3\CMS\Backend\Tests\Unit\Http\Fixtures;

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

use Psr\Http\Message\ServerRequestInterface;

/**
* Test fixture
*/
class RouteDispatcherClassFixture
{
public function mainAction(ServerRequestInterface $request)
{
throw new \RuntimeException(
'I have been called. Good!',
1520756142
);
}
}
@@ -0,0 +1,31 @@
<?php
namespace TYPO3\CMS\Backend\Tests\Unit\Http\Fixtures;

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

use Psr\Http\Message\ServerRequestInterface;

/**
* Test fixture
*/
class RouteDispatcherClassInvokeFixture
{
public function __invoke(ServerRequestInterface $request)
{
throw new \RuntimeException(
'I have been called. Good!',
1520756623
);
}
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Backend\Tests\Unit\Http\Fixtures;

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

/**
* Test fixture
*/
class RouteDispatcherClassWithoutInvokeFixture
{
}
@@ -0,0 +1,31 @@
<?php
namespace TYPO3\CMS\Backend\Tests\Unit\Http\Fixtures;

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

use Psr\Http\Message\ServerRequestInterface;

/**
* Test fixture
*/
class RouteDispatcherStaticClassFixture
{
public static function mainAction(ServerRequestInterface $request)
{
throw new \RuntimeException(
'I have been called. Good!',
1520757000
);
}
}
265 changes: 265 additions & 0 deletions typo3/sysext/backend/Tests/Unit/Http/RouteDispatcherTest.php
@@ -0,0 +1,265 @@
<?php
namespace TYPO3\CMS\Backend\Tests\Unit\Http;

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

use Prophecy\Argument;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Http\RouteDispatcher;
use TYPO3\CMS\Backend\Routing\Route;
use TYPO3\CMS\Backend\Routing\Router;
use TYPO3\CMS\Backend\Tests\Unit\Http\Fixtures\RouteDispatcherClassFixture;
use TYPO3\CMS\Backend\Tests\Unit\Http\Fixtures\RouteDispatcherClassInvokeFixture;
use TYPO3\CMS\Backend\Tests\Unit\Http\Fixtures\RouteDispatcherClassWithoutInvokeFixture;
use TYPO3\CMS\Backend\Tests\Unit\Http\Fixtures\RouteDispatcherStaticClassFixture;
use TYPO3\CMS\Core\FormProtection\AbstractFormProtection;
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;

/**
* Test case
*/
class RouteDispatcherTest extends UnitTestCase
{
public function tearDown()
{
FormProtectionFactory::purgeInstances();
GeneralUtility::purgeInstances();
parent::tearDown();
}

/**
* @test
*/
public function dispatchThrowsExceptionIfTargetIsNotCallable()
{
$formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
$formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
FormProtectionFactory::set('default', $formProtectionProphecy->reveal());

$requestProphecy = $this->prophesize(ServerRequestInterface::class);
$responseProphecy = $this->prophesize(ResponseInterface::class);
$routerProphecy = $this->prophesize(Router::class);
GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
$routeProphecy = $this->prophesize(Route::class);
$routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
$routeProphecy->getOption('access')->willReturn('public');
$routeProphecy->getOption('module')->willReturn(false);
$requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
$requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());

$target = 42;
$routeProphecy->getOption('target')->willReturn($target);
$requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());

$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionCode(1425381442);

$subject = new RouteDispatcher();
$subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
}

/**
* @test
*/
public function dispatchCallsTargetIfTargetIsArray()
{
$formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
$formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
FormProtectionFactory::set('default', $formProtectionProphecy->reveal());

$requestProphecy = $this->prophesize(ServerRequestInterface::class);
$responseProphecy = $this->prophesize(ResponseInterface::class);
$routerProphecy = $this->prophesize(Router::class);
GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
$routeProphecy = $this->prophesize(Route::class);
$routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
$routeProphecy->getOption('access')->willReturn('public');
$routeProphecy->getOption('module')->willReturn(false);
$requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
$requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());

$target = [
RouteDispatcherClassFixture::class,
'mainAction'
];
$routeProphecy->getOption('target')->willReturn($target);
$requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());

$this->expectException(\RuntimeException::class);
$this->expectExceptionCode(1520756142);

$subject = new RouteDispatcher();
$subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
}

/**
* @test
*/
public function dispatchCallsTargetIfTargetIsClosure()
{
$formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
$formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
FormProtectionFactory::set('default', $formProtectionProphecy->reveal());

$requestProphecy = $this->prophesize(ServerRequestInterface::class);
$responseProphecy = $this->prophesize(ResponseInterface::class);
$routerProphecy = $this->prophesize(Router::class);
GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
$routeProphecy = $this->prophesize(Route::class);
$routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
$routeProphecy->getOption('access')->willReturn('public');
$routeProphecy->getOption('module')->willReturn(false);
$requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
$requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());

$target = function (ServerRequestInterface $request) {
throw new \RuntimeException('I have been called. Good!', 1520756466);
};
$routeProphecy->getOption('target')->willReturn($target);
$requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());

$this->expectException(\RuntimeException::class);
$this->expectExceptionCode(1520756466);

$subject = new RouteDispatcher();
$subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
}

/**
* @test
*/
public function dispatchCallsTargetIfTargetIsClassImplementingInvoke()
{
$formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
$formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
FormProtectionFactory::set('default', $formProtectionProphecy->reveal());

$requestProphecy = $this->prophesize(ServerRequestInterface::class);
$responseProphecy = $this->prophesize(ResponseInterface::class);
$routerProphecy = $this->prophesize(Router::class);
GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
$routeProphecy = $this->prophesize(Route::class);
$routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
$routeProphecy->getOption('access')->willReturn('public');
$routeProphecy->getOption('module')->willReturn(false);
$requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
$requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());

$target = RouteDispatcherClassInvokeFixture::class;
$routeProphecy->getOption('target')->willReturn($target);
$requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());

$this->expectException(\RuntimeException::class);
$this->expectExceptionCode(1520756623);

$subject = new RouteDispatcher();
$subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
}

/**
* @test
*/
public function dispatchThrowsExceptionIfTargetWithClassNameOnlyDoesNotImplementInvoke()
{
$formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
$formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
FormProtectionFactory::set('default', $formProtectionProphecy->reveal());

$requestProphecy = $this->prophesize(ServerRequestInterface::class);
$responseProphecy = $this->prophesize(ResponseInterface::class);
$routerProphecy = $this->prophesize(Router::class);
GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
$routeProphecy = $this->prophesize(Route::class);
$routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
$routeProphecy->getOption('access')->willReturn('public');
$routeProphecy->getOption('module')->willReturn(false);
$requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
$requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());

$target = RouteDispatcherClassWithoutInvokeFixture::class;
$routeProphecy->getOption('target')->willReturn($target);
$requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());

$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionCode(1442431631);

$subject = new RouteDispatcher();
$subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
}

/**
* @test
*/
public function dispatchCallsClassMethodCombinationGivenAsString()
{
$formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
$formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
FormProtectionFactory::set('default', $formProtectionProphecy->reveal());

$requestProphecy = $this->prophesize(ServerRequestInterface::class);
$responseProphecy = $this->prophesize(ResponseInterface::class);
$routerProphecy = $this->prophesize(Router::class);
GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
$routeProphecy = $this->prophesize(Route::class);
$routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
$routeProphecy->getOption('access')->willReturn('public');
$routeProphecy->getOption('module')->willReturn(false);
$requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
$requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());

$target = RouteDispatcherClassFixture::class . '::mainAction';
$routeProphecy->getOption('target')->willReturn($target);
$requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());

$this->expectException(\RuntimeException::class);
$this->expectExceptionCode(1520756142);

$subject = new RouteDispatcher();
$subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
}

/**
* @test
*/
public function dispatchCallsStaticClassMethodCombinationGivenAsString()
{
$formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
$formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
FormProtectionFactory::set('default', $formProtectionProphecy->reveal());

$requestProphecy = $this->prophesize(ServerRequestInterface::class);
$responseProphecy = $this->prophesize(ResponseInterface::class);
$routerProphecy = $this->prophesize(Router::class);
GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
$routeProphecy = $this->prophesize(Route::class);
$routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
$routeProphecy->getOption('access')->willReturn('public');
$routeProphecy->getOption('module')->willReturn(false);
$requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
$requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());

$target = RouteDispatcherStaticClassFixture::class . '::mainAction';
$routeProphecy->getOption('target')->willReturn($target);
$requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());

$this->expectException(\RuntimeException::class);
$this->expectExceptionCode(1520757000);

$subject = new RouteDispatcher();
$subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
}
}

0 comments on commit 0970664

Please sign in to comment.