Files
Classeo/backend/tests/Functional/Administration/Api/RefreshTokenEndpointTest.php
Mathias STRASSER 2ed60fdcc1 feat: Audit trail pour actions sensibles
Story 1.7 - Implémente un système complet d'audit trail pour tracer
toutes les actions sensibles (authentification, modifications de données,
exports) avec immuabilité garantie par PostgreSQL.

Fonctionnalités principales:
- Table audit_log append-only avec contraintes PostgreSQL (RULE)
- AuditLogger centralisé avec injection automatique du contexte
- Correlation ID pour traçabilité distribuée (HTTP + async)
- Handlers pour événements d'authentification
- Commande d'archivage des logs anciens
- Pas de PII dans les logs (emails/IPs hashés)

Infrastructure:
- Middlewares Messenger pour propagation du Correlation ID
- HTTP middleware pour génération/propagation du Correlation ID
- Support multi-tenant avec TenantResolver
2026-02-04 00:11:58 +01:00

106 lines
3.0 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Functional\Administration\Api;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use PHPUnit\Framework\Attributes\Test;
/**
* [P0] Functional tests for the refresh token endpoint.
*
* Verifies:
* - Endpoint accessibility
* - Missing token handling (401)
* - Invalid token handling
* - Cookie-based authentication
*/
final class RefreshTokenEndpointTest extends ApiTestCase
{
protected static ?bool $alwaysBootKernel = true;
#[Test]
public function refreshEndpointReturns401WithoutCookie(): void
{
// GIVEN: No refresh_token cookie
$client = static::createClient();
// WHEN: Calling refresh endpoint
$response = $client->request('POST', '/api/token/refresh', [
'json' => [],
'headers' => [
'Host' => 'localhost',
],
]);
// THEN: Returns 401 Unauthorized
self::assertResponseStatusCodeSame(401);
}
#[Test]
public function logoutEndpointIsAccessibleWithoutToken(): void
{
// GIVEN: No authentication
$client = static::createClient();
// WHEN: Calling logout endpoint
$response = $client->request('POST', '/api/token/logout', [
'json' => [],
'headers' => [
'Host' => 'localhost',
],
]);
// THEN: Returns 200 OK (idempotent - no token to invalidate)
self::assertResponseIsSuccessful();
}
#[Test]
public function logoutEndpointClearsCookies(): void
{
// GIVEN: A client
$client = static::createClient();
// WHEN: Calling logout
$response = $client->request('POST', '/api/token/logout', [
'headers' => [
'Host' => 'localhost',
'Cookie' => 'refresh_token=some-token-value',
],
]);
// THEN: Response sets expired cookies
$setCookieHeaders = $response->getHeaders(false)['set-cookie'] ?? [];
$this->assertNotEmpty($setCookieHeaders);
$hasClearedCookie = false;
foreach ($setCookieHeaders as $cookie) {
if (str_contains($cookie, 'refresh_token=') && str_contains($cookie, 'expires=')) {
$hasClearedCookie = true;
break;
}
}
$this->assertTrue($hasClearedCookie, 'Should set expired refresh_token cookie');
}
#[Test]
public function refreshEndpointWithInvalidTokenReturns401(): void
{
// GIVEN: An invalid/malformed token in cookie
$client = static::createClient();
// WHEN: Calling refresh with invalid cookie
$response = $client->request('POST', '/api/token/refresh', [
'json' => [],
'headers' => [
'Host' => 'localhost',
'Cookie' => 'refresh_token=invalid-token-format',
],
]);
// THEN: Returns 401 Unauthorized
self::assertResponseStatusCodeSame(401);
}
}