cleanupTestData(); parent::tearDown(); } // --- Tests lecture collection --- public function testListUsersAsAdminReturns200(): void { $client = $this->authenticatedClient('admin', 'admin'); $response = $client->request('GET', '/api/users'); self::assertResponseIsSuccessful(); $data = $response->toArray(); self::assertArrayHasKey('member', $data); // Au moins 3 users fixture. self::assertGreaterThanOrEqual(3, $data['totalItems']); } public function testListUsersAsUserWithViewPermissionReturns200(): void { // Un non-admin portant core.users.view doit pouvoir lister les users. $credentials = $this->createUserWithPermission('core.users.view'); $client = $this->authenticatedClient($credentials['username'], $credentials['password']); $client->request('GET', '/api/users'); self::assertResponseIsSuccessful(); } public function testListUsersAsStandardUserReturns403(): void { // alice n'a aucune permission RBAC : acces refuse. $client = $this->authenticatedClient('alice', 'alice'); $client->request('GET', '/api/users'); self::assertResponseStatusCodeSame(403); } // --- Tests suppression --- public function testDeleteNonAdminUserAsAdminReturns204(): void { // Confirme que la suppression d'un user non-admin fonctionne. $em = $this->getEm(); /** @var UserPasswordHasherInterface $hasher */ $hasher = self::getContainer()->get(UserPasswordHasherInterface::class); $target = new User(); $target->setUsername('test_deletable_user'); $target->setIsAdmin(false); $target->setPassword($hasher->hashPassword($target, 'secret')); $em->persist($target); $em->flush(); $targetId = $target->getId(); $em->clear(); $client = $this->authenticatedClient('admin', 'admin'); $client->request('DELETE', '/api/users/'.$targetId); self::assertResponseStatusCodeSame(204); // Verification cote base : le user n'existe plus. $em = $this->getEm(); $em->clear(); self::assertNull($em->getRepository(User::class)->find($targetId)); } public function testDeleteSecondAdminReturns204(): void { // Quand il y a 2 admins, supprimer le second est autorise (garde non declenchee). $em = $this->getEm(); /** @var UserPasswordHasherInterface $hasher */ $hasher = self::getContainer()->get(UserPasswordHasherInterface::class); $secondAdmin = new User(); $secondAdmin->setUsername('test_second_admin'); $secondAdmin->setIsAdmin(true); $secondAdmin->setPassword($hasher->hashPassword($secondAdmin, 'secret')); $em->persist($secondAdmin); $em->flush(); $secondAdminId = $secondAdmin->getId(); $em->clear(); // Auth en tant qu'admin fixture, supprime le second admin. $client = $this->authenticatedClient('admin', 'admin'); $client->request('DELETE', '/api/users/'.$secondAdminId); self::assertResponseStatusCodeSame(204); $em = $this->getEm(); $em->clear(); self::assertNull($em->getRepository(User::class)->find($secondAdminId)); } public function testDeleteLastAdminReturns400(): void { // Scenario "dernier admin global" : un seul admin existe (fixture admin). // Il tente de se supprimer lui-meme -> garde activee -> 400. $em = $this->getEm(); /** @var null|User $fixtureAdmin */ $fixtureAdmin = $em->getRepository(User::class)->findOneBy(['username' => 'admin']); self::assertNotNull($fixtureAdmin, 'L\'user admin fixture doit exister.'); $fixtureAdminId = $fixtureAdmin->getId(); // Garantit qu'il n'y a qu'un seul admin au moment du test : // s'assure que test_second_admin n'existe pas (tearDown le purge, mais // soyons defensifs si un test precedent n'a pas nettoye). $em->createQuery( 'DELETE FROM '.User::class.' u WHERE u.username LIKE :prefix AND u.username != :admin' )->setParameters(['prefix' => 'test_%', 'admin' => 'admin'])->execute(); // Auth en tant que l'admin fixture et tente l'auto-suppression. $client = $this->authenticatedClient('admin', 'admin'); $response = $client->request('DELETE', '/api/users/'.$fixtureAdminId); self::assertResponseStatusCodeSame(400); // Verification cote base : l'admin fixture doit toujours exister. $em = $this->getEm(); $em->clear(); self::assertNotNull( $em->getRepository(User::class)->find($fixtureAdminId), 'Le dernier admin ne doit PAS etre supprime.', ); } public function testDeleteAsStandardUserReturns403(): void { $em = $this->getEm(); /** @var null|User $alice */ $alice = $em->getRepository(User::class)->findOneBy(['username' => 'alice']); self::assertNotNull($alice); /** @var null|User $bob */ $bob = $em->getRepository(User::class)->findOneBy(['username' => 'bob']); self::assertNotNull($bob); // alice sans permission ne peut pas supprimer bob. $client = $this->authenticatedClient('alice', 'alice'); $client->request('DELETE', '/api/users/'.$bob->getId()); self::assertResponseStatusCodeSame(403); } /** * Purge les entites de test creees par cette suite. * Ne touche JAMAIS aux fixtures (admin / alice / bob). */ private function cleanupTestData(): void { $em = $this->getEm(); // Purge des users jetables crees par les tests (y compris testuser_ de createUserWithPermission). $em->createQuery( 'DELETE FROM '.User::class.' u WHERE u.username LIKE :prefix' )->setParameter('prefix', self::TEST_USER_PREFIX.'%')->execute(); // Purge des roles jetables crees par createUserWithPermission. $em->createQuery( 'DELETE FROM '.Role::class.' r WHERE r.code LIKE :prefix' )->setParameter('prefix', self::TEST_ROLE_PREFIX.'%')->execute(); } }