bind(FakeController::class, new FakeController()); $log = new ArrayObject(); $resolver->bind(FakeMiddleware::class, new FakeMiddleware('m', $log)); return [new Router($resolver), $resolver, $log]; } public function testDispatchesToController(): void { [$router] = $this->makeRouter(); $router->registerController(FakeController::class); $response = $router->dispatch(new TestRequest('GET', '/entries')); $this->assertSame(200, $response->status()); $this->assertSame('index', $response->body()); } public function testInjectsRouteParameters(): void { [$router] = $this->makeRouter(); $router->registerController(FakeController::class); $response = $router->dispatch(new TestRequest('GET', '/entries/hello-world')); $this->assertSame('show:hello-world', $response->body()); } public function testAppliesRouteMiddleware(): void { [$router, , $log] = $this->makeRouter(); $router->registerController(FakeController::class); $router->dispatch(new TestRequest('POST', '/entries')); $this->assertSame(['m'], $log->getArrayCopy()); } public function testDuplicateRouteThrows(): void { [$router] = $this->makeRouter(); $router->registerController(FakeController::class); $this->expectException(RouteRegistrationException::class); $router->registerController(FakeController::class); } public function testNotFoundThrowsWithoutHandler(): void { [$router] = $this->makeRouter(); $router->registerController(FakeController::class); $this->expectException(RouteNotFoundException::class); $router->dispatch(new TestRequest('GET', '/missing')); } public function testNotFoundHandlerIsCalledWhenSet(): void { [$router] = $this->makeRouter(); $router->registerController(FakeController::class); $router->setNotFoundHandler(static fn (): TestResponse => new TestResponse(404, 'custom-nf')); $response = $router->dispatch(new TestRequest('GET', '/missing')); $this->assertSame(404, $response->status()); $this->assertSame('custom-nf', $response->body()); } public function testTrailingSlashIsNormalized(): void { [$router] = $this->makeRouter(); $router->registerController(FakeController::class); $response = $router->dispatch(new TestRequest('GET', '/entries/')); $this->assertSame('index', $response->body()); } public function testMethodIsCaseInsensitive(): void { [$router] = $this->makeRouter(); $router->registerController(FakeController::class); $response = $router->dispatch(new TestRequest('get', '/entries')); $this->assertSame('index', $response->body()); } public function testLeadingDoubleSlashDoesNotMatch(): void { [$router] = $this->makeRouter(); $router->registerController(FakeController::class); $this->expectException(RouteNotFoundException::class); $router->dispatch(new TestRequest('GET', '//entries')); } public function testPathsAreCaseSensitive(): void { [$router] = $this->makeRouter(); $router->registerController(FakeController::class); $this->expectException(RouteNotFoundException::class); $router->dispatch(new TestRequest('GET', '/Entries')); } public function testDuplicateRouteAcrossControllersThrows(): void { [$router] = $this->makeRouter(); $router->registerController(FakeController::class); $this->expectException(RouteRegistrationException::class); $router->registerController(CollidingController::class); } }