L'élève avait accès à ses compétences mais pas à ses notes numériques. Cette fonctionnalité lui donne une vue complète de sa progression scolaire avec moyennes par matière, détail par évaluation, statistiques de classe, et un mode "découverte" pour révéler ses notes à son rythme (FR14, FR15). Les notes ne sont visibles qu'après publication par l'enseignant, ce qui garantit que l'élève les découvre avant ses parents (délai 24h story 6.7).
115 lines
3.5 KiB
PHP
115 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Unit\Scolarite\Infrastructure\Security;
|
|
|
|
use App\Administration\Domain\Model\User\Role;
|
|
use App\Administration\Domain\Model\User\UserId;
|
|
use App\Administration\Infrastructure\Security\SecurityUser;
|
|
use App\Scolarite\Infrastructure\Security\GradeParentVoter;
|
|
use App\Shared\Domain\Tenant\TenantId;
|
|
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 GradeParentVoterTest extends TestCase
|
|
{
|
|
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
|
|
|
|
private GradeParentVoter $voter;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->voter = new GradeParentVoter();
|
|
}
|
|
|
|
#[Test]
|
|
public function itAbstainsForUnrelatedAttributes(): void
|
|
{
|
|
$token = $this->tokenWithSecurityUser(Role::PARENT->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, [GradeParentVoter::VIEW]);
|
|
|
|
self::assertSame(Voter::ACCESS_DENIED, $result);
|
|
}
|
|
|
|
#[Test]
|
|
public function itDeniesAccessToNonSecurityUserInstances(): void
|
|
{
|
|
$user = $this->createMock(UserInterface::class);
|
|
$user->method('getRoles')->willReturn([Role::PARENT->value]);
|
|
|
|
$token = $this->createMock(TokenInterface::class);
|
|
$token->method('getUser')->willReturn($user);
|
|
|
|
$result = $this->voter->vote($token, null, [GradeParentVoter::VIEW]);
|
|
|
|
self::assertSame(Voter::ACCESS_DENIED, $result);
|
|
}
|
|
|
|
#[Test]
|
|
public function itGrantsViewToParent(): void
|
|
{
|
|
$token = $this->tokenWithSecurityUser(Role::PARENT->value);
|
|
|
|
$result = $this->voter->vote($token, null, [GradeParentVoter::VIEW]);
|
|
|
|
self::assertSame(Voter::ACCESS_GRANTED, $result);
|
|
}
|
|
|
|
#[Test]
|
|
#[DataProvider('nonParentRolesProvider')]
|
|
public function itDeniesViewToNonParentRoles(string $role): void
|
|
{
|
|
$token = $this->tokenWithSecurityUser($role);
|
|
|
|
$result = $this->voter->vote($token, null, [GradeParentVoter::VIEW]);
|
|
|
|
self::assertSame(Voter::ACCESS_DENIED, $result);
|
|
}
|
|
|
|
/** @return iterable<string, array{string}> */
|
|
public static function nonParentRolesProvider(): 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];
|
|
yield 'ELEVE' => [Role::ELEVE->value];
|
|
}
|
|
|
|
private function tokenWithSecurityUser(
|
|
string $role,
|
|
string $userId = '550e8400-e29b-41d4-a716-446655440001',
|
|
): TokenInterface {
|
|
$securityUser = new SecurityUser(
|
|
UserId::fromString($userId),
|
|
'test@example.com',
|
|
'hashed_password',
|
|
TenantId::fromString(self::TENANT_ID),
|
|
[$role],
|
|
);
|
|
|
|
$token = $this->createMock(TokenInterface::class);
|
|
$token->method('getUser')->willReturn($securityUser);
|
|
|
|
return $token;
|
|
}
|
|
}
|