diff --git a/src/App.php b/src/App.php index c49aa80..8a4815d 100644 --- a/src/App.php +++ b/src/App.php @@ -13,6 +13,7 @@ use React\Http\Message\Response; use React\Promise\Deferred; use React\Promise\PromiseInterface; +use function React\Async\await; class App { @@ -245,6 +246,54 @@ public function run(): void $this->sapi->run(\Closure::fromCallable([$this, 'handleRequest'])); } + /** + * Invokes the app to handle a single HTTP request according to any registered routes and middleware. + * + * This method allows you to pass in a single HTTP request object that will + * be processed according to any registered routes and middleware and will + * return an HTTP response object as a result. + * + * ```php + * $app = new FrameworkX\App(); + * $app->get('/', fn() => React\Http\Message\Response::plaintext("Hello!\n")); + * + * $request = new React\Http\Message\ServerRequest('GET', 'https://example.com/'); + * $response = $app($request); + * + * assert($response instanceof Psr\Http\Message\ResponseInterface); + * assert($response->getStatusCode() === 200); + * assert($response->getBody()->getContents() === "Hello\n"); + * ``` + * + * This is particularly useful for higher-level integration test suites and + * for custom integrations with other runtime environments like serverless + * functions or other frameworks. Otherwise, most applications would likely + * want to use the `run()` method to run the application and automatically + * accept incoming HTTP requests according to the PHP SAPI in use. + * + * @param ServerRequestInterface $request The HTTP request object to process. + * @return ResponseInterface This method returns an HTTP response object + * according to any registered routes and middleware. If any handler is + * async, it will await its execution before returning, running the + * event loop as needed. If the request can not be routed or any handler + * fails, it will return a matching HTTP error response object. + * @throws void This method never throws. If the request can not be routed + * or any handler fails, it will be turned into a valid error response + * before returning. + * @see self::run() + */ + public function __invoke(ServerRequestInterface $request): ResponseInterface + { + $response = $this->handleRequest($request); + if ($response instanceof PromiseInterface) { + /** @throws void */ + $response = await($response); + assert($response instanceof ResponseInterface); + } + + return $response; + } + /** * @param ServerRequestInterface $request * @return ResponseInterface|PromiseInterface diff --git a/tests/AppMiddlewareTest.php b/tests/AppMiddlewareTest.php index 5fcc067..dadd24f 100644 --- a/tests/AppMiddlewareTest.php +++ b/tests/AppMiddlewareTest.php @@ -170,7 +170,7 @@ public function testMapMethodWithMiddlewareAddsGivenMethodsOnRouter(): void $app->map(['GET', 'POST'], '/', $middleware, $controller); } - public function testMiddlewareCallsNextReturnsResponseFromRouter(): void + public function testInvokeWithMiddlewareCallsNextReturnsResponseFromRouter(): void { $app = $this->createAppWithoutLogger(); @@ -192,19 +192,15 @@ public function testMiddlewareCallsNextReturnsResponseFromRouter(): void $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testMiddlewareCallsNextWithModifiedRequestReturnsResponseFromRouter(): void + public function testInvokeWithMiddlewareCallsNextWithModifiedRequestReturnsResponseFromRouter(): void { $app = $this->createAppWithoutLogger(); @@ -226,19 +222,15 @@ public function testMiddlewareCallsNextWithModifiedRequestReturnsResponseFromRou $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals('Alice', (string) $response->getBody()); } - public function testMiddlewareCallsNextReturnsResponseModifiedInMiddlewareFromRouter(): void + public function testInvokeWithMiddlewareCallsNextReturnsResponseModifiedInMiddlewareFromRouter(): void { $app = $this->createAppWithoutLogger(); @@ -261,19 +253,15 @@ public function testMiddlewareCallsNextReturnsResponseModifiedInMiddlewareFromRo $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals('Alice', (string) $response->getBody()); } - public function testMiddlewareCallsNextReturnsDeferredResponseModifiedInMiddlewareFromRouter(): void + public function testInvokeWithMiddlewareCallsNextReturnsDeferredResponseModifiedInMiddlewareFromRouter(): void { $app = $this->createAppWithoutLogger(); @@ -299,27 +287,15 @@ public function testMiddlewareCallsNextReturnsDeferredResponseModifiedInMiddlewa $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); - - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals('Alice', (string) $response->getBody()); } - public function testMiddlewareCallsNextReturnsCoroutineResponseModifiedInMiddlewareFromRouter(): void + public function testInvokeWithMiddlewareCallsNextReturnsCoroutineResponseModifiedInMiddlewareFromRouter(): void { $app = $this->createAppWithoutLogger(); @@ -346,27 +322,15 @@ public function testMiddlewareCallsNextReturnsCoroutineResponseModifiedInMiddlew $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); - - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals('Alice', (string) $response->getBody()); } - public function testMiddlewareCallsNextWhichThrowsExceptionReturnsInternalServerErrorResponse(): void + public function testInvokeWithMiddlewareCallsNextWhichThrowsExceptionReturnsInternalServerErrorResponse(): void { $app = $this->createAppWithoutLogger(); @@ -383,13 +347,9 @@ public function testMiddlewareCallsNextWhichThrowsExceptionReturnsInternalServer $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringContainsString("Error 500: Internal Server Error\n", (string) $response->getBody()); @@ -397,7 +357,7 @@ public function testMiddlewareCallsNextWhichThrowsExceptionReturnsInternalServer $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got uncaught RuntimeException with message Foo in AppMiddlewareTest.php:$line.

\n", (string) $response->getBody()); } - public function testMiddlewareWhichThrowsExceptionReturnsInternalServerErrorResponse(): void + public function testInvokeWithMiddlewareWhichThrowsExceptionReturnsInternalServerErrorResponse(): void { $app = $this->createAppWithoutLogger(); @@ -412,13 +372,9 @@ public function testMiddlewareWhichThrowsExceptionReturnsInternalServerErrorResp $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringContainsString("Error 500: Internal Server Error\n", (string) $response->getBody()); @@ -426,7 +382,7 @@ public function testMiddlewareWhichThrowsExceptionReturnsInternalServerErrorResp $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got uncaught RuntimeException with message Foo in AppMiddlewareTest.php:$line.

\n", (string) $response->getBody()); } - public function testGlobalMiddlewareCallsNextReturnsResponseFromController(): void + public function testInvokeWithGlobalMiddlewareCallsNextReturnsResponseFromController(): void { $app = $this->createAppWithoutLogger(function (ServerRequestInterface $request, callable $next) { return $next($request); @@ -444,19 +400,15 @@ public function testGlobalMiddlewareCallsNextReturnsResponseFromController(): vo $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testGlobalMiddlewareInstanceCallsNextReturnsResponseFromController(): void + public function testInvokeWithGlobalMiddlewareInstanceCallsNextReturnsResponseFromController(): void { $middleware = new class { public function __invoke(ServerRequestInterface $request, callable $next): Response @@ -479,19 +431,15 @@ public function __invoke(ServerRequestInterface $request, callable $next): Respo $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testGlobalMiddlewareClassNameCallsNextReturnsResponseFromController(): void + public function testInvokeWithGlobalMiddlewareClassNameCallsNextReturnsResponseFromController(): void { $middleware = new class { public function __invoke(ServerRequestInterface $request, callable $next): Response @@ -514,19 +462,15 @@ public function __invoke(ServerRequestInterface $request, callable $next): Respo $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testGlobalMiddlewareClassNameAndSameForRouterCallsSameMiddlewareInstanceTwiceAndNextReturnsResponseFromController(): void + public function testInvokeWithGlobalMiddlewareClassNameAndSameForRouterCallsSameMiddlewareInstanceTwiceAndNextReturnsResponseFromController(): void { $middleware = new class { /** @var int */ @@ -551,19 +495,15 @@ public function __invoke(ServerRequestInterface $request, callable $next): Respo $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("2\n", (string) $response->getBody()); } - public function testGlobalMiddlewareCallsNextWithModifiedRequestWillBeUsedForRouting(): void + public function testInvokeWithGlobalMiddlewareCallsNextWithModifiedRequestWillBeUsedForRouting(): void { $app = $this->createAppWithoutLogger(function (ServerRequestInterface $request, callable $next): Response { return $next($request->withUri($request->getUri()->withPath('/users'))); @@ -581,19 +521,15 @@ public function testGlobalMiddlewareCallsNextWithModifiedRequestWillBeUsedForRou $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testGlobalMiddlewareCallsNextReturnsModifiedResponseWhenModifyingResponseFromRouter(): void + public function testInvokeWithGlobalMiddlewareCallsNextReturnsModifiedResponseWhenModifyingResponseFromRouter(): void { $app = $this->createAppWithoutLogger(function (ServerRequestInterface $request, callable $next) { $response = $next($request); @@ -612,19 +548,15 @@ public function testGlobalMiddlewareCallsNextReturnsModifiedResponseWhenModifyin $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testGlobalMiddlewareReturnsResponseWithoutCallingNextReturnsResponseWithoutCallingRouter(): void + public function testInvokeWithGlobalMiddlewareReturnsResponseWithoutCallingNextReturnsResponseWithoutCallingRouter(): void { $app = $this->createAppWithoutLogger(function () { return new Response( @@ -643,13 +575,9 @@ public function testGlobalMiddlewareReturnsResponseWithoutCallingNextReturnsResp $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); @@ -657,7 +585,7 @@ public function testGlobalMiddlewareReturnsResponseWithoutCallingNextReturnsResp $this->assertFalse($called); } - public function testGlobalMiddlewareReturnsPromiseWhichResolvesWithResponseWithoutCallingNextDoesNotCallRouter(): void + public function testInvokeWithGlobalMiddlewareReturnsPromiseWhichResolvesWithResponseWithoutCallingNextDoesNotCallRouter(): void { $app = $this->createAppWithoutLogger(function () { return resolve(new Response( @@ -676,21 +604,9 @@ public function testGlobalMiddlewareReturnsPromiseWhichResolvesWithResponseWitho $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); - - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); @@ -698,7 +614,7 @@ public function testGlobalMiddlewareReturnsPromiseWhichResolvesWithResponseWitho $this->assertFalse($called); } - public function testGlobalMiddlewareCallsNextReturnsPromiseWhichResolvesWithModifiedResponseWhenModifyingPromiseWhichResolvesToResponseFromRouter(): void + public function testInvokeWithGlobalMiddlewareReturnsResponseWhenGlobalMiddlewareModifiesAsyncResponsePromiseFromRoutedController(): void { $app = $this->createAppWithoutLogger(function (ServerRequestInterface $request, callable $next) { return $next($request)->then(function (ResponseInterface $response) { @@ -716,27 +632,15 @@ public function testGlobalMiddlewareCallsNextReturnsPromiseWhichResolvesWithModi $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); - - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testGlobalMiddlewareCallsNextReturnsPromiseWhichResolvesWithModifiedResponseWhenModifyingCoroutineWhichYieldsResponseFromRouter(): void + public function testInvokeWithGlobalMiddlewareReturnsResponseWhenGlobalMiddlewareYieldsModifiedResponseFromAsyncGeneratorResponseFromRoutedController(): void { $app = $this->createAppWithoutLogger(function (ServerRequestInterface $request, callable $next) { $generator = $next($request); @@ -760,21 +664,9 @@ public function testGlobalMiddlewareCallsNextReturnsPromiseWhichResolvesWithModi $request = new ServerRequest('GET', 'http://localhost/'); - // $response = $app->handleRequest($request); - $ref = new \ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); - - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); diff --git a/tests/AppTest.php b/tests/AppTest.php index 43679f5..30506fa 100644 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -29,8 +29,8 @@ use React\Promise\Deferred; use React\Promise\Promise; use React\Promise\PromiseInterface; -use ReflectionMethod; use ReflectionProperty; +use function React\Async\async; // @phpstan-ignore-line use function React\Async\await; use function React\Promise\reject; use function React\Promise\resolve; @@ -852,20 +852,16 @@ public function testRedirectMethodWithCustomRedirectCodeAddsAnyRouteOnRouterWhic $this->assertStringContainsString("

Redirecting to /users...

\n", (string) $response->getBody()); } - public function testHandleRequestWithProxyRequestReturnsResponseWithMessageThatProxyRequestsAreNotAllowed(): void + public function testInvokeWithProxyRequestReturnsResponseWithMessageThatProxyRequestsAreNotAllowed(): void { $app = $this->createAppWithoutLogger(); $request = new ServerRequest('GET', 'http://google.com/'); $request = $request->withRequestTarget('http://google.com/'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(400, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -874,19 +870,15 @@ public function testHandleRequestWithProxyRequestReturnsResponseWithMessageThatP $this->assertStringContainsString("

Please check your settings and retry.

\n", (string) $response->getBody()); } - public function testHandleRequestWithUnknownRouteReturnsResponseWithFileNotFoundMessage(): void + public function testInvokeWithUnknownRouteReturnsResponseWithFileNotFoundMessage(): void { $app = $this->createAppWithoutLogger(); $request = new ServerRequest('GET', 'http://localhost/invalid'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(404, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -895,7 +887,7 @@ public function testHandleRequestWithUnknownRouteReturnsResponseWithFileNotFound $this->assertStringContainsString("

Please check the URL in the address bar and try again.

\n", (string) $response->getBody()); } - public function testHandleRequestWithInvalidRequestMethodReturnsResponseWithSingleMethodNotAllowedMessage(): void + public function testInvokeWithInvalidRequestMethodReturnsResponseWithSingleMethodNotAllowedMessage(): void { $app = $this->createAppWithoutLogger(); @@ -903,13 +895,9 @@ public function testHandleRequestWithInvalidRequestMethodReturnsResponseWithSing $request = new ServerRequest('POST', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -919,7 +907,7 @@ public function testHandleRequestWithInvalidRequestMethodReturnsResponseWithSing $this->assertStringContainsString("

Please check the URL in the address bar and try again with GET request.

\n", (string) $response->getBody()); } - public function testHandleRequestWithInvalidRequestMethodReturnsResponseWithMultipleMethodNotAllowedMessage(): void + public function testInvokeWithInvalidRequestMethodReturnsResponseWithMultipleMethodNotAllowedMessage(): void { $app = $this->createAppWithoutLogger(); @@ -929,13 +917,9 @@ public function testHandleRequestWithInvalidRequestMethodReturnsResponseWithMult $request = new ServerRequest('DELETE', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -945,7 +929,7 @@ public function testHandleRequestWithInvalidRequestMethodReturnsResponseWithMult $this->assertStringContainsString("

Please check the URL in the address bar and try again with GET/HEAD/POST request.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsResponseFromMatchingRouteHandler(): void + public function testInvokeWithMatchingRouteReturnsResponseFromMatchingRouteHandler(): void { $app = $this->createAppWithoutLogger(); @@ -961,19 +945,15 @@ public function testHandleRequestWithMatchingRouteReturnsResponseFromMatchingRou $request = new ServerRequest('GET', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMatchingAsteriskRouteHandler(): void + public function testInvokeWithOptionsAsteriskRequestReturnsResponseFromMatchingAsteriskRouteHandler(): void { $app = $this->createAppWithoutLogger(); @@ -990,19 +970,15 @@ public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMa $request = new ServerRequest('OPTIONS', 'http://localhost'); $request = $request->withRequestTarget('*'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMatchingDeprecatedEmptyRouteHandler(): void + public function testInvokeWithOptionsAsteriskRequestReturnsResponseFromMatchingDeprecatedEmptyRouteHandler(): void { $app = $this->createAppWithoutLogger(); @@ -1019,19 +995,15 @@ public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMa $request = new ServerRequest('OPTIONS', 'http://localhost'); $request = $request->withRequestTarget('*'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWithResponseWhenHandlerReturnsPromiseWhichFulfillsWithResponse(): void + public function testInvokeWithMatchingRouteReturnsResponseWhenHandlerReturnsPromiseWhichFulfillsWithResponse(): void { $app = $this->createAppWithoutLogger(); @@ -1047,28 +1019,20 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); - - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); - - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPendingPromiseWhenHandlerReturnsPendingPromise(): void + public function testInvokeWithMatchingRouteReturnsNeverWhenHandlerReturnsPendingPromise(): void { + if (!function_exists('React\Async\async')) { + $this->markTestSkipped('Requires reactphp/async v4 (PHP 8.1+)'); + } + $app = $this->createAppWithoutLogger(); $app->get('/users', function () { @@ -1077,13 +1041,8 @@ public function testHandleRequestWithMatchingRouteReturnsPendingPromiseWhenHandl $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); + /** @var PromiseInterface $promise */ + $promise = async($app)($request); // @phpstan-ignore-line $resolved = false; $promise->then(function () use (&$resolved) { @@ -1095,7 +1054,7 @@ public function testHandleRequestWithMatchingRouteReturnsPendingPromiseWhenHandl $this->assertFalse($resolved); } - public function testHandleRequestWithMatchingRouteReturnsResponseWhenHandlerReturnsCoroutineWhichReturnsResponseWithoutYielding(): void + public function testInvokeWithMatchingRouteReturnsResponseWhenHandlerReturnsCoroutineWhichReturnsResponseWithoutYielding(): void { $app = $this->createAppWithoutLogger(); @@ -1115,19 +1074,15 @@ public function testHandleRequestWithMatchingRouteReturnsResponseWhenHandlerRetu $request = new ServerRequest('GET', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWithResponseWhenHandlerReturnsCoroutineWhichReturnsResponseAfterYieldingResolvedPromise(): void + public function testInvokeWithMatchingRouteReturnsResponseWhenHandlerReturnsCoroutineWhichReturnsResponseAfterYieldingResolvedPromise(): void { $app = $this->createAppWithoutLogger(); @@ -1145,27 +1100,15 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); - - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWithResponseWhenHandlerReturnsCoroutineWhichReturnsResponseAfterCatchingExceptionFromYieldingRejectedPromise(): void + public function testInvokeWithMatchingRouteReturnsResponseWhenHandlerReturnsCoroutineWhichReturnsResponseAfterCatchingExceptionFromYieldingRejectedPromise(): void { $app = $this->createAppWithoutLogger(); @@ -1188,28 +1131,20 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); - - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPendingPromiseWhenHandlerReturnsCoroutineThatYieldsPendingPromise(): void + public function testInvokeWithMatchingRouteReturnsNeverWhenHandlerReturnsCoroutineThatYieldsPendingPromise(): void { + if (!function_exists('React\Async\async')) { + $this->markTestSkipped('Requires reactphp/async v4 (PHP 8.1+)'); + } + $app = $this->createAppWithoutLogger(); $app->get('/users', function () { @@ -1218,13 +1153,8 @@ public function testHandleRequestWithMatchingRouteReturnsPendingPromiseWhenHandl $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); + $promise = async($app)($request); // @phpstan-ignore-line $resolved = false; $promise->then(function () use (&$resolved) { @@ -1236,7 +1166,7 @@ public function testHandleRequestWithMatchingRouteReturnsPendingPromiseWhenHandl $this->assertFalse($resolved); } - public function testHandleRequestWithMatchingRouteReturnsResponseWhenHandlerReturnsResponseAfterAwaitingPromiseResolvingWithResponse(): void + public function testInvokeWithMatchingRouteReturnsResponseWhenHandlerReturnsResponseAfterAwaitingPromiseResolvedWithResponse(): void { $app = $this->createAppWithoutLogger(); @@ -1252,22 +1182,18 @@ public function testHandleRequestWithMatchingRouteReturnsResponseWhenHandlerRetu $request = new ServerRequest('GET', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPromiseResolvingWithResponseWhenHandlerReturnsResponseAfterAwaitingPromiseResolvingWithResponse(): void + public function testInvokeWithMatchingRouteReturnsResponseWhenHandlerReturnsResponseAfterAwaitingPromiseResolvingDeferredWithResponse(): void { - if (PHP_VERSION_ID < 80100 || !function_exists('React\Async\async')) { - $this->markTestSkipped('Requires PHP 8.1+ with react/async 4+'); + if (!function_exists('React\Async\async')) { + $this->markTestSkipped('Requires reactphp/async v4 (PHP 8.1+)'); } $app = $this->createAppWithoutLogger(); @@ -1280,13 +1206,8 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseResolvingWithRes $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); + $promise = async($app)($request); // @phpstan-ignore-line $response = null; $promise->then(function ($value) use (&$response) { @@ -1310,7 +1231,7 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseResolvingWithRes $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteAndRouteVariablesReturnsResponseFromHandlerWithRouteVariablesAssignedAsRequestAttributes(): void + public function testInvokeWithMatchingRouteAndRouteVariablesReturnsResponseFromHandlerWithRouteVariablesAssignedAsRequestAttributes(): void { $app = $this->createAppWithoutLogger(); @@ -1329,19 +1250,15 @@ public function testHandleRequestWithMatchingRouteAndRouteVariablesReturnsRespon $request = new ServerRequest('GET', 'http://localhost/users/alice'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); $this->assertEquals("Hello alice\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerThrowsException(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerThrowsException(): void { $app = $this->createAppWithoutLogger(); @@ -1352,13 +1269,9 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $request = new ServerRequest('GET', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1368,7 +1281,7 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got uncaught RuntimeException with message Foo in AppTest.php:$line.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWithInternalServerErrorResponseWhenHandlerReturnsPromiseWhichRejectsWithException(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsPromiseWhichRejectsWithException(): void { $app = $this->createAppWithoutLogger(); @@ -1379,21 +1292,9 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); - - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); - - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1403,7 +1304,7 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got uncaught RuntimeException with message Foo in AppTest.php:$line.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWithInternalServerErrorResponseWhenHandlerReturnsPromiseWhichRejectsWithNull(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsPromiseWhichRejectsWithNull(): void { if (method_exists(PromiseInterface::class, 'catch')) { $this->markTestSkipped('Only supported for legacy Promise v2, Promise v3 always rejects with Throwable'); @@ -1417,21 +1318,9 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); - - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); - - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1441,7 +1330,7 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got React\Promise\RejectedPromise.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWithInternalServerErrorResponseWhenHandlerReturnsCoroutineWhichYieldsRejectedPromise(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsCoroutineWhichYieldsRejectedPromise(): void { $app = $this->createAppWithoutLogger(); @@ -1452,21 +1341,9 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); - - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1476,7 +1353,7 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got uncaught RuntimeException with message Foo in AppTest.php:$line.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsCoroutineWhichThrowsExceptionWithoutYielding(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsCoroutineWhichThrowsExceptionWithoutYielding(): void { $app = $this->createAppWithoutLogger(); @@ -1490,13 +1367,9 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $request = new ServerRequest('GET', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1506,7 +1379,7 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got uncaught RuntimeException with message Foo in AppTest.php:$line.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWithInternalServerErrorResponseWhenHandlerReturnsCoroutineWhichThrowsExceptionAfterYielding(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsCoroutineWhichThrowsExceptionAfterYielding(): void { $app = $this->createAppWithoutLogger(); @@ -1518,21 +1391,9 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); + $response = $app($request); + assert($response instanceof ResponseInterface); - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); - - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1542,7 +1403,7 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got uncaught RuntimeException with message Foo in AppTest.php:$line.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerThrowsAfterAwaitingPromiseRejectingWithException(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerThrowsAfterAwaitingPromiseRejectedWithException(): void { $app = $this->createAppWithoutLogger(); @@ -1553,13 +1414,9 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $request = new ServerRequest('GET', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1569,10 +1426,10 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got uncaught RuntimeException with message Foo in AppTest.php:$line.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWithInternalServerErrorResponseWhenHandlerThrowsAfterAwaitingPromiseRejectingWithException(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerThrowsAfterAwaitingPromiseRejectingDeferredWithException(): void { - if (PHP_VERSION_ID < 80100 || !function_exists('React\Async\async')) { - $this->markTestSkipped('Requires PHP 8.1+ with react/async 4+'); + if (!function_exists('React\Async\async')) { + $this->markTestSkipped('Requires reactphp/async v4 (PHP 8.1+)'); } $app = $this->createAppWithoutLogger(); @@ -1588,13 +1445,8 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); + $promise = async($app)($request); // @phpstan-ignore-line $response = null; $promise->then(function ($value) use (&$response) { @@ -1616,7 +1468,7 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got uncaught RuntimeException with message Foo in AppTest.php:$line.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWithInternalServerErrorResponseWhenHandlerReturnsCoroutineWhichReturnsNull(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsCoroutineWhichReturnsNull(): void { $app = $this->createAppWithoutLogger(); @@ -1627,21 +1479,9 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); + $response = $app($request); + assert($response instanceof ResponseInterface); - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); - - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1651,7 +1491,7 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got null.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsCoroutineWhichYieldsNullImmediately(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsCoroutineWhichYieldsNullImmediately(): void { $app = $this->createAppWithoutLogger(); @@ -1662,13 +1502,9 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $request = new ServerRequest('GET', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1678,7 +1514,7 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $this->assertStringContainsString("

Expected request handler to yield React\Promise\PromiseInterface but got null near or before AppTest.php:$line.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsWrongValue(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsWrongValue(): void { $app = $this->createAppWithoutLogger(); @@ -1688,13 +1524,9 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $request = new ServerRequest('GET', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1704,7 +1536,7 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got null.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerClassDoesNotExist(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerClassDoesNotExist(): void { $app = $this->createAppWithoutLogger(); @@ -1712,13 +1544,9 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $request = new ServerRequest('GET', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1795,7 +1623,7 @@ public function provideInvalidClasses(): \Generator * @param class-string $class * @param string $error */ - public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerClassIsInvalid(string $class, string $error): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerClassIsInvalid(string $class, string $error): void { $app = $this->createAppWithoutLogger(); @@ -1803,13 +1631,9 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $request = new ServerRequest('GET', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1819,7 +1643,7 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $this->assertStringMatchesFormat("%a

Expected request handler to return Psr\Http\Message\ResponseInterface but got uncaught BadMethodCallException with message Request handler class " . $class . " failed to load: $error in Container.php:%d.

\n%a", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerClassRequiresUnexpectedCallableParameter(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerClassRequiresUnexpectedCallableParameter(): void { $app = $this->createAppWithoutLogger(); @@ -1832,13 +1656,9 @@ public function __invoke(int $value): void { } $request = new ServerRequest('GET', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1848,7 +1668,7 @@ public function __invoke(int $value): void { } $this->assertStringMatchesFormat("%a

Expected request handler to return Psr\Http\Message\ResponseInterface but got uncaught TypeError with message %s in AppTest.php:$line.

\n%a", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerClassHasNoInvokeMethod(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerClassHasNoInvokeMethod(): void { $app = $this->createAppWithoutLogger(); @@ -1858,13 +1678,9 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $request = new ServerRequest('GET', 'http://localhost/users'); - // $response = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $response = $ref->invoke($app, $request); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1874,7 +1690,7 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $this->assertStringMatchesFormat("%a

Expected request handler to return Psr\Http\Message\ResponseInterface but got uncaught BadMethodCallException with message Request handler class %s has no public __invoke() method in Container.php:%d.

\n%a", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWithInternalServerErrorResponseWhenHandlerReturnsPromiseWhichFulfillsWithWrongValue(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsPromiseWhichFulfillsWithWrongValue(): void { $app = $this->createAppWithoutLogger(); @@ -1884,21 +1700,9 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); + $response = $app($request); + assert($response instanceof ResponseInterface); - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); - - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody()); @@ -1908,7 +1712,7 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit $this->assertStringContainsString("

Expected request handler to return Psr\Http\Message\ResponseInterface but got null.

\n", (string) $response->getBody()); } - public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsWrongValueAfterYielding(): void + public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsWrongValueAfterYielding(): void { $app = $this->createAppWithoutLogger(); @@ -1919,21 +1723,9 @@ public function testHandleRequestWithMatchingRouteReturnsInternalServerErrorResp $request = new ServerRequest('GET', 'http://localhost/users'); - // $promise = $app->handleRequest($request); - $ref = new ReflectionMethod($app, 'handleRequest'); - $ref->setAccessible(true); - $promise = $ref->invoke($app, $request); - - /** @var PromiseInterface $promise */ - $this->assertInstanceOf(PromiseInterface::class, $promise); - - $response = null; - $promise->then(function ($value) use (&$response) { - $response = $value; - }); + $response = $app($request); + assert($response instanceof ResponseInterface); - /** @var ResponseInterface $response */ - $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); $this->assertStringMatchesFormat("\n%a\n", (string) $response->getBody());