userRepository = new InMemoryUserRepository(); $this->clock = new class implements Clock { public function now(): DateTimeImmutable { return new DateTimeImmutable('2026-02-07 10:00:00'); } }; $this->handler = new InviteUserHandler($this->userRepository, $this->clock); } #[Test] public function invitesUserSuccessfully(): void { $command = new InviteUserCommand( tenantId: self::TENANT_ID, schoolName: self::SCHOOL_NAME, email: 'teacher@example.com', role: Role::PROF->value, firstName: 'Jean', lastName: 'Dupont', ); $user = ($this->handler)($command); self::assertSame(StatutCompte::EN_ATTENTE, $user->statut); self::assertSame('Jean', $user->firstName); self::assertSame('Dupont', $user->lastName); self::assertSame('teacher@example.com', (string) $user->email); self::assertNotNull($user->invitedAt); } #[Test] public function throwsWhenEmailAlreadyUsedInTenant(): void { // Pre-populate with existing user $existingUser = User::inviter( email: new Email('teacher@example.com'), role: Role::PROF, tenantId: TenantId::fromString(self::TENANT_ID), schoolName: self::SCHOOL_NAME, firstName: 'Existing', lastName: 'User', invitedAt: new DateTimeImmutable('2026-02-01 10:00:00'), ); $this->userRepository->save($existingUser); $command = new InviteUserCommand( tenantId: self::TENANT_ID, schoolName: self::SCHOOL_NAME, email: 'teacher@example.com', role: Role::PROF->value, firstName: 'Jean', lastName: 'Dupont', ); $this->expectException(EmailDejaUtiliseeException::class); ($this->handler)($command); } #[Test] public function savesUserToRepository(): void { $command = new InviteUserCommand( tenantId: self::TENANT_ID, schoolName: self::SCHOOL_NAME, email: 'teacher@example.com', role: Role::PROF->value, firstName: 'Jean', lastName: 'Dupont', ); $user = ($this->handler)($command); $found = $this->userRepository->get($user->id); self::assertSame((string) $user->id, (string) $found->id); } }