Files
myep-team/tests/Security/Voter/UploadVoterTest.php
T
2026-10-01 10:07:05 +02:00

132 lines
4.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Security\Voter;
use App\Entity\SeasonAssignment;
use App\Entity\Teamer;
use App\Entity\Upload;
use App\Entity\User;
use App\Security\Voter\UploadVoter;
use PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
/**
* A document the office issues for one teamer is the first upload in this app that belongs to a
* teamer without being owned by them, so the rules around it are the ones worth pinning: the
* intended teamer gets in, every other teamer does not, and the library stays as open as it was.
*/
class UploadVoterTest extends TestCase
{
private Security&Stub $security;
protected function setUp(): void
{
$this->security = $this->createStub(Security::class);
$this->security
->method('isGranted')
->willReturnCallback(static fn (string $role): bool => 'ROLE_TEAMER' === $role)
;
}
public function testATeamerMayDownloadASeasonDocumentIssuedToThem(): void
{
$teamer = new Teamer();
$this->assertSame(
VoterInterface::ACCESS_GRANTED,
$this->vote(UploadVoter::DOWNLOAD, $this->seasonDocument($teamer), $teamer)
);
}
public function testATeamerMayNotDownloadAnotherTeamersSeasonDocument(): void
{
$this->assertSame(
VoterInterface::ACCESS_DENIED,
$this->vote(UploadVoter::DOWNLOAD, $this->seasonDocument(new Teamer()), new Teamer())
);
}
public function testASeasonDocumentOnAnUnstaffedAssignmentReachesNoTeamer(): void
{
$upload = (new Upload())
->setType(Upload::TYPE_SEASON_DOCUMENT_OUT)
->setOwner(new User())
;
$upload->setSeasonAssignment(new SeasonAssignment());
$this->assertSame(
VoterInterface::ACCESS_DENIED,
$this->vote(UploadVoter::DOWNLOAD, $upload, new Teamer())
);
}
/**
* Downloading is all the new rule grants: the file stays the office's, so the teamer it was
* issued to may not delete it or reach it through any other attribute.
*/
#[\PHPUnit\Framework\Attributes\DataProvider('otherAttributes')]
public function testTheNewRuleGrantsNothingButDownloading(string $attribute): void
{
$teamer = new Teamer();
$this->assertSame(
VoterInterface::ACCESS_DENIED,
$this->vote($attribute, $this->seasonDocument($teamer), $teamer)
);
}
/**
* @return array<string, array{string}>
*/
public static function otherAttributes(): array
{
return [
'viewing' => [UploadVoter::VIEW],
'deleting' => [UploadVoter::DELETE],
];
}
/**
* The library is deliberately open to all staff, which is exactly why an individually issued
* document must never carry its type - so the blanket rule has to stay untouched.
*/
public function testTheLibraryStaysReadableByEveryTeamer(): void
{
$upload = (new Upload())
->setType(Upload::TYPE_DOCUMENT)
->setOwner(new User())
;
$this->assertSame(
VoterInterface::ACCESS_GRANTED,
$this->vote(UploadVoter::DOWNLOAD, $upload, new Teamer())
);
}
private function seasonDocument(Teamer $teamer): Upload
{
$seasonAssignment = (new SeasonAssignment())->setTeamer($teamer);
$upload = (new Upload())
->setType(Upload::TYPE_SEASON_DOCUMENT_OUT)
->setOwner(new User())
;
$upload->setSeasonAssignment($seasonAssignment);
return $upload;
}
private function vote(string $attribute, Upload $upload, Teamer $teamer): int
{
$token = $this->createStub(TokenInterface::class);
$token->method('getUser')->willReturn((new User())->setTeamer($teamer));
return (new UploadVoter($this->security))->vote($token, $upload, [$attribute]);
}
}