diff options
| -rw-r--r-- | src/Group.php | 17 | ||||
| -rw-r--r-- | src/Router.php | 27 | ||||
| -rw-r--r-- | tests/Unit/RouterTest.php | 204 | ||||
| -rw-r--r-- | tests/Utils/TestMiddleware.php | 15 |
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); |
