Files
Coltura/tests/Module/Core/Api/UserApiTest.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();
}
}