Files
Classeo/backend/tests/Functional/Administration/Api/PasswordResetEndpointsTest.php
Mathias STRASSER dc2be898d5
Some checks failed
CI / Backend Tests (push) Has been cancelled
CI / Frontend Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Naming Conventions (push) Has been cancelled
CI / Build Check (push) Has been cancelled
feat: Provisionner automatiquement un nouvel établissement
Lorsqu'un super-admin crée un établissement via l'interface, le système
doit automatiquement créer la base tenant, exécuter les migrations,
créer le premier utilisateur admin et envoyer l'invitation — le tout
de manière asynchrone pour ne pas bloquer la réponse HTTP.

Ce mécanisme rend chaque établissement opérationnel dès sa création
sans intervention manuelle sur l'infrastructure.
2026-04-16 09:27:25 +02:00

105 lines
3.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Functional\Administration\Api;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use PHPUnit\Framework\Attributes\Test;
/**
* Tests that password reset endpoints are accessible without authentication.
*
* These endpoints MUST be public because users who forgot their password
* cannot authenticate to request a reset.
*/
final class PasswordResetEndpointsTest extends ApiTestCase
{
/**
* Opt-in for API Platform 5.0 behavior where kernel boot is explicit.
*
* @see https://github.com/api-platform/core/issues/6971
*/
protected static ?bool $alwaysBootKernel = true;
#[Test]
public function passwordForgotEndpointIsAccessibleWithoutAuthentication(): void
{
$client = static::createClient();
$response = $client->request('POST', '/api/password/forgot', [
'json' => ['email' => 'test@example.com'],
'headers' => [
'Host' => 'localhost',
],
]);
// Should NOT return 401 Unauthorized
// It should return 200 (success) or 429 (rate limited), but never 401
$status = $response->getStatusCode();
self::assertNotEquals(401, $status, 'Password forgot endpoint should be accessible without JWT');
self::assertContains($status, [200, 201, 429], 'Expected 200/201 (success) or 429 (rate limited)');
}
#[Test]
public function passwordResetEndpointIsAccessibleWithoutAuthentication(): void
{
$client = static::createClient();
$response = $client->request('POST', '/api/password/reset', [
'json' => [
'token' => 'invalid-token-for-test',
'password' => 'NewSecurePassword123!',
],
'headers' => [
'Host' => 'localhost',
],
]);
// Should NOT return 401 Unauthorized
// It should return 400 (invalid token) or 410 (expired), but never 401
self::assertNotEquals(401, $response->getStatusCode(), 'Password reset endpoint should be accessible without JWT');
// With an invalid token, we expect a 400 Bad Request
self::assertResponseStatusCodeSame(400);
}
#[Test]
public function passwordForgotValidatesEmailFormat(): void
{
$client = static::createClient();
$response = $client->request('POST', '/api/password/forgot', [
'json' => ['email' => 'not-an-email'],
'headers' => [
'Host' => 'localhost',
],
]);
// Invalid email format returns validation error (422)
// This is acceptable because it doesn't reveal user existence
// (format validation happens before checking if user exists)
self::assertResponseStatusCodeSame(422);
}
#[Test]
public function passwordResetValidatesPasswordRequirements(): void
{
$client = static::createClient();
// Password too short
$response = $client->request('POST', '/api/password/reset', [
'json' => [
'token' => 'some-token',
'password' => 'short',
],
'headers' => [
'Host' => 'localhost',
],
]);
// Should return 422 Unprocessable Entity for validation errors
self::assertResponseStatusCodeSame(422);
}
}