Files
michaelschiemer/tests/Unit/Framework/Http/MiddlewarePipelineTest.php
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

202 lines
6.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Unit\Framework\Http;
use App\Framework\Http\HttpMiddleware;
use App\Framework\Http\HttpResponse;
use App\Framework\Http\Method;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\MiddlewareManager;
use App\Framework\Http\Next;
use App\Framework\Http\Request;
use App\Framework\Http\RequestStateManager;
use App\Framework\Http\Status;
test('middleware pipeline executes in order', function () {
$executionOrder = [];
$middleware1 = new class ($executionOrder) implements HttpMiddleware {
public function __construct(private array &$order)
{
}
public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
{
$this->order[] = 'before-1';
$context = $next($context);
$this->order[] = 'after-1';
return $context;
}
};
$middleware2 = new class ($executionOrder) implements HttpMiddleware {
public function __construct(private array &$order)
{
}
public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
{
$this->order[] = 'before-2';
$context = $next($context);
$this->order[] = 'after-2';
return $context;
}
};
$manager = new MiddlewareManager();
$manager->addMiddleware($middleware1, 100);
$manager->addMiddleware($middleware2, 50);
$request = new Request(Method::GET, '/test', [], '', []);
$stateManager = new RequestStateManager();
$finalHandler = function (MiddlewareContext $context) use (&$executionOrder) {
$executionOrder[] = 'handler';
return $context->withResponse(new HttpResponse(Status::OK));
};
$context = $manager->process(new MiddlewareContext($request), $finalHandler, $stateManager);
expect($executionOrder)->toBe([
'before-1',
'before-2',
'handler',
'after-2',
'after-1',
]);
});
test('middleware can short-circuit pipeline', function () {
$executed = [];
$middleware1 = new class ($executed) implements HttpMiddleware {
public function __construct(private array &$executed)
{
}
public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
{
$this->executed[] = 'middleware-1';
// Short-circuit by not calling next
return $context->withResponse(new HttpResponse(Status::FORBIDDEN));
}
};
$middleware2 = new class ($executed) implements HttpMiddleware {
public function __construct(private array &$executed)
{
}
public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
{
$this->executed[] = 'middleware-2';
return $next($context);
}
};
$manager = new MiddlewareManager();
$manager->addMiddleware($middleware1, 100);
$manager->addMiddleware($middleware2, 50);
$request = new Request(Method::GET, '/test', [], '', []);
$stateManager = new RequestStateManager();
$finalHandler = function (MiddlewareContext $context) use (&$executed) {
$executed[] = 'handler';
return $context->withResponse(new HttpResponse(Status::OK));
};
$context = $manager->process(new MiddlewareContext($request), $finalHandler, $stateManager);
expect($executed)->toBe(['middleware-1']);
expect($context->response?->status)->toBe(Status::FORBIDDEN);
});
test('middleware can modify request', function () {
$modifyMiddleware = new class () implements HttpMiddleware {
public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
{
$modifiedHeaders = $context->request->headers;
$modifiedHeaders['X-Custom-Header'] = 'test-value';
$modifiedRequest = new Request(
$context->request->method,
$context->request->path,
$modifiedHeaders,
$context->request->body,
$context->request->server
);
return $next($context->withRequest($modifiedRequest));
}
};
$manager = new MiddlewareManager();
$manager->addMiddleware($modifyMiddleware);
$request = new Request(Method::GET, '/test', [], '', []);
$stateManager = new RequestStateManager();
$receivedRequest = null;
$finalHandler = function (MiddlewareContext $context) use (&$receivedRequest) {
$receivedRequest = $context->request;
return $context->withResponse(new HttpResponse(Status::OK));
};
$manager->process(new MiddlewareContext($request), $finalHandler, $stateManager);
expect($receivedRequest?->headers['X-Custom-Header'] ?? null)->toBe('test-value');
});
test('middleware state management works', function () {
$stateManager = new RequestStateManager();
$middleware1 = new class () implements HttpMiddleware {
public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
{
$stateManager->set('key1', 'value1');
$stateManager->set('shared', 'from-middleware-1');
return $next($context);
}
};
$middleware2 = new class () implements HttpMiddleware {
public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
{
expect($stateManager->get('key1'))->toBe('value1');
expect($stateManager->get('shared'))->toBe('from-middleware-1');
$stateManager->set('key2', 'value2');
$stateManager->set('shared', 'from-middleware-2');
return $next($context);
}
};
$manager = new MiddlewareManager();
$manager->addMiddleware($middleware1);
$manager->addMiddleware($middleware2);
$request = new Request(Method::GET, '/test', [], '', []);
$finalHandler = function (MiddlewareContext $context) use ($stateManager) {
expect($stateManager->get('key1'))->toBe('value1');
expect($stateManager->get('key2'))->toBe('value2');
expect($stateManager->get('shared'))->toBe('from-middleware-2');
return $context->withResponse(new HttpResponse(Status::OK));
};
$manager->process(new MiddlewareContext($request), $finalHandler, $stateManager);
});