diff --git a/CHANGELOG.md b/CHANGELOG.md index ce5f9a2..862ff22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased] + +### Fixed + +- User generated errors were interpreted as "not found" errors. + ## 4.0.1 - 2026-03-19 ### Fixed diff --git a/src/Http/Router.php b/src/Http/Router.php index 6deed48..ada236e 100644 --- a/src/Http/Router.php +++ b/src/Http/Router.php @@ -6,7 +6,6 @@ use Innmind\Http\{ ServerRequest, Response, - Response\StatusCode, }; use Innmind\Router\{ Component, @@ -14,6 +13,8 @@ Any, Handle, Respond, + Exception\NotFound, + Exception\NoRouteProvided, }; use Innmind\Immutable\{ Maybe, @@ -45,24 +46,28 @@ public function __construct( public function __invoke(ServerRequest $request): Attempt { $recover = $this->recover; + $notFound = $this->notFound; /** * @psalm-suppress MixedArgumentTypeCoercion */ $route = Route::of( Any::from($this->routes) + ->mapError(static fn($e) => match (true) { + $e instanceof NoRouteProvided => new NotFound, + default => $e, + }) + ->otherwise(static fn(\Throwable $e) => Component::of( + static fn($request) => $notFound + ->filter(static fn() => $e instanceof NotFound) + ->match( + static fn($handle) => $handle($request), + static fn() => Attempt::error($e), + ), + )) ->otherwise(Respond::withHttpErrors()) ->otherwise(static fn($e) => Handle::via( static fn($request) => $recover($request, $e), - )) - ->or(Handle::via( - fn($request, SideEffect $_) => $this->notFound->match( - static fn($handle) => $handle($request), - static fn() => Attempt::result(Response::of( - StatusCode::notFound, - $request->protocolVersion(), - )), - ), )), ); diff --git a/tests/ApplicationTest.php b/tests/ApplicationTest.php index 1b1059d..0b5d151 100644 --- a/tests/ApplicationTest.php +++ b/tests/ApplicationTest.php @@ -1406,4 +1406,40 @@ public function testRecoverRouteError(): BlackBox\Proof $this->assertSame($expected, $response); }); } + + #[\Innmind\BlackBox\PHPUnit\Framework\Attributes\Group('wip')] + public function testRouteErrorIsNotSwallowed(): BlackBox\Proof + { + return $this + ->forAll( + Set::of(...ProtocolVersion::cases()), + Set::sequence( + Set::compose( + static fn($key, $value) => [$key, $value], + Set::strings()->randomize(), + Set::strings(), + ), + )->between(0, 10), + ) + ->prove(function($protocol, $variables) { + $expected = new \Exception; + + $app = Application::http(Factory::build(), Environment::test($variables)) + ->service(Services::serviceA, static fn() => static fn() => Attempt::error($expected)) + ->routes(Routes::class); + + $error = $app + ->run(ServerRequest::of( + Url::of('/foo'), + Method::get, + $protocol, + )) + ->match( + static fn() => null, + static fn($e) => $e, + ); + + $this->assertSame($expected, $error); + }); + } }