first commit.
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use Libs\Neuron;
|
||||
use Libs\Router;
|
||||
use Tests\Support\HttpStubs;
|
||||
use Tests\Support\Response;
|
||||
|
||||
/**
|
||||
* Base para tests de endpoint: despacha peticiones al router in-process
|
||||
* (sin servidor HTTP) y expone helpers de aserción.
|
||||
*
|
||||
* No incluye helpers de autenticación: Duckbrain no define login por defecto,
|
||||
* así que simular una sesión es responsabilidad de cada proyecto. Extiende esta
|
||||
* clase (o crea tu propia subclase) y añade tu helper, p. ej. `loggedInAs()`.
|
||||
*
|
||||
* Nota: `#[RunTestsInSeparateProcesses]` NO se hereda en PHPUnit 11, así que
|
||||
* cada clase concreta de endpoint debe declarar el atributo para aislar el
|
||||
* estado estático (Synapsis, Router::$params, superglobales, http_response_code).
|
||||
*/
|
||||
abstract class EndpointTestCase extends TestCase
|
||||
{
|
||||
protected function dispatch(
|
||||
string $method,
|
||||
string $path,
|
||||
array $data = [],
|
||||
array $headers = []
|
||||
): Response {
|
||||
$method = strtoupper($method);
|
||||
|
||||
$_SERVER['REQUEST_METHOD'] = $method;
|
||||
$_SERVER['REQUEST_URI'] = $path;
|
||||
$_SERVER['HTTP_ACCEPT'] = $headers['Accept'] ?? 'text/html';
|
||||
|
||||
$_GET = [];
|
||||
$_POST = [];
|
||||
|
||||
$query = parse_url($path, PHP_URL_QUERY);
|
||||
if (is_string($query)) {
|
||||
parse_str($query, $_GET);
|
||||
}
|
||||
|
||||
if (in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) {
|
||||
$_POST = $data;
|
||||
}
|
||||
|
||||
foreach ($headers as $name => $value) {
|
||||
$key = 'HTTP_' . strtoupper(str_replace('-', '_', $name));
|
||||
$_SERVER[$key] = $value;
|
||||
}
|
||||
|
||||
Router::$params = new Neuron();
|
||||
HttpStubs::reset();
|
||||
http_response_code(200);
|
||||
|
||||
ob_start();
|
||||
try {
|
||||
Router::apply(parse_url($path, PHP_URL_PATH) ?: $path);
|
||||
} finally {
|
||||
$body = (string) ob_get_clean();
|
||||
}
|
||||
|
||||
return new Response(
|
||||
http_response_code(),
|
||||
$body,
|
||||
HttpStubs::$headers,
|
||||
HttpStubs::$cookies
|
||||
);
|
||||
}
|
||||
|
||||
protected function get(string $path, array $headers = []): Response
|
||||
{
|
||||
return $this->dispatch('GET', $path, [], $headers);
|
||||
}
|
||||
|
||||
protected function post(string $path, array $data = [], array $headers = []): Response
|
||||
{
|
||||
return $this->dispatch('POST', $path, $data, $headers);
|
||||
}
|
||||
|
||||
protected function patch(string $path, array $data = [], array $headers = []): Response
|
||||
{
|
||||
return $this->dispatch('PATCH', $path, $data, $headers);
|
||||
}
|
||||
|
||||
protected function delete(string $path, array $data = [], array $headers = []): Response
|
||||
{
|
||||
return $this->dispatch('DELETE', $path, $data, $headers);
|
||||
}
|
||||
|
||||
protected function assertStatus(int $expected, Response $response): void
|
||||
{
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$response->status,
|
||||
"Expected status {$expected}, got {$response->status}. Body: {$response->body}"
|
||||
);
|
||||
}
|
||||
|
||||
protected function assertRedirect(string $expectedPath, Response $response): void
|
||||
{
|
||||
$target = $response->redirectTarget();
|
||||
|
||||
$this->assertNotNull($target, 'Expected a redirect header (HX-Redirect or Location).');
|
||||
$this->assertStringEndsWith($expectedPath, $target);
|
||||
}
|
||||
|
||||
protected function assertHtmxTrigger(string $event, Response $response): void
|
||||
{
|
||||
$this->assertSame($event, $response->trigger());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user