diff options
Diffstat (limited to 'tests/lib/Files/Node')
-rw-r--r-- | tests/lib/Files/Node/FileTest.php | 309 | ||||
-rw-r--r-- | tests/lib/Files/Node/FolderTest.php | 1044 | ||||
-rw-r--r-- | tests/lib/Files/Node/HookConnectorTest.php | 327 | ||||
-rw-r--r-- | tests/lib/Files/Node/IntegrationTest.php | 151 | ||||
-rw-r--r-- | tests/lib/Files/Node/NodeTestCase.php | 793 | ||||
-rw-r--r-- | tests/lib/Files/Node/RootTest.php | 253 |
6 files changed, 2877 insertions, 0 deletions
diff --git a/tests/lib/Files/Node/FileTest.php b/tests/lib/Files/Node/FileTest.php new file mode 100644 index 00000000000..eec34d156ad --- /dev/null +++ b/tests/lib/Files/Node/FileTest.php @@ -0,0 +1,309 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Files\Node; + +use OC\Files\Node\File; +use OC\Files\Node\Root; +use OCP\Constants; +use OCP\Files\NotPermittedException; + +/** + * Class FileTest + * + * @group DB + * + * @package Test\Files\Node + */ +class FileTest extends NodeTestCase { + protected function createTestNode($root, $view, $path, array $data = [], $internalPath = '', $storage = null) { + if ($data || $internalPath || $storage) { + return new File($root, $view, $path, $this->getFileInfo($data, $internalPath, $storage)); + } else { + return new File($root, $view, $path); + } + } + + protected function getNodeClass() { + return '\OC\Files\Node\File'; + } + + protected function getNonExistingNodeClass() { + return '\OC\Files\Node\NonExistingFile'; + } + + protected function getViewDeleteMethod() { + return 'unlink'; + } + + public function testGetContent(): void { + /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + + $hook = function ($file): void { + throw new \Exception('Hooks are not supposed to be called'); + }; + + $root->listen('\OC\Files', 'preWrite', $hook); + $root->listen('\OC\Files', 'postWrite', $hook); + + $this->view->expects($this->once()) + ->method('file_get_contents') + ->with('/bar/foo') + ->willReturn('bar'); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); + + $node = new File($root, $this->view, '/bar/foo'); + $this->assertEquals('bar', $node->getContent()); + } + + + public function testGetContentNotPermitted(): void { + $this->expectException(NotPermittedException::class); + + /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => 0])); + + $node = new File($root, $this->view, '/bar/foo'); + $node->getContent(); + } + + public function testPutContent(): void { + /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); + + $this->view->expects($this->once()) + ->method('file_put_contents') + ->with('/bar/foo', 'bar') + ->willReturn(true); + + $node = new File($root, $this->view, '/bar/foo'); + $node->putContent('bar'); + } + + + public function testPutContentNotPermitted(): void { + $this->expectException(NotPermittedException::class); + + /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); + + $node = new File($root, $this->view, '/bar/foo'); + $node->putContent('bar'); + } + + public function testGetMimeType(): void { + /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['mimetype' => 'text/plain'])); + + $node = new File($root, $this->view, '/bar/foo'); + $this->assertEquals('text/plain', $node->getMimeType()); + } + + public function testFOpenRead(): void { + $stream = fopen('php://memory', 'w+'); + fwrite($stream, 'bar'); + rewind($stream); + + $root = new Root( + $this->manager, + $this->view, + $this->user, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + + $hook = function ($file): void { + throw new \Exception('Hooks are not supposed to be called'); + }; + + $root->listen('\OC\Files', 'preWrite', $hook); + $root->listen('\OC\Files', 'postWrite', $hook); + + $this->view->expects($this->once()) + ->method('fopen') + ->with('/bar/foo', 'r') + ->willReturn($stream); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); + + $node = new File($root, $this->view, '/bar/foo'); + $fh = $node->fopen('r'); + $this->assertEquals($stream, $fh); + $this->assertEquals('bar', fread($fh, 3)); + } + + public function testFOpenWrite(): void { + $stream = fopen('php://memory', 'w+'); + + $root = new Root( + $this->manager, + $this->view, + $this->user, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + $hooksCalled = 0; + $hook = function ($file) use (&$hooksCalled): void { + $hooksCalled++; + }; + + $root->listen('\OC\Files', 'preWrite', $hook); + $root->listen('\OC\Files', 'postWrite', $hook); + + $this->view->expects($this->once()) + ->method('fopen') + ->with('/bar/foo', 'w') + ->willReturn($stream); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); + + $node = new File($root, $this->view, '/bar/foo'); + $fh = $node->fopen('w'); + $this->assertEquals($stream, $fh); + fwrite($fh, 'bar'); + rewind($fh); + $this->assertEquals('bar', fread($stream, 3)); + $this->assertEquals(2, $hooksCalled); + } + + + public function testFOpenReadNotPermitted(): void { + $this->expectException(NotPermittedException::class); + + $root = new Root( + $this->manager, + $this->view, + $this->user, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + $hook = function ($file): void { + throw new \Exception('Hooks are not supposed to be called'); + }; + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => 0])); + + $node = new File($root, $this->view, '/bar/foo'); + $node->fopen('r'); + } + + + public function testFOpenReadWriteNoReadPermissions(): void { + $this->expectException(NotPermittedException::class); + + $root = new Root( + $this->manager, + $this->view, + $this->user, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + $hook = function (): void { + throw new \Exception('Hooks are not supposed to be called'); + }; + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_UPDATE])); + + $node = new File($root, $this->view, '/bar/foo'); + $node->fopen('w'); + } + + + public function testFOpenReadWriteNoWritePermissions(): void { + $this->expectException(NotPermittedException::class); + + $root = new Root( + $this->manager, + $this->view, + $this->user, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + $hook = function (): void { + throw new \Exception('Hooks are not supposed to be called'); + }; + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); + + $node = new File($root, $this->view, '/bar/foo'); + $node->fopen('w'); + } +} diff --git a/tests/lib/Files/Node/FolderTest.php b/tests/lib/Files/Node/FolderTest.php new file mode 100644 index 00000000000..439535cf2c1 --- /dev/null +++ b/tests/lib/Files/Node/FolderTest.php @@ -0,0 +1,1044 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Files\Node; + +use OC\Files\Cache\Cache; +use OC\Files\Cache\CacheEntry; +use OC\Files\Config\CachedMountInfo; +use OC\Files\FileInfo; +use OC\Files\Mount\Manager; +use OC\Files\Mount\MountPoint; +use OC\Files\Node\File; +use OC\Files\Node\Folder; +use OC\Files\Node\Node; +use OC\Files\Node\Root; +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OC\Files\Search\SearchOrder; +use OC\Files\Search\SearchQuery; +use OC\Files\Storage\Storage; +use OC\Files\Storage\Temporary; +use OC\Files\Storage\Wrapper\Jail; +use OC\Files\View; +use OCP\Constants; +use OCP\Files\Cache\ICacheEntry; +use OCP\Files\InvalidPathException; +use OCP\Files\IRootFolder; +use OCP\Files\Mount\IMountPoint; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use OCP\Files\Search\ISearchOrder; +use OCP\Files\Storage\IStorage; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Class FolderTest + * + * @group DB + * + * @package Test\Files\Node + */ +class FolderTest extends NodeTestCase { + protected function createTestNode($root, $view, $path, array $data = [], $internalPath = '', $storage = null) { + $view->expects($this->any()) + ->method('getRoot') + ->willReturn(''); + if ($data || $internalPath || $storage) { + return new Folder($root, $view, $path, $this->getFileInfo($data, $internalPath, $storage)); + } else { + return new Folder($root, $view, $path); + } + } + + protected function getNodeClass() { + return '\OC\Files\Node\Folder'; + } + + protected function getNonExistingNodeClass() { + return '\OC\Files\Node\NonExistingFolder'; + } + + protected function getViewDeleteMethod() { + return 'rmdir'; + } + + public function testGetDirectoryContent(): void { + $manager = $this->createMock(Manager::class); + /** + * @var View|\PHPUnit\Framework\MockObject\MockObject $view + */ + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $this->view->expects($this->any()) + ->method('getDirectoryContent') + ->with('/bar/foo') + ->willReturn([ + new FileInfo('/bar/foo/asd', null, 'foo/asd', ['fileid' => 2, 'path' => '/bar/foo/asd', 'name' => 'asd', 'size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'], null), + new FileInfo('/bar/foo/qwerty', null, 'foo/qwerty', ['fileid' => 3, 'path' => '/bar/foo/qwerty', 'name' => 'qwerty', 'size' => 200, 'mtime' => 55, 'mimetype' => 'httpd/unix-directory'], null), + ]); + $this->view->method('getFileInfo') + ->willReturn($this->createMock(FileInfo::class)); + $this->view->method('getRelativePath') + ->willReturn('/bar/foo'); + + $node = new Folder($root, $this->view, '/bar/foo'); + $children = $node->getDirectoryListing(); + $this->assertEquals(2, count($children)); + $this->assertInstanceOf('\OC\Files\Node\File', $children[0]); + $this->assertInstanceOf('\OC\Files\Node\Folder', $children[1]); + $this->assertEquals('asd', $children[0]->getName()); + $this->assertEquals('qwerty', $children[1]->getName()); + $this->assertEquals(2, $children[0]->getId()); + $this->assertEquals(3, $children[1]->getId()); + } + + public function testGet(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $node = new File($root, $view, '/bar/foo/asd'); + $root->method('get') + ->with('/bar/foo/asd') + ->willReturn($node); + + $parentNode = new Folder($root, $view, '/bar/foo'); + self::assertEquals($node, $parentNode->get('asd')); + } + + public function testNodeExists(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $child = new Folder($root, $view, '/bar/foo/asd'); + + $root->method('get') + ->with('/bar/foo/asd') + ->willReturn($child); + + $node = new Folder($root, $view, '/bar/foo'); + $this->assertTrue($node->nodeExists('asd')); + } + + public function testNodeExistsNotExists(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $root->method('get') + ->with('/bar/foo/asd') + ->willThrowException(new NotFoundException()); + + $node = new Folder($root, $view, '/bar/foo'); + $this->assertFalse($node->nodeExists('asd')); + } + + public function testNewFolder(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $view->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); + + $view->method('mkdir') + ->with('/bar/foo/asd') + ->willReturn(true); + + $node = new Folder($root, $view, '/bar/foo'); + $child = new Folder($root, $view, '/bar/foo/asd', null, $node); + $result = $node->newFolder('asd'); + $this->assertEquals($child, $result); + } + + public function testNewFolderDeepParent(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $view->method('getFileInfo') + ->with('/foobar') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); + + $view->method('mkdir') + ->with('/foobar/asd/sdf') + ->willReturn(true); + + $node = new Folder($root, $view, '/foobar'); + $child = new Folder($root, $view, '/foobar/asd/sdf', null, null); + $result = $node->newFolder('asd/sdf'); + $this->assertEquals($child, $result); + } + + + public function testNewFolderNotPermitted(): void { + $this->expectException(NotPermittedException::class); + + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->method('getUser') + ->willReturn($this->user); + + $view->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); + + $node = new Folder($root, $view, '/bar/foo'); + $node->newFolder('asd'); + } + + public function testNewFile(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $view->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); + + $view->method('touch') + ->with('/bar/foo/asd') + ->willReturn(true); + + $node = new Folder($root, $view, '/bar/foo'); + $child = new File($root, $view, '/bar/foo/asd', null, $node); + $result = $node->newFile('asd'); + $this->assertEquals($child, $result); + } + + + public function testNewFileNotPermitted(): void { + $this->expectException(NotPermittedException::class); + + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->method('getUser') + ->willReturn($this->user); + + $view->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); + + $node = new Folder($root, $view, '/bar/foo'); + $node->newFile('asd'); + } + + public function testGetFreeSpace(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->method('getUser') + ->willReturn($this->user); + + $view->method('free_space') + ->with('/bar/foo') + ->willReturn(100); + + $node = new Folder($root, $view, '/bar/foo'); + $this->assertEquals(100, $node->getFreeSpace()); + } + + public function testSearch(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->method('getUser') + ->willReturn($this->user); + /** @var Storage\IStorage&MockObject $storage */ + $storage = $this->createMock(IStorage::class); + $storage->method('getId')->willReturn('test::1'); + $cache = new Cache($storage); + + $storage->method('getCache') + ->willReturn($cache); + + $storage->expects($this->atLeastOnce()) + ->method('getOwner') + ->with('qwerty') + ->willReturn(false); + + $mount = $this->createMock(IMountPoint::class); + $mount->expects($this->atLeastOnce()) + ->method('getStorage') + ->willReturn($storage); + $mount->expects($this->atLeastOnce()) + ->method('getInternalPath') + ->willReturn('foo'); + + $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('foo', ['size' => 200, 'mtime' => 55, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('foo/qwerty', ['size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain']); + + $root->method('getMountsIn') + ->with('/bar/foo') + ->willReturn([]); + + $root->method('getMount') + ->with('/bar/foo') + ->willReturn($mount); + + $node = new Folder($root, $view, '/bar/foo'); + $result = $node->search('qw'); + $cache->clear(); + $this->assertEquals(1, count($result)); + $this->assertEquals('/bar/foo/qwerty', $result[0]->getPath()); + } + + public function testSearchInRoot(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->onlyMethods(['getUser', 'getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + /** @var \PHPUnit\Framework\MockObject\MockObject|Storage $storage */ + $storage = $this->createMock(IStorage::class); + $storage->method('getId')->willReturn('test::2'); + $cache = new Cache($storage); + + $mount = $this->createMock(IMountPoint::class); + $mount->method('getStorage') + ->willReturn($storage); + $mount->method('getInternalPath') + ->willReturn('files'); + + $storage->method('getCache') + ->willReturn($cache); + $storage->method('getOwner') + ->willReturn('owner'); + + $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('files', ['size' => 200, 'mtime' => 55, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('files/foo', ['size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain']); + + $root->method('getMountsIn') + ->with('') + ->willReturn([]); + + $root->method('getMount') + ->with('') + ->willReturn($mount); + + $result = $root->search('foo'); + $cache->clear(); + $this->assertEquals(1, count($result)); + $this->assertEquals('/foo', $result[0]->getPath()); + } + + public function testSearchInStorageRoot(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->method('getUser') + ->willReturn($this->user); + $storage = $this->createMock(IStorage::class); + $storage->method('getId')->willReturn('test::1'); + $cache = new Cache($storage); + + $mount = $this->createMock(IMountPoint::class); + $mount->method('getStorage') + ->willReturn($storage); + $mount->method('getInternalPath') + ->willReturn(''); + + $storage->method('getCache') + ->willReturn($cache); + $storage->method('getOwner') + ->willReturn('owner'); + + $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('foo', ['size' => 200, 'mtime' => 55, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('foo/qwerty', ['size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain']); + + + $root->method('getMountsIn') + ->with('/bar') + ->willReturn([]); + + $root->method('getMount') + ->with('/bar') + ->willReturn($mount); + + $node = new Folder($root, $view, '/bar'); + $result = $node->search('qw'); + $cache->clear(); + $this->assertEquals(1, count($result)); + $this->assertEquals('/bar/foo/qwerty', $result[0]->getPath()); + } + + public function testSearchSubStorages(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + $storage = $this->createMock(IStorage::class); + $storage->method('getId')->willReturn('test::1'); + $cache = new Cache($storage); + $subStorage = $this->createMock(IStorage::class); + $subStorage->method('getId')->willReturn('test::2'); + $subCache = new Cache($subStorage); + $subMount = $this->getMockBuilder(MountPoint::class)->setConstructorArgs([Temporary::class, ''])->getMock(); + + $mount = $this->createMock(IMountPoint::class); + $mount->method('getStorage') + ->willReturn($storage); + $mount->method('getInternalPath') + ->willReturn('foo'); + + $subMount->method('getStorage') + ->willReturn($subStorage); + + $subMount->method('getMountPoint') + ->willReturn('/bar/foo/bar/'); + + $storage->method('getCache') + ->willReturn($cache); + $storage->method('getOwner') + ->willReturn('owner'); + + $subStorage->method('getCache') + ->willReturn($subCache); + $subStorage->method('getOwner') + ->willReturn('owner'); + + $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('foo', ['size' => 200, 'mtime' => 55, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('foo/qwerty', ['size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain']); + + $subCache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $subCache->insert('asd', ['size' => 200, 'mtime' => 55, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $subCache->insert('asd/qwerty', ['size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain']); + + + $root->method('getMountsIn') + ->with('/bar/foo') + ->willReturn([$subMount]); + + $root->method('getMount') + ->with('/bar/foo') + ->willReturn($mount); + + + $node = new Folder($root, $view, '/bar/foo'); + $result = $node->search('qw'); + $cache->clear(); + $subCache->clear(); + $this->assertEquals(2, count($result)); + } + + public function testIsSubNode(): void { + $rootFolderMock = $this->createMock(IRootFolder::class); + $file = new Node($rootFolderMock, $this->view, '/foo/bar'); + $folder = new Folder($rootFolderMock, $this->view, '/foo'); + $this->assertTrue($folder->isSubNode($file)); + $this->assertFalse($folder->isSubNode($folder)); + + $file = new Node($rootFolderMock, $this->view, '/foobar'); + $this->assertFalse($folder->isSubNode($file)); + } + + public function testGetById(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->onlyMethods(['getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $storage = $this->createMock(Storage::class); + $mount = new MountPoint($storage, '/bar'); + $storage->method('getId')->willReturn(''); + $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([$storage])->getMock(); + + $fileInfo = new CacheEntry(['path' => 'foo/qwerty', 'mimetype' => 'text/plain'], null); + + $storage->method('getCache') + ->willReturn($cache); + $storage->method('getOwner') + ->willReturn('owner'); + + $this->userMountCache->expects($this->any()) + ->method('getMountsForFileId') + ->with(1) + ->willReturn([new CachedMountInfo( + $this->user, + 1, + 0, + '/bar/', + 'test', + 1, + '' + )]); + + $cache->method('get') + ->with(1) + ->willReturn($fileInfo); + + $root->method('getMountsIn') + ->with('/bar/foo') + ->willReturn([]); + + $manager->method('getMountsByMountProvider') + ->willReturn([$mount]); + + $node = new Folder($root, $view, '/bar/foo'); + $result = $node->getById(1); + $this->assertEquals(1, count($result)); + $this->assertEquals('/bar/foo/qwerty', $result[0]->getPath()); + } + + public function testGetByIdMountRoot(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->onlyMethods(['getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $storage = $this->createMock(Storage::class); + $mount = new MountPoint($storage, '/bar'); + $storage->method('getId')->willReturn(''); + $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([$storage])->getMock(); + + $fileInfo = new CacheEntry(['path' => '', 'mimetype' => 'text/plain'], null); + + $storage->method('getCache') + ->willReturn($cache); + $storage->method('getOwner') + ->willReturn('owner'); + + $this->userMountCache->expects($this->any()) + ->method('getMountsForFileId') + ->with(1) + ->willReturn([new CachedMountInfo( + $this->user, + 1, + 0, + '/bar/', + 'test', + 1, + '' + )]); + + $cache->method('get') + ->with(1) + ->willReturn($fileInfo); + + $manager->method('getMountsByMountProvider') + ->willReturn([$mount]); + + $node = new Folder($root, $view, '/bar'); + $result = $node->getById(1); + $this->assertEquals(1, count($result)); + $this->assertEquals('/bar', $result[0]->getPath()); + } + + public function testGetByIdOutsideFolder(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->onlyMethods(['getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $storage = $this->createMock(Storage::class); + $mount = new MountPoint($storage, '/bar'); + $storage->method('getId')->willReturn(''); + $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([$storage])->getMock(); + + $fileInfo = new CacheEntry(['path' => 'foobar', 'mimetype' => 'text/plain'], null); + + $storage->method('getCache') + ->willReturn($cache); + $storage->method('getOwner') + ->willReturn('owner'); + + $this->userMountCache->expects($this->any()) + ->method('getMountsForFileId') + ->with(1) + ->willReturn([new CachedMountInfo( + $this->user, + 1, + 0, + '/bar/', + 'test', + 1, + '' + )]); + + $cache->method('get') + ->with(1) + ->willReturn($fileInfo); + + $manager->method('getMountsByMountProvider') + ->willReturn([$mount]); + + $node = new Folder($root, $view, '/bar/foo'); + $result = $node->getById(1); + $this->assertEquals(0, count($result)); + } + + public function testGetByIdMultipleStorages(): void { + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->onlyMethods(['getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $storage = $this->createMock(Storage::class); + $mount1 = new MountPoint($storage, '/bar'); + $mount2 = new MountPoint($storage, '/bar/foo/asd'); + $storage->method('getId')->willReturn(''); + $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([$storage])->getMock(); + + $fileInfo = new CacheEntry(['path' => 'foo/qwerty', 'mimetype' => 'text/plain'], null); + + $storage->method('getCache') + ->willReturn($cache); + $storage->method('getOwner') + ->willReturn('owner'); + + $this->userMountCache->method('getMountsForFileId') + ->with(1) + ->willReturn([ + new CachedMountInfo( + $this->user, + 1, + 0, + '/bar/', + 'test', + 1, + '' + ), + ]); + + $cache->method('get') + ->with(1) + ->willReturn($fileInfo); + + $manager->method('getMountsByMountProvider') + ->willReturn([$mount1, $mount2]); + + $node = new Folder($root, $view, '/bar/foo'); + $result = $node->getById(1); + $this->assertEquals(2, count($result)); + $this->assertEquals('/bar/foo/qwerty', $result[0]->getPath()); + $this->assertEquals('/bar/foo/asd/foo/qwerty', $result[1]->getPath()); + } + + public static function uniqueNameProvider(): array { + return [ + // input, existing, expected + ['foo', [], 'foo'], + ['foo', ['foo'], 'foo (2)'], + ['foo', ['foo', 'foo (2)'], 'foo (3)'], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('uniqueNameProvider')] + public function testGetUniqueName($name, $existingFiles, $expected): void { + $manager = $this->createMock(Manager::class); + $folderPath = '/bar/foo'; + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->onlyMethods(['getUser', 'getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + + $view->expects($this->any()) + ->method('file_exists') + ->willReturnCallback(function ($path) use ($existingFiles, $folderPath) { + foreach ($existingFiles as $existing) { + if ($folderPath . '/' . $existing === $path) { + return true; + } + } + return false; + }); + + $node = new Folder($root, $view, $folderPath); + $this->assertEquals($expected, $node->getNonExistingName($name)); + } + + public function testRecent(): void { + $manager = $this->createMock(Manager::class); + $folderPath = '/bar/foo'; + $view = $this->getRootViewMock(); + /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Files\Node\Root $root */ + $root = $this->getMockBuilder(Root::class) + ->onlyMethods(['getUser', 'getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + /** @var \PHPUnit\Framework\MockObject\MockObject|FileInfo $folderInfo */ + $folderInfo = $this->getMockBuilder(FileInfo::class) + ->disableOriginalConstructor()->getMock(); + + $baseTime = time(); + $storage = new Temporary(); + $mount = new MountPoint($storage, ''); + + $folderInfo->expects($this->any()) + ->method('getMountPoint') + ->willReturn($mount); + $root->method('getMount') + ->willReturn($mount); + $root->method('getMountsIn') + ->willReturn([]); + + $cache = $storage->getCache(); + + $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('bar', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('bar/foo', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('bar/asd', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $id1 = $cache->put('bar/foo/inside.txt', [ + 'storage_mtime' => $baseTime, + 'mtime' => $baseTime, + 'mimetype' => 'text/plain', + 'size' => 3, + 'permissions' => Constants::PERMISSION_ALL, + ]); + $id2 = $cache->put('bar/foo/old.txt', [ + 'storage_mtime' => $baseTime - 100, + 'mtime' => $baseTime - 100, + 'mimetype' => 'text/plain', + 'size' => 3, + 'permissions' => Constants::PERMISSION_READ, + ]); + $cache->put('bar/asd/outside.txt', [ + 'storage_mtime' => $baseTime, + 'mtime' => $baseTime, + 'mimetype' => 'text/plain', + 'size' => 3, + ]); + $id3 = $cache->put('bar/foo/older.txt', [ + 'storage_mtime' => $baseTime - 600, + 'mtime' => $baseTime - 600, + 'mimetype' => 'text/plain', + 'size' => 3, + 'permissions' => Constants::PERMISSION_ALL, + ]); + + $node = new Folder($root, $view, $folderPath, $folderInfo); + + + $nodes = $node->getRecent(5); + $ids = array_map(function (Node $node) { + return (int)$node->getId(); + }, $nodes); + $this->assertEquals([$id1, $id2, $id3], $ids); + } + + public function testRecentFolder(): void { + $manager = $this->createMock(Manager::class); + $folderPath = '/bar/foo'; + $view = $this->getRootViewMock(); + /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Files\Node\Root $root */ + $root = $this->getMockBuilder(Root::class) + ->onlyMethods(['getUser', 'getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + /** @var \PHPUnit\Framework\MockObject\MockObject|FileInfo $folderInfo */ + $folderInfo = $this->getMockBuilder(FileInfo::class) + ->disableOriginalConstructor()->getMock(); + + $baseTime = time(); + $storage = new Temporary(); + $mount = new MountPoint($storage, ''); + + $folderInfo->expects($this->any()) + ->method('getMountPoint') + ->willReturn($mount); + + $root->method('getMount') + ->willReturn($mount); + $root->method('getMountsIn') + ->willReturn([]); + + $cache = $storage->getCache(); + + $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('bar', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('bar/foo', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $id1 = $cache->put('bar/foo/folder', [ + 'storage_mtime' => $baseTime, + 'mtime' => $baseTime, + 'mimetype' => \OCP\Files\FileInfo::MIMETYPE_FOLDER, + 'size' => 3, + 'permissions' => 0, + ]); + $id2 = $cache->put('bar/foo/folder/bar.txt', [ + 'storage_mtime' => $baseTime, + 'mtime' => $baseTime, + 'mimetype' => 'text/plain', + 'size' => 3, + 'parent' => $id1, + 'permissions' => Constants::PERMISSION_ALL, + ]); + $id3 = $cache->put('bar/foo/folder/asd.txt', [ + 'storage_mtime' => $baseTime - 100, + 'mtime' => $baseTime - 100, + 'mimetype' => 'text/plain', + 'size' => 3, + 'parent' => $id1, + 'permissions' => Constants::PERMISSION_ALL, + ]); + + $node = new Folder($root, $view, $folderPath, $folderInfo); + + + $nodes = $node->getRecent(5); + $ids = array_map(function (Node $node) { + return (int)$node->getId(); + }, $nodes); + $this->assertEquals([$id2, $id3], $ids); + $this->assertEquals($baseTime, $nodes[0]->getMTime()); + $this->assertEquals($baseTime - 100, $nodes[1]->getMTime()); + } + + public function testRecentJail(): void { + $manager = $this->createMock(Manager::class); + $folderPath = '/bar/foo'; + $view = $this->getRootViewMock(); + /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Files\Node\Root $root */ + $root = $this->getMockBuilder(Root::class) + ->onlyMethods(['getUser', 'getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + /** @var \PHPUnit\Framework\MockObject\MockObject|FileInfo $folderInfo */ + $folderInfo = $this->getMockBuilder(FileInfo::class) + ->disableOriginalConstructor()->getMock(); + + $baseTime = time(); + $storage = new Temporary(); + $jail = new Jail([ + 'storage' => $storage, + 'root' => 'folder', + ]); + $mount = new MountPoint($jail, '/bar/foo'); + + $folderInfo->expects($this->any()) + ->method('getMountPoint') + ->willReturn($mount); + $root->method('getMount') + ->willReturn($mount); + $root->method('getMountsIn') + ->willReturn([]); + + $cache = $storage->getCache(); + + $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('folder', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $id1 = $cache->put('folder/inside.txt', [ + 'storage_mtime' => $baseTime, + 'mtime' => $baseTime, + 'mimetype' => 'text/plain', + 'size' => 3, + 'permissions' => Constants::PERMISSION_ALL, + ]); + + $cache->put('outside.txt', [ + 'storage_mtime' => $baseTime - 100, + 'mtime' => $baseTime - 100, + 'mimetype' => 'text/plain', + 'size' => 3, + ]); + + $node = new Folder($root, $view, $folderPath, $folderInfo); + + $nodes = $node->getRecent(5); + $ids = array_map(function (Node $node) { + return (int)$node->getId(); + }, $nodes); + $this->assertEquals([$id1], $ids); + } + + public static function offsetLimitProvider(): array { + return [ + [0, 10, ['/bar/foo/foo1', '/bar/foo/foo2', '/bar/foo/foo3', '/bar/foo/foo4', '/bar/foo/sub1/foo5', '/bar/foo/sub1/foo6', '/bar/foo/sub2/foo7', '/bar/foo/sub2/foo8'], []], + [0, 5, ['/bar/foo/foo1', '/bar/foo/foo2', '/bar/foo/foo3', '/bar/foo/foo4', '/bar/foo/sub1/foo5'], []], + [0, 2, ['/bar/foo/foo1', '/bar/foo/foo2'], []], + [3, 2, ['/bar/foo/foo4', '/bar/foo/sub1/foo5'], []], + [3, 5, ['/bar/foo/foo4', '/bar/foo/sub1/foo5', '/bar/foo/sub1/foo6', '/bar/foo/sub2/foo7', '/bar/foo/sub2/foo8'], []], + [5, 2, ['/bar/foo/sub1/foo6', '/bar/foo/sub2/foo7'], []], + [6, 2, ['/bar/foo/sub2/foo7', '/bar/foo/sub2/foo8'], []], + [7, 2, ['/bar/foo/sub2/foo8'], []], + [10, 2, [], []], + [0, 5, ['/bar/foo/sub2/foo7', '/bar/foo/foo1', '/bar/foo/sub1/foo5', '/bar/foo/foo2', '/bar/foo/foo3'], [new SearchOrder(ISearchOrder::DIRECTION_ASCENDING, 'mtime')]], + [3, 2, ['/bar/foo/foo2', '/bar/foo/foo3'], [new SearchOrder(ISearchOrder::DIRECTION_ASCENDING, 'mtime')]], + [0, 5, ['/bar/foo/sub1/foo5', '/bar/foo/sub1/foo6', '/bar/foo/sub2/foo7', '/bar/foo/foo1', '/bar/foo/foo2'], [ + new SearchOrder(ISearchOrder::DIRECTION_DESCENDING, 'size'), + new SearchOrder(ISearchOrder::DIRECTION_ASCENDING, 'mtime') + ]], + ]; + } + + /** + * @param int $offset + * @param int $limit + * @param string[] $expectedPaths + * @param ISearchOrder[] $ordering + * @throws NotFoundException + * @throws InvalidPathException + */ + #[\PHPUnit\Framework\Attributes\DataProvider('offsetLimitProvider')] + public function testSearchSubStoragesLimitOffset(int $offset, int $limit, array $expectedPaths, array $ordering): void { + if (!$ordering) { + $ordering = [new SearchOrder(ISearchOrder::DIRECTION_ASCENDING, 'fileid')]; + } + + $manager = $this->createMock(Manager::class); + $view = $this->getRootViewMock(); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + $storage = $this->createMock(IStorage::class); + $storage->method('getId')->willReturn('test::1'); + $cache = new Cache($storage); + $subStorage1 = $this->createMock(IStorage::class); + $subStorage1->method('getId')->willReturn('test::2'); + $subCache1 = new Cache($subStorage1); + $subMount1 = $this->getMockBuilder(MountPoint::class)->setConstructorArgs([Temporary::class, ''])->getMock(); + $subStorage2 = $this->createMock(IStorage::class); + $subStorage2->method('getId')->willReturn('test::3'); + $subCache2 = new Cache($subStorage2); + $subMount2 = $this->getMockBuilder(MountPoint::class)->setConstructorArgs([Temporary::class, ''])->getMock(); + + $mount = $this->createMock(IMountPoint::class); + $mount->method('getStorage') + ->willReturn($storage); + $mount->method('getInternalPath') + ->willReturn('foo'); + + $subMount1->method('getStorage') + ->willReturn($subStorage1); + + $subMount1->method('getMountPoint') + ->willReturn('/bar/foo/sub1/'); + + $storage->method('getCache') + ->willReturn($cache); + $storage->method('getOwner') + ->willReturn('owner'); + + $subStorage1->method('getCache') + ->willReturn($subCache1); + $subStorage1->method('getOwner') + ->willReturn('owner'); + + $subMount2->method('getStorage') + ->willReturn($subStorage2); + + $subMount2->method('getMountPoint') + ->willReturn('/bar/foo/sub2/'); + + $subStorage2->method('getCache') + ->willReturn($subCache2); + $subStorage2->method('getOwner') + ->willReturn('owner'); + + + $cache->insert('', ['size' => 0, 'mtime' => 10, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('foo', ['size' => 0, 'mtime' => 10, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $cache->insert('foo/foo1', ['size' => 200, 'mtime' => 10, 'mimetype' => 'text/plain']); + $cache->insert('foo/foo2', ['size' => 200, 'mtime' => 20, 'mimetype' => 'text/plain']); + $cache->insert('foo/foo3', ['size' => 200, 'mtime' => 30, 'mimetype' => 'text/plain']); + $cache->insert('foo/foo4', ['size' => 200, 'mtime' => 40, 'mimetype' => 'text/plain']); + + $subCache1->insert('', ['size' => 0, 'mtime' => 10, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $subCache1->insert('foo5', ['size' => 300, 'mtime' => 15, 'mimetype' => 'text/plain']); + $subCache1->insert('foo6', ['size' => 300, 'mtime' => 50, 'mimetype' => 'text/plain']); + + $subCache2->insert('', ['size' => 0, 'mtime' => 10, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $subCache2->insert('foo7', ['size' => 200, 'mtime' => 5, 'mimetype' => 'text/plain']); + $subCache2->insert('foo8', ['size' => 200, 'mtime' => 60, 'mimetype' => 'text/plain']); + + $root->method('getMountsIn') + ->with('/bar/foo') + ->willReturn([$subMount1, $subMount2]); + + $root->method('getMount') + ->with('/bar/foo') + ->willReturn($mount); + + $node = new Folder($root, $view, '/bar/foo'); + $comparison = new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%foo%'); + $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + $comparison, + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', ICacheEntry::DIRECTORY_MIMETYPE)]), + ]); + $query = new SearchQuery($operator, $limit, $offset, $ordering); + $result = $node->search($query); + $cache->clear(); + $subCache1->clear(); + $subCache2->clear(); + $ids = array_map(function (Node $info) { + return $info->getPath(); + }, $result); + $this->assertEquals($expectedPaths, $ids); + } +} diff --git a/tests/lib/Files/Node/HookConnectorTest.php b/tests/lib/Files/Node/HookConnectorTest.php new file mode 100644 index 00000000000..3f3957bab1d --- /dev/null +++ b/tests/lib/Files/Node/HookConnectorTest.php @@ -0,0 +1,327 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Files\Node; + +use OC\Files\Filesystem; +use OC\Files\Node\HookConnector; +use OC\Files\Node\Root; +use OC\Files\Storage\Temporary; +use OC\Files\View; +use OC\Memcache\ArrayCache; +use OCP\EventDispatcher\GenericEvent as APIGenericEvent; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Config\IUserMountCache; +use OCP\Files\Events\Node\AbstractNodeEvent; +use OCP\Files\Events\Node\AbstractNodesEvent; +use OCP\Files\Events\Node\BeforeNodeCopiedEvent; +use OCP\Files\Events\Node\BeforeNodeCreatedEvent; +use OCP\Files\Events\Node\BeforeNodeDeletedEvent; +use OCP\Files\Events\Node\BeforeNodeRenamedEvent; +use OCP\Files\Events\Node\BeforeNodeTouchedEvent; +use OCP\Files\Events\Node\BeforeNodeWrittenEvent; +use OCP\Files\Events\Node\NodeCopiedEvent; +use OCP\Files\Events\Node\NodeCreatedEvent; +use OCP\Files\Events\Node\NodeDeletedEvent; +use OCP\Files\Events\Node\NodeRenamedEvent; +use OCP\Files\Events\Node\NodeTouchedEvent; +use OCP\Files\Events\Node\NodeWrittenEvent; +use OCP\Files\Node; +use OCP\ICacheFactory; +use OCP\IUserManager; +use OCP\Server; +use Psr\Log\LoggerInterface; +use Symfony\Component\EventDispatcher\GenericEvent; +use Test\TestCase; +use Test\Traits\MountProviderTrait; +use Test\Traits\UserTrait; + +/** + * Class HookConnectorTest + * + * @group DB + * + * @package Test\Files\Node + */ +class HookConnectorTest extends TestCase { + use UserTrait; + use MountProviderTrait; + + /** @var IEventDispatcher */ + protected $eventDispatcher; + + private LoggerInterface $logger; + + /** @var View */ + private $view; + + /** @var Root */ + private $root; + + /** @var string */ + private $userId; + + protected function setUp(): void { + parent::setUp(); + $this->userId = $this->getUniqueID(); + $this->createUser($this->userId, 'pass'); + // this will setup the FS + $this->loginAsUser($this->userId); + $this->registerMount($this->userId, new Temporary(), '/' . $this->userId . '/files/'); + $cacheFactory = $this->createMock(ICacheFactory::class); + $cacheFactory->method('createLocal') + ->willReturnCallback(function () { + return new ArrayCache(); + }); + $this->view = new View(); + $this->root = new Root( + Filesystem::getMountManager(), + $this->view, + Server::get(IUserManager::class)->get($this->userId), + Server::get(IUserMountCache::class), + $this->createMock(LoggerInterface::class), + $this->createMock(IUserManager::class), + $this->createMock(IEventDispatcher::class), + $cacheFactory, + ); + $this->eventDispatcher = Server::get(IEventDispatcher::class); + $this->logger = Server::get(LoggerInterface::class); + } + + protected function tearDown(): void { + parent::tearDown(); + \OC_Hook::clear('OC_Filesystem'); + \OC_Util::tearDownFS(); + } + + public static function viewToNodeProvider(): array { + return [ + [function (): void { + Filesystem::file_put_contents('test.txt', 'asd'); + }, 'preWrite', '\OCP\Files::preWrite', BeforeNodeWrittenEvent::class], + [function (): void { + Filesystem::file_put_contents('test.txt', 'asd'); + }, 'postWrite', '\OCP\Files::postWrite', NodeWrittenEvent::class], + [function (): void { + Filesystem::file_put_contents('test.txt', 'asd'); + }, 'preCreate', '\OCP\Files::preCreate', BeforeNodeCreatedEvent::class], + [function (): void { + Filesystem::file_put_contents('test.txt', 'asd'); + }, 'postCreate', '\OCP\Files::postCreate', NodeCreatedEvent::class], + [function (): void { + Filesystem::mkdir('test.txt'); + }, 'preCreate', '\OCP\Files::preCreate', BeforeNodeCreatedEvent::class], + [function (): void { + Filesystem::mkdir('test.txt'); + }, 'postCreate', '\OCP\Files::postCreate', NodeCreatedEvent::class], + [function (): void { + Filesystem::touch('test.txt'); + }, 'preTouch', '\OCP\Files::preTouch', BeforeNodeTouchedEvent::class], + [function (): void { + Filesystem::touch('test.txt'); + }, 'postTouch', '\OCP\Files::postTouch', NodeTouchedEvent::class], + [function (): void { + Filesystem::touch('test.txt'); + }, 'preCreate', '\OCP\Files::preCreate', BeforeNodeCreatedEvent::class], + [function (): void { + Filesystem::touch('test.txt'); + }, 'postCreate', '\OCP\Files::postCreate', NodeCreatedEvent::class], + [function (): void { + Filesystem::file_put_contents('test.txt', 'asd'); + Filesystem::unlink('test.txt'); + }, 'preDelete', '\OCP\Files::preDelete', BeforeNodeDeletedEvent::class], + [function (): void { + Filesystem::file_put_contents('test.txt', 'asd'); + Filesystem::unlink('test.txt'); + }, 'postDelete', '\OCP\Files::postDelete', NodeDeletedEvent::class], + [function (): void { + Filesystem::mkdir('test.txt'); + Filesystem::rmdir('test.txt'); + }, 'preDelete', '\OCP\Files::preDelete', BeforeNodeDeletedEvent::class], + [function (): void { + Filesystem::mkdir('test.txt'); + Filesystem::rmdir('test.txt'); + }, 'postDelete', '\OCP\Files::postDelete', NodeDeletedEvent::class], + ]; + } + + /** + * @param callable $operation + * @param string $expectedHook + */ + #[\PHPUnit\Framework\Attributes\DataProvider('viewToNodeProvider')] + public function testViewToNode(callable $operation, $expectedHook, $expectedLegacyEvent, $expectedEvent): void { + $connector = new HookConnector($this->root, $this->view, $this->eventDispatcher, $this->logger); + $connector->viewToNode(); + $hookCalled = false; + /** @var Node $hookNode */ + $hookNode = null; + + $this->root->listen('\OC\Files', $expectedHook, function ($node) use (&$hookNode, &$hookCalled): void { + $hookCalled = true; + $hookNode = $node; + }); + + $dispatcherCalled = false; + /** @var Node $dispatcherNode */ + $dispatcherNode = null; + $this->eventDispatcher->addListener($expectedLegacyEvent, function ($event) use (&$dispatcherCalled, &$dispatcherNode): void { + /** @var GenericEvent|APIGenericEvent $event */ + $dispatcherCalled = true; + $dispatcherNode = $event->getSubject(); + }); + + $newDispatcherCalled = false; + $newDispatcherNode = null; + $this->eventDispatcher->addListener($expectedEvent, function ($event) use ($expectedEvent, &$newDispatcherCalled, &$newDispatcherNode): void { + if ($event instanceof $expectedEvent) { + /** @var AbstractNodeEvent $event */ + $newDispatcherCalled = true; + $newDispatcherNode = $event->getNode(); + } + }); + + $operation(); + + $this->assertTrue($hookCalled); + $this->assertEquals('/' . $this->userId . '/files/test.txt', $hookNode->getPath()); + + $this->assertTrue($dispatcherCalled); + $this->assertEquals('/' . $this->userId . '/files/test.txt', $dispatcherNode->getPath()); + + $this->assertTrue($newDispatcherCalled); + $this->assertEquals('/' . $this->userId . '/files/test.txt', $newDispatcherNode->getPath()); + } + + public static function viewToNodeProviderCopyRename(): array { + return [ + [function (): void { + Filesystem::file_put_contents('source', 'asd'); + Filesystem::rename('source', 'target'); + }, 'preRename', '\OCP\Files::preRename', BeforeNodeRenamedEvent::class], + [function (): void { + Filesystem::file_put_contents('source', 'asd'); + Filesystem::rename('source', 'target'); + }, 'postRename', '\OCP\Files::postRename', NodeRenamedEvent::class], + [function (): void { + Filesystem::file_put_contents('source', 'asd'); + Filesystem::copy('source', 'target'); + }, 'preCopy', '\OCP\Files::preCopy', BeforeNodeCopiedEvent::class], + [function (): void { + Filesystem::file_put_contents('source', 'asd'); + Filesystem::copy('source', 'target'); + }, 'postCopy', '\OCP\Files::postCopy', NodeCopiedEvent::class], + ]; + } + + /** + * @param callable $operation + * @param string $expectedHook + */ + #[\PHPUnit\Framework\Attributes\DataProvider('viewToNodeProviderCopyRename')] + public function testViewToNodeCopyRename(callable $operation, $expectedHook, $expectedLegacyEvent, $expectedEvent): void { + $connector = new HookConnector($this->root, $this->view, $this->eventDispatcher, $this->logger); + $connector->viewToNode(); + $hookCalled = false; + /** @var Node $hookSourceNode */ + $hookSourceNode = null; + /** @var Node $hookTargetNode */ + $hookTargetNode = null; + + $this->root->listen('\OC\Files', $expectedHook, function ($sourceNode, $targetNode) use (&$hookCalled, &$hookSourceNode, &$hookTargetNode): void { + $hookCalled = true; + $hookSourceNode = $sourceNode; + $hookTargetNode = $targetNode; + }); + + $dispatcherCalled = false; + /** @var Node $dispatcherSourceNode */ + $dispatcherSourceNode = null; + /** @var Node $dispatcherTargetNode */ + $dispatcherTargetNode = null; + $this->eventDispatcher->addListener($expectedLegacyEvent, function ($event) use (&$dispatcherSourceNode, &$dispatcherTargetNode, &$dispatcherCalled): void { + /** @var GenericEvent|APIGenericEvent $event */ + $dispatcherCalled = true; + [$dispatcherSourceNode, $dispatcherTargetNode] = $event->getSubject(); + }); + + $newDispatcherCalled = false; + /** @var Node $dispatcherSourceNode */ + $newDispatcherSourceNode = null; + /** @var Node $dispatcherTargetNode */ + $newDispatcherTargetNode = null; + $this->eventDispatcher->addListener($expectedEvent, function ($event) use ($expectedEvent, &$newDispatcherSourceNode, &$newDispatcherTargetNode, &$newDispatcherCalled): void { + if ($event instanceof $expectedEvent) { + /** @var AbstractNodesEvent$event */ + $newDispatcherCalled = true; + $newDispatcherSourceNode = $event->getSource(); + $newDispatcherTargetNode = $event->getTarget(); + } + }); + + $operation(); + + $this->assertTrue($hookCalled); + $this->assertEquals('/' . $this->userId . '/files/source', $hookSourceNode->getPath()); + $this->assertEquals('/' . $this->userId . '/files/target', $hookTargetNode->getPath()); + + $this->assertTrue($dispatcherCalled); + $this->assertEquals('/' . $this->userId . '/files/source', $dispatcherSourceNode->getPath()); + $this->assertEquals('/' . $this->userId . '/files/target', $dispatcherTargetNode->getPath()); + + $this->assertTrue($newDispatcherCalled); + $this->assertEquals('/' . $this->userId . '/files/source', $newDispatcherSourceNode->getPath()); + $this->assertEquals('/' . $this->userId . '/files/target', $newDispatcherTargetNode->getPath()); + } + + public function testPostDeleteMeta(): void { + $connector = new HookConnector($this->root, $this->view, $this->eventDispatcher, $this->logger); + $connector->viewToNode(); + $hookCalled = false; + /** @var Node $hookNode */ + $hookNode = null; + + $this->root->listen('\OC\Files', 'postDelete', function ($node) use (&$hookNode, &$hookCalled): void { + $hookCalled = true; + $hookNode = $node; + }); + + $dispatcherCalled = false; + /** @var Node $dispatcherNode */ + $dispatcherNode = null; + $this->eventDispatcher->addListener('\OCP\Files::postDelete', function ($event) use (&$dispatcherCalled, &$dispatcherNode): void { + /** @var GenericEvent|APIGenericEvent $event */ + $dispatcherCalled = true; + $dispatcherNode = $event->getSubject(); + }); + + $newDispatcherCalled = false; + /** @var Node $dispatcherNode */ + $newDispatcherNode = null; + $this->eventDispatcher->addListener(NodeDeletedEvent::class, function ($event) use (&$newDispatcherCalled, &$newDispatcherNode): void { + if ($event instanceof NodeDeletedEvent) { + /** @var AbstractNodeEvent $event */ + $newDispatcherCalled = true; + $newDispatcherNode = $event->getNode(); + } + }); + + Filesystem::file_put_contents('test.txt', 'asd'); + $info = Filesystem::getFileInfo('test.txt'); + Filesystem::unlink('test.txt'); + + $this->assertTrue($hookCalled); + $this->assertEquals($hookNode->getId(), $info->getId()); + + $this->assertTrue($dispatcherCalled); + $this->assertEquals($dispatcherNode->getId(), $info->getId()); + + $this->assertTrue($newDispatcherCalled); + $this->assertEquals($newDispatcherNode->getId(), $info->getId()); + } +} diff --git a/tests/lib/Files/Node/IntegrationTest.php b/tests/lib/Files/Node/IntegrationTest.php new file mode 100644 index 00000000000..f059afa1625 --- /dev/null +++ b/tests/lib/Files/Node/IntegrationTest.php @@ -0,0 +1,151 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Files\Node; + +use OC\Files\Node\Root; +use OC\Files\Storage\Storage; +use OC\Files\Storage\Temporary; +use OC\Files\View; +use OC\Memcache\ArrayCache; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Config\IUserMountCache; +use OCP\Files\Mount\IMountManager; +use OCP\ICacheFactory; +use OCP\IUserManager; +use OCP\Server; +use Psr\Log\LoggerInterface; +use Test\Traits\UserTrait; + +/** + * Class IntegrationTest + * + * @group DB + * + * @package Test\Files\Node + */ +class IntegrationTest extends \Test\TestCase { + use UserTrait; + + /** + * @var \OC\Files\Node\Root $root + */ + private $root; + + /** + * @var Storage[] + */ + private $storages; + + /** + * @var View $view + */ + private $view; + + protected function setUp(): void { + parent::setUp(); + + $manager = Server::get(IMountManager::class); + + \OC_Hook::clear('OC_Filesystem'); + + $user = $this->createUser($this->getUniqueID('user'), ''); + $this->loginAsUser($user->getUID()); + $cacheFactory = $this->createMock(ICacheFactory::class); + $cacheFactory->method('createLocal') + ->willReturnCallback(function () { + return new ArrayCache(); + }); + + $this->view = new View(); + $this->root = new Root( + $manager, + $this->view, + $user, + Server::get(IUserMountCache::class), + $this->createMock(LoggerInterface::class), + $this->createMock(IUserManager::class), + $this->createMock(IEventDispatcher::class), + $cacheFactory, + ); + $storage = new Temporary([]); + $subStorage = new Temporary([]); + $this->storages[] = $storage; + $this->storages[] = $subStorage; + $this->root->mount($storage, '/'); + $this->root->mount($subStorage, '/substorage/'); + $manager->removeMount('/' . $user->getUID()); + } + + protected function tearDown(): void { + foreach ($this->storages as $storage) { + $storage->getCache()->clear(); + } + + $this->logout(); + parent::tearDown(); + } + + public function testBasicFile(): void { + $file = $this->root->newFile('/foo.txt'); + $this->assertCount(2, $this->root->getDirectoryListing()); + $this->assertTrue($this->root->nodeExists('/foo.txt')); + $id = $file->getId(); + $this->assertInstanceOf('\OC\Files\Node\File', $file); + $file->putContent('qwerty'); + $this->assertEquals('text/plain', $file->getMimeType()); + $this->assertEquals('qwerty', $file->getContent()); + $this->assertFalse($this->root->nodeExists('/bar.txt')); + $target = $file->move('/bar.txt'); + $this->assertEquals($id, $target->getId()); + $this->assertEquals($id, $file->getId()); + $this->assertFalse($this->root->nodeExists('/foo.txt')); + $this->assertTrue($this->root->nodeExists('/bar.txt')); + $this->assertEquals('bar.txt', $file->getName()); + $this->assertEquals('bar.txt', $file->getInternalPath()); + + $file->move('/substorage/bar.txt'); + $this->assertEquals($id, $file->getId()); + $this->assertEquals('qwerty', $file->getContent()); + } + + public function testBasicFolder(): void { + $folder = $this->root->newFolder('/foo'); + $this->assertTrue($this->root->nodeExists('/foo')); + $file = $folder->newFile('/bar'); + $this->assertTrue($this->root->nodeExists('/foo/bar')); + $file->putContent('qwerty'); + + $listing = $folder->getDirectoryListing(); + $this->assertEquals(1, count($listing)); + $this->assertEquals($file->getId(), $listing[0]->getId()); + $this->assertEquals($file->getStorage(), $listing[0]->getStorage()); + + + $rootListing = $this->root->getDirectoryListing(); + $this->assertEquals(2, count($rootListing)); + + $folder->move('/asd'); + /** + * @var \OC\Files\Node\File $file + */ + $file = $folder->get('/bar'); + $this->assertInstanceOf('\OC\Files\Node\File', $file); + $this->assertFalse($this->root->nodeExists('/foo/bar')); + $this->assertTrue($this->root->nodeExists('/asd/bar')); + $this->assertEquals('qwerty', $file->getContent()); + $folder->move('/substorage/foo'); + /** + * @var \OC\Files\Node\File $file + */ + $file = $folder->get('/bar'); + $this->assertInstanceOf('\OC\Files\Node\File', $file); + $this->assertTrue($this->root->nodeExists('/substorage/foo/bar')); + $this->assertEquals('qwerty', $file->getContent()); + } +} diff --git a/tests/lib/Files/Node/NodeTestCase.php b/tests/lib/Files/Node/NodeTestCase.php new file mode 100644 index 00000000000..4aecd0fef11 --- /dev/null +++ b/tests/lib/Files/Node/NodeTestCase.php @@ -0,0 +1,793 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Files\Node; + +use OC\Files\FileInfo; +use OC\Files\Mount\Manager; +use OC\Files\Node\File; +use OC\Files\Node\Folder; +use OC\Files\Node\Root; +use OC\Files\Storage\Storage; +use OC\Files\View; +use OC\Memcache\ArrayCache; +use OC\User\User; +use OCP\Constants; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Config\IUserMountCache; +use OCP\Files\InvalidPathException; +use OCP\Files\IRootFolder; +use OCP\Files\Mount\IMountPoint; +use OCP\Files\Node; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\Files\Storage\IStorage; +use OCP\ICacheFactory; +use OCP\IUser; +use OCP\IUserManager; +use Psr\Log\LoggerInterface; + +/** + * Class NodeTest + * + * @package Test\Files\Node + */ +abstract class NodeTestCase extends \Test\TestCase { + /** @var User */ + protected $user; + /** @var \OC\Files\Mount\Manager */ + protected $manager; + /** @var View|\PHPUnit\Framework\MockObject\MockObject */ + protected $view; + /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject */ + protected $root; + /** @var IUserMountCache|\PHPUnit\Framework\MockObject\MockObject */ + protected $userMountCache; + /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */ + protected $logger; + /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */ + protected $userManager; + /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */ + protected $eventDispatcher; + /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ + protected $cacheFactory; + + protected function setUp(): void { + parent::setUp(); + + $this->user = $this->createMock(IUser::class); + $this->manager = $this->getMockBuilder(Manager::class) + ->disableOriginalConstructor() + ->getMock(); + $this->view = $this->getMockBuilder(View::class) + ->disableOriginalConstructor() + ->getMock(); + $this->view->expects($this->any()) + ->method('getRoot') + ->willReturn(''); + $this->userMountCache = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache') + ->disableOriginalConstructor() + ->getMock(); + $this->logger = $this->createMock(LoggerInterface::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + $this->cacheFactory = $this->createMock(ICacheFactory::class); + $this->cacheFactory->method('createLocal') + ->willReturnCallback(function () { + return new ArrayCache(); + }); + $this->root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->getMock(); + } + + /** + * @return View|\PHPUnit\Framework\MockObject\MockObject $view + */ + protected function getRootViewMock() { + $view = $this->createMock(View::class); + $view->expects($this->any()) + ->method('getRoot') + ->willReturn(''); + return $view; + } + + /** + * @param IRootFolder $root + * @param View $view + * @param string $path + * @return Node + */ + abstract protected function createTestNode($root, $view, $path, array $data = [], $internalPath = '', $storage = null); + + /** + * @return string + */ + abstract protected function getNodeClass(); + + /** + * @return string + */ + abstract protected function getNonExistingNodeClass(); + + /** + * @return string + */ + abstract protected function getViewDeleteMethod(); + + protected function getMockStorage() { + $storage = $this->getMockBuilder(IStorage::class) + ->disableOriginalConstructor() + ->getMock(); + $storage->expects($this->any()) + ->method('getId') + ->willReturn('home::someuser'); + return $storage; + } + + protected function getFileInfo($data, $internalPath = '', $storage = null) { + $mount = $this->createMock(IMountPoint::class); + $mount->method('getStorage') + ->willReturn($storage); + return new FileInfo('', $this->getMockStorage(), $internalPath, $data, $mount); + } + + public function testDelete(): void { + $this->root->expects($this->exactly(2)) + ->method('emit') + ->willReturn(true); + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); + + $this->view->expects($this->once()) + ->method($this->getViewDeleteMethod()) + ->with('/bar/foo') + ->willReturn(true); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $node->delete(); + } + + public function testDeleteHooks(): void { + $test = $this; + $hooksRun = 0; + /** + * @param \OC\Files\Node\File $node + */ + $preListener = function ($node) use (&$test, &$hooksRun): void { + $test->assertInstanceOf($this->getNodeClass(), $node); + $test->assertEquals('foo', $node->getInternalPath()); + $test->assertEquals('/bar/foo', $node->getPath()); + $test->assertEquals(1, $node->getId()); + $hooksRun++; + }; + + /** + * @param \OC\Files\Node\File $node + */ + $postListener = function ($node) use (&$test, &$hooksRun): void { + $test->assertInstanceOf($this->getNonExistingNodeClass(), $node); + $test->assertEquals('foo', $node->getInternalPath()); + $test->assertEquals('/bar/foo', $node->getPath()); + $test->assertEquals(1, $node->getId()); + $test->assertEquals('text/plain', $node->getMimeType()); + $hooksRun++; + }; + + $root = new Root( + $this->manager, + $this->view, + $this->user, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + + $root->listen('\OC\Files', 'preDelete', $preListener); + $root->listen('\OC\Files', 'postDelete', $postListener); + + $this->view->expects($this->any()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1, 'mimetype' => 'text/plain'], 'foo')); + + $this->view->expects($this->once()) + ->method($this->getViewDeleteMethod()) + ->with('/bar/foo') + ->willReturn(true); + + $node = $this->createTestNode($root, $this->view, '/bar/foo'); + $node->delete(); + $this->assertEquals(2, $hooksRun); + } + + + public function testDeleteNotPermitted(): void { + $this->expectException(NotPermittedException::class); + + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $node->delete(); + } + + + public function testStat(): void { + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $stat = [ + 'fileid' => 1, + 'size' => 100, + 'etag' => 'qwerty', + 'mtime' => 50, + 'permissions' => 0 + ]; + + $this->view->expects($this->once()) + ->method('stat') + ->with('/bar/foo') + ->willReturn($stat); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $this->assertEquals($stat, $node->stat()); + } + + public function testGetId(): void { + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $stat = $this->getFileInfo([ + 'fileid' => 1, + 'size' => 100, + 'etag' => 'qwerty', + 'mtime' => 50 + ]); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($stat); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $this->assertEquals(1, $node->getId()); + } + + public function testGetSize(): void { + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + + $stat = $this->getFileInfo([ + 'fileid' => 1, + 'size' => 100, + 'etag' => 'qwerty', + 'mtime' => 50 + ]); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($stat); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $this->assertEquals(100, $node->getSize()); + } + + public function testGetEtag(): void { + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $stat = $this->getFileInfo([ + 'fileid' => 1, + 'size' => 100, + 'etag' => 'qwerty', + 'mtime' => 50 + ]); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($stat); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $this->assertEquals('qwerty', $node->getEtag()); + } + + public function testGetMTime(): void { + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $stat = $this->getFileInfo([ + 'fileid' => 1, + 'size' => 100, + 'etag' => 'qwerty', + 'mtime' => 50 + ]); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($stat); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $this->assertEquals(50, $node->getMTime()); + } + + public function testGetStorage(): void { + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + /** + * @var Storage|\PHPUnit\Framework\MockObject\MockObject $storage + */ + $storage = $this->getMockBuilder('\OC\Files\Storage\Storage') + ->disableOriginalConstructor() + ->getMock(); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo', [], 'foo', $storage); + $this->assertEquals($storage, $node->getStorage()); + } + + public function testGetPath(): void { + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $this->assertEquals('/bar/foo', $node->getPath()); + } + + public function testGetInternalPath(): void { + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + /** + * @var Storage|\PHPUnit\Framework\MockObject\MockObject $storage + */ + $storage = $this->getMockBuilder('\OC\Files\Storage\Storage') + ->disableOriginalConstructor() + ->getMock(); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo([], 'foo')); + + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $this->assertEquals('foo', $node->getInternalPath()); + } + + public function testGetName(): void { + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $this->assertEquals('foo', $node->getName()); + } + + public function testTouchSetMTime(): void { + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $this->view->expects($this->once()) + ->method('touch') + ->with('/bar/foo', 100) + ->willReturn(true); + + $this->view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $node->touch(100); + $this->assertEquals(100, $node->getMTime()); + } + + public function testTouchHooks(): void { + $test = $this; + $hooksRun = 0; + /** + * @param \OC\Files\Node\File $node + */ + $preListener = function ($node) use (&$test, &$hooksRun): void { + $test->assertEquals('foo', $node->getInternalPath()); + $test->assertEquals('/bar/foo', $node->getPath()); + $hooksRun++; + }; + + /** + * @param \OC\Files\Node\File $node + */ + $postListener = function ($node) use (&$test, &$hooksRun): void { + $test->assertEquals('foo', $node->getInternalPath()); + $test->assertEquals('/bar/foo', $node->getPath()); + $hooksRun++; + }; + + $root = new Root( + $this->manager, + $this->view, + $this->user, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + $root->listen('\OC\Files', 'preTouch', $preListener); + $root->listen('\OC\Files', 'postTouch', $postListener); + + $this->view->expects($this->once()) + ->method('touch') + ->with('/bar/foo', 100) + ->willReturn(true); + + $this->view->expects($this->any()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL], 'foo')); + + $node = $this->createTestNode($root, $this->view, '/bar/foo'); + $node->touch(100); + $this->assertEquals(2, $hooksRun); + } + + + public function testTouchNotPermitted(): void { + $this->expectException(NotPermittedException::class); + + $this->root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $this->view->expects($this->any()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $node->touch(100); + } + + + public function testInvalidPath(): void { + $this->expectException(InvalidPathException::class); + + $node = $this->createTestNode($this->root, $this->view, '/../foo'); + $node->getFileInfo(); + } + + public function testCopySameStorage(): void { + $this->view->expects($this->any()) + ->method('copy') + ->with('/bar/foo', '/bar/asd') + ->willReturn(true); + + $this->view->expects($this->any()) + ->method('getFileInfo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 3])); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $parentNode = new Folder($this->root, $this->view, '/bar'); + $newNode = $this->createTestNode($this->root, $this->view, '/bar/asd'); + + $this->root->method('get') + ->willReturnMap([ + ['/bar/asd', $newNode], + ['/bar', $parentNode] + ]); + + $target = $node->copy('/bar/asd'); + $this->assertInstanceOf($this->getNodeClass(), $target); + $this->assertEquals(3, $target->getId()); + } + + + public function testCopyNotPermitted(): void { + $this->expectException(NotPermittedException::class); + + /** + * @var Storage|\PHPUnit\Framework\MockObject\MockObject $storage + */ + $storage = $this->createMock('\OC\Files\Storage\Storage'); + + $this->root->expects($this->never()) + ->method('getMount'); + + $storage->expects($this->never()) + ->method('copy'); + + $this->view->expects($this->any()) + ->method('getFileInfo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ, 'fileid' => 3])); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $parentNode = new Folder($this->root, $this->view, '/bar'); + + $this->root->expects($this->once()) + ->method('get') + ->willReturnMap([ + ['/bar', $parentNode] + ]); + + $node->copy('/bar/asd'); + } + + + public function testCopyNoParent(): void { + $this->expectException(NotFoundException::class); + + $this->view->expects($this->never()) + ->method('copy'); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + + $this->root->expects($this->once()) + ->method('get') + ->with('/bar/asd') + ->willThrowException(new NotFoundException()); + + $node->copy('/bar/asd/foo'); + } + + + public function testCopyParentIsFile(): void { + $this->expectException(NotPermittedException::class); + + $this->view->expects($this->never()) + ->method('copy'); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $parentNode = new File($this->root, $this->view, '/bar'); + + $this->root->expects($this->once()) + ->method('get') + ->willReturnMap([ + ['/bar', $parentNode] + ]); + + $node->copy('/bar/asd'); + } + + public function testMoveSameStorage(): void { + $this->view->expects($this->any()) + ->method('rename') + ->with('/bar/foo', '/bar/asd') + ->willReturn(true); + + $this->view->expects($this->any()) + ->method('getFileInfo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1])); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $parentNode = new Folder($this->root, $this->view, '/bar'); + + $this->root->expects($this->any()) + ->method('get') + ->willReturnMap([['/bar', $parentNode], ['/bar/asd', $node]]); + + $target = $node->move('/bar/asd'); + $this->assertInstanceOf($this->getNodeClass(), $target); + $this->assertEquals(1, $target->getId()); + $this->assertEquals('/bar/asd', $node->getPath()); + } + + public static function moveOrCopyProvider(): array { + return [ + ['move', 'rename', 'preRename', 'postRename'], + ['copy', 'copy', 'preCopy', 'postCopy'], + ]; + } + + /** + * @param string $operationMethod + * @param string $viewMethod + * @param string $preHookName + * @param string $postHookName + */ + #[\PHPUnit\Framework\Attributes\DataProvider('moveOrCopyProvider')] + public function testMoveCopyHooks($operationMethod, $viewMethod, $preHookName, $postHookName): void { + /** @var IRootFolder|\PHPUnit\Framework\MockObject\MockObject $root */ + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) + ->onlyMethods(['get']) + ->getMock(); + + $this->view->expects($this->any()) + ->method($viewMethod) + ->with('/bar/foo', '/bar/asd') + ->willReturn(true); + + $this->view->expects($this->any()) + ->method('getFileInfo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1])); + + /** + * @var \OC\Files\Node\File|\PHPUnit\Framework\MockObject\MockObject $node + */ + $node = $this->createTestNode($root, $this->view, '/bar/foo'); + $parentNode = new Folder($root, $this->view, '/bar'); + $targetTestNode = $this->createTestNode($root, $this->view, '/bar/asd'); + + $root->expects($this->any()) + ->method('get') + ->willReturnMap([['/bar', $parentNode], ['/bar/asd', $targetTestNode]]); + + $hooksRun = 0; + + $preListener = function (Node $sourceNode, Node $targetNode) use (&$hooksRun, $node): void { + $this->assertSame($node, $sourceNode); + $this->assertInstanceOf($this->getNodeClass(), $sourceNode); + $this->assertInstanceOf($this->getNonExistingNodeClass(), $targetNode); + $this->assertEquals('/bar/asd', $targetNode->getPath()); + $hooksRun++; + }; + + $postListener = function (Node $sourceNode, Node $targetNode) use (&$hooksRun, $node, $targetTestNode): void { + $this->assertSame($node, $sourceNode); + $this->assertNotSame($node, $targetNode); + $this->assertSame($targetTestNode, $targetNode); + $this->assertInstanceOf($this->getNodeClass(), $sourceNode); + $this->assertInstanceOf($this->getNodeClass(), $targetNode); + $hooksRun++; + }; + + $preWriteListener = function (Node $targetNode) use (&$hooksRun): void { + $this->assertInstanceOf($this->getNonExistingNodeClass(), $targetNode); + $this->assertEquals('/bar/asd', $targetNode->getPath()); + $hooksRun++; + }; + + $postWriteListener = function (Node $targetNode) use (&$hooksRun, $targetTestNode): void { + $this->assertSame($targetTestNode, $targetNode); + $hooksRun++; + }; + + $root->listen('\OC\Files', $preHookName, $preListener); + $root->listen('\OC\Files', 'preWrite', $preWriteListener); + $root->listen('\OC\Files', $postHookName, $postListener); + $root->listen('\OC\Files', 'postWrite', $postWriteListener); + + $node->$operationMethod('/bar/asd'); + + $this->assertEquals(4, $hooksRun); + } + + + public function testMoveNotPermitted(): void { + $this->expectException(NotPermittedException::class); + + $this->view->expects($this->any()) + ->method('getFileInfo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); + + $this->view->expects($this->never()) + ->method('rename'); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $parentNode = new Folder($this->root, $this->view, '/bar'); + + $this->root->expects($this->once()) + ->method('get') + ->with('/bar') + ->willReturn($parentNode); + + $node->move('/bar/asd'); + } + + + public function testMoveNoParent(): void { + $this->expectException(NotFoundException::class); + + /** + * @var Storage|\PHPUnit\Framework\MockObject\MockObject $storage + */ + $storage = $this->createMock('\OC\Files\Storage\Storage'); + + $storage->expects($this->never()) + ->method('rename'); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + + $this->root->expects($this->once()) + ->method('get') + ->with('/bar') + ->willThrowException(new NotFoundException()); + + $node->move('/bar/asd'); + } + + + public function testMoveParentIsFile(): void { + $this->expectException(NotPermittedException::class); + + $this->view->expects($this->never()) + ->method('rename'); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $parentNode = new File($this->root, $this->view, '/bar'); + + $this->root->expects($this->once()) + ->method('get') + ->with('/bar') + ->willReturn($parentNode); + + $node->move('/bar/asd'); + } + + + public function testMoveFailed(): void { + $this->expectException(NotPermittedException::class); + + $this->view->expects($this->any()) + ->method('rename') + ->with('/bar/foo', '/bar/asd') + ->willReturn(false); + + $this->view->expects($this->any()) + ->method('getFileInfo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1])); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $parentNode = new Folder($this->root, $this->view, '/bar'); + + $this->root->expects($this->any()) + ->method('get') + ->willReturnMap([['/bar', $parentNode], ['/bar/asd', $node]]); + + $node->move('/bar/asd'); + } + + + public function testCopyFailed(): void { + $this->expectException(NotPermittedException::class); + + $this->view->expects($this->any()) + ->method('copy') + ->with('/bar/foo', '/bar/asd') + ->willReturn(false); + + $this->view->expects($this->any()) + ->method('getFileInfo') + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1])); + + $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $parentNode = new Folder($this->root, $this->view, '/bar'); + + $this->root->expects($this->any()) + ->method('get') + ->willReturnMap([['/bar', $parentNode], ['/bar/asd', $node]]); + + $node->copy('/bar/asd'); + } +} diff --git a/tests/lib/Files/Node/RootTest.php b/tests/lib/Files/Node/RootTest.php new file mode 100644 index 00000000000..d90e6a2cc6e --- /dev/null +++ b/tests/lib/Files/Node/RootTest.php @@ -0,0 +1,253 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Files\Node; + +use OC\Files\FileInfo; +use OC\Files\Mount\Manager; +use OC\Files\Node\Folder; +use OC\Files\Node\Root; +use OC\Files\Storage\Storage; +use OC\Files\View; +use OC\Memcache\ArrayCache; +use OC\User\NoUserException; +use OC\User\User; +use OCP\Cache\CappedMemoryCache; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Config\IUserMountCache; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\ICacheFactory; +use OCP\IUser; +use OCP\IUserManager; +use Psr\Log\LoggerInterface; + +/** + * Class RootTest + * + * @package Test\Files\Node + */ +class RootTest extends \Test\TestCase { + /** @var User */ + private $user; + /** @var \OC\Files\Mount\Manager */ + private $manager; + /** @var IUserMountCache|\PHPUnit\Framework\MockObject\MockObject */ + private $userMountCache; + /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $logger; + /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */ + private $userManager; + /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */ + private $eventDispatcher; + /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ + protected $cacheFactory; + + protected function setUp(): void { + parent::setUp(); + + $this->user = $this->createMock(IUser::class); + $this->manager = $this->getMockBuilder(Manager::class) + ->disableOriginalConstructor() + ->getMock(); + $this->userMountCache = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache') + ->disableOriginalConstructor() + ->getMock(); + $this->logger = $this->createMock(LoggerInterface::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + $this->cacheFactory = $this->createMock(ICacheFactory::class); + $this->cacheFactory->method('createLocal') + ->willReturnCallback(function () { + return new ArrayCache(); + }); + } + + /** + * @return View|\PHPUnit\Framework\MockObject\MockObject $view + */ + protected function getRootViewMock() { + $view = $this->createMock(View::class); + $view->expects($this->any()) + ->method('getRoot') + ->willReturn(''); + return $view; + } + + protected function getFileInfo($data) { + return new FileInfo('', null, '', $data, null); + } + + public function testGet(): void { + /** + * @var Storage $storage + */ + $storage = $this->getMockBuilder('\OC\Files\Storage\Storage') + ->disableOriginalConstructor() + ->getMock(); + $view = $this->getRootViewMock(); + $root = new Root( + $this->manager, + $view, + $this->user, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + + $view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn($this->getFileInfo(['fileid' => 10, 'path' => 'bar/foo', 'name', 'mimetype' => 'text/plain'])); + + $root->mount($storage, ''); + $node = $root->get('/bar/foo'); + $this->assertEquals(10, $node->getId()); + $this->assertInstanceOf('\OC\Files\Node\File', $node); + } + + + public function testGetNotFound(): void { + $this->expectException(NotFoundException::class); + + /** + * @var Storage $storage + */ + $storage = $this->getMockBuilder('\OC\Files\Storage\Storage') + ->disableOriginalConstructor() + ->getMock(); + $view = $this->getRootViewMock(); + $root = new Root( + $this->manager, + $view, + $this->user, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + + $view->expects($this->once()) + ->method('getFileInfo') + ->with('/bar/foo') + ->willReturn(false); + + $root->mount($storage, ''); + $root->get('/bar/foo'); + } + + + public function testGetInvalidPath(): void { + $this->expectException(NotPermittedException::class); + + $view = $this->getRootViewMock(); + $root = new Root( + $this->manager, + $view, + $this->user, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + + $root->get('/../foo'); + } + + + public function testGetNoStorages(): void { + $this->expectException(NotFoundException::class); + + $view = $this->getRootViewMock(); + $root = new Root( + $this->manager, + $view, + $this->user, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + + $root->get('/bar/foo'); + } + + public function testGetUserFolder(): void { + $root = new Root( + $this->manager, + $this->getRootViewMock(), + $this->user, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + $user = $this->createMock(IUser::class); + $user + ->expects($this->once()) + ->method('getUID') + ->willReturn('MyUserId'); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('MyUserId') + ->willReturn($user); + /** @var CappedMemoryCache|\PHPUnit\Framework\MockObject\MockObject $cappedMemoryCache */ + $cappedMemoryCache = $this->createMock(CappedMemoryCache::class); + $cappedMemoryCache + ->expects($this->once()) + ->method('hasKey') + ->willReturn(true); + $folder = $this->createMock(Folder::class); + $cappedMemoryCache + ->expects($this->once()) + ->method('get') + ->with('MyUserId') + ->willReturn($folder); + + $this->invokePrivate($root, 'userFolderCache', [$cappedMemoryCache]); + $this->assertEquals($folder, $root->getUserFolder('MyUserId')); + } + + + public function testGetUserFolderWithNoUserObj(): void { + $this->expectException(NoUserException::class); + $this->expectExceptionMessage('Backends provided no user object'); + + $root = new Root( + $this->createMock(Manager::class), + $this->getRootViewMock(), + null, + $this->userMountCache, + $this->logger, + $this->userManager, + $this->eventDispatcher, + $this->cacheFactory, + ); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('NotExistingUser') + ->willReturn(null); + $this->logger + ->expects($this->once()) + ->method('error') + ->with( + 'Backends provided no user object for NotExistingUser', + $this->anything() + ); + + $root->getUserFolder('NotExistingUser'); + } +} |