engine = self::getContainer()->get(\Twig\Environment::class);
}
private function buildController(?EntityManagerInterface $entityManager = null, ?StoredObjectManagerInterface $storedObjectManager = null): WebdavController
{
if (null === $storedObjectManager) {
$storedObjectManager = new MockedStoredObjectManager();
}
if (null === $entityManager) {
$entityManager = $this->createMock(EntityManagerInterface::class);
}
$security = $this->prophesize(Security::class);
$security->isGranted(Argument::in(['SEE_AND_EDIT', 'SEE']), Argument::type(StoredObject::class))
->willReturn(true);
return new WebdavController($this->engine, $storedObjectManager, $security->reveal(), $entityManager);
}
private function buildDocument(): StoredObject
{
$object = (new StoredObject())
->registerVersion(type: 'application/vnd.oasis.opendocument.text')
->getStoredObject();
$reflectionObject = new \ReflectionClass($object);
$reflectionObjectUuid = $reflectionObject->getProperty('uuid');
$reflectionObjectUuid->setValue($object, Uuid::fromString('716e6688-4579-4938-acf3-c4ab5856803b'));
return $object;
}
public function testGet(): void
{
$controller = $this->buildController();
$response = $controller->getDocument($this->buildDocument());
self::assertEquals(200, $response->getStatusCode());
self::assertEquals('abcde', $response->getContent());
self::assertContains('etag', $response->headers->keys());
self::assertStringContainsString('ab56b4', $response->headers->get('etag'));
}
public function testOptionsOnDocument(): void
{
$controller = $this->buildController();
$response = $controller->optionsDocument($this->buildDocument());
self::assertEquals(200, $response->getStatusCode());
self::assertContains('allow', $response->headers->keys());
foreach (explode(',', 'OPTIONS,GET,HEAD,PROPFIND') as $method) {
self::assertStringContainsString($method, $response->headers->get('allow'));
}
self::assertContains('dav', $response->headers->keys());
self::assertStringContainsString('1', $response->headers->get('dav'));
}
public function testOptionsOnDirectory(): void
{
$controller = $this->buildController();
$response = $controller->optionsDirectory($this->buildDocument());
self::assertEquals(200, $response->getStatusCode());
self::assertContains('allow', $response->headers->keys());
foreach (explode(',', 'OPTIONS,GET,HEAD,PROPFIND') as $method) {
self::assertStringContainsString($method, $response->headers->get('allow'));
}
self::assertContains('dav', $response->headers->keys());
self::assertStringContainsString('1', $response->headers->get('dav'));
}
/**
* @dataProvider generateDataPropfindDocument
*/
public function testPropfindDocument(string $requestContent, int $expectedStatusCode, string $expectedXmlResponse, string $message): void
{
$controller = $this->buildController();
$request = new Request([], [], [], [], [], [], $requestContent);
$request->setMethod('PROPFIND');
$response = $controller->propfindDocument($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);
}
/**
* @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 =
<<<'XML'
XML;
$response =
<<<'XML'
/dav/1234/get/716e6688-4579-4938-acf3-c4ab5856803b/d
application/vnd.oasis.opendocument.text
HTTP/1.1 200 OK
HTTP/1.1 404 Not Found
XML;
yield [$content, 207, $response, 'get IsReadOnly and contenttype from server'];
$content =
<<<'XML'
XML;
$response =
<<<'XML'
/dav/1234/get/716e6688-4579-4938-acf3-c4ab5856803b/d
HTTP/1.1 404 Not Found
XML;
yield [$content, 207, $response, 'get property IsReadOnly'];
yield [
<<<'XML'
XML,
207,
<<<'XML'
/dav/1234/get/716e6688-4579-4938-acf3-c4ab5856803b/d
HTTP/1.1 404 Not Found
XML,
'Test requesting an unknow property',
];
yield [
<<<'XML'
XML,
207,
<<<'XML'
/dav/1234/get/716e6688-4579-4938-acf3-c4ab5856803b/d
Wed, 13 Sep 2023 14:15:00 +0200
HTTP/1.1 200 OK
XML,
'test getting the last modified date',
];
yield [
<<<'XML'
XML,
207,
<<<'XML'
/dav/1234/get/716e6688-4579-4938-acf3-c4ab5856803b/d
Wed, 13 Sep 2023 14:15:00 +0200
5
"ab56b4d92b40713acc5af89985d4b786"
application/vnd.oasis.opendocument.text
HTTP/1.1 200 OK
XML,
'test finding all properties',
];
}
public static function generateDataPropfindDirectory(): iterable
{
yield [
<<<'XML'
XML,
207,
<<<'XML'
/dav/1234/get/716e6688-4579-4938-acf3-c4ab5856803b/
httpd/unix-directory
HTTP/1.1 200 OK
HTTP/1.1 404 Not Found
XML,
'test resourceType and IsReadOnly ',
];
yield [
<<<'XML'
XML,
207,
<<<'XML'
/dav/1234/get/716e6688-4579-4938-acf3-c4ab5856803b/
HTTP/1.1 404 Not Found
XML,
'test creatableContentsInfo',
];
}
}
class MockedStoredObjectManager implements StoredObjectManagerInterface
{
public function getLastModified(StoredObject|StoredObjectVersion $document): \DateTimeInterface
{
return new \DateTimeImmutable('2023-09-13T14:15', new \DateTimeZone('+02:00'));
}
public function getContentLength(StoredObject|StoredObjectVersion $document): int
{
return 5;
}
public function read(StoredObject|StoredObjectVersion $document): string
{
return 'abcde';
}
public function write(StoredObject $document, string $clearContent, ?string $contentType = null): StoredObjectVersion
{
return $document->registerVersion();
}
public function etag(StoredObject|StoredObjectVersion $document): string
{
return 'ab56b4d92b40713acc5af89985d4b786';
}
public function clearCache(): void {}
public function delete(StoredObjectVersion $storedObjectVersion): void {}
public function exists(StoredObject|StoredObjectVersion $document): bool
{
return true;
}
}