Skip to content

Commit 5cd96dd

Browse files
committed
CleanUpOldSessionTokens
1 parent 774f75d commit 5cd96dd

File tree

4 files changed

+64
-110
lines changed

4 files changed

+64
-110
lines changed

src/Domain/Identity/Command/CleanUpOldSessionTokens.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PhpList\Core\Domain\Identity\Command;
66

7+
use Doctrine\ORM\EntityManagerInterface;
78
use PhpList\Core\Domain\Identity\Repository\AdministratorTokenRepository;
89
use Symfony\Component\Console\Attribute\AsCommand;
910
use Symfony\Component\Console\Command\Command;
@@ -18,11 +19,13 @@
1819
class CleanUpOldSessionTokens extends Command
1920
{
2021
private AdministratorTokenRepository $tokenRepository;
22+
private EntityManagerInterface $entityManager;
2123

22-
public function __construct(AdministratorTokenRepository $tokenRepository)
24+
public function __construct(AdministratorTokenRepository $tokenRepository, EntityManagerInterface $entityManager)
2325
{
2426
parent::__construct();
2527
$this->tokenRepository = $tokenRepository;
28+
$this->entityManager = $entityManager;
2629
}
2730

2831
/**
@@ -31,7 +34,17 @@ public function __construct(AdministratorTokenRepository $tokenRepository)
3134
protected function execute(InputInterface $input, OutputInterface $output): int
3235
{
3336
try {
34-
$deletedCount = $this->tokenRepository->removeExpired();
37+
$expiredTokens = $this->tokenRepository->getExpired();
38+
39+
$deletedCount = 0;
40+
41+
foreach ($expiredTokens as $token) {
42+
$this->entityManager->remove($token);
43+
$deletedCount++;
44+
}
45+
46+
$this->entityManager->flush();
47+
3548
$output->writeln(sprintf('Successfully removed %d expired session token(s).', $deletedCount));
3649
} catch (Throwable $throwable) {
3750
$output->writeln(sprintf('Error removing expired session tokens: %s', $throwable->getMessage()));

src/Domain/Identity/Repository/AdministratorTokenRepository.php

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use DateTimeImmutable;
99
use DateTimeZone;
1010
use Doctrine\Common\Collections\Criteria;
11+
use Exception;
1112
use PhpList\Core\Domain\Common\Repository\AbstractRepository;
1213
use PhpList\Core\Domain\Common\Repository\CursorPaginationTrait;
1314
use PhpList\Core\Domain\Common\Repository\Interfaces\PaginatableRepositoryInterface;
@@ -45,31 +46,19 @@ public function findOneUnexpiredByKey(string $key): ?AdministratorToken
4546
}
4647

4748
/**
48-
* Removes all expired tokens.
49+
* Get all expired tokens.
4950
*
50-
* This method should be called regularly to clean up the tokens.
51-
*
52-
* @return int the number of removed tokens
51+
* @return AdministratorToken[]
52+
* @throws Exception
5353
*/
54-
public function removeExpired(): int
54+
public function getExpired(): array
5555
{
5656
$now = new DateTimeImmutable('now', new DateTimeZone('UTC'));
5757

58-
$expiredTokens = $this->createQueryBuilder('at')
58+
return $this->createQueryBuilder('at')
5959
->where('at.expiry <= :date')
6060
->setParameter('date', $now)
6161
->getQuery()
6262
->getResult();
63-
64-
$deletedCount = 0;
65-
66-
foreach ($expiredTokens as $token) {
67-
$this->getEntityManager()->remove($token);
68-
$deletedCount++;
69-
}
70-
71-
$this->getEntityManager()->flush();
72-
73-
return $deletedCount;
7463
}
7564
}

tests/Integration/Domain/Identity/Repository/AdministratorTokenRepositoryTest.php

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -122,56 +122,6 @@ public function testFindOneUnexpiredByKeyNotFindsUnexpiredTokenWithNonMatchingKe
122122
self::assertNull($model);
123123
}
124124

