$queryParams Paramètres de query string * @param array $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 $body Corps de la requête (form data) * @param array $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 $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'), ); } }