Rework Router

This commit is contained in:
PJ Dietz 2015-05-06 18:58:46 -04:00
parent 9083f2a444
commit ec7dceac98
4 changed files with 339 additions and 273 deletions

46
src/Routing/RouteMap.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace WellRESTed\Routing;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class RouteMap implements RouteMapInterface
{
/**
* Register middleware with the router for a given path and method.
*
* $method may be:
* - A single verb ("GET"),
* - A comma-separated list of verbs ("GET,PUT,DELETE")
* - "*" to indicate any method.
* @see MethodMapInterface::addMethod
*
* $target may be:
* - An exact path (e.g., "/path/")
* - An prefix path ending with "*"" ("/path/*"")
* - A URI template with variables enclosed in "{}" ("/path/{id}")
* - A regular expression ("~/cat/([0-9]+)~")
*
* $middleware may be:
* - An instance implementing MiddlewareInterface
* - A string containing the fully qualified class name of a class
* implementing MiddlewareInterface
* - A callable that returns an instance implementing MiddleInterface
* - A callable maching the signature of MiddlewareInteraface::dispatch
* @see DispatchedInterface::dispatch
*
* @param string $target Request target or pattern to match
* @param string $method HTTP method(s) to match
* @param mixed $middleware Middleware to dispatch
*/
public function add($method, $target, $middleware)
{
// TODO: Implement addRoute() method.
}
public function dispatch(ServerRequestInterface $request, ResponseInterface &$response)
{
// TODO: Implement dispatch() method.
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace WellRESTed\Routing;
interface RouteMapInterface extends MiddlewareInterface
{
/**
* Register middleware with the router for a given path and method.
*
* $method may be:
* - A single verb ("GET"),
* - A comma-separated list of verbs ("GET,PUT,DELETE")
* - "*" to indicate any method.
* @see MethodMapInterface::addMethod
*
* $target may be:
* - An exact path (e.g., "/path/")
* - An prefix path ending with "*"" ("/path/*"")
* - A URI template with variables enclosed in "{}" ("/path/{id}")
* - A regular expression ("~/cat/([0-9]+)~")
*
* $middleware may be:
* - An instance implementing MiddlewareInterface
* - A string containing the fully qualified class name of a class
* implementing MiddlewareInterface
* - A callable that returns an instance implementing MiddleInterface
* - A callable maching the signature of MiddlewareInteraface::dispatch
* @see DispatchedInterface::dispatch
*
* @param string $target Request target or pattern to match
* @param string $method HTTP method(s) to match
* @param mixed $middleware Middleware to dispatch
*/
public function add($method, $target, $middleware);
}

View File

@ -8,117 +8,93 @@ use WellRESTed\HttpExceptions\HttpException;
use WellRESTed\Message\Response; use WellRESTed\Message\Response;
use WellRESTed\Message\ServerRequest; use WellRESTed\Message\ServerRequest;
use WellRESTed\Message\Stream; use WellRESTed\Message\Stream;
use WellRESTed\Routing\ResponsePrep\ContentLengthPrep;
use WellRESTed\Routing\ResponsePrep\HeadPrep;
use WellRESTed\Routing\Route\RouteFactory;
use WellRESTed\Routing\Route\RouteFactoryInterface;
class Router implements MiddlewareInterface class Router implements MiddlewareInterface, RouteMapInterface
{ {
/** @var DispatcherInterface */ /** @var DispatcherInterface */
protected $dispatcher; private $dispatcher;
/** @var MiddlewareInterface[] List of middleware to dispatch immediatly before outputting the response */ /** @var mixed[] List of middleware to dispatch immediately before concluding the request-response cycle. */
protected $responsePreparationHooks; private $finalizationHooks;
/** @var MiddlewareInterface[] List of middleware to dispatch before the router evaluates the route. */ /** @var mixed[] List of middleware to dispatch after the router dispatches the matched route. */
private $preRouteHooks;
/** @var MiddlewareInterface[] List of middleware to dispatch after the router dispatches all other middleware */
private $postRouteHooks; private $postRouteHooks;
/** @var mixed[] List of middleware to dispatch before the router dispatches the matched route. */
private $preRouteHooks;
/** @var array Hash array of status code => middleware */ /** @var array Hash array of status code => middleware */
private $statusHandlers; private $statusHooks;
/** @var RouteTable Collection of routes */ /** @var RouteMapInterface */
private $routeTable; private $routeMap;
/** @var RouteFactoryInterface */
private $routeFactory;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
public function __construct() public function __construct()
{ {
$this->responsePreparationHooks = $this->getResponsePreparationHooks(); $this->dispatcher = $this->getDispatcher();
$this->routeFactory = $this->getRouteFactory(); $this->finalizationHooks = $this->getFinalizationHooks();
$this->routeTable = $this->getRouteTable(); $this->postRouteHooks = $this->getPostRouteHooks();
$this->statusHandlers = []; $this->preRouteHooks = $this->getPreRouteHooks();
$this->statusHooks = $this->getStatusHooks();
$this->routeMap = $this->getRouteMap();
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// MiddlewareInterface
/**
* Create and return a route given a string path, a handler, and optional
* extra arguments.
*
* The method will determine the most appropriate route subclass to use
* and will forward the arguments on to the subclass's constructor.
*
* - Paths with no special characters will generate StaticRoutes
* - Paths ending with * will generate PrefixRoutes
* - Paths containing URI variables (e.g., {id}) will generate TemplateRoutes
* - Regular exressions will generate RegexRoutes
*
* @param string $target Path, prefix, or pattern to match
* @param mixed $middleware Middleware to dispatch
* @param mixed $extra
*/
public function add($target, $middleware, $extra = null)
{
if (is_array($middleware)) {
$map = $this->getMethodMap();
$map->addMap($middleware);
$middleware = $map;
}
$this->routeFactory->registerRoute($this->routeTable, $target, $middleware, $extra);
}
public function addPreRouteHook($middleware)
{
if (!isset($this->preRouteHooks)) {
$this->preRouteHooks = [];
}
$this->preRouteHooks[] = $middleware;
}
public function addPostRouteHook($middleware)
{
if (!isset($this->postRouteHooks)) {
$this->postRouteHooks = [];
}
$this->postRouteHooks[] = $middleware;
}
public function addResponsePreparationHook($middleware)
{
$this->responsePreparationHooks[] = $middleware;
}
public function setStatusHandler($statusCode, $middleware)
{
$this->statusHandlers[$statusCode] = $middleware;
}
public function dispatch(ServerRequestInterface $request, ResponseInterface &$response) public function dispatch(ServerRequestInterface $request, ResponseInterface &$response)
{ {
$this->disptachPreRouteHooks($request, $response); $this->dispatchPreRouteHooks($request, $response);
try { try {
$this->routeTable->dispatch($request, $response); $this->routeMap->dispatch($request, $response);
} catch (HttpException $e) { } catch (HttpException $e) {
$response = $response->withStatus($e->getCode()); $response = $response->withStatus($e->getCode());
$response = $response->withBody(new Stream($e->getMessage())); $response = $response->withBody(new Stream($e->getMessage()));
} }
$statusCode = $response->getStatusCode(); $this->dispatchStatusHooks($request, $response);
if (isset($this->statusHandlers[$statusCode])) { $this->dispatchPostRouteHooks($request, $response);
$middleware = $this->statusHandlers[$statusCode]; $this->dispatchFinalizationHooks($request, $response);
$dispatcher = $this->getDispatcher();
$dispatcher->dispatch($middleware, $request, $response);
}
$this->disptachPostRouteHooks($request, $response);
$this->dispatchResponsePreparationHooks($request, $response);
} }
// ------------------------------------------------------------------------
// RouteMapInterface
/**
* Register middleware with the router for a given path and method.
*
* $method may be:
* - A single verb ("GET"),
* - A comma-separated list of verbs ("GET,PUT,DELETE")
* - "*" to indicate any method.
* @see MethodMapInterface::addMethod
*
* $target may be:
* - An exact path (e.g., "/path/")
* - An prefix path ending with "*"" ("/path/*"")
* - A URI template with variables enclosed in "{}" ("/path/{id}")
* - A regular expression ("~/cat/([0-9]+)~")
*
* $middleware may be:
* - An instance implementing MiddlewareInterface
* - A string containing the fully qualified class name of a class
* implementing MiddlewareInterface
* - A callable that returns an instance implementing MiddleInterface
* - A callable maching the signature of MiddlewareInteraface::dispatch
* @see DispatchedInterface::dispatch
*
* @param string $target Request target or pattern to match
* @param string $method HTTP method(s) to match
* @param mixed $middleware Middleware to dispatch
*/
public function add($method, $target, $middleware)
{
$this->routeMap->add($method, $target, $middleware);
}
// ------------------------------------------------------------------------
public function respond() public function respond()
{ {
$request = $this->getRequest(); $request = $this->getRequest();
@ -128,34 +104,91 @@ class Router implements MiddlewareInterface
$responder->respond($response); $responder->respond($response);
} }
// ------------------------------------------------------------------------
// Hooks
public function addPreRouteHook($middleware)
{
$this->preRouteHooks[] = $middleware;
}
public function addPostRouteHook($middleware)
{
$this->postRouteHooks[] = $middleware;
}
public function addFinalizationHook($middleware)
{
$this->finalizationHooks[] = $middleware;
}
public function setStatusHook($statusCode, $middleware)
{
$this->statusHooks[$statusCode] = $middleware;
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// The following methods provide instaces the router will use. Override // The following methods provide instaces the router will use. Override
// to provide custom classes or configured instances. // to provide custom classes or configured instances.
// @codeCoverageIgnoreStart
/** /**
* Return an instance that can dispatch middleware. * Return an instance that can dispatch middleware.
*
* Override to provide a custom class. * Override to provide a custom class.
* *
* @return DispatcherInterface * @return DispatcherInterface
*/ */
protected function getDispatcher() protected function getDispatcher()
{ {
if (!isset($this->dispatcher)) { return new Dispatcher();
$this->dispatcher = new Dispatcher();
}
return $this->dispatcher;
} }
/** /**
* @return MethodMapInterface * Return an instance that maps routes to middleware.
*
* Override to provide a custom class.
*
* @return RouteMapInterface
*/ */
protected function getMethodMap() protected function getRouteMap()
{ {
return new MethodMap(); return new RouteMap();
} }
/**
* @return array
*/
protected function getPreRouteHooks()
{
return [];
}
/**
* @return array
*/
protected function getPostRouteHooks()
{
return [];
}
/**
* @return array
*/
protected function getStatusHooks()
{
return [];
}
/**
* @return array
*/
protected function getFinalizationHooks()
{
return [];
}
// @codeCoverageIgnoreStart
/** /**
* @return ServerRequestInterface * @return ServerRequestInterface
*/ */
@ -180,62 +213,38 @@ class Router implements MiddlewareInterface
return new Response(); return new Response();
} }
/**
* @return MiddlewareInterface[]
*/
protected function getResponsePreparationHooks()
{
return [
new ContentLengthPrep(),
new HeadPrep()
];
}
/**
* @return RouteFactoryInterface
*/
protected function getRouteFactory()
{
return new RouteFactory();
}
/**
* @return RouteTableInterface
*/
protected function getRouteTable()
{
return new RouteTable();
}
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
private function disptachPreRouteHooks(ServerRequestInterface $request, ResponseInterface &$response) private function dispatchPreRouteHooks(ServerRequestInterface $request, ResponseInterface &$response)
{ {
if ($this->preRouteHooks) { foreach ($this->preRouteHooks as $hook) {
$dispatcher = $this->getDispatcher(); $this->dispatcher->dispatch($hook, $request, $response);
foreach ($this->preRouteHooks as $hook) {
$dispatcher->dispatch($hook, $request, $response);
}
} }
} }
private function disptachPostRouteHooks(ServerRequestInterface $request, ResponseInterface &$response) private function dispatchPostRouteHooks(ServerRequestInterface $request, ResponseInterface &$response)
{ {
if ($this->postRouteHooks) { foreach ($this->postRouteHooks as $hook) {
$dispatcher = $this->getDispatcher(); $this->dispatcher->dispatch($hook, $request, $response);
foreach ($this->postRouteHooks as $hook) {
$dispatcher->dispatch($hook, $request, $response);
}
} }
} }
private function dispatchResponsePreparationHooks(ServerRequestInterface $request, ResponseInterface &$response) private function dispatchFinalizationHooks(ServerRequestInterface $request, ResponseInterface &$response)
{ {
$dispatcher = $this->getDispatcher(); foreach ($this->finalizationHooks as $hook) {
foreach ($this->responsePreparationHooks as $hook) { $this->dispatcher->dispatch($hook, $request, $response);
$dispatcher->dispatch($hook, $request, $response); }
}
private function dispatchStatusHooks(ServerRequestInterface $request, ResponseInterface &$response)
{
$statusCode = $response->getStatusCode();
if (isset($this->statusHooks[$statusCode])) {
$middleware = $this->statusHooks[$statusCode];
$dispatcher = $this->getDispatcher();
$dispatcher->dispatch($middleware, $request, $response);
} }
} }
} }

View File

@ -6,46 +6,26 @@ use Prophecy\Argument;
use WellRESTed\HttpExceptions\NotFoundException; use WellRESTed\HttpExceptions\NotFoundException;
use WellRESTed\Routing\Router; use WellRESTed\Routing\Router;
// TODO Tests that ensure hooks are called at correct times
// TODO Test default finalization hooks
/** /**
* @coversDefaultClass WellRESTed\Routing\Router * @coversDefaultClass WellRESTed\Routing\Router
* @uses WellRESTed\Routing\Router * @uses WellRESTed\Routing\Router
* @uses WellRESTed\Message\Stream * @uses WellRESTed\Message\Stream
* @uses WellRESTed\Routing\Dispatcher * @uses WellRESTed\Routing\Dispatcher
* @uses WellRESTed\Routing\MethodMap * @uses WellRESTed\Routing\RouteMap
* @uses WellRESTed\Routing\ResponsePrep\ContentLengthPrep
* @uses WellRESTed\Routing\ResponsePrep\HeadPrep
* @uses WellRESTed\Routing\Route\PrefixRoute
* @uses WellRESTed\Routing\Route\RegexRoute
* @uses WellRESTed\Routing\Route\Route
* @uses WellRESTed\Routing\Route\RouteFactory
* @uses WellRESTed\Routing\Route\StaticRoute
* @uses WellRESTed\Routing\Route\TemplateRoute
* @uses WellRESTed\Routing\RouteTable
*/ */
class RouterTest extends \PHPUnit_Framework_TestCase class RouterTest extends \PHPUnit_Framework_TestCase
{ {
private $dispatcher;
private $middleware;
private $request; private $request;
private $responder;
private $response; private $response;
public function setUp() public function setUp()
{ {
$this->dispatcher = $this->prophesize('WellRESTed\Routing\DispatcherInterface'); parent::setUp();
$this->dispatcher->dispatch(Argument::any())->willReturn();
$this->middleware = $this->prophesize('WellRESTed\Routing\MiddlewareInterface');
$this->middleware->dispatch(Argument::cetera())->willReturn();
$this->request = $this->prophesize('Psr\Http\Message\ServerRequestInterface'); $this->request = $this->prophesize('Psr\Http\Message\ServerRequestInterface');
$this->request->getRequestTarget()->willReturn("/");
$this->request->getMethod()->willReturn("GET");
$this->responder = $this->prophesize('WellRESTed\Routing\ResponderInterface');
$this->responder->respond(Argument::any())->willReturn();
$this->response = $this->prophesize('Psr\Http\Message\ResponseInterface'); $this->response = $this->prophesize('Psr\Http\Message\ResponseInterface');
$this->response->withStatus(Argument::any())->willReturn($this->response->reveal());
$this->response->withBody(Argument::any())->willReturn($this->response->reveal());
$this->response->hasHeader("Content-length")->willReturn(true);
$this->response->getStatusCode()->willReturn(200);
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -53,6 +33,12 @@ class RouterTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers ::__construct * @covers ::__construct
* @covers ::getDispatcher
* @covers ::getRouteMap
* @covers ::getPreRouteHooks
* @covers ::getPostRouteHooks
* @covers ::getFinalizationHooks
* @covers ::getStatusHooks
*/ */
public function testCreatesInstance() public function testCreatesInstance()
{ {
@ -66,85 +52,26 @@ class RouterTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers ::add * @covers ::add
*/ */
public function testAddWithSimpleRouteRegistersRoute() public function testAddRegistersRouteWithRouteMap()
{ {
$factory = $this->prophesize('WellRESTed\Routing\Route\RouteFactoryInterface'); $routeMap = $this->prophesize('WellRESTed\Routing\RouteMapInterface');
$factory->registerRoute(Argument::cetera())->willReturn(); $routeMap->add(Argument::cetera())->willReturn();
$router = $this->getMockBuilder('WellRESTed\Routing\Router') $router = $this->getMockBuilder('WellRESTed\Routing\Router')
->setMethods(["getRouteFactory"]) ->setMethods(["getRouteMap"])
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$router->expects($this->any()) $router->expects($this->any())
->method("getRouteFactory") ->method("getRouteMap")
->will($this->returnValue($factory->reveal())); ->will($this->returnValue($routeMap->reveal()));
$router->__construct(); $router->__construct();
$target = "/cats/"; $method = "GET";
$middleware = $this->middleware->reveal(); $target = "/path/{id}";
$router->add($target, $middleware); $middleware = "Middleware";
$factory->registerRoute(Argument::any(), $target, $middleware, Argument::any())->shouldHaveBeenCalled(); $router->add($method, $target, $middleware);
} $routeMap->add($method, $target, $middleware)->shouldHaveBeenCalled();
/**
* @covers ::add
*/
public function testAddWithMapAddsMiddlewareToMethodMap()
{
$map = $this->prophesize('WellRESTed\Routing\MethodMapInterface');
$map->addMap(Argument::any())->willReturn();
$factory = $this->prophesize('WellRESTed\Routing\Route\RouteFactoryInterface');
$factory->registerRoute(Argument::cetera())->willReturn();
$router = $this->getMockBuilder('WellRESTed\Routing\Router')
->setMethods(["getRouteFactory", "getMethodMap"])
->disableOriginalConstructor()
->getMock();
$router->expects($this->any())
->method("getMethodMap")
->will($this->returnValue($map->reveal()));
$router->expects($this->any())
->method("getRouteFactory")
->will($this->returnValue($factory->reveal()));
$router->__construct();
$target = "/cats/";
$middleware = ["GET" => $this->middleware->reveal()];
$router->add($target, $middleware);
$map->addMap($middleware)->shouldHaveBeenCalled();
}
/**
* @covers ::add
*/
public function testAddWithMapRegistersMethodMap()
{
$map = $this->prophesize('WellRESTed\Routing\MethodMapInterface');
$map->addMap(Argument::any())->willReturn();
$factory = $this->prophesize('WellRESTed\Routing\Route\RouteFactoryInterface');
$factory->registerRoute(Argument::cetera())->willReturn();
$router = $this->getMockBuilder('WellRESTed\Routing\Router')
->setMethods(["getRouteFactory", "getMethodMap"])
->disableOriginalConstructor()
->getMock();
$router->expects($this->any())
->method("getMethodMap")
->will($this->returnValue($map->reveal()));
$router->expects($this->any())
->method("getRouteFactory")
->will($this->returnValue($factory->reveal()));
$router->__construct();
$target = "/cats/";
$middleware = ["GET" => $this->middleware->reveal()];
$router->add($target, $middleware);
$factory->registerRoute(Argument::any(), $target, $map, Argument::any())->shouldHaveBeenCalled();
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -153,18 +80,25 @@ class RouterTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers ::dispatch * @covers ::dispatch
*/ */
public function testDispatchesMatchedRoute() public function testDispatchesRouteMap()
{ {
$this->request->getRequestTarget()->willReturn("/cats/"); $routeMap = $this->prophesize('WellRESTed\Routing\RouteMapInterface');
$routeMap->dispatch(Argument::cetera())->willReturn();
$router = new Router(); $router = $this->getMockBuilder('WellRESTed\Routing\Router')
$router->add("/cats/", $this->middleware->reveal()); ->setMethods(["getRouteMap"])
->disableOriginalConstructor()
->getMock();
$router->expects($this->any())
->method("getRouteMap")
->will($this->returnValue($routeMap->reveal()));
$router->__construct();
$request = $this->request->reveal(); $request = $this->request->reveal();
$response = $this->response->reveal(); $resonse = $this->response->reveal();
$router->dispatch($request, $response); $router->dispatch($request, $resonse);
$this->middleware->dispatch(Argument::cetera())->shouldHaveBeenCalled(); $routeMap->dispatch($request, Argument::any())->shouldHaveBeenCalled();
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -172,9 +106,9 @@ class RouterTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers ::addPreRouteHook * @covers ::addPreRouteHook
* @covers ::disptachPreRouteHooks * @covers ::dispatchPreRouteHooks
*/ */
public function testDispatchesPreRouteHooks() public function testDispatchesPreRouteHook()
{ {
$hook = $this->prophesize('\WellRESTed\Routing\MiddlewareInterface'); $hook = $this->prophesize('\WellRESTed\Routing\MiddlewareInterface');
$hook->dispatch(Argument::cetera())->willReturn(); $hook->dispatch(Argument::cetera())->willReturn();
@ -183,7 +117,6 @@ class RouterTest extends \PHPUnit_Framework_TestCase
$router = new Router(); $router = new Router();
$router->addPreRouteHook($hook->reveal()); $router->addPreRouteHook($hook->reveal());
$router->add("/cats/", $this->middleware->reveal());
$request = $this->request->reveal(); $request = $this->request->reveal();
$response = $this->response->reveal(); $response = $this->response->reveal();
@ -194,9 +127,9 @@ class RouterTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers ::addPostRouteHook * @covers ::addPostRouteHook
* @covers ::disptachPostRouteHooks * @covers ::dispatchPostRouteHooks
*/ */
public function testDispatchesPostRouteHooks() public function testDispatchesPostRouteHook()
{ {
$hook = $this->prophesize('\WellRESTed\Routing\MiddlewareInterface'); $hook = $this->prophesize('\WellRESTed\Routing\MiddlewareInterface');
$hook->dispatch(Argument::cetera())->willReturn(); $hook->dispatch(Argument::cetera())->willReturn();
@ -205,7 +138,6 @@ class RouterTest extends \PHPUnit_Framework_TestCase
$router = new Router(); $router = new Router();
$router->addPostRouteHook($hook->reveal()); $router->addPostRouteHook($hook->reveal());
$router->add("/cats/", $this->middleware->reveal());
$request = $this->request->reveal(); $request = $this->request->reveal();
$response = $this->response->reveal(); $response = $this->response->reveal();
@ -215,10 +147,10 @@ class RouterTest extends \PHPUnit_Framework_TestCase
} }
/** /**
* @covers ::addResponsePreparationHook * @covers ::addFinalizationHook
* @covers ::dispatchResponsePreparationHooks * @covers ::dispatchFinalizationHooks
*/ */
public function testDispatchesResponsePreparationHooks() public function testDispatchesFinalHooks()
{ {
$hook = $this->prophesize('\WellRESTed\Routing\MiddlewareInterface'); $hook = $this->prophesize('\WellRESTed\Routing\MiddlewareInterface');
$hook->dispatch(Argument::cetera())->willReturn(); $hook->dispatch(Argument::cetera())->willReturn();
@ -226,8 +158,7 @@ class RouterTest extends \PHPUnit_Framework_TestCase
$this->request->getRequestTarget()->willReturn("/cats/"); $this->request->getRequestTarget()->willReturn("/cats/");
$router = new Router(); $router = new Router();
$router->addResponsePreparationHook($hook->reveal()); $router->addFinalizationHook($hook->reveal());
$router->add("/cats/", $this->middleware->reveal());
$request = $this->request->reveal(); $request = $this->request->reveal();
$response = $this->response->reveal(); $response = $this->response->reveal();
@ -241,9 +172,10 @@ class RouterTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers ::dispatch * @covers ::dispatch
* @covers ::setStatusHandler * @covers ::dispatchStatusHooks
* @covers ::setStatusHook
*/ */
public function testDispatchesHandlerForStatusCode() public function testDispatchesHookForStatusCode()
{ {
$this->response->getStatusCode()->willReturn(403); $this->response->getStatusCode()->willReturn(403);
@ -251,8 +183,7 @@ class RouterTest extends \PHPUnit_Framework_TestCase
$statusMiddleware->dispatch(Argument::cetera())->willReturn(); $statusMiddleware->dispatch(Argument::cetera())->willReturn();
$router = new Router(); $router = new Router();
$router->add("/cats/", $this->middleware->reveal()); $router->setStatusHook(403, $statusMiddleware->reveal());
$router->setStatusHandler(403, $statusMiddleware->reveal());
$request = $this->request->reveal(); $request = $this->request->reveal();
$response = $this->response->reveal(); $response = $this->response->reveal();
@ -263,15 +194,33 @@ class RouterTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers ::dispatch * @covers ::dispatch
* @covers ::setStatusHandler * @covers ::dispatchStatusHooks
* @covers ::setStatusHook
*/ */
public function testDispatchesHandlerForStatusCodeForHttpException() public function testDispatchesStatusHookForHttpException()
{ {
$this->request->getRequestTarget()->willReturn("/cats/"); $statusMiddleware = $this->prophesize('\WellRESTed\Routing\MiddlewareInterface');
$this->middleware->dispatch(Argument::cetera())->willThrow(new NotFoundException()); $statusMiddleware->dispatch(Argument::cetera())->willReturn();
$router = new Router(); $routeMap = $this->prophesize('WellRESTed\Routing\RouteMapInterface');
$router->add("/cats/", $this->middleware->reveal()); $routeMap->dispatch(Argument::cetera())->willThrow(new NotFoundException());
$this->response->withStatus(Argument::any())->will(
function ($args) {
$this->getStatusCode()->willReturn($args[0]);
return $this;
}
);
$this->response->withBody(Argument::any())->willReturn($this->response->reveal());
$router = $this->getMockBuilder('WellRESTed\Routing\Router')
->setMethods(["getRouteMap"])
->disableOriginalConstructor()
->getMock();
$router->expects($this->any())
->method("getRouteMap")
->will($this->returnValue($routeMap->reveal()));
$router->__construct();
$request = $this->request->reveal(); $request = $this->request->reveal();
$response = $this->response->reveal(); $response = $this->response->reveal();
@ -285,16 +234,27 @@ class RouterTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers ::respond * @covers ::respond
* @covers ::getRequest
* @covers ::getResponse
* @covers ::getResponder
*/ */
public function testRespondDispatchesRequest() public function testRespondDispatchesRequest()
{ {
$target = "/cats/"; $middleware = $this->prophesize('\WellRESTed\Routing\MiddlewareInterface');
$middleware->dispatch(Argument::cetera())->willReturn();
$this->request->getRequestTarget()->willReturn($target); $responder = $this->prophesize('WellRESTed\Routing\ResponderInterface');
$this->responder->respond(Argument::any())->willReturn(); $responder->respond(Argument::any())->willReturn();
$routeMap = $this->prophesize('WellRESTed\Routing\RouteMapInterface');
$routeMap->dispatch(Argument::cetera())->will(
function ($args) use ($middleware) {
$middleware->reveal()->dispatch($args[0], $args[1]);
}
);
$router = $this->getMockBuilder('WellRESTed\Routing\Router') $router = $this->getMockBuilder('WellRESTed\Routing\Router')
->setMethods(["getRequest", "getResponse", "getResponder"]) ->setMethods(["getRequest", "getResponse", "getResponder", "getRouteMap"])
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$router->expects($this->any()) $router->expects($this->any())
@ -305,25 +265,39 @@ class RouterTest extends \PHPUnit_Framework_TestCase
->will($this->returnValue($this->response->reveal())); ->will($this->returnValue($this->response->reveal()));
$router->expects($this->any()) $router->expects($this->any())
->method("getResponder") ->method("getResponder")
->will($this->returnValue($this->responder->reveal())); ->will($this->returnValue($responder->reveal()));
$router->expects($this->any())
->method("getRouteMap")
->will($this->returnValue($routeMap->reveal()));
$router->__construct(); $router->__construct();
$router->add($target, $this->middleware->reveal());
$router->respond(); $router->respond();
$this->middleware->dispatch(Argument::cetera())->shouldHaveBeenCalled(); $middleware->dispatch(Argument::cetera())->shouldHaveBeenCalled();
} }
/** /**
* @covers ::respond * @covers ::respond
* @covers ::getRequest
* @covers ::getResponse
* @covers ::getResponder
*/ */
public function testSendsResponseToResponder() public function testSendsResponseToResponder()
{ {
$target = "/cats/"; $middleware = $this->prophesize('\WellRESTed\Routing\MiddlewareInterface');
$middleware->dispatch(Argument::cetera())->willReturn();
$this->request->getRequestTarget()->willReturn($target); $responder = $this->prophesize('WellRESTed\Routing\ResponderInterface');
$responder->respond(Argument::any())->willReturn();
$routeMap = $this->prophesize('WellRESTed\Routing\RouteMapInterface');
$routeMap->dispatch(Argument::cetera())->will(
function ($args) use ($middleware) {
$middleware->reveal()->dispatch($args[0], $args[1]);
}
);
$router = $this->getMockBuilder('WellRESTed\Routing\Router') $router = $this->getMockBuilder('WellRESTed\Routing\Router')
->setMethods(["getRequest", "getResponse", "getResponder"]) ->setMethods(["getRequest", "getResponse", "getResponder", "getRouteMap"])
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$router->expects($this->any()) $router->expects($this->any())
@ -334,11 +308,13 @@ class RouterTest extends \PHPUnit_Framework_TestCase
->will($this->returnValue($this->response->reveal())); ->will($this->returnValue($this->response->reveal()));
$router->expects($this->any()) $router->expects($this->any())
->method("getResponder") ->method("getResponder")
->will($this->returnValue($this->responder->reveal())); ->will($this->returnValue($responder->reveal()));
$router->expects($this->any())
->method("getRouteMap")
->will($this->returnValue($routeMap->reveal()));
$router->__construct(); $router->__construct();
$router->add($target, $this->middleware->reveal());
$router->respond(); $router->respond();
$this->responder->respond($this->response->reveal())->shouldHaveBeenCalled(); $responder->respond($this->response->reveal())->shouldHaveBeenCalled();
} }
} }