Files
Classeo/backend/tests/Unit/Administration/Infrastructure/Security/ClassVoterTest.php
Mathias STRASSER 44ebe5e511 feat: Liaison parents-enfants avec gestion des tuteurs
Les parents doivent pouvoir suivre la scolarité de leurs enfants (notes,
emploi du temps, devoirs). Cela nécessite un lien formalisé entre le
compte parent et le compte élève, géré par les administrateurs.

Le lien est établi soit manuellement via l'interface d'administration,
soit automatiquement lors de l'activation du compte parent lorsque
l'invitation inclut un élève cible. Ce lien conditionne l'accès aux
données scolaires de l'enfant (autorisations vérifiées par un voter
dédié).
2026-02-12 08:38:19 +01:00

209 lines
5.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Unit\Administration\Infrastructure\Security;
use App\Administration\Domain\Model\User\Role;
use App\Administration\Infrastructure\Api\Resource\ClassResource;
use App\Administration\Infrastructure\Security\ClassVoter;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
final class ClassVoterTest extends TestCase
{
private ClassVoter $voter;
protected function setUp(): void
{
$this->voter = new ClassVoter();
}
#[Test]
public function itAbstainsForUnrelatedAttributes(): void
{
$token = $this->tokenWithRole(Role::ADMIN->value);
$result = $this->voter->vote($token, null, ['SOME_OTHER_ATTRIBUTE']);
self::assertSame(Voter::ACCESS_ABSTAIN, $result);
}
#[Test]
public function itDeniesAccessToUnauthenticatedUsers(): void
{
$token = $this->createMock(TokenInterface::class);
$token->method('getUser')->willReturn(null);
$result = $this->voter->vote($token, null, [ClassVoter::VIEW]);
self::assertSame(Voter::ACCESS_DENIED, $result);
}
// --- VIEW ---
#[Test]
#[DataProvider('viewAllowedRolesProvider')]
public function itGrantsViewToStaffRoles(string $role): void
{
$token = $this->tokenWithRole($role);
$result = $this->voter->vote($token, null, [ClassVoter::VIEW]);
self::assertSame(Voter::ACCESS_GRANTED, $result);
}
/**
* @return iterable<string, array{string}>
*/
public static function viewAllowedRolesProvider(): iterable
{
yield 'SUPER_ADMIN' => [Role::SUPER_ADMIN->value];
yield 'ADMIN' => [Role::ADMIN->value];
yield 'PROF' => [Role::PROF->value];
yield 'VIE_SCOLAIRE' => [Role::VIE_SCOLAIRE->value];
yield 'SECRETARIAT' => [Role::SECRETARIAT->value];
}
#[Test]
#[DataProvider('viewDeniedRolesProvider')]
public function itDeniesViewToNonStaffRoles(string $role): void
{
$token = $this->tokenWithRole($role);
$result = $this->voter->vote($token, null, [ClassVoter::VIEW]);
self::assertSame(Voter::ACCESS_DENIED, $result);
}
/**
* @return iterable<string, array{string}>
*/
public static function viewDeniedRolesProvider(): iterable
{
yield 'PARENT' => [Role::PARENT->value];
yield 'ELEVE' => [Role::ELEVE->value];
}
#[Test]
public function itSupportsViewWithClassResourceSubject(): void
{
$token = $this->tokenWithRole(Role::ADMIN->value);
$subject = new ClassResource();
$result = $this->voter->vote($token, $subject, [ClassVoter::VIEW]);
self::assertSame(Voter::ACCESS_GRANTED, $result);
}
// --- CREATE ---
#[Test]
#[DataProvider('adminRolesProvider')]
public function itGrantsCreateToAdminRoles(string $role): void
{
$token = $this->tokenWithRole($role);
$result = $this->voter->vote($token, null, [ClassVoter::CREATE]);
self::assertSame(Voter::ACCESS_GRANTED, $result);
}
#[Test]
#[DataProvider('nonAdminRolesProvider')]
public function itDeniesCreateToNonAdminRoles(string $role): void
{
$token = $this->tokenWithRole($role);
$result = $this->voter->vote($token, null, [ClassVoter::CREATE]);
self::assertSame(Voter::ACCESS_DENIED, $result);
}
// --- EDIT ---
#[Test]
#[DataProvider('adminRolesProvider')]
public function itGrantsEditToAdminRoles(string $role): void
{
$token = $this->tokenWithRole($role);
$result = $this->voter->vote($token, new ClassResource(), [ClassVoter::EDIT]);
self::assertSame(Voter::ACCESS_GRANTED, $result);
}
#[Test]
#[DataProvider('nonAdminRolesProvider')]
public function itDeniesEditToNonAdminRoles(string $role): void
{
$token = $this->tokenWithRole($role);
$result = $this->voter->vote($token, new ClassResource(), [ClassVoter::EDIT]);
self::assertSame(Voter::ACCESS_DENIED, $result);
}
// --- DELETE ---
#[Test]
#[DataProvider('adminRolesProvider')]
public function itGrantsDeleteToAdminRoles(string $role): void
{
$token = $this->tokenWithRole($role);
$result = $this->voter->vote($token, new ClassResource(), [ClassVoter::DELETE]);
self::assertSame(Voter::ACCESS_GRANTED, $result);
}
#[Test]
#[DataProvider('nonAdminRolesProvider')]
public function itDeniesDeleteToNonAdminRoles(string $role): void
{
$token = $this->tokenWithRole($role);
$result = $this->voter->vote($token, new ClassResource(), [ClassVoter::DELETE]);
self::assertSame(Voter::ACCESS_DENIED, $result);
}
// --- Data Providers ---
/**
* @return iterable<string, array{string}>
*/
public static function adminRolesProvider(): iterable
{
yield 'SUPER_ADMIN' => [Role::SUPER_ADMIN->value];
yield 'ADMIN' => [Role::ADMIN->value];
}
/**
* @return iterable<string, array{string}>
*/
public static function nonAdminRolesProvider(): iterable
{
yield 'PROF' => [Role::PROF->value];
yield 'VIE_SCOLAIRE' => [Role::VIE_SCOLAIRE->value];
yield 'SECRETARIAT' => [Role::SECRETARIAT->value];
yield 'PARENT' => [Role::PARENT->value];
yield 'ELEVE' => [Role::ELEVE->value];
}
private function tokenWithRole(string $role): TokenInterface
{
$user = $this->createMock(UserInterface::class);
$user->method('getRoles')->willReturn([$role]);
$token = $this->createMock(TokenInterface::class);
$token->method('getUser')->willReturn($user);
return $token;
}
}