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'), ); } }