. */ declare(strict_types=1); namespace App\Tests\Services\OAuth; use App\Services\OAuth\OAuthClientAdminManager; use Doctrine\ORM\EntityManagerInterface; use League\Bundle\OAuth2ServerBundle\Manager\AccessTokenManagerInterface; use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface; use League\Bundle\OAuth2ServerBundle\Model\AccessToken; use League\Bundle\OAuth2ServerBundle\Model\Client; use League\Bundle\OAuth2ServerBundle\ValueObject\Grant; use League\Bundle\OAuth2ServerBundle\ValueObject\RedirectUri; use League\Bundle\OAuth2ServerBundle\ValueObject\Scope; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; final class OAuthClientAdminManagerTest extends KernelTestCase { public function testListIncludesLiveTokenCountAndDeleteRemovesClientAndTokens(): void { self::bootKernel(); $container = static::getContainer(); $clientManager = $container->get(ClientManagerInterface::class); $accessTokenManager = $container->get(AccessTokenManagerInterface::class); $manager = $container->get(OAuthClientAdminManager::class); $client = new Client('Admin Manager Test App', 'test-admin-mgr-'.bin2hex(random_bytes(8)), null); $client->setRedirectUris(new RedirectUri('https://client.example.invalid/callback')); $client->setGrants(new Grant('authorization_code'), new Grant('refresh_token')); $client->setScopes(new Scope('read_only')); $client->setActive(true); $clientManager->save($client); $accessToken = new AccessToken( 'at-'.bin2hex(random_bytes(16)), new \DateTimeImmutable('+1 hour'), $client, 'some-user', [new Scope('read_only')], ); $accessTokenManager->save($accessToken); $rows = $manager->listClientsWithLiveTokenCounts(); $row = self::findRowForClient($rows, $client->getIdentifier()); self::assertNotNull($row); self::assertSame(1, $row['liveTokenCount']); self::assertTrue($manager->deleteClient($client->getIdentifier())); self::assertNull($clientManager->find($client->getIdentifier())); // AccessTokenManager::find() uses EntityManager::find(), which checks the identity map before // hitting the DB - without clearing it here, it would just hand back the same in-memory object // we already hold, masking whether the row was actually deleted. $container->get(EntityManagerInterface::class)->clear(); self::assertNull($accessTokenManager->find($accessToken->getIdentifier())); self::assertFalse($manager->deleteClient($client->getIdentifier())); } /** * @param list $rows * @return array{client: \League\Bundle\OAuth2ServerBundle\Model\ClientInterface, liveTokenCount: int}|null */ private static function findRowForClient(array $rows, string $identifier): ?array { foreach ($rows as $row) { if ($row['client']->getIdentifier() === $identifier) { return $row; } } return null; } }