createUser('user1@example.com', $tenantAlpha); $user2 = $this->createUser('user2@example.com', $tenantAlpha); $source->save($user1); $source->save($user2); $registry = $this->createRegistry([ new TenantConfig($tenantAlpha, 'ecole-alpha', 'sqlite:///:memory:'), ]); $command = new MigrateUsersToPostgresCommand($source, $target, $registry); $tester = new CommandTester($command); $tester->execute([]); self::assertSame(0, $tester->getStatusCode()); self::assertCount(2, $target->findAllByTenant($tenantAlpha)); self::assertStringContainsString('2 utilisateur(s) migré(s)', $tester->getDisplay()); } #[Test] public function isIdempotent(): void { $source = new InMemoryUserRepository(); $target = new InMemoryUserRepository(); $tenantAlpha = TenantId::fromString(self::TENANT_ALPHA_ID); $user = $this->createUser('user@example.com', $tenantAlpha); $source->save($user); $registry = $this->createRegistry([ new TenantConfig($tenantAlpha, 'ecole-alpha', 'sqlite:///:memory:'), ]); $command = new MigrateUsersToPostgresCommand($source, $target, $registry); // Run twice $tester = new CommandTester($command); $tester->execute([]); $tester->execute([]); // Should still have exactly 1 user, not 2 self::assertCount(1, $target->findAllByTenant($tenantAlpha)); } #[Test] public function handlesMultipleTenants(): void { $source = new InMemoryUserRepository(); $target = new InMemoryUserRepository(); $tenantAlpha = TenantId::fromString(self::TENANT_ALPHA_ID); $tenantBeta = TenantId::fromString(self::TENANT_BETA_ID); $source->save($this->createUser('alpha@example.com', $tenantAlpha)); $source->save($this->createUser('beta@example.com', $tenantBeta)); $registry = $this->createRegistry([ new TenantConfig($tenantAlpha, 'ecole-alpha', 'sqlite:///:memory:'), new TenantConfig($tenantBeta, 'ecole-beta', 'sqlite:///:memory:'), ]); $command = new MigrateUsersToPostgresCommand($source, $target, $registry); $tester = new CommandTester($command); $tester->execute([]); self::assertSame(0, $tester->getStatusCode()); self::assertCount(1, $target->findAllByTenant($tenantAlpha)); self::assertCount(1, $target->findAllByTenant($tenantBeta)); } #[Test] public function handlesEmptyTenant(): void { $source = new InMemoryUserRepository(); $target = new InMemoryUserRepository(); $tenantAlpha = TenantId::fromString(self::TENANT_ALPHA_ID); $registry = $this->createRegistry([ new TenantConfig($tenantAlpha, 'ecole-alpha', 'sqlite:///:memory:'), ]); $command = new MigrateUsersToPostgresCommand($source, $target, $registry); $tester = new CommandTester($command); $tester->execute([]); self::assertSame(0, $tester->getStatusCode()); self::assertStringContainsString('aucun utilisateur', $tester->getDisplay()); } #[Test] public function preservesUserIds(): void { $source = new InMemoryUserRepository(); $target = new InMemoryUserRepository(); $tenantAlpha = TenantId::fromString(self::TENANT_ALPHA_ID); $user = $this->createUser('user@example.com', $tenantAlpha); $originalId = (string) $user->id; $source->save($user); $registry = $this->createRegistry([ new TenantConfig($tenantAlpha, 'ecole-alpha', 'sqlite:///:memory:'), ]); $command = new MigrateUsersToPostgresCommand($source, $target, $registry); $tester = new CommandTester($command); $tester->execute([]); $migratedUsers = $target->findAllByTenant($tenantAlpha); self::assertSame($originalId, (string) $migratedUsers[0]->id); } private function createUser(string $email, TenantId $tenantId): User { return User::creer( email: new Email($email), role: Role::PARENT, tenantId: $tenantId, schoolName: 'École Test', dateNaissance: null, createdAt: new DateTimeImmutable('2026-01-15T10:00:00+00:00'), ); } /** * @param TenantConfig[] $configs */ private function createRegistry(array $configs): TenantRegistry { return new class($configs) implements TenantRegistry { /** @param TenantConfig[] $configs */ public function __construct(private readonly array $configs) { } public function getConfig(TenantId $tenantId): TenantConfig { foreach ($this->configs as $config) { if ($config->tenantId->equals($tenantId)) { return $config; } } throw new RuntimeException('Tenant not found'); } public function getBySubdomain(string $subdomain): TenantConfig { foreach ($this->configs as $config) { if ($config->subdomain === $subdomain) { return $config; } } throw new RuntimeException('Tenant not found'); } public function exists(string $subdomain): bool { foreach ($this->configs as $config) { if ($config->subdomain === $subdomain) { return true; } } return false; } /** @return TenantConfig[] */ public function getAllConfigs(): array { return $this->configs; } }; } }