summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Light <sam@lightscale.co.uk>2026-06-10 19:00:33 +0100
committerSam Light <sam@lightscale.co.uk>2026-06-10 19:00:33 +0100
commiteb9aa22170ce921d2816db539dd865986f0ba6bc (patch)
tree89495041a4da27e7b4536b058a9a6dc041fdf843
parentfe39ea0aa5a6993ad5edaa6f0f338763ed610ec9 (diff)
testing middleware dispatch
-rw-r--r--src/Group.php17
-rw-r--r--src/Router.php27
-rw-r--r--tests/Unit/RouterTest.php204
-rw-r--r--tests/Utils/TestMiddleware.php15
4 files changed, 246 insertions, 17 deletions
diff --git a/src/Group.php b/src/Group.php
index 9faae2c..7d9db95 100644
--- a/src/Group.php
+++ b/src/Group.php
@@ -9,12 +9,14 @@ use Lightscale\Router\Contracts\Middleware;
class Group
{
use Concerns\HasAncestors;
- use Concerns\HasMiddleware;
+ use Concerns\HasMiddleware {
+ Concerns\HasMiddleware::getMiddleware as protected traitGetMiddleware;
+ }
/** @param Middleware[] $middleware */
public function __construct(
private Router $router,
- private ?Group $parent,
+ private ?self $parent,
private ?string $prefix = null,
private ?string $name = null,
array $middleware = [],
@@ -86,6 +88,17 @@ class Group
return 0 === count($names) ? null : implode('', $names);
}
+ /** @return Middleware[] */
+ public function getMiddleware(): array
+ {
+ $r = [];
+ foreach ($this->getAncestorsAndSelf() as $g) {
+ $r = [...$r, ...$g->traitGetMiddleware()];
+ }
+
+ return $r;
+ }
+
/** @param callable(GroupDefinition): void $cb */
public function group(callable $cb): void
{
diff --git a/src/Router.php b/src/Router.php
index d70b278..fcfc771 100644
--- a/src/Router.php
+++ b/src/Router.php
@@ -119,14 +119,13 @@ class Router
}
/** @return Middleware[] */
- private function getRouteMiddleware(Route $route): array
+ private function getRouteMiddleware(?Route $route): array
{
return [
...$this->getMiddleware(),
- ...($route->getGroup() ?? []),
- ...$route->getMiddleware(),
+ ...($route?->getGroup()?->getMiddleware() ?? []),
+ ...($route?->getMiddleware() ?? []),
];
- return [];
}
public function dispatch(RequestInterface $request): ResponseInterface
@@ -134,18 +133,18 @@ class Router
$uri = $request->getUri();
$match = $this->findRoute($request->getMethod(), $uri->getPath());
- if (null === $match) {
- return $this->strategy->notFound($request);
- }
-
return $this->strategy->runMiddleware(
$request,
- $this->getRouteMiddleware($match->route),
- fn ($request) => $this->strategy->runRoute(new RouteCall(
- $request,
- $match->route,
- $match->segmentMatch->parameters,
- ))
+ $this->getRouteMiddleware($match?->route),
+ fn ($request) => (
+ null === $match ?
+ $this->strategy->notFound($request) :
+ $this->strategy->runRoute(new RouteCall(
+ $request,
+ $match->route,
+ $match->segmentMatch->parameters,
+ ))
+ )
);
}
diff --git a/tests/Unit/RouterTest.php b/tests/Unit/RouterTest.php
index 66b745f..bba7301 100644
--- a/tests/Unit/RouterTest.php
+++ b/tests/Unit/RouterTest.php
@@ -175,6 +175,10 @@ it('calls strategy notFound with dispatch', function () {
->with($request)
->andReturn($response);
+ $strat->shouldReceive('runMiddleware')
+ ->with($request, [], Mockery::type('callable'))
+ ->andReturn($response);
+
$router->setStrategy($strat);
$result = $router->dispatch($request);
expect($result)->toBe($response);
@@ -341,4 +345,202 @@ it('can create a group with middleware')
->expect((new Router())->middleware(new TestMiddleware()))
->toBeInstanceOf(Group::class);
-it('runs middleware on dispatch')->todo();
+describe('middleware', function () {
+ beforeEach(function () {
+ $this->factory = new Psr17Factory();
+ $this->router = new Router();
+ });
+
+ it('dispatches router ', function () {
+ $this->router->addMiddleware($mw = new TestMiddleware());
+ $res = $this->factory->createResponse();
+ $this->router->get('/', fn () => $res);
+
+ expect($this->router->dispatch($this->factory->createServerRequest('get', '/')))
+ ->toBe($res);
+ $mw->assertCalledOnce();
+ });
+
+ it('dispatches group ', function () {
+ $res = $this->factory->createResponse();
+ $this->router->middleware($mw = new TestMiddleware())->group(function ($group) use ($res) {
+ $group->get('/', fn () => $res);
+ });
+
+ expect($this->router->dispatch($this->factory->createServerRequest('get', '/')))
+ ->toBe($res);
+ $mw->assertCalledOnce();
+ });
+
+ it('dispatches specific group only', function () {
+ $res1 = $this->factory->createResponse();
+ $this->router->middleware($mw = new TestMiddleware())->group(function ($group) use ($res1) {
+ $group->get('/mw', fn () => $res1);
+ });
+
+ $res2 = $this->factory->createResponse();
+ $this->router->name('group')->group(function ($group) use ($res2) {
+ $group->get('/none', fn () => $res2);
+ });
+
+ expect($this->router->dispatch($this->factory->createServerRequest('get', '/none')))
+ ->toBe($res2);
+ $mw->assertNotCalled();
+
+ expect($this->router->dispatch($this->factory->createServerRequest('get', '/mw')))
+ ->toBe($res1);
+ $mw->assertCalledOnce();
+ });
+
+ it('dispatches nested groups', function () {
+ $mw1 = new TestMiddleware();
+ $mw2 = new TestMiddleware();
+ $res1 = $this->factory->createResponse();
+ $res2 = $this->factory->createResponse();
+ $this->router->middleware($mw1)->group(function ($group) use ($mw2, $res1, $res2) {
+ $group->get('/test1', fn () => $res1);
+ $group->middleware($mw2)->group(function ($group) use ($res2) {
+ $group->get('/test2', fn () => $res2);
+ });
+ });
+
+ expect($this->router->dispatch(
+ $this->factory->createServerRequest('get', '/test1')
+ ))->toBe($res1);
+
+ $mw1->assertCalledOnce();
+ $mw2->assertNotCalled();
+
+ expect($this->router->dispatch(
+ $this->factory->createServerRequest('get', '/test2')
+ ))->toBe($res2);
+
+ $mw1->assertCalledTimes(2);
+ $mw2->assertCalledOnce();
+ });
+
+ it('dispatches route ', function () {
+ $res = $this->factory->createResponse();
+ $this->router->get('/', fn () => $res)->middleware($mw = new TestMiddleware());
+
+ expect($this->router->dispatch($this->factory->createServerRequest('get', '/')))
+ ->toBe($res);
+ $mw->assertCalled();
+ });
+
+ it('dispatches specific route only', function () {
+ $res1 = $this->factory->createResponse();
+ $this->router->get('/r1', fn () => $res1)->middleware($mw = new TestMiddleware());
+
+ $res2 = $this->factory->createResponse();
+ $this->router->get('/r2', fn () => $res2);
+
+ expect($this->router->dispatch($this->factory->createServerRequest('get', '/r2')))
+ ->toBe($res2);
+ $mw->assertNotCalled();
+
+ expect($this->router->dispatch($this->factory->createServerRequest('get', '/r1')))
+ ->toBe($res1);
+ $mw->assertCalledOnce();
+ });
+
+ it('runs dispatches all types', function () {
+ $this->router->addMiddleware([
+ $mw1 = new TestMiddleware(),
+ $mw2 = new TestMiddleware(),
+ ]);
+
+ $mw3 = new TestMiddleware();
+ $mw4 = new TestMiddleware();
+ $mw5 = new TestMiddleware();
+ $mw6 = new TestMiddleware();
+ $mw7 = new TestMiddleware();
+
+ $res1 = $this->factory->createResponse();
+ $res2 = $this->factory->createResponse();
+ $res3 = $this->factory->createResponse();
+ $res4 = $this->factory->createResponse();
+ $res5 = $this->factory->createResponse();
+ $res6 = $this->factory->createResponse();
+
+ $this->router->middleware($mw3)->group(function ($group) use (
+ $mw4, $res1, $res2, $res3, $res4, $mw5, $mw6
+ ) {
+ $group->get('/test1', fn () => $res1);
+ $group->get('/test2', fn () => $res2)->middleware($mw4);
+ $group->middleware($mw5)->group(function ($group) use ($res3, $res4, $mw6) {
+ $group->get('/test3', fn () => $res3);
+ $group->get('/test4', fn () => $res4)->middleware($mw6);
+ });
+ });
+
+ $this->router->get('/test5', fn () => $res5)->middleware($mw7);
+ $this->router->get('/test6', fn () => $res6);
+
+ expect($this->router->dispatch($this->factory->createServerRequest(
+ 'get', '/test1'
+ )))->toBe($res1);
+ $mw1->assertCalledTimes(1);
+ $mw2->assertCalledTimes(1);
+ $mw3->assertCalledTimes(1);
+ $mw4->assertCalledTimes(0);
+ $mw5->assertCalledTimes(0);
+ $mw6->assertCalledTimes(0);
+ $mw7->assertCalledTimes(0);
+
+ expect($this->router->dispatch($this->factory->createServerRequest(
+ 'get', '/test2'
+ )))->toBe($res2);
+ $mw1->assertCalledTimes(2);
+ $mw2->assertCalledTimes(2);
+ $mw3->assertCalledTimes(2);
+ $mw4->assertCalledTimes(1);
+ $mw5->assertCalledTimes(0);
+ $mw6->assertCalledTimes(0);
+ $mw7->assertCalledTimes(0);
+
+ expect($this->router->dispatch($this->factory->createServerRequest(
+ 'get', '/test3'
+ )))->toBe($res3);
+ $mw1->assertCalledTimes(3);
+ $mw2->assertCalledTimes(3);
+ $mw3->assertCalledTimes(3);
+ $mw4->assertCalledTimes(1);
+ $mw5->assertCalledTimes(1);
+ $mw6->assertCalledTimes(0);
+ $mw7->assertCalledTimes(0);
+
+ expect($this->router->dispatch($this->factory->createServerRequest(
+ 'get', '/test4'
+ )))->toBe($res4);
+ $mw1->assertCalledTimes(4);
+ $mw2->assertCalledTimes(4);
+ $mw3->assertCalledTimes(4);
+ $mw4->assertCalledTimes(1);
+ $mw5->assertCalledTimes(2);
+ $mw6->assertCalledTimes(1);
+ $mw7->assertCalledTimes(0);
+
+ expect($this->router->dispatch($this->factory->createServerRequest(
+ 'get', '/test5'
+ )))->toBe($res5);
+ $mw1->assertCalledTimes(5);
+ $mw2->assertCalledTimes(5);
+ $mw3->assertCalledTimes(4);
+ $mw4->assertCalledTimes(1);
+ $mw5->assertCalledTimes(2);
+ $mw6->assertCalledTimes(1);
+ $mw7->assertCalledTimes(1);
+
+ expect($this->router->dispatch($this->factory->createServerRequest(
+ 'get', '/test6'
+ )))->toBe($res6);
+ $mw1->assertCalledTimes(6);
+ $mw2->assertCalledTimes(6);
+ $mw3->assertCalledTimes(4);
+ $mw4->assertCalledTimes(1);
+ $mw5->assertCalledTimes(2);
+ $mw6->assertCalledTimes(1);
+ $mw7->assertCalledTimes(1);
+ });
+});
diff --git a/tests/Utils/TestMiddleware.php b/tests/Utils/TestMiddleware.php
index eb3ea04..e273d05 100644
--- a/tests/Utils/TestMiddleware.php
+++ b/tests/Utils/TestMiddleware.php
@@ -34,6 +34,21 @@ class TestMiddleware implements Middleware
Assert::assertGreaterThan(0, $this->calls);
}
+ public function assertCalledTimes(int $val): void
+ {
+ Assert::assertSame($val, $this->calls);
+ }
+
+ public function assertCalledOnce(): void
+ {
+ $this->assertCalledTimes(1);
+ }
+
+ public function assertNotCalled(): void
+ {
+ $this->assertCalledTimes(0);
+ }
+
public function assertCallNumber(int $num): void
{
Assert::assertSame($num, $this->call);