userRepository = new InMemoryUserRepository(); $this->clock = new class implements Clock { public function now(): DateTimeImmutable { return new DateTimeImmutable('2026-02-14 10:00:00'); } }; $this->handler = new ResendInvitationHandler($this->userRepository, $this->clock); } #[Test] public function resendsInvitationSuccessfully(): void { $user = User::inviter( email: new Email('teacher@example.com'), role: Role::PROF, tenantId: TenantId::fromString(self::TENANT_ID), schoolName: 'École Alpha', firstName: 'Jean', lastName: 'Dupont', invitedAt: new DateTimeImmutable('2026-02-01 10:00:00'), ); $this->userRepository->save($user); $command = new ResendInvitationCommand(userId: (string) $user->id); ($this->handler)($command); $updated = $this->userRepository->get($user->id); self::assertEquals(new DateTimeImmutable('2026-02-14 10:00:00'), $updated->invitedAt); } #[Test] public function throwsWhenUserIsActive(): void { $user = User::inviter( email: new Email('teacher@example.com'), role: Role::PROF, tenantId: TenantId::fromString(self::TENANT_ID), schoolName: 'École Alpha', firstName: 'Jean', lastName: 'Dupont', invitedAt: new DateTimeImmutable('2026-02-01 10:00:00'), ); $user->activer( '$argon2id$hashed', new DateTimeImmutable('2026-02-02 10:00:00'), new ConsentementParentalPolicy($this->clock), ); $this->userRepository->save($user); $this->expectException(UtilisateurDejaInviteException::class); ($this->handler)(new ResendInvitationCommand(userId: (string) $user->id)); } #[Test] public function throwsWhenUserNotFound(): void { $this->expectException(UserNotFoundException::class); ($this->handler)(new ResendInvitationCommand(userId: (string) UserId::generate())); } }