This commit is contained in:
2025-06-20 17:31:13 +02:00
parent 9158e33854
commit a38116cca4
65 changed files with 2298 additions and 2298 deletions

View File

@@ -85,6 +85,69 @@ class TempUrlLocalStorageGeneratorTest extends TestCase
self::assertEquals($expected, $urlGenerator->validateSignature($signature, $method, $objectName, $expiration), $message);
}
public static function generateValidateSignatureData(): iterable
{
yield [
TempUrlLocalStorageGeneratorTest::expectedSignature('GET', $object_name = 'testABC', $expiration = 1734307200 + 180),
'GET',
$object_name,
$expiration,
\DateTimeImmutable::createFromFormat('U', (string) ($expiration - 10)),
true,
'Valid signature, not expired',
];
yield [
TempUrlLocalStorageGeneratorTest::expectedSignature('HEAD', $object_name = 'testABC', $expiration = 1734307200 + 180),
'HEAD',
$object_name,
$expiration,
\DateTimeImmutable::createFromFormat('U', (string) ($expiration - 10)),
true,
'Valid signature, not expired',
];
yield [
TempUrlLocalStorageGeneratorTest::expectedSignature('GET', $object_name = 'testABC', $expiration = 1734307200 + 180).'A',
'GET',
$object_name,
$expiration,
\DateTimeImmutable::createFromFormat('U', (string) ($expiration - 10)),
false,
'Invalid signature',
];
yield [
TempUrlLocalStorageGeneratorTest::expectedSignature('GET', $object_name = 'testABC', $expiration = 1734307200 + 180),
'GET',
$object_name,
$expiration,
\DateTimeImmutable::createFromFormat('U', (string) ($expiration + 1)),
false,
'Signature expired',
];
yield [
TempUrlLocalStorageGeneratorTest::expectedSignature('GET', $object_name = 'testABC', $expiration = 1734307200 + 180),
'GET',
$object_name.'____',
$expiration,
\DateTimeImmutable::createFromFormat('U', (string) ($expiration - 10)),
false,
'Invalid object name',
];
yield [
TempUrlLocalStorageGeneratorTest::expectedSignature('POST', $object_name = 'testABC', $expiration = 1734307200 + 180),
'POST',
$object_name,
$expiration,
\DateTimeImmutable::createFromFormat('U', (string) ($expiration - 10)),
false,
'Wrong method',
];
}
/**
* @dataProvider generateValidateSignaturePostData
*/
@@ -164,69 +227,6 @@ class TempUrlLocalStorageGeneratorTest extends TestCase
];
}
public static function generateValidateSignatureData(): iterable
{
yield [
TempUrlLocalStorageGeneratorTest::expectedSignature('GET', $object_name = 'testABC', $expiration = 1734307200 + 180),
'GET',
$object_name,
$expiration,
\DateTimeImmutable::createFromFormat('U', (string) ($expiration - 10)),
true,
'Valid signature, not expired',
];
yield [
TempUrlLocalStorageGeneratorTest::expectedSignature('HEAD', $object_name = 'testABC', $expiration = 1734307200 + 180),
'HEAD',
$object_name,
$expiration,
\DateTimeImmutable::createFromFormat('U', (string) ($expiration - 10)),
true,
'Valid signature, not expired',
];
yield [
TempUrlLocalStorageGeneratorTest::expectedSignature('GET', $object_name = 'testABC', $expiration = 1734307200 + 180).'A',
'GET',
$object_name,
$expiration,
\DateTimeImmutable::createFromFormat('U', (string) ($expiration - 10)),
false,
'Invalid signature',
];
yield [
TempUrlLocalStorageGeneratorTest::expectedSignature('GET', $object_name = 'testABC', $expiration = 1734307200 + 180),
'GET',
$object_name,
$expiration,
\DateTimeImmutable::createFromFormat('U', (string) ($expiration + 1)),
false,
'Signature expired',
];
yield [
TempUrlLocalStorageGeneratorTest::expectedSignature('GET', $object_name = 'testABC', $expiration = 1734307200 + 180),
'GET',
$object_name.'____',
$expiration,
\DateTimeImmutable::createFromFormat('U', (string) ($expiration - 10)),
false,
'Invalid object name',
];
yield [
TempUrlLocalStorageGeneratorTest::expectedSignature('POST', $object_name = 'testABC', $expiration = 1734307200 + 180),
'POST',
$object_name,
$expiration,
\DateTimeImmutable::createFromFormat('U', (string) ($expiration - 10)),
false,
'Wrong method',
];
}
private function buildGenerator(?UrlGeneratorInterface $urlGenerator = null, ?ClockInterface $clock = null): TempUrlLocalStorageGenerator
{
return new TempUrlLocalStorageGenerator(

View File

@@ -31,6 +31,20 @@ use Symfony\Contracts\HttpClient\HttpClientInterface;
*/
final class StoredObjectManagerTest extends TestCase
{
/**
* @dataProvider getDataProviderForRead
*/
public function testRead(StoredObject $storedObject, string $encodedContent, string $clearContent, ?string $exceptionClass = null)
{
if (null !== $exceptionClass) {
$this->expectException($exceptionClass);
}
$storedObjectManager = $this->getSubject($storedObject, $encodedContent);
self::assertEquals($clearContent, $storedObjectManager->read($storedObject));
}
public static function getDataProviderForRead(): \Generator
{
/* HAPPY SCENARIO */
@@ -96,6 +110,40 @@ final class StoredObjectManagerTest extends TestCase
];
}
/**
* @dataProvider getDataProviderForWrite
*/
public function testWrite(StoredObject $storedObject, string $encodedContent, string $clearContent, ?string $exceptionClass = null, ?int $errorCode = null)
{
if (null !== $exceptionClass) {
$this->expectException($exceptionClass);
}
$previousVersion = $storedObject->getCurrentVersion();
$previousFilename = $previousVersion->getFilename();
$client = new MockHttpClient(function ($method, $url, $options) use ($encodedContent, $previousFilename, $errorCode) {
self::assertEquals('PUT', $method);
self::assertStringStartsWith('https://example.com/', $url);
self::assertStringNotContainsString($previousFilename, $url, 'test that the PUT operation is not performed on the same file');
self::assertArrayHasKey('body', $options);
self::assertEquals($encodedContent, $options['body']);
if (-1 === $errorCode) {
throw new TransportException();
}
return new MockResponse('', ['http_code' => $errorCode ?? 201]);
});
$storedObjectManager = new StoredObjectManager($client, $this->getTempUrlGenerator($storedObject));
$newVersion = $storedObjectManager->write($storedObject, $clearContent);
self::assertNotSame($previousVersion, $newVersion);
self::assertSame($storedObject->getCurrentVersion(), $newVersion);
}
public static function getDataProviderForWrite(): \Generator
{
/* HAPPY SCENARIO */
@@ -150,54 +198,6 @@ final class StoredObjectManagerTest extends TestCase
];
}
/**
* @dataProvider getDataProviderForRead
*/
public function testRead(StoredObject $storedObject, string $encodedContent, string $clearContent, ?string $exceptionClass = null)
{
if (null !== $exceptionClass) {
$this->expectException($exceptionClass);
}
$storedObjectManager = $this->getSubject($storedObject, $encodedContent);
self::assertEquals($clearContent, $storedObjectManager->read($storedObject));
}
/**
* @dataProvider getDataProviderForWrite
*/
public function testWrite(StoredObject $storedObject, string $encodedContent, string $clearContent, ?string $exceptionClass = null, ?int $errorCode = null)
{
if (null !== $exceptionClass) {
$this->expectException($exceptionClass);
}
$previousVersion = $storedObject->getCurrentVersion();
$previousFilename = $previousVersion->getFilename();
$client = new MockHttpClient(function ($method, $url, $options) use ($encodedContent, $previousFilename, $errorCode) {
self::assertEquals('PUT', $method);
self::assertStringStartsWith('https://example.com/', $url);
self::assertStringNotContainsString($previousFilename, $url, 'test that the PUT operation is not performed on the same file');
self::assertArrayHasKey('body', $options);
self::assertEquals($encodedContent, $options['body']);
if (-1 === $errorCode) {
throw new TransportException();
}
return new MockResponse('', ['http_code' => $errorCode ?? 201]);
});
$storedObjectManager = new StoredObjectManager($client, $this->getTempUrlGenerator($storedObject));
$newVersion = $storedObjectManager->write($storedObject, $clearContent);
self::assertNotSame($previousVersion, $newVersion);
self::assertSame($storedObject->getCurrentVersion(), $newVersion);
}
public function testDelete(): void
{
$storedObject = new StoredObject();

View File

@@ -82,6 +82,38 @@ class TempUrlOpenstackGeneratorTest extends KernelTestCase
self::assertEquals($expected, $signedUrl);
}
public static function dataProviderGenerate(): iterable
{
$now = \DateTimeImmutable::createFromFormat('U', '1702041743');
$expireDelay = 1800;
$baseUrls = [
'https://objectstore.example/v1/my_account/container/',
'https://objectstore.example/v1/my_account/container',
];
$objectName = 'object';
$method = 'GET';
$key = 'MYKEY';
$signedUrl = new SignedUrl(
'GET',
'https://objectstore.example/v1/my_account/container/object?temp_url_sig=0aeef353a5f6e22d125c76c6ad8c644a59b222ba1b13eaeb56bf3d04e28b081d11dfcb36601ab3aa7b623d79e1ef03017071bbc842fb7b34afec2baff895bf80&temp_url_expires=1702043543',
\DateTimeImmutable::createFromFormat('U', '1702043543'),
$objectName
);
foreach ($baseUrls as $baseUrl) {
yield [
$baseUrl,
$now,
$key,
$method,
$objectName,
$expireDelay,
$signedUrl,
];
}
}
/**
* @dataProvider dataProviderGeneratePost
*/
@@ -125,38 +157,6 @@ class TempUrlOpenstackGeneratorTest extends KernelTestCase
self::assertGreaterThanOrEqual(20, strlen($signedUrl->prefix));
}
public static function dataProviderGenerate(): iterable
{
$now = \DateTimeImmutable::createFromFormat('U', '1702041743');
$expireDelay = 1800;
$baseUrls = [
'https://objectstore.example/v1/my_account/container/',
'https://objectstore.example/v1/my_account/container',
];
$objectName = 'object';
$method = 'GET';
$key = 'MYKEY';
$signedUrl = new SignedUrl(
'GET',
'https://objectstore.example/v1/my_account/container/object?temp_url_sig=0aeef353a5f6e22d125c76c6ad8c644a59b222ba1b13eaeb56bf3d04e28b081d11dfcb36601ab3aa7b623d79e1ef03017071bbc842fb7b34afec2baff895bf80&temp_url_expires=1702043543',
\DateTimeImmutable::createFromFormat('U', '1702043543'),
$objectName
);
foreach ($baseUrls as $baseUrl) {
yield [
$baseUrl,
$now,
$key,
$method,
$objectName,
$expireDelay,
$signedUrl,
];
}
}
public static function dataProviderGeneratePost(): iterable
{
$now = \DateTimeImmutable::createFromFormat('U', '1702041743');

View File

@@ -61,6 +61,55 @@ class StoredObjectContentToLocalStorageControllerTest extends TestCase
$controller->contentOperate($request);
}
public static function generateOperateContentWithExceptionDataProvider(): iterable
{
yield [
new Request(['object_name' => '', 'sig' => '', 'exp' => 0]),
BadRequestHttpException::class,
'Object name parameter is missing',
false,
'',
false,
];
yield [
new Request(['object_name' => 'testABC', 'sig' => '', 'exp' => 0]),
BadRequestHttpException::class,
'Expiration is not set or equal to zero',
false,
'',
false,
];
yield [
new Request(['object_name' => 'testABC', 'sig' => '', 'exp' => (new \DateTimeImmutable())->getTimestamp()]),
BadRequestHttpException::class,
'Signature is not set or is a blank string',
false,
'',
false,
];
yield [
new Request(['object_name' => 'testABC', 'sig' => '1234', 'exp' => (new \DateTimeImmutable())->getTimestamp()]),
AccessDeniedHttpException::class,
'Invalid signature',
false,
'',
false,
];
yield [
new Request(['object_name' => 'testABC', 'sig' => '1234', 'exp' => (new \DateTimeImmutable())->getTimestamp()]),
NotFoundHttpException::class,
'Object does not exists on disk',
false,
'',
true,
];
}
public function testOperateContentGetHappyScenario(): void
{
$objectName = 'testABC';
@@ -286,53 +335,4 @@ class StoredObjectContentToLocalStorageControllerTest extends TestCase
'Filename does not start with signed prefix',
];
}
public static function generateOperateContentWithExceptionDataProvider(): iterable
{
yield [
new Request(['object_name' => '', 'sig' => '', 'exp' => 0]),
BadRequestHttpException::class,
'Object name parameter is missing',
false,
'',
false,
];
yield [
new Request(['object_name' => 'testABC', 'sig' => '', 'exp' => 0]),
BadRequestHttpException::class,
'Expiration is not set or equal to zero',
false,
'',
false,
];
yield [
new Request(['object_name' => 'testABC', 'sig' => '', 'exp' => (new \DateTimeImmutable())->getTimestamp()]),
BadRequestHttpException::class,
'Signature is not set or is a blank string',
false,
'',
false,
];
yield [
new Request(['object_name' => 'testABC', 'sig' => '1234', 'exp' => (new \DateTimeImmutable())->getTimestamp()]),
AccessDeniedHttpException::class,
'Invalid signature',
false,
'',
false,
];
yield [
new Request(['object_name' => 'testABC', 'sig' => '1234', 'exp' => (new \DateTimeImmutable())->getTimestamp()]),
NotFoundHttpException::class,
'Object does not exists on disk',
false,
'',
true,
];
}
}

