mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-12 21:34:25 +00:00
219 lines
8.5 KiB
PHP
219 lines
8.5 KiB
PHP
<?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 Chill\DocStoreBundle\Tests\Service;
|
|
|
|
use Chill\DocStoreBundle\AsyncUpload\SignedUrl;
|
|
use Chill\DocStoreBundle\AsyncUpload\TempUrlGeneratorInterface;
|
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
|
use Chill\DocStoreBundle\Exception\StoredObjectManagerException;
|
|
use Chill\DocStoreBundle\Service\StoredObjectManager;
|
|
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Symfony\Component\HttpClient\Exception\TransportException;
|
|
use Symfony\Component\HttpClient\MockHttpClient;
|
|
use Symfony\Component\HttpClient\Response\MockResponse;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @covers \Chill\DocStoreBundle\Service\StoredObjectManager
|
|
*/
|
|
final class StoredObjectManagerTest extends TestCase
|
|
{
|
|
public static function getDataProvider(): \Generator
|
|
{
|
|
/* HAPPY SCENARIO */
|
|
|
|
// Encrypted object
|
|
yield [
|
|
(new StoredObject())
|
|
->setFilename('encrypted.txt')
|
|
->setKeyInfos(['k' => base64_encode('S9NIHMaFHOWzLPez3jZOIHBaNfBrMQUR5zvqBz6kme8')])
|
|
->setIv(unpack('C*', 'abcdefghijklmnop')),
|
|
hex2bin('741237d255fd4f7eddaaa9058912a84caae28a41b10b34d4e3e3abe41d3b9b47cb0dd8f22c3c883d4f0e9defa75ff662'), // Binary encoded string
|
|
'The quick brown fox jumps over the lazy dog', // clear
|
|
];
|
|
|
|
// Non-encrypted object
|
|
yield [
|
|
(new StoredObject())->setFilename('non-encrypted.txt'), // The StoredObject
|
|
'The quick brown fox jumps over the lazy dog', // Encrypted
|
|
'The quick brown fox jumps over the lazy dog', // Clear
|
|
];
|
|
|
|
/* UNHAPPY SCENARIO */
|
|
|
|
// Encrypted object with issue during HTTP communication
|
|
yield [
|
|
(new StoredObject())
|
|
->setFilename('error_during_http_request.txt')
|
|
->setKeyInfos(['k' => base64_encode('S9NIHMaFHOWzLPez3jZOIHBaNfBrMQUR5zvqBz6kme8')])
|
|
->setIv(unpack('C*', 'abcdefghijklmnop')),
|
|
hex2bin('741237d255fd4f7eddaaa9058912a84caae28a41b10b34d4e3e3abe41d3b9b47cb0dd8f22c3c883d4f0e9defa75ff662'), // Binary encoded string
|
|
'The quick brown fox jumps over the lazy dog', // clear
|
|
StoredObjectManagerException::class,
|
|
];
|
|
|
|
// Encrypted object with issue during HTTP communication: Invalid status code
|
|
yield [
|
|
(new StoredObject())
|
|
->setFilename('invalid_statuscode.txt')
|
|
->setKeyInfos(['k' => base64_encode('S9NIHMaFHOWzLPez3jZOIHBaNfBrMQUR5zvqBz6kme8')])
|
|
->setIv(unpack('C*', 'abcdefghijklmnop')),
|
|
hex2bin('741237d255fd4f7eddaaa9058912a84caae28a41b10b34d4e3e3abe41d3b9b47cb0dd8f22c3c883d4f0e9defa75ff662'), // Binary encoded string
|
|
'The quick brown fox jumps over the lazy dog', // clear
|
|
StoredObjectManagerException::class,
|
|
];
|
|
|
|
// Erroneous encrypted: Unable to decrypt exception.
|
|
yield [
|
|
(new StoredObject())
|
|
->setFilename('unable_to_decrypt.txt')
|
|
->setKeyInfos(['k' => base64_encode('WRONG_PASS_PHRASE')])
|
|
->setIv(unpack('C*', 'abcdefghijklmnop')),
|
|
'WRONG_ENCODED_VALUE', // Binary encoded string
|
|
'The quick brown fox jumps over the lazy dog', // clear
|
|
StoredObjectManagerException::class,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider getDataProvider
|
|
*/
|
|
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 getDataProvider
|
|
*/
|
|
public function testWrite(StoredObject $storedObject, string $encodedContent, string $clearContent, ?string $exceptionClass = null)
|
|
{
|
|
if (null !== $exceptionClass) {
|
|
$this->expectException($exceptionClass);
|
|
}
|
|
|
|
$storedObjectManager = $this->getSubject($storedObject, $encodedContent);
|
|
|
|
$storedObjectManager->write($storedObject, $clearContent);
|
|
|
|
self::assertEquals($clearContent, $storedObjectManager->read($storedObject));
|
|
}
|
|
|
|
public function testWriteWithDeleteAt()
|
|
{
|
|
$storedObject = new StoredObject();
|
|
|
|
$expectedRequests = [
|
|
function ($method, $url, $options): MockResponse {
|
|
self::assertEquals('PUT', $method);
|
|
self::assertArrayHasKey('headers', $options);
|
|
self::assertIsArray($options['headers']);
|
|
self::assertCount(0, array_filter($options['headers'], fn (string $header) => str_contains($header, 'X-Delete-At')));
|
|
|
|
return new MockResponse('', ['http_code' => 201]);
|
|
},
|
|
|
|
function ($method, $url, $options): MockResponse {
|
|
self::assertEquals('PUT', $method);
|
|
self::assertArrayHasKey('headers', $options);
|
|
self::assertIsArray($options['headers']);
|
|
self::assertCount(1, array_filter($options['headers'], fn (string $header) => str_contains($header, 'X-Delete-At')));
|
|
self::assertContains('X-Delete-At: 1711014260', $options['headers']);
|
|
|
|
return new MockResponse('', ['http_code' => 201]);
|
|
},
|
|
];
|
|
$client = new MockHttpClient($expectedRequests);
|
|
|
|
$manager = new StoredObjectManager($client, $this->getTempUrlGenerator($storedObject));
|
|
|
|
$manager->write($storedObject, 'ok');
|
|
|
|
// with a deletedAt date
|
|
$storedObject->setDeleteAt(\DateTimeImmutable::createFromFormat('U', '1711014260'));
|
|
$manager->write($storedObject, 'ok');
|
|
}
|
|
|
|
private function getHttpClient(string $encodedContent): HttpClientInterface
|
|
{
|
|
$callback = static function ($method, $url, $options) use ($encodedContent) {
|
|
if (Request::METHOD_GET === $method) {
|
|
switch ($url) {
|
|
case 'https://example.com/non-encrypted.txt':
|
|
case 'https://example.com/encrypted.txt':
|
|
return new MockResponse($encodedContent, ['http_code' => 200]);
|
|
|
|
case 'https://example.com/error_during_http_request.txt':
|
|
return new TransportException('error_during_http_request.txt');
|
|
|
|
case 'https://example.com/invalid_statuscode.txt':
|
|
return new MockResponse($encodedContent, ['http_code' => 404]);
|
|
}
|
|
}
|
|
|
|
if (Request::METHOD_PUT === $method) {
|
|
switch ($url) {
|
|
case 'https://example.com/non-encrypted.txt':
|
|
case 'https://example.com/encrypted.txt':
|
|
return new MockResponse($encodedContent, ['http_code' => 201]);
|
|
|
|
case 'https://example.com/error_during_http_request.txt':
|
|
throw new TransportException('error_during_http_request.txt');
|
|
case 'https://example.com/invalid_statuscode.txt':
|
|
return new MockResponse($encodedContent, ['http_code' => 404]);
|
|
}
|
|
}
|
|
|
|
return new MockResponse('Not found');
|
|
};
|
|
|
|
return new MockHttpClient($callback);
|
|
}
|
|
|
|
private function getSubject(StoredObject $storedObject, string $encodedContent): StoredObjectManagerInterface
|
|
{
|
|
return new StoredObjectManager(
|
|
$this->getHttpClient($encodedContent),
|
|
$this->getTempUrlGenerator($storedObject)
|
|
);
|
|
}
|
|
|
|
private function getTempUrlGenerator(StoredObject $storedObject): TempUrlGeneratorInterface
|
|
{
|
|
$response = new SignedUrl(
|
|
'PUT',
|
|
'https://example.com/'.$storedObject->getFilename(),
|
|
new \DateTimeImmutable('1 hours'),
|
|
$storedObject->getFilename()
|
|
);
|
|
|
|
$tempUrlGenerator = $this->createMock(TempUrlGeneratorInterface::class);
|
|
|
|
$tempUrlGenerator
|
|
->method('generate')
|
|
->withAnyParameters()
|
|
->willReturn($response);
|
|
|
|
return $tempUrlGenerator;
|
|
}
|
|
}
|