196 lines
7.0 KiB
PHP
196 lines
7.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Module\Core\Api;
|
|
|
|
use App\Module\Core\Domain\Entity\Role;
|
|
use App\Module\Core\Domain\Entity\User;
|
|
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
|
|
|
/**
|
|
* Tests fonctionnels de l'exposition API Platform de l'entite User.
|
|
*
|
|
* Strategie :
|
|
* - Les fixtures chargent 3 users : admin (is_admin=true), alice, bob.
|
|
* - Les tests de lecture s'appuient sur les fixtures sans les modifier.
|
|
* - Les tests de suppression et de guard "dernier admin" creent des users
|
|
* additionnels via EntityManager, purges en tearDown.
|
|
* - On ne supprime JAMAIS les users fixture (admin / alice / bob).
|
|
*
|
|
* @internal
|
|
*/
|
|
final class UserApiTest extends AbstractApiTestCase
|
|
{
|
|
private const TEST_USER_PREFIX = 'test_';
|
|
private const TEST_ROLE_PREFIX = 'test_';
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
$this->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();
|
|
}
|
|
}
|