test(Router): cover default callback resolution through Synapsis

This commit is contained in:
kj
2026-09-08 12:05:55 -03:00
parent 26589062f5
commit 7b1d5e42bd

View File

@@ -8,7 +8,9 @@ use Libs\Request;
use Libs\Router;
use LogicException;
use PDOException;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
use PHPUnit\Framework\Attributes\Test;
use ReflectionClass;
use ReflectionProperty;
use Tests\TestCase;
use TypeError;
@@ -21,7 +23,10 @@ use TypeError;
* negociacion por Accept entre representacion JSON y texto plano. Tambien la
* frontera de Router::apply(): que toda excepcion de la cadena (middlewares,
* callback final, notFound) llegue al $exceptionCallback deteniendo la
* ejecucion sin exit() y dejando intacto el flujo normal.
* ejecucion sin exit() y dejando intacto el flujo normal. Tambien que los
* valores por defecto declarados en las propiedades $notFoundCallback y
* $exceptionCallback (callables string) se resuelvan de punta a punta a
* traves de Synapsis::resolve() dentro de apply().
*/
final class RouterTest extends TestCase
{
@@ -36,7 +41,7 @@ final class RouterTest extends TestCase
$this->postBackup = $_POST;
$_SERVER['REQUEST_METHOD'] = 'GET';
unset($_SERVER['HTTP_ACCEPT']);
http_response_code(200);
@http_response_code(200); // Ver tearDown: inofensivo mientras no haya un header('HTTP/...') activo.
$this->resetRouterState();
}
@@ -45,7 +50,11 @@ final class RouterTest extends TestCase
$_SERVER = $this->serverBackup;
$_GET = $this->getBackup;
$_POST = $this->postBackup;
http_response_code(200);
// El @ cubre el warning de PHP 8.5 ("...has no effect") que dispara
// cualquier http_response_code() posterior a un header('HTTP/...'),
// inevitable en el proceso aislado del test de notFound (el valor
// se aplica igualmente, como confirma el getter).
@http_response_code(200);
$this->resetRouterState();
}
@@ -243,6 +252,46 @@ final class RouterTest extends TestCase
$this->assertSame(400, $caught[0]->getCode(), 'The status channel must survive custom exception types');
$this->assertFalse($finalRan, 'A Request that throws during DI resolution must stop the route');
}
#[Test]
public function declaredDefaultExceptionCallbackRendersThroughTheBoundary(): void
{
// Se fuerza el valor declarado en la clase, no el que reasigna
// resetRouterState(): es justo el camino property default ->
// Synapsis::resolve(string callable) que debe seguir funcionando.
Router::$exceptionCallback = (new ReflectionClass(Router::class))
->getDefaultProperties()['exceptionCallback'];
Router::get('/default-exception', function (): void {
throw new Exception('rendered by the default handler', 422);
});
ob_start();
Router::apply('/default-exception');
$output = ob_get_clean();
$this->assertSame(422, http_response_code(), 'The declared default handler must derive the status from the code');
$this->assertStringContainsString('rendered by the default handler', $output);
$this->assertStringContainsString('#0', $output, 'The declared default handler must render the plain text trace');
}
#[Test]
#[RunInSeparateProcess]
public function declaredDefaultNotFoundCallbackRendersThe404Body(): void
{
Router::$notFoundCallback = (new ReflectionClass(Router::class))
->getDefaultProperties()['notFoundCallback'];
ob_start();
Router::apply('/no-such-route');
$output = ob_get_clean();
// Proceso aislado: defaultNotFound() emite header('HTTP/1.0 404 ...') y en
// PHP 8.5 eso hace que TODO http_response_code() posterior avise, sin forma
// de limpiar el estado en el mismo proceso. En CLI el header no toca
// http_response_code(), asi que la asercion util es el cuerpo renderizado.
$this->assertStringContainsString('Error 404 - Page Not Found', $output);
}
}
// phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses