feat: Activation de compte utilisateur avec validation token
L'inscription Classeo se fait via invitation : un admin crée un compte, l'utilisateur reçoit un lien d'activation par email pour définir son mot de passe. Ce flow sécurisé évite les inscriptions non autorisées et garantit que seuls les utilisateurs légitimes accèdent au système. Points clés de l'implémentation : - Tokens d'activation à usage unique stockés en cache (Redis/filesystem) - Validation du consentement parental pour les mineurs < 15 ans (RGPD) - L'échec d'activation ne consume pas le token (retry possible) - Users dans un cache séparé sans TTL (pas d'expiration) - Hot reload en dev (FrankenPHP sans mode worker) Story: 1.3 - Inscription et activation de compte
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Domain\Model\ActivationToken;
|
||||
|
||||
use App\Administration\Domain\Model\ActivationToken\ActivationTokenId;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
final class ActivationTokenIdTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function generateCreatesValidUuid(): void
|
||||
{
|
||||
$id = ActivationTokenId::generate();
|
||||
|
||||
self::assertInstanceOf(ActivationTokenId::class, $id);
|
||||
self::assertTrue(Uuid::isValid((string) $id));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function fromStringCreatesIdFromValidUuid(): void
|
||||
{
|
||||
$uuid = '550e8400-e29b-41d4-a716-446655440000';
|
||||
$id = ActivationTokenId::fromString($uuid);
|
||||
|
||||
self::assertSame($uuid, (string) $id);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function equalsReturnsTrueForSameValue(): void
|
||||
{
|
||||
$uuid = '550e8400-e29b-41d4-a716-446655440000';
|
||||
$id1 = ActivationTokenId::fromString($uuid);
|
||||
$id2 = ActivationTokenId::fromString($uuid);
|
||||
|
||||
self::assertTrue($id1->equals($id2));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function equalsReturnsFalseForDifferentValue(): void
|
||||
{
|
||||
$id1 = ActivationTokenId::generate();
|
||||
$id2 = ActivationTokenId::generate();
|
||||
|
||||
self::assertFalse($id1->equals($id2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Domain\Model\ActivationToken;
|
||||
|
||||
use App\Administration\Domain\Event\ActivationTokenGenerated;
|
||||
use App\Administration\Domain\Event\ActivationTokenUsed;
|
||||
use App\Administration\Domain\Exception\ActivationTokenAlreadyUsedException;
|
||||
use App\Administration\Domain\Exception\ActivationTokenExpiredException;
|
||||
use App\Administration\Domain\Model\ActivationToken\ActivationToken;
|
||||
use App\Administration\Domain\Model\ActivationToken\ActivationTokenId;
|
||||
use App\Shared\Infrastructure\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class ActivationTokenTest extends TestCase
|
||||
{
|
||||
private const string USER_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
|
||||
private const string EMAIL = 'user@example.com';
|
||||
private const string ROLE = 'ROLE_PARENT';
|
||||
private const string SCHOOL_NAME = 'École Alpha';
|
||||
|
||||
#[Test]
|
||||
public function generateCreatesTokenWithCorrectProperties(): void
|
||||
{
|
||||
$userId = self::USER_ID;
|
||||
$email = self::EMAIL;
|
||||
$tenantId = TenantId::fromString(self::TENANT_ID);
|
||||
$role = self::ROLE;
|
||||
$schoolName = self::SCHOOL_NAME;
|
||||
$now = new DateTimeImmutable('2026-01-15 10:00:00');
|
||||
|
||||
$token = ActivationToken::generate(
|
||||
userId: $userId,
|
||||
email: $email,
|
||||
tenantId: $tenantId,
|
||||
role: $role,
|
||||
schoolName: $schoolName,
|
||||
createdAt: $now,
|
||||
);
|
||||
|
||||
self::assertInstanceOf(ActivationTokenId::class, $token->id);
|
||||
self::assertSame($userId, $token->userId);
|
||||
self::assertSame($email, $token->email);
|
||||
self::assertTrue($tenantId->equals($token->tenantId));
|
||||
self::assertSame($role, $token->role);
|
||||
self::assertSame($schoolName, $token->schoolName);
|
||||
self::assertEquals($now, $token->createdAt);
|
||||
self::assertFalse($token->isUsed());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function generateRecordsActivationTokenGeneratedEvent(): void
|
||||
{
|
||||
$token = $this->createToken();
|
||||
|
||||
$events = $token->pullDomainEvents();
|
||||
|
||||
self::assertCount(1, $events);
|
||||
self::assertInstanceOf(ActivationTokenGenerated::class, $events[0]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function tokenValueIsUuidV4Format(): void
|
||||
{
|
||||
$token = $this->createToken();
|
||||
|
||||
self::assertMatchesRegularExpression(
|
||||
'/^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/i',
|
||||
$token->tokenValue,
|
||||
);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function expiresAtIs7DaysAfterCreation(): void
|
||||
{
|
||||
$createdAt = new DateTimeImmutable('2026-01-15 10:00:00');
|
||||
$expectedExpiration = new DateTimeImmutable('2026-01-22 10:00:00');
|
||||
|
||||
$token = ActivationToken::generate(
|
||||
userId: self::USER_ID,
|
||||
email: self::EMAIL,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
role: self::ROLE,
|
||||
schoolName: self::SCHOOL_NAME,
|
||||
createdAt: $createdAt,
|
||||
);
|
||||
|
||||
self::assertEquals($expectedExpiration, $token->expiresAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function isExpiredReturnsFalseWhenNotExpired(): void
|
||||
{
|
||||
$createdAt = new DateTimeImmutable('2026-01-15 10:00:00');
|
||||
$checkAt = new DateTimeImmutable('2026-01-20 10:00:00');
|
||||
|
||||
$token = ActivationToken::generate(
|
||||
userId: self::USER_ID,
|
||||
email: self::EMAIL,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
role: self::ROLE,
|
||||
schoolName: self::SCHOOL_NAME,
|
||||
createdAt: $createdAt,
|
||||
);
|
||||
|
||||
self::assertFalse($token->isExpired($checkAt));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function isExpiredReturnsTrueWhenExpired(): void
|
||||
{
|
||||
$createdAt = new DateTimeImmutable('2026-01-15 10:00:00');
|
||||
$checkAt = new DateTimeImmutable('2026-01-25 10:00:00');
|
||||
|
||||
$token = ActivationToken::generate(
|
||||
userId: self::USER_ID,
|
||||
email: self::EMAIL,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
role: self::ROLE,
|
||||
schoolName: self::SCHOOL_NAME,
|
||||
createdAt: $createdAt,
|
||||
);
|
||||
|
||||
self::assertTrue($token->isExpired($checkAt));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function isExpiredReturnsTrueAtExactExpirationMoment(): void
|
||||
{
|
||||
$createdAt = new DateTimeImmutable('2026-01-15 10:00:00');
|
||||
$checkAt = new DateTimeImmutable('2026-01-22 10:00:00');
|
||||
|
||||
$token = ActivationToken::generate(
|
||||
userId: self::USER_ID,
|
||||
email: self::EMAIL,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
role: self::ROLE,
|
||||
schoolName: self::SCHOOL_NAME,
|
||||
createdAt: $createdAt,
|
||||
);
|
||||
|
||||
self::assertTrue($token->isExpired($checkAt));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function useMarksTokenAsUsed(): void
|
||||
{
|
||||
$token = $this->createToken();
|
||||
$usedAt = new DateTimeImmutable('2026-01-16 10:00:00');
|
||||
|
||||
$token->use($usedAt);
|
||||
|
||||
self::assertTrue($token->isUsed());
|
||||
self::assertEquals($usedAt, $token->usedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function useRecordsActivationTokenUsedEvent(): void
|
||||
{
|
||||
$token = $this->createToken();
|
||||
$token->pullDomainEvents();
|
||||
|
||||
$usedAt = new DateTimeImmutable('2026-01-16 10:00:00');
|
||||
$token->use($usedAt);
|
||||
|
||||
$events = $token->pullDomainEvents();
|
||||
self::assertCount(1, $events);
|
||||
self::assertInstanceOf(ActivationTokenUsed::class, $events[0]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function useThrowsExceptionWhenTokenAlreadyUsed(): void
|
||||
{
|
||||
$token = $this->createToken();
|
||||
$firstUse = new DateTimeImmutable('2026-01-16 10:00:00');
|
||||
$token->use($firstUse);
|
||||
|
||||
$this->expectException(ActivationTokenAlreadyUsedException::class);
|
||||
|
||||
$secondUse = new DateTimeImmutable('2026-01-17 10:00:00');
|
||||
$token->use($secondUse);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function useThrowsExceptionWhenTokenExpired(): void
|
||||
{
|
||||
$createdAt = new DateTimeImmutable('2026-01-15 10:00:00');
|
||||
$usedAt = new DateTimeImmutable('2026-01-25 10:00:00');
|
||||
|
||||
$token = ActivationToken::generate(
|
||||
userId: self::USER_ID,
|
||||
email: self::EMAIL,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
role: self::ROLE,
|
||||
schoolName: self::SCHOOL_NAME,
|
||||
createdAt: $createdAt,
|
||||
);
|
||||
|
||||
$this->expectException(ActivationTokenExpiredException::class);
|
||||
|
||||
$token->use($usedAt);
|
||||
}
|
||||
|
||||
private function createToken(): ActivationToken
|
||||
{
|
||||
return ActivationToken::generate(
|
||||
userId: self::USER_ID,
|
||||
email: self::EMAIL,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
role: self::ROLE,
|
||||
schoolName: self::SCHOOL_NAME,
|
||||
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
||||
);
|
||||
}
|
||||
}
|
||||
220
backend/tests/Unit/Administration/Domain/Model/User/UserTest.php
Normal file
220
backend/tests/Unit/Administration/Domain/Model/User/UserTest.php
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Domain\Model\User;
|
||||
|
||||
use App\Administration\Domain\Event\CompteActive;
|
||||
use App\Administration\Domain\Event\CompteCreated;
|
||||
use App\Administration\Domain\Exception\CompteNonActivableException;
|
||||
use App\Administration\Domain\Model\ConsentementParental\ConsentementParental;
|
||||
use App\Administration\Domain\Model\User\Email;
|
||||
use App\Administration\Domain\Model\User\Role;
|
||||
use App\Administration\Domain\Model\User\StatutCompte;
|
||||
use App\Administration\Domain\Model\User\User;
|
||||
use App\Administration\Domain\Policy\ConsentementParentalPolicy;
|
||||
use App\Shared\Domain\Clock;
|
||||
use App\Shared\Infrastructure\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class UserTest extends TestCase
|
||||
{
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
|
||||
private const string SCHOOL_NAME = 'École Alpha';
|
||||
|
||||
private Clock $clock;
|
||||
private ConsentementParentalPolicy $consentementPolicy;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->clock = new class implements Clock {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('2026-01-31 10:00:00');
|
||||
}
|
||||
};
|
||||
|
||||
$this->consentementPolicy = new ConsentementParentalPolicy($this->clock);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function creerCreatesUserWithPendingStatus(): void
|
||||
{
|
||||
$user = $this->createUser();
|
||||
|
||||
self::assertSame(StatutCompte::EN_ATTENTE, $user->statut);
|
||||
self::assertNull($user->hashedPassword);
|
||||
self::assertNull($user->activatedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function creerRecordsCompteCreatedEvent(): void
|
||||
{
|
||||
$user = $this->createUser();
|
||||
|
||||
$events = $user->pullDomainEvents();
|
||||
|
||||
self::assertCount(1, $events);
|
||||
self::assertInstanceOf(CompteCreated::class, $events[0]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function activerSetsPasswordAndChangesStatusToActive(): void
|
||||
{
|
||||
$user = $this->createUser();
|
||||
$hashedPassword = '$argon2id$hashed';
|
||||
$activatedAt = new DateTimeImmutable('2026-01-31 10:00:00');
|
||||
|
||||
$user->activer($hashedPassword, $activatedAt, $this->consentementPolicy);
|
||||
|
||||
self::assertSame(StatutCompte::ACTIF, $user->statut);
|
||||
self::assertSame($hashedPassword, $user->hashedPassword);
|
||||
self::assertEquals($activatedAt, $user->activatedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function activerRecordsCompteActiveEvent(): void
|
||||
{
|
||||
$user = $this->createUser();
|
||||
$user->pullDomainEvents();
|
||||
|
||||
$user->activer('$argon2id$hashed', new DateTimeImmutable(), $this->consentementPolicy);
|
||||
|
||||
$events = $user->pullDomainEvents();
|
||||
self::assertCount(1, $events);
|
||||
self::assertInstanceOf(CompteActive::class, $events[0]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function activerThrowsWhenStatusIsNotPending(): void
|
||||
{
|
||||
$user = $this->createUser();
|
||||
$user->activer('$argon2id$hashed', new DateTimeImmutable(), $this->consentementPolicy);
|
||||
|
||||
$this->expectException(CompteNonActivableException::class);
|
||||
|
||||
$user->activer('$argon2id$another', new DateTimeImmutable(), $this->consentementPolicy);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function activerThrowsForMinorWithoutConsent(): void
|
||||
{
|
||||
// Créer un utilisateur mineur (14 ans)
|
||||
$user = User::creer(
|
||||
email: new Email('eleve@example.com'),
|
||||
role: Role::ELEVE,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
schoolName: self::SCHOOL_NAME,
|
||||
dateNaissance: new DateTimeImmutable('2012-06-15'), // 13 ans
|
||||
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
||||
);
|
||||
|
||||
$this->expectException(CompteNonActivableException::class);
|
||||
$this->expectExceptionMessage('consentement parental manquant');
|
||||
|
||||
$user->activer('$argon2id$hashed', new DateTimeImmutable(), $this->consentementPolicy);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function activerSucceedsForMinorWithConsent(): void
|
||||
{
|
||||
// Créer un utilisateur mineur (14 ans)
|
||||
$user = User::creer(
|
||||
email: new Email('eleve@example.com'),
|
||||
role: Role::ELEVE,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
schoolName: self::SCHOOL_NAME,
|
||||
dateNaissance: new DateTimeImmutable('2012-06-15'),
|
||||
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
||||
);
|
||||
|
||||
// Enregistrer le consentement parental
|
||||
$consentement = ConsentementParental::accorder(
|
||||
parentId: 'parent-uuid',
|
||||
eleveId: (string) $user->id,
|
||||
at: new DateTimeImmutable('2026-01-20 10:00:00'),
|
||||
ipAddress: '192.168.1.1',
|
||||
);
|
||||
$user->enregistrerConsentementParental($consentement);
|
||||
|
||||
// L'activation devrait maintenant réussir
|
||||
$user->activer('$argon2id$hashed', new DateTimeImmutable(), $this->consentementPolicy);
|
||||
|
||||
self::assertSame(StatutCompte::ACTIF, $user->statut);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function activerSucceedsForAdultWithoutConsent(): void
|
||||
{
|
||||
// Créer un utilisateur adulte (16 ans)
|
||||
$user = User::creer(
|
||||
email: new Email('eleve@example.com'),
|
||||
role: Role::ELEVE,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
schoolName: self::SCHOOL_NAME,
|
||||
dateNaissance: new DateTimeImmutable('2010-01-01'), // 16 ans
|
||||
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
||||
);
|
||||
|
||||
// Pas de consentement nécessaire
|
||||
$user->activer('$argon2id$hashed', new DateTimeImmutable(), $this->consentementPolicy);
|
||||
|
||||
self::assertSame(StatutCompte::ACTIF, $user->statut);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function peutSeConnecterReturnsTrueOnlyWhenActive(): void
|
||||
{
|
||||
$user = $this->createUser();
|
||||
|
||||
self::assertFalse($user->peutSeConnecter());
|
||||
|
||||
$user->activer('$argon2id$hashed', new DateTimeImmutable(), $this->consentementPolicy);
|
||||
|
||||
self::assertTrue($user->peutSeConnecter());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function necessiteConsentementParentalReturnsTrueForMinor(): void
|
||||
{
|
||||
$user = User::creer(
|
||||
email: new Email('eleve@example.com'),
|
||||
role: Role::ELEVE,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
schoolName: self::SCHOOL_NAME,
|
||||
dateNaissance: new DateTimeImmutable('2012-06-15'), // 13 ans
|
||||
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
||||
);
|
||||
|
||||
self::assertTrue($user->necessiteConsentementParental($this->consentementPolicy));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function necessiteConsentementParentalReturnsFalseForAdult(): void
|
||||
{
|
||||
$user = User::creer(
|
||||
email: new Email('parent@example.com'),
|
||||
role: Role::PARENT,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
schoolName: self::SCHOOL_NAME,
|
||||
dateNaissance: null, // Parents n'ont pas de date de naissance enregistrée
|
||||
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
||||
);
|
||||
|
||||
self::assertFalse($user->necessiteConsentementParental($this->consentementPolicy));
|
||||
}
|
||||
|
||||
private function createUser(): User
|
||||
{
|
||||
return User::creer(
|
||||
email: new Email('user@example.com'),
|
||||
role: Role::PARENT,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
schoolName: self::SCHOOL_NAME,
|
||||
dateNaissance: null,
|
||||
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Domain\Policy;
|
||||
|
||||
use App\Administration\Domain\Policy\ConsentementParentalPolicy;
|
||||
use App\Shared\Domain\Clock;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class ConsentementParentalPolicyTest extends TestCase
|
||||
{
|
||||
private Clock $clock;
|
||||
private ConsentementParentalPolicy $policy;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->clock = new class implements Clock {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('2026-01-31 10:00:00');
|
||||
}
|
||||
};
|
||||
|
||||
$this->policy = new ConsentementParentalPolicy($this->clock);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function consentementRequisPourUtilisateurDe14Ans(): void
|
||||
{
|
||||
$dateNaissance = new DateTimeImmutable('2012-01-31');
|
||||
|
||||
self::assertTrue($this->policy->estRequis($dateNaissance));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function consentementRequisPourUtilisateurDe10Ans(): void
|
||||
{
|
||||
$dateNaissance = new DateTimeImmutable('2016-01-31');
|
||||
|
||||
self::assertTrue($this->policy->estRequis($dateNaissance));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function consentementNonRequisPourUtilisateurDe15Ans(): void
|
||||
{
|
||||
$dateNaissance = new DateTimeImmutable('2011-01-30');
|
||||
|
||||
self::assertFalse($this->policy->estRequis($dateNaissance));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function consentementNonRequisPourUtilisateurDe16Ans(): void
|
||||
{
|
||||
$dateNaissance = new DateTimeImmutable('2010-01-31');
|
||||
|
||||
self::assertFalse($this->policy->estRequis($dateNaissance));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function consentementNonRequisSiDateNaissanceNulle(): void
|
||||
{
|
||||
self::assertFalse($this->policy->estRequis(null));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('agesBordureProvider')]
|
||||
public function consentementRequisAuxAgesBordure(
|
||||
string $dateNaissance,
|
||||
bool $consentementRequis,
|
||||
string $description,
|
||||
): void {
|
||||
$result = $this->policy->estRequis(new DateTimeImmutable($dateNaissance));
|
||||
|
||||
self::assertSame($consentementRequis, $result, $description);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return iterable<string, array{string, bool, string}>
|
||||
*/
|
||||
public static function agesBordureProvider(): iterable
|
||||
{
|
||||
// Current date is 2026-01-31
|
||||
yield '14 ans et 364 jours' => [
|
||||
'2011-02-01',
|
||||
true,
|
||||
'Un jour avant 15 ans → consentement requis',
|
||||
];
|
||||
|
||||
yield '15 ans exactement' => [
|
||||
'2011-01-31',
|
||||
false,
|
||||
'Le jour des 15 ans → consentement non requis',
|
||||
];
|
||||
|
||||
yield '15 ans et 1 jour' => [
|
||||
'2011-01-30',
|
||||
false,
|
||||
'Un jour après 15 ans → consentement non requis',
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user