125-
public function testRemoveExpiredRemovesExpiredToken()
126-
{
127-
$this->loadFixtures([DetachedAdministratorTokenFixture::class]);
128-
129-
$idOfExpiredToken = 1;
130-
$this->repository->removeExpired();
131-
132-
$token = $this->repository->find($idOfExpiredToken);
133-
self::assertNull($token);
134-
}
135-
136-
public function testRemoveExpiredKeepsUnexpiredToken()
137-
{
138-
$this->assertNotYear2037Yet();
139-
140-
$this->loadFixtures([DetachedAdministratorTokenFixture::class]);
141-
142-
$idOfUnexpiredToken = 2;
143-
$this->repository->removeExpired();
144-
145-
$token = $this->repository->find($idOfUnexpiredToken);
146-
self::assertNotNull($token);
147-
}
148-
149-
/**
150-
* Asserts that it's not year 2037 yet (which is the year the "not expired" token in the fixture
151-
* data set expires).
152-
*/
153-
private function assertNotYear2037Yet(): void
154-
{
155-
$currentYear = (int)date('Y');
156-
if ($currentYear >= 2037) {
157-
self::markTestIncomplete('The tests token has an expiry in the year 2037. Please update this test.');
158-
}
159-
}
160-
161-
public function testRemoveExpiredForNoExpiredTokensReturnsZero()
162-
{
163-
self::assertSame(0, $this->repository->removeExpired());
164-
}
165-
166-
public function testRemoveExpiredForOneExpiredTokenReturnsOne()
167-
{
168-
$this->assertNotYear2037Yet();
169-
170-
$this->loadFixtures([DetachedAdministratorTokenFixture::class]);
171-
172-
self::assertSame(1, $this->repository->removeExpired());
173-
}
174-
175125
public function testSavePersistsAndFlushesModel()
176126
{
177127
$this->loadFixtures([AdministratorFixture::class]);

tests/Unit/Domain/Identity/Command/CleanUpOldSessionTokensTest.php

Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,67 +4,69 @@
44

55
namespace PhpList\Core\Tests\Unit\Domain\Identity\Command;
66

7-
use Exception;
7+
use Doctrine\ORM\EntityManagerInterface;
88
use PhpList\Core\Domain\Identity\Command\CleanUpOldSessionTokens;
99
use PhpList\Core\Domain\Identity\Repository\AdministratorTokenRepository;
10-
use PHPUnit\Framework\MockObject\MockObject;
1110
use PHPUnit\Framework\TestCase;
12-
use Symfony\Component\Console\Application;
11+
use Symfony\Component\Console\Command\Command;
1312
use Symfony\Component\Console\Tester\CommandTester;
1413

15-
class CleanUpOldSessionTokensTest extends TestCase
14+
final class CleanUpOldSessionTokensTest extends TestCase
1615
{
17-
private AdministratorTokenRepository&MockObject $tokenRepository;
18-
private CommandTester $commandTester;
19-
20-
protected function setUp(): void
16+
public function testItRemovesAllExpiredTokensAndOutputsSuccess(): void
2117
{
22-
$this->tokenRepository = $this->createMock(AdministratorTokenRepository::class);
18+
$repo = $this->createMock(AdministratorTokenRepository::class);
19+
$em = $this->createMock(EntityManagerInterface::class);
2320

24-
$command = new CleanUpOldSessionTokens($this->tokenRepository);
21+
$token1 = (object) ['id' => 1];
22+
$token2 = (object) ['id' => 2];
23+
$expired = [$token1, $token2];
2524

26-
$application = new Application();
27-
$application->add($command);
25+
$repo->expects($this->once())
26+
->method('getExpired')
27+
->willReturn($expired);
2828

29-
$this->commandTester = new CommandTester($command);
30-
}
29+
$removed = [];
30+
$em->expects($this->exactly(\count($expired)))
31+
->method('remove')
32+
->willReturnCallback(function (object $o) use (&$removed) {
33+
$removed[] = $o;
34+
});
3135

32-
public function testExecuteSuccessfully(): void
33-
{
34-
$this->tokenRepository->expects($this->once())
35-
->method('removeExpired')
36-
->willReturn(5);
36+
$em->expects($this->once())
37+
->method('flush');
3738

38-
$this->commandTester->execute([]);
39+
$command = new CleanUpOldSessionTokens($repo, $em);
40+
$tester = new CommandTester($command);
3941

40-
$output = $this->commandTester->getDisplay();
41-
$this->assertStringContainsString('Successfully removed 5 expired session token(s)', $output);
42-
$this->assertEquals(0, $this->commandTester->getStatusCode());
43-
}
42+
$exitCode = $tester->execute([]);
4443

45-
public function testExecuteWithNoExpiredTokens(): void
46-
{
47-
$this->tokenRepository->expects($this->once())
48-
->method('removeExpired')
49-
->willReturn(0);
44+
self::assertSame(Command::SUCCESS, $exitCode);
5045

51-
$this->commandTester->execute([]);
46+
$display = $tester->getDisplay();
47+
self::assertStringContainsString('Successfully removed 2 expired session token(s).', $display);
5248

53-
$output = $this->commandTester->getDisplay();
54-
$this->assertStringContainsString('Successfully removed 0 expired session token(s)', $output);
55-
$this->assertEquals(0, $this->commandTester->getStatusCode());
49+
self::assertEqualsCanonicalizing($expired, $removed);
5650
}
5751

58-
public function testExecuteWithException(): void
52+
public function testItHandlesExceptionsAndOutputsFailure(): void
5953
{
60-
$this->tokenRepository->expects($this->once())
61-
->method('removeExpired')
62-
->willThrowException(new Exception('Test exception'));
54+
$repo = $this->createMock(AdministratorTokenRepository::class);
55+
$em = $this->createMock(EntityManagerInterface::class);
56+
57+
$repo->expects($this->once())
58+
->method('getExpired')
59+
->willThrowException(new \RuntimeException('boom'));
60+
61+
$em->expects($this->never())->method('remove');
62+
$em->expects($this->never())->method('flush');
63+
64+
$command = new CleanUpOldSessionTokens($repo, $em);
65+
$tester = new CommandTester($command);
6366

64-
$this->commandTester->execute([]);
67+
$exitCode = $tester->execute([]);
6568

66-
$output = $this->commandTester->getDisplay();
67-
$this->assertStringContainsString('Error removing expired session tokens: Test exception', $output);
68-
$this->assertEquals(1, $this->commandTester->getStatusCode());
69+
self::assertSame(Command::FAILURE, $exitCode);
70+
self::assertStringContainsString('Error removing expired session tokens: boom', $tester->getDisplay());
6971
}
7072
}

0 commit comments

Comments
 (0)