86 lines
2.9 KiB
PHP
86 lines
2.9 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Doctrine;
|
|
|
|
use ApiPlatform\Doctrine\Orm\Util\QueryNameGenerator;
|
|
use App\Doctrine\ScenarioOwnerExtension;
|
|
use App\Entity\Scenario;
|
|
use App\Entity\User;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use PHPUnit\Framework\MockObject\Stub;
|
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
use Symfony\Bundle\SecurityBundle\Security;
|
|
|
|
/**
|
|
* Unit-covers the AbstractOwnerScopeExtension base via its simplest concrete
|
|
* subclass (Scenario owns the `owner` column directly). The HTTP-level
|
|
* functional tests always run authenticated against their own resource, so the
|
|
* base's two early-return guards (wrong resource class, anonymous user) are only
|
|
* reachable here in isolation.
|
|
*/
|
|
final class ScenarioOwnerExtensionTest extends KernelTestCase
|
|
{
|
|
private function queryBuilder(): \Doctrine\ORM\QueryBuilder
|
|
{
|
|
self::bootKernel();
|
|
/** @var EntityManagerInterface $em */
|
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
return $em->createQueryBuilder()
|
|
->select('s')
|
|
->from(Scenario::class, 's');
|
|
}
|
|
|
|
private function extensionWithUser(?object $user): ScenarioOwnerExtension
|
|
{
|
|
/** @var Security&Stub $security */
|
|
$security = $this->createStub(Security::class);
|
|
$security->method('getUser')->willReturn($user);
|
|
|
|
return new ScenarioOwnerExtension($security);
|
|
}
|
|
|
|
public function testAddsOwnerFilterForAnAuthenticatedUserOnItsOwnResource(): void
|
|
{
|
|
$qb = $this->queryBuilder();
|
|
$extension = $this->extensionWithUser(new User());
|
|
|
|
$extension->applyToCollection($qb, new QueryNameGenerator(), Scenario::class);
|
|
|
|
self::assertStringContainsStringIgnoringCase(
|
|
'.owner = :',
|
|
(string) $qb->getDQL(),
|
|
'An authenticated read of its own resource must be scoped to the owner.',
|
|
);
|
|
}
|
|
|
|
public function testDoesNothingForAResourceItDoesNotGuard(): void
|
|
{
|
|
$qb = $this->queryBuilder();
|
|
$extension = $this->extensionWithUser(new User());
|
|
|
|
// Bucket is guarded by a different extension — this one must no-op.
|
|
$extension->applyToCollection($qb, new QueryNameGenerator(), \App\Entity\Bucket::class);
|
|
|
|
self::assertStringNotContainsString(
|
|
'owner',
|
|
(string) $qb->getDQL(),
|
|
'The extension must not touch queries for a resource it does not guard.',
|
|
);
|
|
}
|
|
|
|
public function testDoesNothingForAnAnonymousRequest(): void
|
|
{
|
|
$qb = $this->queryBuilder();
|
|
$extension = $this->extensionWithUser(null);
|
|
|
|
$extension->applyToItem($qb, new QueryNameGenerator(), Scenario::class, ['id' => 'x']);
|
|
|
|
self::assertStringNotContainsString(
|
|
'owner',
|
|
(string) $qb->getDQL(),
|
|
'With no authenticated user the extension must not add an owner filter '
|
|
.'(it is a fail-safe behind the resource-level ROLE_USER wall).',
|
|
);
|
|
}
|
|
}
|