View File

@@ -136,63 +136,6 @@ class WebdavControllerTest extends KernelTestCase
self::assertXmlStringEqualsXmlString($expectedXmlResponse, $response->getContent(), $message);
}
/**
* @dataProvider generateDataPropfindDirectory
*/
public function testPropfindDirectory(string $requestContent, int $expectedStatusCode, string $expectedXmlResponse, string $message): void
{
$controller = $this->buildController();
$request = new Request([], [], [], [], [], [], $requestContent);
$request->setMethod('PROPFIND');
$request->headers->add(['Depth' => '0']);
$response = $controller->propfindDirectory($this->buildDocument(), '1234', $request);
self::assertEquals($expectedStatusCode, $response->getStatusCode());
self::assertContains('content-type', $response->headers->keys());
self::assertStringContainsString('text/xml', $response->headers->get('content-type'));
self::assertTrue((new \DOMDocument())->loadXML($response->getContent()), $message.' test that the xml response is a valid xml');
self::assertXmlStringEqualsXmlString($expectedXmlResponse, $response->getContent(), $message);
}
public function testHeadDocument(): void
{
$controller = $this->buildController();
$response = $controller->headDocument($this->buildDocument());
self::assertEquals(200, $response->getStatusCode());
self::assertContains('content-length', $response->headers->keys());
self::assertContains('content-type', $response->headers->keys());
self::assertContains('etag', $response->headers->keys());
self::assertEquals('ab56b4d92b40713acc5af89985d4b786', $response->headers->get('etag'));
self::assertEquals('application/vnd.oasis.opendocument.text', $response->headers->get('content-type'));
self::assertEquals(5, $response->headers->get('content-length'));
}
public function testPutDocument(): void
{
$document = $this->buildDocument();
$entityManager = $this->createMock(EntityManagerInterface::class);
$storedObjectManager = $this->createMock(StoredObjectManagerInterface::class);
// entity manager must be flushed
$entityManager->expects($this->once())
->method('flush');
// object must be written by StoredObjectManager
$storedObjectManager->expects($this->once())
->method('write')
->with($this->identicalTo($document), $this->identicalTo('1234'));
$controller = $this->buildController($entityManager, $storedObjectManager);
$request = new Request(content: '1234');
$response = $controller->putDocument($document, $request);
self::assertEquals(204, $response->getStatusCode());
self::assertEquals('', $response->getContent());
}
public static function generateDataPropfindDocument(): iterable
{
$content =
@@ -347,6 +290,25 @@ class WebdavControllerTest extends KernelTestCase
];
}
/**
* @dataProvider generateDataPropfindDirectory
*/
public function testPropfindDirectory(string $requestContent, int $expectedStatusCode, string $expectedXmlResponse, string $message): void
{
$controller = $this->buildController();
$request = new Request([], [], [], [], [], [], $requestContent);
$request->setMethod('PROPFIND');
$request->headers->add(['Depth' => '0']);
$response = $controller->propfindDirectory($this->buildDocument(), '1234', $request);
self::assertEquals($expectedStatusCode, $response->getStatusCode());
self::assertContains('content-type', $response->headers->keys());
self::assertStringContainsString('text/xml', $response->headers->get('content-type'));
self::assertTrue((new \DOMDocument())->loadXML($response->getContent()), $message.' test that the xml response is a valid xml');
self::assertXmlStringEqualsXmlString($expectedXmlResponse, $response->getContent(), $message);
}
public static function generateDataPropfindDirectory(): iterable
{
yield [
@@ -414,6 +376,44 @@ class WebdavControllerTest extends KernelTestCase
'test creatableContentsInfo',
];
}
public function testHeadDocument(): void
{
$controller = $this->buildController();
$response = $controller->headDocument($this->buildDocument());
self::assertEquals(200, $response->getStatusCode());
self::assertContains('content-length', $response->headers->keys());
self::assertContains('content-type', $response->headers->keys());
self::assertContains('etag', $response->headers->keys());
self::assertEquals('ab56b4d92b40713acc5af89985d4b786', $response->headers->get('etag'));
self::assertEquals('application/vnd.oasis.opendocument.text', $response->headers->get('content-type'));
self::assertEquals(5, $response->headers->get('content-length'));
}
public function testPutDocument(): void
{
$document = $this->buildDocument();
$entityManager = $this->createMock(EntityManagerInterface::class);
$storedObjectManager = $this->createMock(StoredObjectManagerInterface::class);
// entity manager must be flushed
$entityManager->expects($this->once())
->method('flush');
// object must be written by StoredObjectManager
$storedObjectManager->expects($this->once())
->method('write')
->with($this->identicalTo($document), $this->identicalTo('1234'));
$controller = $this->buildController($entityManager, $storedObjectManager);
$request = new Request(content: '1234');
$response = $controller->putDocument($document, $request);
self::assertEquals(204, $response->getStatusCode());
self::assertEquals('', $response->getContent());
}
}
class MockedStoredObjectManager implements StoredObjectManagerInterface

