responseManipulator = new ResponseManipulator(); $this->middleware = new RemovePoweredByMiddleware($this->responseManipulator); // Create a test request $this->request = new HttpRequest( method: Method::GET, path: '/test' ); $this->stateManager = new RequestStateManager(new WeakMap(), $this->request); $this->context = new MiddlewareContext($this->request); }); it('removes X-Powered-By header from response', function () { // Create response with X-Powered-By header $headers = new Headers([ 'Content-Type' => 'text/html', 'X-Powered-By' => 'PHP/8.2.0', ]); $response = new HttpResponse(Status::OK, $headers, 'test content'); // Create next handler that returns context with response $next = new class ($response) implements Next { public function __construct(private HttpResponse $response) { } public function __invoke(MiddlewareContext $context): MiddlewareContext { return $context->withResponse($this->response); } }; $result = $this->middleware->__invoke( $this->context, $next, $this->stateManager ); expect($result->hasResponse())->toBeTrue(); expect($result->response->headers->has('X-Powered-By'))->toBeFalse(); expect($result->response->headers->has('Content-Type'))->toBeTrue(); expect($result->response->headers->getFirst('Content-Type'))->toBe('text/html'); }); it('leaves response unchanged when no X-Powered-By header', function () { // Create response without X-Powered-By header $headers = new Headers([ 'Content-Type' => 'application/json', ]); $response = new HttpResponse(Status::OK, $headers, '{"test": true}'); $next = new class ($response) implements Next { public function __construct(private HttpResponse $response) { } public function __invoke(MiddlewareContext $context): MiddlewareContext { return $context->withResponse($this->response); } }; $result = $this->middleware->__invoke( $this->context, $next, $this->stateManager ); expect($result->hasResponse())->toBeTrue(); expect($result->response->headers->has('X-Powered-By'))->toBeFalse(); expect($result->response->headers->has('Content-Type'))->toBeTrue(); expect($result->response->headers->getFirst('Content-Type'))->toBe('application/json'); }); it('passes through context when no response present', function () { // Next handler that doesn't set a response $next = new class () implements Next { public function __invoke(MiddlewareContext $context): MiddlewareContext { return $context; } }; $result = $this->middleware->__invoke( $this->context, $next, $this->stateManager ); expect($result->hasResponse())->toBeFalse(); }); it('removes multiple X-Powered-By headers', function () { // Create response with multiple headers including X-Powered-By $headers = new Headers([ 'Content-Type' => 'text/html', 'X-Powered-By' => 'PHP/8.2.0', 'Cache-Control' => 'no-cache', 'Server' => 'nginx', ]); $response = new HttpResponse(Status::OK, $headers, 'test content'); $next = new class ($response) implements Next { public function __construct(private HttpResponse $response) { } public function __invoke(MiddlewareContext $context): MiddlewareContext { return $context->withResponse($this->response); } }; $result = $this->middleware->__invoke( $this->context, $next, $this->stateManager ); expect($result->hasResponse())->toBeTrue(); expect($result->response->headers->has('X-Powered-By'))->toBeFalse(); expect($result->response->headers->has('Content-Type'))->toBeTrue(); expect($result->response->headers->has('Cache-Control'))->toBeTrue(); expect($result->response->headers->has('Server'))->toBeTrue(); }); it('preserves response body and status', function () { $headers = new Headers([ 'Content-Type' => 'application/json', 'X-Powered-By' => 'Custom-Server/1.0', ]); $response = new HttpResponse(Status::CREATED, $headers, '{"created": true}'); $next = new class ($response) implements Next { public function __construct(private HttpResponse $response) { } public function __invoke(MiddlewareContext $context): MiddlewareContext { return $context->withResponse($this->response); } }; $result = $this->middleware->__invoke( $this->context, $next, $this->stateManager ); expect($result->hasResponse())->toBeTrue(); expect($result->response->status)->toBe(Status::CREATED); expect($result->response->body)->toBe('{"created": true}'); expect($result->response->headers->has('X-Powered-By'))->toBeFalse(); expect($result->response->headers->getFirst('Content-Type'))->toBe('application/json'); });