Add cron job for removing expired stored objects

Introduced `RemoveExpiredStoredObjectCronJob` to automate the deletion of expired stored objects every 7 days. Enhanced associated tests and updated relevant interfaces and classes to support the new cron job functionality.
This commit is contained in:
2024-08-28 11:41:43 +02:00
parent c38f7c1179
commit 0db2652f08
10 changed files with 344 additions and 12 deletions

View File

@@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Tests\Service\StoredObjectCleaner;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\StoredObjectRepositoryInterface;
use Chill\DocStoreBundle\Service\StoredObjectCleaner\RemoveExpiredStoredObjectCronJob;
use Chill\DocStoreBundle\Service\StoredObjectCleaner\RemoveOldVersionMessage;
use Chill\MainBundle\Entity\CronJobExecution;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Clock\MockClock;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;
/**
* @internal
*
* @coversNothing
*/
class RemoveExpiredStoredObjectCronJobTest extends TestCase
{
/**
* @dataProvider buildTestCanRunData
*/
public function testCanRun(?CronJobExecution $cronJobExecution, bool $expected): void
{
$repository = $this->createMock(StoredObjectRepositoryInterface::class);
$clock = new MockClock(new \DateTimeImmutable('2024-01-01 00:00:00', new \DateTimeZone('+00:00')));
$cronJob = new RemoveExpiredStoredObjectCronJob($clock, $this->buildMessageBus(), $repository);
self::assertEquals($expected, $cronJob->canRun($cronJobExecution));
}
public static function buildTestCanRunData(): iterable
{
yield [
(new CronJobExecution('remove-expired-stored-object'))->setLastEnd(new \DateTimeImmutable('2023-12-25 00:00:00', new \DateTimeZone('+00:00'))),
true,
];
yield [
(new CronJobExecution('remove-expired-stored-object'))->setLastEnd(new \DateTimeImmutable('2023-12-24 23:59:59', new \DateTimeZone('+00:00'))),
true,
];
yield [
(new CronJobExecution('remove-expired-stored-object'))->setLastEnd(new \DateTimeImmutable('2023-12-25 00:00:01', new \DateTimeZone('+00:00'))),
false,
];
yield [
null,
true,
];
}
public function testRun(): void
{
$repository = $this->createMock(StoredObjectRepositoryInterface::class);
$repository->expects($this->atLeastOnce())->method('findByExpired')->withAnyParameters()->willReturnCallback(
function (\DateTimeImmutable $date): iterable {
yield $this->buildStoredObject(3);
yield $this->buildStoredObject(1);
}
);
$clock = new MockClock();
$cronJob = new RemoveExpiredStoredObjectCronJob($clock, $this->buildMessageBus(true), $repository);
$actual = $cronJob->run([]);
self::assertEquals(3, $actual['last-deleted-stored-object-id']);
}
private function buildStoredObject(int $id): StoredObject
{
$object = new StoredObject();
$object->registerVersion();
$class = new \ReflectionClass($object);
$idProperty = $class->getProperty('id');
$idProperty->setValue($object, $id);
$classVersion = new \ReflectionClass($object->getCurrentVersion());
$idPropertyVersion = $classVersion->getProperty('id');
$idPropertyVersion->setValue($object->getCurrentVersion(), $id);
return $object;
}
private function buildMessageBus(bool $expectDistpatchAtLeastOnce = false): MessageBusInterface
{
$messageBus = $this->createMock(MessageBusInterface::class);
$methodDispatch = match ($expectDistpatchAtLeastOnce) {
true => $messageBus->expects($this->atLeastOnce())->method('dispatch')->with($this->isInstanceOf(RemoveOldVersionMessage::class)),
false => $messageBus->method('dispatch'),
};
$methodDispatch->willReturnCallback(function (RemoveOldVersionMessage $message) {
return new Envelope($message);
});
return $messageBus;
}
}

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Tests\Service\StoredObjectCleaner;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Entity\StoredObjectVersion;
use Chill\DocStoreBundle\Repository\StoredObjectVersionRepository;
use Chill\DocStoreBundle\Service\StoredObjectCleaner\RemoveOldVersionMessage;
use Chill\DocStoreBundle\Service\StoredObjectCleaner\RemoveOldVersionMessageHandler;
@@ -19,6 +20,7 @@ use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use Symfony\Component\Clock\MockClock;
/**
* @internal
@@ -44,8 +46,78 @@ class RemoveOldVersionMessageHandlerTest extends TestCase
$storedObjectManager = $this->createMock(StoredObjectManagerInterface::class);
$storedObjectManager->expects($this->once())->method('delete')->with($this->identicalTo($version));
$handler = new RemoveOldVersionMessageHandler($storedObjectVersionRepository, new NullLogger(), $entityManager, $storedObjectManager);
$handler = new RemoveOldVersionMessageHandler($storedObjectVersionRepository, new NullLogger(), $entityManager, $storedObjectManager, new MockClock());
$handler(new RemoveOldVersionMessage(1));
}
public function testInvokeWithStoredObjectToDelete(): void
{
$object = new StoredObject();
$object->setDeleteAt(new \DateTimeImmutable('2023-12-01'));
$version = $object->registerVersion();
$storedObjectVersionRepository = $this->createMock(StoredObjectVersionRepository::class);
$storedObjectVersionRepository->expects($this->once())->method('find')
->with($this->identicalTo(1))
->willReturn($version);
$entityManager = $this->createMock(EntityManagerInterface::class);
$entityManager->expects($this->exactly(2))->method('remove')->with(
$this->logicalOr($this->identicalTo($version), $this->identicalTo($object))
);
$entityManager->expects($this->once())->method('flush');
$entityManager->expects($this->once())->method('clear');
$handler = new RemoveOldVersionMessageHandler(
$storedObjectVersionRepository,
new NullLogger(),
$entityManager,
new DummyStoredObjectManager(),
new MockClock(new \DateTimeImmutable('2024-01-01'))
);
$handler(new RemoveOldVersionMessage(1));
self::assertCount(0, $object->getVersions());
}
}
class DummyStoredObjectManager implements StoredObjectManagerInterface
{
public function getLastModified(StoredObject|StoredObjectVersion $document): \DateTimeInterface
{
throw new \RuntimeException();
}
public function getContentLength(StoredObject|StoredObjectVersion $document): int
{
throw new \RuntimeException();
}
public function read(StoredObject|StoredObjectVersion $document): string
{
throw new \RuntimeException();
}
public function write(StoredObject $document, string $clearContent, ?string $contentType = null): StoredObjectVersion
{
throw new \RuntimeException();
}
public function delete(StoredObjectVersion $storedObjectVersion): void
{
$object = $storedObjectVersion->getStoredObject();
$object->removeVersion($storedObjectVersion);
}
public function etag(StoredObject|StoredObjectVersion $document): string
{
throw new \RuntimeException();
}
public function clearCache(): void
{
throw new \RuntimeException();
}
}