View File

@@ -87,6 +87,16 @@ class PersonDocumentACLAwareRepositoryTest extends KernelTestCase
self::assertIsInt($nb, 'test that the query could be executed');
}
public static function provideDataBuildFetchQueryForPerson(): iterable
{
yield [null, null, null];
yield [new \DateTimeImmutable('1 year ago'), null, null];
yield [null, new \DateTimeImmutable('1 year ago'), null];
yield [new \DateTimeImmutable('2 years ago'), new \DateTimeImmutable('1 year ago'), null];
yield [null, null, 'test'];
yield [new \DateTimeImmutable('2 years ago'), new \DateTimeImmutable('1 year ago'), 'test'];
}
/**
* @dataProvider provideDateForFetchQueryForAccompanyingPeriod
*/
@@ -142,14 +152,4 @@ class PersonDocumentACLAwareRepositoryTest extends KernelTestCase
yield [$period, null, null, 'test'];
yield [$period, new \DateTimeImmutable('2 years ago'), new \DateTimeImmutable('1 year ago'), 'test'];
}
public static function provideDataBuildFetchQueryForPerson(): iterable
{
yield [null, null, null];
yield [new \DateTimeImmutable('1 year ago'), null, null];
yield [null, new \DateTimeImmutable('1 year ago'), null];
yield [new \DateTimeImmutable('2 years ago'), new \DateTimeImmutable('1 year ago'), null];
yield [null, null, 'test'];
yield [new \DateTimeImmutable('2 years ago'), new \DateTimeImmutable('1 year ago'), 'test'];
}
}

