Update Router to work with updated Routes and ErrorHandlers

Deprecate:
	- Router::setStaticRoute
	- Router::setPrefixRoute
This commit is contained in:
PJ Dietz 2015-02-19 22:03:50 -05:00
parent 4deac492dd
commit 04561076d5
2 changed files with 330 additions and 372 deletions

View File

@ -16,6 +16,8 @@ use pjdietz\WellRESTed\Interfaces\RequestInterface;
use pjdietz\WellRESTed\Interfaces\ResponseInterface; use pjdietz\WellRESTed\Interfaces\ResponseInterface;
use pjdietz\WellRESTed\Interfaces\Routes\PrefixRouteInterface; use pjdietz\WellRESTed\Interfaces\Routes\PrefixRouteInterface;
use pjdietz\WellRESTed\Interfaces\Routes\StaticRouteInterface; use pjdietz\WellRESTed\Interfaces\Routes\StaticRouteInterface;
use pjdietz\WellRESTed\Routes\PrefixRoute;
use pjdietz\WellRESTed\Routes\StaticRoute;
/** /**
* Router * Router
@ -27,13 +29,13 @@ class Router implements HandlerInterface
/** @var array Array of Route objects */ /** @var array Array of Route objects */
private $routes; private $routes;
/** @var array Hash array mapping path prefixes to handlers */ /** @var array Hash array mapping path prefixes to routes */
private $prefixRoutes; private $prefixRoutes;
/** @var array Hash array mapping exact paths to handlers */ /** @var array Hash array mapping exact paths to routes */
private $staticRoutes; private $staticRoutes;
/** @var array Hash array of status code => qualified HandlerInterface names for error handling. */ /** @var array Hash array of status code => error handler */
private $errorHandlers; private $errorHandlers;
/** Create a new Router. */ /** Create a new Router. */
@ -67,16 +69,16 @@ class Router implements HandlerInterface
} }
/** /**
* Append a new route to the route route table. * Append a new route to the route table.
* *
* @param HandlerInterface $route * @param HandlerInterface $route
*/ */
public function addRoute(HandlerInterface $route) public function addRoute(HandlerInterface $route)
{ {
if ($route instanceof StaticRouteInterface) { if ($route instanceof StaticRouteInterface) {
$this->setStaticRoute($route->getPaths(), $route->getHandler()); $this->addStaticRoute($route);
} elseif ($route instanceof PrefixRouteInterface) { } elseif ($route instanceof PrefixRouteInterface) {
$this->setPrefixRoute($route->getPrefixes(), $route->getHandler()); $this->addPrefixRoute($route);
} else { } else {
$this->routes[] = $route; $this->routes[] = $route;
} }
@ -96,43 +98,11 @@ class Router implements HandlerInterface
} }
} }
/**
* Add a route for paths beginning with a given prefix.
*
* @param string|array $prefixes Prefix of a path to match
* @param string $handler Fully qualified name to an autoloadable handler class
*/
public function setPrefixRoute($prefixes, $handler)
{
if (is_string($prefixes)) {
$prefixes = array($prefixes);
}
foreach ($prefixes as $prefix) {
$this->prefixRoutes[$prefix] = $handler;
}
}
/**
* Add a route for an exact match to a path.
*
* @param string|array $paths Path component of the URI or a list of paths
* @param string $handler Fully qualified name to an autoloadable handler class
*/
public function setStaticRoute($paths, $handler)
{
if (is_string($paths)) {
$paths = array($paths);
}
foreach ($paths as $path) {
$this->staticRoutes[$path] = $handler;
}
}
/** /**
* Add a custom error handler. * Add a custom error handler.
* *
* @param integer $statusCode The error status code. * @param integer $statusCode The error status code
* @param string $errorHandler Fully qualified name to an autoloadable handler class. * @param callable|string|HandlerInterface $errorHandler
*/ */
public function setErrorHandler($statusCode, $errorHandler) public function setErrorHandler($statusCode, $errorHandler)
{ {
@ -142,7 +112,7 @@ class Router implements HandlerInterface
/** /**
* Add custom error handlers. * Add custom error handlers.
* *
* @param array $errorHandlers Array mapping integer error codes to qualified handler names. * @param array $errorHandlers Array mapping integer error codes to handlers
*/ */
public function setErrorHandlers(array $errorHandlers) public function setErrorHandlers(array $errorHandlers)
{ {
@ -185,11 +155,25 @@ class Router implements HandlerInterface
return $response; return $response;
} }
private function addStaticRoute(StaticRouteInterface $staticRoute)
{
foreach ($staticRoute->getPaths() as $path) {
$this->staticRoutes[$path] = $staticRoute;
}
}
private function addPrefixRoute(PrefixRouteInterface $prefixRoute)
{
foreach ($prefixRoute->getPrefixes() as $prefix) {
$this->prefixRoutes[$prefix] = $prefixRoute;
}
}
private function getErrorResponse($status, $request, $args = null, $response = null) private function getErrorResponse($status, $request, $args = null, $response = null)
{ {
if (isset($this->errorHandlers[$status])) { if (isset($this->errorHandlers[$status])) {
/** @var HandlerInterface $errorHandler */ $unpacker = new HandlerUnpacker();
$errorHandler = new $this->errorHandlers[$status](); $errorHandler = $unpacker->unpack($this->errorHandlers[$status]);
// Pass the response triggering this along to the error handler. // Pass the response triggering this along to the error handler.
$errorArgs = array("response" => $response); $errorArgs = array("response" => $response);
if ($args) { if ($args) {
@ -201,7 +185,7 @@ class Router implements HandlerInterface
} }
/** /**
* Returning the matching static handler, or null if none match. * Returning the handler associated with the matching static route, or null if none match.
* *
* @param $path string The request's path * @param $path string The request's path
* @return HandlerInterface|null * @return HandlerInterface|null
@ -209,8 +193,8 @@ class Router implements HandlerInterface
private function getStaticHandler($path) private function getStaticHandler($path)
{ {
if (isset($this->staticRoutes[$path])) { if (isset($this->staticRoutes[$path])) {
// Instantiate and return the handler identified by the path. $route = $this->staticRoutes[$path];
return new $this->staticRoutes[$path](); return $route->getHandler();
} }
return null; return null;
} }
@ -237,7 +221,8 @@ class Router implements HandlerInterface
}); });
} }
// Instantiate and return the handler identified as the best match. // Instantiate and return the handler identified as the best match.
return new $this->prefixRoutes[$matches[0]](); $route = $this->prefixRoutes[$matches[0]];
return $route->getHandler();
} }
return null; return null;
} }
@ -295,4 +280,28 @@ class Router implements HandlerInterface
} }
return $response; return $response;
} }
////////////////
// Deprecated //
////////////////
/**
* @deprecated Use {@see addRoute} instead.
* @see addRoute
*/
public function setPrefixRoute($prefixes, $handler)
{
$this->addPrefixRoute(new PrefixRoute($prefixes, $handler));
trigger_error("Router::setPrefixRoute is deprecated. Use addRoute", E_USER_DEPRECATED);
}
/**
* @deprecated Use {@see addRoute} instead.
* @see addRoute
*/
public function setStaticRoute($paths, $handler)
{
$this->addStaticRoute(new StaticRoute($paths, $handler));
trigger_error("Router::setStaticRoute is deprecated. Use addRoute", E_USER_DEPRECATED);
}
} }

View File

@ -2,245 +2,341 @@
namespace pjdietz\WellRESTed\Test; namespace pjdietz\WellRESTed\Test;
use pjdietz\WellRESTed\Exceptions\HttpExceptions\ForbiddenException; use pjdietz\WellRESTed\Exceptions\HttpExceptions\HttpException;
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
use pjdietz\WellRESTed\Interfaces\RequestInterface;
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
use pjdietz\WellRESTed\Response;
use pjdietz\WellRESTed\Router; use pjdietz\WellRESTed\Router;
use pjdietz\WellRESTed\Routes\PrefixRoute; use Prophecy\Argument;
use pjdietz\WellRESTed\Routes\StaticRoute;
use pjdietz\WellRESTed\Routes\TemplateRoute;
/**
* @covers pjdietz\WellRESTed\Router
*/
class RouterTest extends \PHPUnit_Framework_TestCase class RouterTest extends \PHPUnit_Framework_TestCase
{ {
public function testAddRoute() private $handler;
private $request;
private $response;
private $route;
public function setUp()
{ {
$path = "/"; $this->request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
$this->response = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\ResponseInterface");
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $this->route = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$mockRequest->expects($this->any()) $this->handler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
->method('getPath')
->will($this->returnValue($path));
$route = new StaticRoute($path, __NAMESPACE__ . '\\RouterTestHandler');
$router = new Router();
$router->addRoute($route);
$resp = $router->getResponse($mockRequest);
$this->assertNotNull($resp);
} }
public function testAddRoutes() public function testMatchesStaticRoute()
{ {
$path = "/"; $this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $this->route->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\StaticRouteInterface");
$mockRequest->expects($this->any()) $this->route->getPaths()->willReturn(["/cats/"]);
->method('getPath') $this->route->getHandler()->willReturn($this->handler->reveal());
->will($this->returnValue($path));
$routes = array(); $this->request->getPath()->willReturn("/cats/");
$routes[] = new StaticRoute("/", __NAMESPACE__ . '\\RouterTestHandler');
$routes[] = new StaticRoute("/another/", __NAMESPACE__ . '\\RouterTestHandler');
$router = new Router(); $router = new Router();
$router->addRoutes($routes); $router->addRoute($this->route->reveal());
$resp = $router->getResponse($mockRequest); $router->getResponse($this->request->reveal());
$this->assertEquals(200, $resp->getStatusCode());
$this->route->getHandler()->shouldHaveBeenCalled();
} }
public function testAddStaticRoute() public function testMatchesPrefixRoute()
{ {
$path = "/cats/"; $this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $this->route->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\PrefixRouteInterface");
$mockRequest->expects($this->any()) $this->route->getPrefixes()->willReturn(["/cats/"]);
->method('getPath') $this->route->getHandler()->willReturn($this->handler->reveal());
->will($this->returnValue($path));
$this->request->getPath()->willReturn("/cats/molly");
$router = new Router(); $router = new Router();
$router->setStaticRoute($path, __NAMESPACE__ . '\\RouterTestHandler'); $router->addRoute($this->route->reveal());
$resp = $router->getResponse($mockRequest); $router->getResponse($this->request->reveal());
$this->assertNotNull($resp);
$this->route->getHandler()->shouldHaveBeenCalled();
} }
public function testAddPrefixRoute() public function testMatchesBestPrefixRoute()
{ {
$path = "/cats/"; $this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $route1 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$mockRequest->expects($this->any()) $route1->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\PrefixRouteInterface");
->method('getPath') $route1->getPrefixes()->willReturn(["/animals/"]);
->will($this->returnValue($path)); $route1->getHandler()->willReturn($this->handler->reveal());
$route2 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$route2->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\PrefixRouteInterface");
$route2->getPrefixes()->willReturn(["/animals/cats/"]);
$route2->getHandler()->willReturn($this->handler->reveal());
$this->request->getPath()->willReturn("/animals/cats/molly");
$router = new Router(); $router = new Router();
$router->setPrefixRoute($path, __NAMESPACE__ . '\\RouterTestHandler'); $router->addRoute($route1->reveal());
$resp = $router->getResponse($mockRequest); $router->addRoute($route2->reveal());
$this->assertNotNull($resp); $router->getResponse($this->request->reveal());
$route1->getHandler()->shouldNotHaveBeenCalled();
$route2->getHandler()->shouldHaveBeenCalled();
} }
public function testMatchStaticRouteBeforePrefixRoute() public function testMatchesStaticRouteBeforePrefixRoute()
{ {
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$mockRequest->expects($this->any())
->method('getPath') $route1 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
->will($this->returnValue("/amimals/cats/molly")); $route1->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\PrefixRouteInterface");
$route1->getPrefixes()->willReturn(["/animals/cats/"]);
$route1->getHandler()->willReturn($this->handler->reveal());
$route2 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$route2->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\StaticRouteInterface");
$route2->getPaths()->willReturn(["/animals/cats/molly"]);
$route2->getHandler()->willReturn($this->handler->reveal());
$this->request->getPath()->willReturn("/animals/cats/molly");
$router = new Router(); $router = new Router();
$router->addRoute(new PrefixRoute("/amimals/", __NAMESPACE__ . '\\NotFoundHandler')); $router->addRoute($route1->reveal());
$router->addRoute(new PrefixRoute("/amimals/cats/", __NAMESPACE__ . '\\NotFoundHandler')); $router->addRoute($route2->reveal());
$router->addRoute(new StaticRoute("/amimals/cats/molly", __NAMESPACE__ . '\\RouterTestHandler')); $router->getResponse($this->request->reveal());
$resp = $router->getResponse($mockRequest);
$this->assertEquals(200, $resp->getStatusCode()); $route1->getHandler()->shouldNotHaveBeenCalled();
$route2->getHandler()->shouldHaveBeenCalled();
} }
public function testMatchBestPrefixRoute() public function testMatchesPrefixRouteBeforeHandlerRoute()
{ {
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$mockRequest->expects($this->any())
->method('getPath') $route1 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
->will($this->returnValue("/amimals/cats/molly")); $route1->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\PrefixRouteInterface");
$route1->getPrefixes()->willReturn(["/animals/cats/"]);
$route1->getHandler()->willReturn($this->handler->reveal());
$route2 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$route2->getResponse(Argument::cetera())->willReturn(null);
$this->request->getPath()->willReturn("/animals/cats/molly");
$router = new Router(); $router = new Router();
$router->addRoute(new PrefixRoute("/amimals/", __NAMESPACE__ . '\\NotFoundHandler')); $router->addRoute($route1->reveal());
$router->addRoute(new PrefixRoute("/amimals/dogs/", __NAMESPACE__ . '\\NotFoundHandler')); $router->addRoute($route2->reveal());
$router->addRoute(new PrefixRoute("/amimals/cats/", __NAMESPACE__ . '\\RouterTestHandler')); $router->getResponse($this->request->reveal());
$resp = $router->getResponse($mockRequest);
$this->assertEquals(200, $resp->getStatusCode()); $route1->getHandler()->shouldHaveBeenCalled();
$route2->getResponse(Argument::cetera())->shouldNotHaveBeenCalled();
} }
public function testMatchBestPrefixRouteBeforeTemplateRoute() public function testReturnsFirstNonNullResponse()
{ {
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $route1 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$mockRequest->expects($this->any()) $route1->getResponse(Argument::cetera())->willReturn(null);
->method('getPath')
->will($this->returnValue("/amimals/cats/molly")); $route2 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$route2->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$route3 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$route3->getResponse(Argument::cetera())->willReturn(null);
$this->request->getPath()->willReturn("/");
$router = new Router(); $router = new Router();
$router->addRoute(new PrefixRoute("/amimals/", __NAMESPACE__ . '\\NotFoundHandler')); $router->addRoutes(
$router->addRoute(new PrefixRoute("/amimals/cats/", __NAMESPACE__ . '\\RouterTestHandler')); [
$router->addRoute(new TemplateRoute("/amimals/cats/*", __NAMESPACE__ . '\\NotFoundHandler')); $route1->reveal(),
$resp = $router->getResponse($mockRequest); $route2->reveal(),
$this->assertEquals(200, $resp->getStatusCode()); $route3->reveal()
]
);
$response = $router->getResponse($this->request->reveal());
$this->assertNotNull($response);
$route1->getResponse(Argument::cetera())->shouldHaveBeenCalled();
$route2->getResponse(Argument::cetera())->shouldHaveBeenCalled();
$route3->getResponse(Argument::cetera())->shouldNotHaveBeenCalled();
} }
public function testRespondWithDefaultErrorForException() public function testReturnsNullWhenNoRouteMatches()
{ {
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $route1 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$mockRequest->expects($this->any()) $route1->getResponse(Argument::cetera())->willReturn(null);
->method('getPath')
->will($this->returnValue("/")); $route2 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$route2->getResponse(Argument::cetera())->willReturn(null);
$route3 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$route3->getResponse(Argument::cetera())->willReturn(null);
$router = new Router(); $router = new Router();
$router->addRoute(new StaticRoute("/", __NAMESPACE__ . '\\ForbiddenExceptionHandler')); $router->addRoutes(
$resp = $router->getResponse($mockRequest); [
$this->assertEquals(403, $resp->getStatusCode()); $route1->reveal(),
$route2->reveal(),
$route3->reveal()
]
);
$response = $router->getResponse($this->request->reveal());
$this->assertNull($response);
$route1->getResponse(Argument::cetera())->shouldHaveBeenCalled();
$route2->getResponse(Argument::cetera())->shouldHaveBeenCalled();
$route3->getResponse(Argument::cetera())->shouldHaveBeenCalled();
} }
public function testRespondWithErrorHandlerForException() public function testRespondsWithErrorResponseForHttpException()
{ {
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $this->route->getResponse(Argument::cetera())->willThrow(new HttpException());
$mockRequest->expects($this->any()) $this->request->getPath()->willReturn("/");
->method('getPath')
->will($this->returnValue("/"));
$router = new Router(); $router = new Router();
$router->addRoute(new StaticRoute("/", __NAMESPACE__ . '\\ForbiddenExceptionHandler')); $router->addRoute($this->route->reveal());
$router->setErrorHandler(403, __NAMESPACE__ . '\\ForbiddenErrorHandler'); $response = $router->getResponse($this->request->reveal());
$resp = $router->getResponse($mockRequest); $this->assertEquals(500, $response->getStatusCode());
$this->assertEquals("YOU SHALL NOT PASS!", $resp->getBody());
} }
public function testRespondWithErrorHandlerForStatusCode() public function testDispatchesErrorHandlerForStatusCode()
{ {
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $this->request->getPath()->willReturn("/");
$mockRequest->expects($this->any()) $this->response->getStatusCode()->willReturn(403);
->method('getPath') $this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
->will($this->returnValue("/"));
$errorHandler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$errorHandler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$router = new Router(); $router = new Router();
$router->addRoute(new StaticRoute("/", __NAMESPACE__ . '\\ForbiddenHandler')); $router->addRoute($this->route->reveal());
$router->setErrorHandler(403, __NAMESPACE__ . '\\ForbiddenErrorHandler'); $router->setErrorHandlers([403 => $errorHandler->reveal()]);
$resp = $router->getResponse($mockRequest); $router->getResponse($this->request->reveal());
$this->assertEquals("YOU SHALL NOT PASS!", $resp->getBody());
$errorHandler->getResponse(Argument::cetera())->shouldHaveBeenCalled();
} }
public function testRespondWithErrorHandlerUsingOriginalResponse() public function testDispatchesErrorHandlerWithOriginalRequest()
{ {
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $this->request->getPath()->willReturn("/");
$mockRequest->expects($this->any()) $this->response->getStatusCode()->willReturn(403);
->method('getPath') $this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
->will($this->returnValue("/"));
$errorHandler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$errorHandler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$request = $this->request->reveal();
$router = new Router(); $router = new Router();
$router->addRoute(new StaticRoute("/", __NAMESPACE__ . '\\MessageHandler')); $router->addRoute($this->route->reveal());
$router->setErrorHandlers([404 => __NAMESPACE__ . '\\MessageErrorHandler']); $router->setErrorHandlers([403 => $errorHandler->reveal()]);
$resp = $router->getResponse($mockRequest); $router->getResponse($request);
$this->assertEquals("<h1>Not Found</h1>", $resp->getBody());
$errorHandler->getResponse(
Argument::that(
function ($arg) use ($request) {
return $arg === $request;
}
),
Argument::any()
)->shouldHaveBeenCalled();
} }
public function testRespondWithErrorHandlerUsingInjection() public function testDispatchesErrorHandlerWithOriginalArguments()
{ {
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $this->request->getPath()->willReturn("/");
$mockRequest->expects($this->any()) $this->response->getStatusCode()->willReturn(403);
->method('getPath') $response = $this->response->reveal();
->will($this->returnValue("/")); $this->route->getResponse(Argument::cetera())->willReturn($response);
$errorHandler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$errorHandler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$arguments = [
"cat" => "Molly",
"dog" => "Bear"
];
$router = new Router(); $router = new Router();
$router->addRoute(new StaticRoute("/", __NAMESPACE__ . '\\ForbiddenHandler')); $router->addRoute($this->route->reveal());
$router->setErrorHandlers([403 => __NAMESPACE__ . '\\InjectionErrorHandler']); $router->setErrorHandlers([403 => $errorHandler->reveal()]);
$resp = $router->getResponse($mockRequest, ["message" => "Pass through"]); $router->getResponse($this->request->reveal(), $arguments);
$this->assertEquals("Pass through", $resp->getBody());
$errorHandler->getResponse(
Argument::any(),
Argument::that(
function ($args) use ($arguments) {
return count(array_diff_assoc($arguments, $args)) === 0;
}
)
)->shouldHaveBeenCalled();
} }
public function testReturnNullWhenNoRouteMatches() public function testDispatchesErrorHandlerWithPreviousResponse()
{ {
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface'); $this->request->getPath()->willReturn("/");
$mockRequest->expects($this->any()) $this->response->getStatusCode()->willReturn(403);
->method('getPath') $response = $this->response->reveal();
->will($this->returnValue("/dog/")); $this->route->getResponse(Argument::cetera())->willReturn($response);
$errorHandler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$errorHandler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$route = new StaticRoute("/cat/", __NAMESPACE__ . '\\RouterTestHandler');
$router = new Router(); $router = new Router();
$router->addRoute($route); $router->addRoute($this->route->reveal());
$resp = $router->getResponse($mockRequest); $router->setErrorHandlers([403 => $errorHandler->reveal()]);
$this->assertNull($resp); $router->getResponse($this->request->reveal());
}
public function testNestedRouters() $errorHandler->getResponse(
{ Argument::any(),
$path = "/cats/"; Argument::that(
function ($arg) use ($response) {
$router1 = new Router(); return isset($arg["response"]) && $arg["response"] === $response;
$router2 = new Router(); }
$router3 = new Router(); )
)->shouldHaveBeenCalled();
$router1->addRoute($router2);
$router2->addRoute($router3);
$router3->addRoute(new StaticRoute($path, __NAMESPACE__ . '\\RouterTestHandler'));
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
$mockRequest->expects($this->any())
->method('getPath')
->will($this->returnValue($path));
$resp = $router1->getResponse($mockRequest);
$this->assertNotNull($resp);
} }
/** /**
* @runInSeparateProcess * @runInSeparateProcess
* @preserveGlobalState disabled * @preserveGlobalState disabled
*/ */
public function testStaticRequestDoesNotMatchRouter() public function testRoutesStaticRequest()
{ {
$_SERVER["REQUEST_URI"] = "/cats/"; $_SERVER["REQUEST_URI"] = "/cats/";
$_SERVER["HTTP_HOST"] = "localhost"; $_SERVER["HTTP_HOST"] = "localhost";
$_SERVER["REQUEST_METHOD"] = "GET"; $_SERVER["REQUEST_METHOD"] = "GET";
$route = new StaticRoute("/dogs/", __NAMESPACE__ . '\\RouterTestHandler'); $this->response->getStatusCode()->willReturn(200);
$this->response->respond()->willReturn();
$this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$this->route->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\StaticRouteInterface");
$this->route->getPaths()->willReturn(["/cats/"]);
$this->route->getHandler()->willReturn($this->handler->reveal());
$router = new Router(); $router = new Router();
$router->addRoute($route); $router->addRoute($this->route->reveal());
ob_start();
$router->respond();
ob_end_clean();
$this->response->respond()->shouldHaveBeenCalled();
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testRoutesStaticRequestToNoRouteResponse()
{
$_SERVER["REQUEST_URI"] = "/cats/";
$_SERVER["HTTP_HOST"] = "localhost";
$_SERVER["REQUEST_METHOD"] = "GET";
$router = new Router();
ob_start(); ob_start();
$router->respond(); $router->respond();
$captured = ob_get_contents(); $captured = ob_get_contents();
@ -253,193 +349,46 @@ class RouterTest extends \PHPUnit_Framework_TestCase
* @runInSeparateProcess * @runInSeparateProcess
* @preserveGlobalState disabled * @preserveGlobalState disabled
*/ */
public function testRespondWithErrorHandlerForNoRoute() public function testRoutesStaticRequestTo404ErrorHandler()
{ {
$_SERVER["REQUEST_URI"] = "/cats/"; $_SERVER["REQUEST_URI"] = "/cats/";
$_SERVER["HTTP_HOST"] = "localhost"; $_SERVER["HTTP_HOST"] = "localhost";
$_SERVER["REQUEST_METHOD"] = "GET"; $_SERVER["REQUEST_METHOD"] = "GET";
$errorHandler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$errorHandler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$router = new Router(); $router = new Router();
$router->setErrorHandler(404, __NAMESPACE__ . '\\MessageHandler'); $router->setErrorHandler(404, $errorHandler->reveal());
ob_start(); ob_start();
$router->respond(); $router->respond();
$captured = ob_get_contents();
ob_end_clean(); ob_end_clean();
$this->assertEquals("Not Found", $captured); $errorHandler->getResponse(Argument::cetera())->shouldHaveBeenCalled();
} }
/** public function testDeprecatedSetStaticRoute()
* @dataProvider nestedRouterRoutesProvider
*/
public function testNestedRouterFromWithRoutes($path, $expectedBody)
{ {
$router = new Router(); $this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$router->addRoutes(array( $this->request->getPath()->willReturn("/cats/");
new TemplateRoute("/cats/*", __NAMESPACE__ . "\\CatRouter"),
new TemplateRoute("/dogs/*", __NAMESPACE__ . "\\DogRouter"),
new NotFoundHandler()
));
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
$mockRequest->expects($this->any())
->method('getPath')
->will($this->returnValue($path));
$resp = $router->getResponse($mockRequest);
$this->assertEquals($expectedBody, $resp->getBody());
}
public function nestedRouterRoutesProvider()
{
return [
["/cats/", "/cats/"],
["/cats/molly", "/cats/molly"],
["/dogs/", "/dogs/"],
["/birds/", "No resource found at /birds/"]
];
}
public function testInjection()
{
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
$mockRequest->expects($this->any())
->method('getPath')
->will($this->returnValue("/2/3"));
$dependencies = [
"add" => function ($a, $b) {
return $a + $b;
}
];
$router = new Router(); $router = new Router();
$router->addRoute(new TemplateRoute("/{a}/{b}", __NAMESPACE__ . "\\InjectionHandler")); @$router->setStaticRoute(["/cats/"], $this->handler->reveal());
$resp = $router->getResponse($mockRequest, $dependencies); $router->getResponse($this->request->reveal());
$this->assertEquals("5", $resp->getBody());
$this->handler->getResponse(Argument::cetera())->shouldHaveBeenCalled();
} }
} public function testDeprecatedSetPrefixRoute()
/**
* Mini Handler class that allways returns a 200 status code Response.
*/
class RouterTestHandler implements HandlerInterface
{
public function getResponse(RequestInterface $request, array $args = null)
{ {
$resp = new Response(); $this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$resp->setStatusCode(200); $this->request->getPath()->willReturn("/cats/molly");
$resp->setBody($request->getPath());
return $resp;
}
}
class CatRouter extends Router $router = new Router();
{ @$router->setPrefixRoute(["/cats/"], $this->handler->reveal());
public function __construct() $router->getResponse($this->request->reveal());
{
parent::__construct();
$this->addRoutes([
new StaticRoute("/cats/", __NAMESPACE__ . "\\RouterTestHandler"),
new StaticRoute("/cats/molly", __NAMESPACE__ . "\\RouterTestHandler"),
new StaticRoute("/cats/oscar", __NAMESPACE__ . "\\RouterTestHandler")
]);
}
}
class DogRouter extends Router $this->handler->getResponse(Argument::cetera())->shouldHaveBeenCalled();
{
public function __construct()
{
parent::__construct();
$this->addRoutes([
new StaticRoute("/dogs/", __NAMESPACE__ . "\\RouterTestHandler"),
new StaticRoute("/dogs/bear", __NAMESPACE__ . "\\RouterTestHandler")
]);
}
}
class NotFoundHandler implements HandlerInterface
{
public function getResponse(RequestInterface $request, array $args = null)
{
$response = new Response(404);
$response->setBody("No resource found at " . $request->getPath());
return $response;
}
}
class ForbiddenHandler implements HandlerInterface
{
public function getResponse(RequestInterface $request, array $args = null)
{
$response = new Response(403);
$response->setBody("Forbidden");
return $response;
}
}
class ForbiddenExceptionHandler implements HandlerInterface
{
public function getResponse(RequestInterface $request, array $args = null)
{
throw new ForbiddenException();
}
}
class ForbiddenErrorHandler implements HandlerInterface
{
public function getResponse(RequestInterface $request, array $args = null)
{
$response = new Response(403);
$response->setBody("YOU SHALL NOT PASS!");
return $response;
}
}
class MessageHandler implements HandlerInterface
{
public function getResponse(RequestInterface $request, array $args = null)
{
$response = new Response(404);
$response->setBody("Not Found");
return $response;
}
}
class MessageErrorHandler implements HandlerInterface
{
public function getResponse(RequestInterface $request, array $args = null)
{
if (isset($args["response"])) {
/** @var ResponseInterface $response */
$response = $args["response"];
$message = "<h1>" . $response->getBody() . "</h1>";
$response->setBody($message);
return $response;
}
return null;
}
}
class InjectionErrorHandler implements HandlerInterface
{
public function getResponse(RequestInterface $request, array $args = null)
{
$response = new Response(403);
$response->setBody($args["message"]);
return $response;
}
}
class InjectionHandler implements HandlerInterface
{
public function getResponse(RequestInterface $request, array $args = null)
{
$response = new Response(200);
$body = $args["add"]($args["a"], $args["b"]);
$response->setBody($body);
return $response;
} }
} }