Files
Coltura/tests/Module/Core/Domain/Security/AdminHeadcountGuardTest.php

128 lines
4.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Module\Core\Domain\Security;
use App\Module\Core\Domain\Entity\User;
use App\Module\Core\Domain\Exception\LastAdminProtectionException;
use App\Module\Core\Domain\Repository\UserRepositoryInterface;
use App\Module\Core\Domain\Security\AdminHeadcountGuard;
use PHPUnit\Framework\TestCase;
/**
* Tests unitaires du gardien d'invariant AdminHeadcountGuard.
*
* Aucun acces base de donnees : UserRepositoryInterface est mocke.
*
* @internal
*/
class AdminHeadcountGuardTest extends TestCase
{
// ---------------------------------------------------------------
// Demote (retrait du flag admin)
// ---------------------------------------------------------------
/**
* Autorise la demote quand il reste plus d'un admin (cas nominal).
*/
public function testAllowsDemotionWhenMoreThanOneAdmin(): void
{
$repo = $this->createMock(UserRepositoryInterface::class);
$repo->method('countAdmins')->willReturn(2);
$guard = new AdminHeadcountGuard($repo);
$user = new User();
$user->setUsername('alice');
// Aucune exception ne doit etre levee
$guard->ensureAtLeastOneAdminRemainsAfterDemotion($user);
$this->addToAssertionCount(1);
}
/**
* Bloque la demote quand il ne reste exactement qu'un admin.
*/
public function testBlocksDemotionWhenExactlyOneAdmin(): void
{
$repo = $this->createMock(UserRepositoryInterface::class);
$repo->method('countAdmins')->willReturn(1);
$guard = new AdminHeadcountGuard($repo);
$user = new User();
$user->setUsername('alice');
$this->expectException(LastAdminProtectionException::class);
$guard->ensureAtLeastOneAdminRemainsAfterDemotion($user);
}
/**
* Bloque la demote de facon defensive si le compteur est a 0 (etat incoherent).
*/
public function testBlocksDemotionDefensivelyWhenZeroAdmin(): void
{
$repo = $this->createMock(UserRepositoryInterface::class);
$repo->method('countAdmins')->willReturn(0);
$guard = new AdminHeadcountGuard($repo);
$user = new User();
$user->setUsername('alice');
$this->expectException(LastAdminProtectionException::class);
$guard->ensureAtLeastOneAdminRemainsAfterDemotion($user);
}
// ---------------------------------------------------------------
// Deletion (suppression de l'utilisateur)
// ---------------------------------------------------------------
/**
* Autorise la suppression quand il reste plus d'un admin (cas nominal).
*/
public function testAllowsDeletionWhenMoreThanOneAdmin(): void
{
$repo = $this->createMock(UserRepositoryInterface::class);
$repo->method('countAdmins')->willReturn(2);
$guard = new AdminHeadcountGuard($repo);
$user = new User();
$user->setUsername('bob');
// Aucune exception ne doit etre levee
$guard->ensureAtLeastOneAdminRemainsAfterDeletion($user);
$this->addToAssertionCount(1);
}
/**
* Bloque la suppression quand il ne reste exactement qu'un admin.
*/
public function testBlocksDeletionWhenExactlyOneAdmin(): void
{
$repo = $this->createMock(UserRepositoryInterface::class);
$repo->method('countAdmins')->willReturn(1);
$guard = new AdminHeadcountGuard($repo);
$user = new User();
$user->setUsername('bob');
$this->expectException(LastAdminProtectionException::class);
$guard->ensureAtLeastOneAdminRemainsAfterDeletion($user);
}
/**
* Bloque la suppression de facon defensive si le compteur est a 0 (etat incoherent).
*/
public function testBlocksDeletionDefensivelyWhenZeroAdmin(): void
{
$repo = $this->createMock(UserRepositoryInterface::class);
$repo->method('countAdmins')->willReturn(0);
$guard = new AdminHeadcountGuard($repo);
$user = new User();
$user->setUsername('bob');
$this->expectException(LastAdminProtectionException::class);
$guard->ensureAtLeastOneAdminRemainsAfterDeletion($user);
}
}