addOption('email', null, InputOption::VALUE_OPTIONAL, 'Email address', 'e2e-login@example.com') ->addOption('password', null, InputOption::VALUE_OPTIONAL, 'Password (plain text)', 'TestPassword123') ->addOption('role', null, InputOption::VALUE_OPTIONAL, 'User role (PARENT, ELEVE, PROF, ADMIN)', 'PARENT') ->addOption('firstName', null, InputOption::VALUE_OPTIONAL, 'First name', '') ->addOption('lastName', null, InputOption::VALUE_OPTIONAL, 'Last name', '') ->addOption('school', null, InputOption::VALUE_OPTIONAL, 'School name', 'École de Test') ->addOption('tenant', null, InputOption::VALUE_OPTIONAL, 'Tenant subdomain (ecole-alpha, ecole-beta)', 'ecole-alpha') ->addOption('internal-run', null, InputOption::VALUE_NONE, 'Internal option to create the test user inside the tenant database'); } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); /** @var string $email */ $email = $input->getOption('email'); /** @var string $password */ $password = $input->getOption('password'); /** @var string $roleOption */ $roleOption = $input->getOption('role'); $roleInput = strtoupper($roleOption); /** @var string $firstName */ $firstName = $input->getOption('firstName'); /** @var string $lastName */ $lastName = $input->getOption('lastName'); /** @var string $schoolName */ $schoolName = $input->getOption('school'); /** @var string $tenantSubdomain */ $tenantSubdomain = $input->getOption('tenant'); $internalRun = $input->getOption('internal-run'); // Convert short role name to full Symfony role format $roleName = str_starts_with($roleInput, 'ROLE_') ? $roleInput : 'ROLE_' . $roleInput; $role = Role::tryFrom($roleName); if ($role === null) { $validRoles = array_map(static fn (Role $r) => str_replace('ROLE_', '', $r->value), Role::cases()); $io->error(sprintf( 'Invalid role "%s". Valid roles: %s', $roleInput, implode(', ', $validRoles) )); return Command::FAILURE; } // Resolve tenant from subdomain try { $tenantConfig = $this->tenantRegistry->getBySubdomain($tenantSubdomain); $tenantId = $tenantConfig->tenantId; } catch (TenantNotFoundException) { $availableTenants = array_map( static fn ($config) => $config->subdomain, $this->tenantRegistry->getAllConfigs() ); $io->error(sprintf( 'Tenant "%s" not found. Available tenants: %s', $tenantSubdomain, implode(', ', $availableTenants) )); return Command::FAILURE; } if (!$internalRun) { return $this->relaunchAgainstTenantDatabase( email: $email, password: $password, roleName: $roleName, firstName: $firstName, lastName: $lastName, schoolName: $schoolName, tenantConfig: $tenantConfig, io: $io, ); } $now = $this->clock->now(); // Check if user already exists $existingUser = $this->userRepository->findByEmail(new Email($email), $tenantId); if ($existingUser !== null) { $io->warning(sprintf('User with email "%s" already exists. Returning existing user.', $email)); $io->table( ['Property', 'Value'], [ ['User ID', (string) $existingUser->id], ['Email', $email], ['Password', $password], ['Role', $existingUser->role->value], ['Status', $existingUser->statut->value], ] ); return Command::SUCCESS; } // Create activated user using reconstitute to bypass domain validation $hashedPassword = $this->passwordHasher->hash($password); $user = User::reconstitute( id: UserId::generate(), email: new Email($email), roles: [$role], tenantId: $tenantId, schoolName: $schoolName, statut: StatutCompte::ACTIF, dateNaissance: null, createdAt: $now, hashedPassword: $hashedPassword, activatedAt: $now, consentementParental: null, firstName: $firstName, lastName: $lastName, ); $this->userRepository->save($user); $io->success('Test user created successfully!'); $io->table( ['Property', 'Value'], [ ['User ID', (string) $user->id], ['Email', $email], ['Password', $password], ['Role', $role->value], ['Tenant', $tenantSubdomain], ['School', $schoolName], ['Status', StatutCompte::ACTIF->value], ] ); return Command::SUCCESS; } private function relaunchAgainstTenantDatabase( string $email, string $password, string $roleName, string $firstName, string $lastName, string $schoolName, TenantConfig $tenantConfig, SymfonyStyle $io, ): int { $process = new Process( command: [ 'php', 'bin/console', 'app:dev:create-test-user', '--email=' . $email, '--password=' . $password, '--role=' . $roleName, '--firstName=' . $firstName, '--lastName=' . $lastName, '--school=' . $schoolName, '--tenant=' . $tenantConfig->subdomain, '--internal-run', ], cwd: $this->projectDir, env: [ ...getenv(), 'DATABASE_URL' => $tenantConfig->databaseUrl, ], timeout: 300, ); $process->run(static function (string $type, string $buffer) use ($io): void { $io->write($buffer); }); if ($process->isSuccessful()) { return Command::SUCCESS; } $io->error(sprintf( 'Failed to create test user in tenant database "%s".', $tenantConfig->subdomain, )); return Command::FAILURE; } }