View File

@@ -50,19 +50,6 @@ class StoredObjectVoterTest extends TestCase
self::assertEquals($expected, $voter->vote($token, $subject, [$attribute]));
}
private function buildStoredObjectVoter(bool $supportsIsCalled, bool $supports, bool $voteOnAttribute): StoredObjectVoterInterface
{
$storedObjectVoter = $this->createMock(StoredObjectVoterInterface::class);
$storedObjectVoter->expects($supportsIsCalled ? $this->once() : $this->never())->method('supports')
->with(self::isInstanceOf(StoredObjectRoleEnum::class), $this->isInstanceOf(StoredObject::class))
->willReturn($supports);
$storedObjectVoter->expects($supportsIsCalled && $supports ? $this->once() : $this->never())->method('voteOnAttribute')
->with(self::isInstanceOf(StoredObjectRoleEnum::class), $this->isInstanceOf(StoredObject::class), $this->isInstanceOf(TokenInterface::class))
->willReturn($voteOnAttribute);
return $storedObjectVoter;
}
public static function provideDataVote(): iterable
{
yield [
@@ -120,4 +107,17 @@ class StoredObjectVoterTest extends TestCase
VoterInterface::ACCESS_GRANTED,
];
}
private function buildStoredObjectVoter(bool $supportsIsCalled, bool $supports, bool $voteOnAttribute): StoredObjectVoterInterface
{
$storedObjectVoter = $this->createMock(StoredObjectVoterInterface::class);
$storedObjectVoter->expects($supportsIsCalled ? $this->once() : $this->never())->method('supports')
->with(self::isInstanceOf(StoredObjectRoleEnum::class), $this->isInstanceOf(StoredObject::class))
->willReturn($supports);
$storedObjectVoter->expects($supportsIsCalled && $supports ? $this->once() : $this->never())->method('voteOnAttribute')
->with(self::isInstanceOf(StoredObjectRoleEnum::class), $this->isInstanceOf(StoredObject::class), $this->isInstanceOf(TokenInterface::class))
->willReturn($voteOnAttribute);
return $storedObjectVoter;
}
}

