Skip to content

Commit 7a7c139

Browse files
authored
Add functional tests for schema validation and voter assertions (#74)
Cover seeDoctrineSchemaIsValid(), seeUserIsGranted() and dontSeeUserIsGranted() from Codeception/module-symfony#246. They pass once composer.lock points at a module-symfony revision containing that pull request. The application gains a UserVoter that grants USER_EDIT only on the account of the authenticated user, so the new assertions run against a real voter instead of a plain role check. It implements VoterInterface rather than extending Voter, whose abstract voteOnAttribute() signature is not the same across the Symfony versions covered by the branches of this repository.
1 parent f528508 commit 7a7c139

3 files changed

Lines changed: 69 additions & 0 deletions

File tree

src/Security/Voter/UserVoter.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Security\Voter;
6+
7+
use App\Entity\User;
8+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
9+
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
10+
11+
use function in_array;
12+
13+
/**
14+
* Grants USER_EDIT only on the account of the authenticated user.
15+
*
16+
* Implements VoterInterface instead of extending Voter, whose abstract
17+
* voteOnAttribute() signature is not the same across the Symfony versions
18+
* covered by the branches of this repository.
19+
*/
20+
final class UserVoter implements VoterInterface
21+
{
22+
public const EDIT = 'USER_EDIT';
23+
24+
/**
25+
* @param mixed[] $attributes
26+
*/
27+
public function vote(TokenInterface $token, mixed $subject, array $attributes, mixed ...$args): int
28+
{
29+
if (!in_array(self::EDIT, $attributes, true) || !$subject instanceof User) {
30+
return self::ACCESS_ABSTAIN;
31+
}
32+
33+
$user = $token->getUser();
34+
35+
return $user instanceof User && $user->getUserIdentifier() === $subject->getUserIdentifier()
36+
? self::ACCESS_GRANTED
37+
: self::ACCESS_DENIED;
38+
}
39+
}

tests/Functional/DoctrineCest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ public function seeNumRecords(FunctionalTester $I)
4444
$I->seeNumRecords(1, User::class);
4545
}
4646

47+
public function seeDoctrineSchemaIsValid(FunctionalTester $I): void
48+
{
49+
$I->seeDoctrineSchemaIsValid();
50+
}
51+
4752
public function queryCountAssertions(FunctionalTester $I): void
4853
{
4954
$I->amOnPage('/run-queries');

tests/Functional/SecurityCest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace App\Tests\Functional;
66

77
use App\Entity\User;
8+
use App\Security\Voter\UserVoter;
89
use App\Tests\Support\FunctionalTester;
910

1011
final class SecurityCest
@@ -70,6 +71,30 @@ public function seeUserHasRoles(FunctionalTester $I)
7071
$I->seeUserHasRoles(['ROLE_USER', 'ROLE_CUSTOMER']);
7172
}
7273

74+
public function seeUserIsGranted(FunctionalTester $I): void
75+
{
76+
$user = $I->grabEntityFromRepository(User::class, [
77+
'email' => 'john_doe@gmail.com',
78+
]);
79+
$I->amLoggedInAs($user);
80+
$I->amOnPage('/');
81+
82+
$I->seeUserIsGranted('ROLE_CUSTOMER');
83+
$I->seeUserIsGranted(UserVoter::EDIT, $user);
84+
}
85+
86+
public function dontSeeUserIsGranted(FunctionalTester $I): void
87+
{
88+
$user = $I->grabEntityFromRepository(User::class, [
89+
'email' => 'john_doe@gmail.com',
90+
]);
91+
$I->amLoggedInAs($user);
92+
$I->amOnPage('/');
93+
94+
$I->dontSeeUserIsGranted('ROLE_ADMIN');
95+
$I->dontSeeUserIsGranted(UserVoter::EDIT, User::create('jane_doe@gmail.com', '123456'));
96+
}
97+
7398
public function seeUserPasswordDoesNotNeedRehash(FunctionalTester $I)
7499
{
75100
$user = $I->grabEntityFromRepository(User::class, [

0 commit comments

Comments
 (0)