error(sprintf('app:seed-e2e est refuse en environnement "%s". Autorise uniquement en dev/test.', $env)); return Command::FAILURE; } $userRole = $this->roleRepository->findByCode(SystemRoles::USER_CODE); if (null === $userRole) { $io->error(sprintf( 'Le role systeme "%s" est introuvable. Lance les migrations + fixtures ou `app:sync-permissions` avant ce seed.', SystemRoles::USER_CODE, )); return Command::FAILURE; } $defaultSite = $this->siteProvider->findByName(self::DEFAULT_SITE_NAME); // Pas de fail fatal si le site manque : les tests sidebar/login // n'en dependent pas. Les tests sites-scope-bypass (a venir) le feront. if (null === $defaultSite) { $io->note(sprintf( 'Site "%s" absent : les personas seront crees sans site. Lance `make fixtures` si tu as besoin des sites.', self::DEFAULT_SITE_NAME, )); } $this->wipeExistingE2EUsers($io); foreach ($this->personasDefinition() as $persona) { $user = new User(); $user->setUsername($persona['username']); $user->setPassword($this->passwordHasher->hashPassword($user, self::SHARED_PASSWORD)); $user->setIsAdmin($persona['isAdmin']); $user->addRbacRole($userRole); foreach ($persona['permissions'] as $code) { $permission = $this->permissionRepository->findByCode($code); if (null === $permission) { throw new RuntimeException(sprintf( 'Permission "%s" introuvable en base. Lance `app:sync-permissions` avant `app:seed-e2e`.', $code, )); } $user->addDirectPermission($permission); } if (null !== $defaultSite && 'e2e.user-nothing' !== $persona['username']) { // user-nothing reste sans site pour pouvoir tester un flow // "aucune permission et aucun site". $user->addSite($defaultSite); $user->setCurrentSite($defaultSite); } $this->userRepository->save($user); $io->text(sprintf( ' - %s (admin=%s, permissions=%d)', $persona['username'], $persona['isAdmin'] ? 'oui' : 'non', count($persona['permissions']), )); } $io->success(sprintf('%d personas E2E seedes.', count($this->personasDefinition()))); return Command::SUCCESS; } private function wipeExistingE2EUsers(SymfonyStyle $io): void { $removed = 0; foreach ($this->personasDefinition() as $persona) { $existing = $this->userRepository->findByUsername($persona['username']); if (null === $existing) { continue; } $this->em->remove($existing); ++$removed; } if ($removed > 0) { $this->em->flush(); $io->text(sprintf('Nettoyage : %d users E2E supprimes.', $removed)); } } /** * Liste des personas — source back, miroir de * `frontend/tests/e2e/_fixtures/personas.ts`. * * @return list}> */ private function personasDefinition(): array { return [ [ 'username' => self::E2E_USERNAME_PREFIX.'super-admin', 'isAdmin' => true, 'permissions' => [], ], [ 'username' => self::E2E_USERNAME_PREFIX.'user-full', 'isAdmin' => false, 'permissions' => [ 'core.users.view', 'core.users.manage', 'core.roles.view', 'core.roles.manage', 'core.audit_log.view', 'sites.view', 'sites.manage', 'sites.bypass_scope', 'catalog.categories.view', 'catalog.categories.manage', // Commercial — Repertoire clients (M1). Mappe ici sur le // persona "tout" en attendant les vrais roles metier // (bureau/compta/commerciale/usine) seedes par ERP-74. // Miroir de frontend/tests/e2e/_fixtures/personas.ts. 'commercial.clients.view', 'commercial.clients.manage', 'commercial.clients.accounting.view', 'commercial.clients.accounting.manage', 'commercial.clients.archive', ], ], [ 'username' => self::E2E_USERNAME_PREFIX.'user-readonly', 'isAdmin' => false, 'permissions' => [ 'core.users.view', 'core.roles.view', 'core.audit_log.view', 'sites.view', ], ], [ 'username' => self::E2E_USERNAME_PREFIX.'user-users-only', 'isAdmin' => false, 'permissions' => ['core.users.view', 'core.users.manage'], ], [ 'username' => self::E2E_USERNAME_PREFIX.'user-audit-only', 'isAdmin' => false, 'permissions' => ['core.audit_log.view'], ], [ 'username' => self::E2E_USERNAME_PREFIX.'user-nothing', 'isAdmin' => false, 'permissions' => [], ], ]; } }