146 lines
5.0 KiB
PHP
146 lines
5.0 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use Psr\Http\Message\ResponseInterface as Response;
|
|
use Slim\Psr7\Factory\ServerRequestFactory;
|
|
use Slim\Psr7\Response as SlimResponse;
|
|
|
|
/**
|
|
* Classe de base pour les tests de contrôleurs.
|
|
*
|
|
* Fournit des helpers pour construire des requêtes PSR-7 sans serveur,
|
|
* des assertions sur les redirections et les réponses JSON,
|
|
* ainsi qu'un stub de Twig qui retourne la réponse inchangée.
|
|
*
|
|
* Chaque test de contrôleur invoque directement l'action (méthode publique)
|
|
* sans passer par le routeur Slim — les middlewares sont testés séparément.
|
|
*/
|
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
|
abstract class ControllerTestBase extends TestCase
|
|
{
|
|
// ── Factories ────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Crée une requête GET avec des paramètres de query optionnels.
|
|
*
|
|
* @param string $uri URI de la requête (ex: '/admin/posts')
|
|
* @param array<string, mixed> $queryParams Paramètres de query string
|
|
* @param array<string, mixed> $serverParams Paramètres serveur (ex: REMOTE_ADDR)
|
|
*/
|
|
protected function makeGet(
|
|
string $uri = '/',
|
|
array $queryParams = [],
|
|
array $serverParams = [],
|
|
): \Psr\Http\Message\ServerRequestInterface {
|
|
$factory = new ServerRequestFactory();
|
|
$request = $factory->createServerRequest('GET', $uri, $serverParams);
|
|
|
|
if ($queryParams !== []) {
|
|
$request = $request->withQueryParams($queryParams);
|
|
}
|
|
|
|
return $request;
|
|
}
|
|
|
|
/**
|
|
* Crée une requête POST avec un corps parsé optionnel.
|
|
*
|
|
* @param string $uri URI de la requête (ex: '/auth/login')
|
|
* @param array<string, mixed> $body Corps de la requête (form data)
|
|
* @param array<string, mixed> $serverParams Paramètres serveur (ex: REMOTE_ADDR)
|
|
*/
|
|
protected function makePost(
|
|
string $uri = '/',
|
|
array $body = [],
|
|
array $serverParams = [],
|
|
): \Psr\Http\Message\ServerRequestInterface {
|
|
$factory = new ServerRequestFactory();
|
|
$request = $factory->createServerRequest('POST', $uri, $serverParams);
|
|
|
|
if ($body !== []) {
|
|
$request = $request->withParsedBody($body);
|
|
}
|
|
|
|
return $request;
|
|
}
|
|
|
|
/**
|
|
* Crée une réponse PSR-7 vide (status 200).
|
|
*/
|
|
protected function makeResponse(): SlimResponse
|
|
{
|
|
return new SlimResponse();
|
|
}
|
|
|
|
/**
|
|
* Crée un mock de Twig dont render() retourne la réponse reçue en premier argument.
|
|
*
|
|
* Cela permet aux tests de contrôleurs qui appellent $this->view->render()
|
|
* de recevoir une réponse 200 sans instancier un environnement Twig réel.
|
|
*
|
|
* @return \Slim\Views\Twig&\PHPUnit\Framework\MockObject\MockObject
|
|
*/
|
|
protected function makeTwigMock(): \Slim\Views\Twig
|
|
{
|
|
$twig = $this->createMock(\Slim\Views\Twig::class);
|
|
$twig->method('render')->willReturnArgument(0);
|
|
|
|
return $twig;
|
|
}
|
|
|
|
// ── Assertions ───────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Vérifie qu'une réponse est une redirection vers l'URL attendue.
|
|
*/
|
|
protected function assertRedirectTo(Response $res, string $expectedLocation): void
|
|
{
|
|
$this->assertSame(302, $res->getStatusCode(), 'Le code HTTP devrait être 302');
|
|
$this->assertSame(
|
|
$expectedLocation,
|
|
$res->getHeaderLine('Location'),
|
|
"La redirection devrait pointer vers {$expectedLocation}",
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Vérifie le code HTTP d'une réponse.
|
|
*/
|
|
protected function assertStatus(Response $res, int $expectedStatus): void
|
|
{
|
|
$this->assertSame($expectedStatus, $res->getStatusCode());
|
|
}
|
|
|
|
/**
|
|
* Vérifie que le corps de la réponse est du JSON contenant les clés attendues.
|
|
*
|
|
* @param array<string, mixed> $expectedSubset Sous-ensemble de clés/valeurs attendues
|
|
*/
|
|
protected function assertJsonContains(Response $res, array $expectedSubset): void
|
|
{
|
|
$body = (string) $res->getBody();
|
|
$decoded = json_decode($body, true);
|
|
|
|
$this->assertIsArray($decoded, 'Le corps de la réponse devrait être du JSON valide');
|
|
|
|
foreach ($expectedSubset as $key => $value) {
|
|
$this->assertArrayHasKey($key, $decoded, "La clé JSON '{$key}' devrait être présente");
|
|
$this->assertSame($value, $decoded[$key], "La valeur JSON de '{$key}' est incorrecte");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Vérifie que le Content-Type de la réponse est application/json.
|
|
*/
|
|
protected function assertJsonContentType(Response $res): void
|
|
{
|
|
$this->assertStringContainsString(
|
|
'application/json',
|
|
$res->getHeaderLine('Content-Type'),
|
|
);
|
|
}
|
|
}
|