View File

@@ -40,29 +40,6 @@ class RemoveOldVersionCronJobTest extends KernelTestCase
self::assertEquals($expected, $cronJob->canRun($cronJobExecution));
}
public function testRun(): void
{
// we create a clock in the future. This led us a chance to having stored object to delete
$clock = new MockClock(new \DateTimeImmutable('2024-01-01 00:00:00', new \DateTimeZone('+00:00')));
$repository = $this->createMock(StoredObjectVersionRepository::class);
$repository->expects($this->once())
->method('findIdsByVersionsOlderThanDateAndNotLastVersionAndNotPointInTime')
->with(new \DateTime('2023-10-03 00:00:00', new \DateTimeZone('+00:00')))
->willReturnCallback(function ($arg) {
yield 1;
yield 3;
yield 2;
})
;
$cronJob = new RemoveOldVersionCronJob($clock, $this->buildMessageBus(true), $repository);
$results = $cronJob->run([]);
self::assertArrayHasKey('last-deleted-stored-object-version-id', $results);
self::assertIsInt($results['last-deleted-stored-object-version-id']);
}
public static function buildTestCanRunData(): iterable
{
yield [
@@ -86,6 +63,29 @@ class RemoveOldVersionCronJobTest extends KernelTestCase
];
}
public function testRun(): void
{
// we create a clock in the future. This led us a chance to having stored object to delete
$clock = new MockClock(new \DateTimeImmutable('2024-01-01 00:00:00', new \DateTimeZone('+00:00')));
$repository = $this->createMock(StoredObjectVersionRepository::class);
$repository->expects($this->once())
->method('findIdsByVersionsOlderThanDateAndNotLastVersionAndNotPointInTime')
->with(new \DateTime('2023-10-03 00:00:00', new \DateTimeZone('+00:00')))
->willReturnCallback(function ($arg) {
yield 1;
yield 3;
yield 2;
})
;
$cronJob = new RemoveOldVersionCronJob($clock, $this->buildMessageBus(true), $repository);
$results = $cronJob->run([]);
self::assertArrayHasKey('last-deleted-stored-object-version-id', $results);
self::assertIsInt($results['last-deleted-stored-object-version-id']);
}
private function buildMessageBus(bool $expectDistpatchAtLeastOnce = false): MessageBusInterface
{
$messageBus = $this->createMock(MessageBusInterface::class);