server = $_SERVER; $this->config = dirname(__DIR__, 2) . '/test_app/config'; $GLOBALS['mockedHeaders'] = []; $GLOBALS['mockedHeadersSent'] = true; } /** * Teardown */ public function tearDown(): void { parent::tearDown(); $_SERVER = $this->server; unset($GLOBALS['mockedHeadersSent']); } /** * test get/set on the app */ public function testAppGetSet(): void { $eventManager = new EventManager(); $app = new class ($this->config, $eventManager) extends BaseApplication { public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { return $middlewareQueue; } }; $server = new Server($app); $this->assertSame($app, $server->getApp()); $this->assertSame($app->getEventManager(), $server->getEventManager()); } /** * test run building a response */ public function testRunWithRequest(): void { $request = new ServerRequest(); $request = $request->withHeader('X-pass', 'request header'); $app = new MiddlewareApplication($this->config); $server = new Server($app); $res = $server->run($request); $this->assertSame( 'source header', $res->getHeaderLine('X-testing'), 'Input response is carried through out middleware' ); $this->assertSame( 'request header', $res->getHeaderLine('X-pass'), 'Request is used in middleware' ); } /** * test run calling plugin hooks */ public function testRunCallingPluginHooks(): void { $request = new ServerRequest(); $request = $request->withHeader('X-pass', 'request header'); /** @var \TestApp\Http\MiddlewareApplication|\Mockery\MockInterface $app */ $app = Mockery::mock(MiddlewareApplication::class, [$this->config]) ->shouldAllowMockingMethod('pluginEvents') ->makePartial(); $app->shouldReceive('pluginBootstrap', 'pluginMiddleware') ->with(Mockery::type(MiddlewareQueue::class)) ->andReturnUsing(function ($middleware) { return $middleware; }); $server = new Server($app); $res = $server->run($request); $this->assertSame( 'source header', $res->getHeaderLine('X-testing'), 'Input response is carried through out middleware' ); $this->assertSame( 'request header', $res->getHeaderLine('X-pass'), 'Request is used in middleware' ); } /** * test run building a request from globals. */ public function testRunWithGlobals(): void { $_SERVER['HTTP_X_PASS'] = 'globalvalue'; $app = new MiddlewareApplication($this->config); $server = new Server($app); $res = $server->run(); $this->assertSame( 'globalvalue', $res->getHeaderLine('X-pass'), 'Default request is made from server' ); } /** * Test middleware being invoked. */ public function testRunMultipleMiddlewareSuccess(): void { $app = new MiddlewareApplication($this->config); $server = new Server($app); $res = $server->run(); $this->assertSame('first', $res->getHeaderLine('X-First')); $this->assertSame('second', $res->getHeaderLine('X-Second')); } /** * Test that run closes session after invoking the application (if CakePHP ServerRequest is used). */ public function testRunClosesSessionIfServerRequestUsed(): void { $sessionMock = $this->createMock(Session::class); $sessionMock->expects($this->once()) ->method('close'); $app = new MiddlewareApplication($this->config); $server = new Server($app); $request = new ServerRequest(['session' => $sessionMock]); $res = $server->run($request); // assert that app was executed correctly $this->assertSame( 200, $res->getStatusCode(), 'Application was expected to be executed' ); $this->assertSame( 'source header', $res->getHeaderLine('X-testing'), 'Application was expected to be executed' ); } /** * Test that run does not close the session if CakePHP ServerRequest is not used. */ public function testRunDoesNotCloseSessionIfServerRequestNotUsed(): void { $request = new LaminasServerRequest(); $app = new MiddlewareApplication($this->config); $server = new Server($app); $res = $server->run($request); // assert that app was executed correctly $this->assertSame( 200, $res->getStatusCode(), 'Application was expected to be executed' ); $this->assertSame( 'source header', $res->getHeaderLine('X-testing'), 'Application was expected to be executed' ); } /** * Test that emit invokes the appropriate methods on the emitter. */ public function testEmit(): void { $app = new MiddlewareApplication($this->config); $server = new Server($app); $response = $server->run(); $final = $response ->withHeader('X-First', 'first') ->withHeader('X-Second', 'second'); $emitter = $this->getMockBuilder(ResponseEmitter::class)->getMock(); $emitter->expects($this->once()) ->method('emit') ->with($final); $server->emit($final, $emitter); } /** * Test that emit invokes the appropriate methods on the emitter. */ public function testEmitCallbackStream(): void { $GLOBALS['mockedHeadersSent'] = false; $response = new LaminasResponse('php://memory', 200, ['x-testing' => 'source header']); $response = $response->withBody(new CallbackStream(function (): void { echo 'body content'; })); $app = new MiddlewareApplication($this->config); $server = new Server($app); ob_start(); $server->emit($response); $result = ob_get_clean(); $this->assertSame('body content', $result); } /** * Ensure that the Server.buildMiddleware event is fired. */ public function testBuildMiddlewareEvent(): void { $app = new MiddlewareApplication($this->config); $server = new Server($app); $called = false; $server->getEventManager()->on('Server.buildMiddleware', function (EventInterface $event, MiddlewareQueue $middlewareQueue) use (&$called): void { $middlewareQueue->add(function ($request, $handler) use (&$called) { $called = true; return $handler->handle($request); }); $this->middlewareQueue = $middlewareQueue; }); $server->run(); $this->assertTrue($called, 'Middleware added in the event was not triggered.'); $this->middlewareQueue->seek(3); $this->assertInstanceOf('Closure', $this->middlewareQueue->current()->getCallable(), '2nd last middleware is a closure'); } /** * test event manager proxies to the application. */ public function testEventManagerProxies(): void { $app = new class ($this->config) extends BaseApplication { public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { return $middlewareQueue; } }; $server = new Server($app); $this->assertSame($app->getEventManager(), $server->getEventManager()); } /** * test event manager cannot be set on applications without events. */ public function testGetEventManagerNonEventedApplication(): void { $app = new class implements HttpApplicationInterface { public function bootstrap(): void { } public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { return $middlewareQueue; } public function handle(ServerRequestInterface $request): ResponseInterface { return new Response(); } }; $server = new Server($app); $this->assertSame(EventManager::instance(), $server->getEventManager()); } /** * test event manager cannot be set on applications without events. */ public function testSetEventManagerNonEventedApplication(): void { $app = new class implements HttpApplicationInterface { public function bootstrap(): void { } public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { return $middlewareQueue; } public function handle(ServerRequestInterface $request): ResponseInterface { return new Response(); } }; $events = new EventManager(); $server = new Server($app); $this->expectException(InvalidArgumentException::class); $server->setEventManager($events); } /** * Test server run works without an application implementing ContainerApplicationInterface */ public function testAppWithoutContainerApplicationInterface(): void { $app = new class implements HttpApplicationInterface { public function bootstrap(): void { } public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { return $middlewareQueue; } public function handle(ServerRequestInterface $request): ResponseInterface { return new Response(); } }; $server = new Server($app); $request = new ServerRequest(); $this->assertInstanceOf(ResponseInterface::class, $server->run($request)); } public function testTerminateEvent(): void { $request = new ServerRequest(); $app = new MiddlewareApplication($this->config); $app->getContainer()->add(ServerRequest::class, $request); $server = new Server($app); $triggered = false; $server->getEventManager()->on( 'Server.terminate', function ($event, $request, $response) use (&$triggered): void { $triggered = true; $this->assertInstanceOf(ServerRequest::class, $request); $this->assertInstanceOf(Response::class, $response); } ); $emitter = new class extends ResponseEmitter { public function emit(ResponseInterface $response, $stream = null): bool { return true; } }; $server->emit(new Response(), $emitter); $this->assertTrue($triggered); } }