Files
Classeo/backend/tests/Unit/Shared/Domain/AggregateRootTest.php
Mathias STRASSER affad287f9 feat: Réinitialisation de mot de passe avec tokens sécurisés
Implémentation complète du flux de réinitialisation de mot de passe (Story 1.5):

Backend:
- Aggregate PasswordResetToken avec TTL 1h, UUID v7, usage unique
- Endpoint POST /api/password/forgot avec rate limiting (3/h par email, 10/h par IP)
- Endpoint POST /api/password/reset avec validation token
- Templates email (demande + confirmation)
- Repository Redis avec TTL 2h pour distinguer expiré/invalide

Frontend:
- Page /mot-de-passe-oublie avec message générique (anti-énumération)
- Page /reset-password/[token] avec validation temps réel des critères
- Gestion erreurs: token invalide, expiré, déjà utilisé

Tests:
- 14 tests unitaires PasswordResetToken
- 7 tests unitaires RequestPasswordResetHandler
- 7 tests unitaires ResetPasswordHandler
- Tests E2E Playwright pour le flux complet
2026-02-02 09:45:15 +01:00

88 lines
2.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Unit\Shared\Domain;
use App\Shared\Domain\AggregateRoot;
use App\Shared\Domain\DomainEvent;
use DateTimeImmutable;
use PHPUnit\Framework\TestCase;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
final class AggregateRootTest extends TestCase
{
public function testPullDomainEventsReturnsRecordedEvents(): void
{
$aggregate = new TestAggregate();
$event1 = new TestDomainEvent('test1');
$event2 = new TestDomainEvent('test2');
$aggregate->doSomething($event1);
$aggregate->doSomething($event2);
$events = $aggregate->pullDomainEvents();
$this->assertCount(2, $events);
$this->assertSame($event1, $events[0]);
$this->assertSame($event2, $events[1]);
}
public function testPullDomainEventsClearsEventsAfterPulling(): void
{
$aggregate = new TestAggregate();
$event = new TestDomainEvent('test');
$aggregate->doSomething($event);
$aggregate->pullDomainEvents();
$secondPull = $aggregate->pullDomainEvents();
$this->assertCount(0, $secondPull);
}
public function testRecordEventAddsEventToInternalList(): void
{
$aggregate = new TestAggregate();
$event = new TestDomainEvent('test');
$aggregate->doSomething($event);
$events = $aggregate->pullDomainEvents();
$this->assertCount(1, $events);
$this->assertInstanceOf(TestDomainEvent::class, $events[0]);
}
}
// Test implementations
final class TestAggregate extends AggregateRoot
{
public function doSomething(DomainEvent $event): void
{
$this->recordEvent($event);
}
}
final readonly class TestDomainEvent implements DomainEvent
{
private DateTimeImmutable $occurredOn;
public function __construct(
public string $data,
private ?UuidInterface $testAggregateId = null,
) {
$this->occurredOn = new DateTimeImmutable();
}
public function occurredOn(): DateTimeImmutable
{
return $this->occurredOn;
}
public function aggregateId(): UuidInterface
{
return $this->testAggregateId ?? Uuid::uuid7();
}
}