diff options
Diffstat (limited to 'apps/dav/tests/unit/Files/FileSearchBackendTest.php')
-rw-r--r-- | apps/dav/tests/unit/Files/FileSearchBackendTest.php | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/apps/dav/tests/unit/Files/FileSearchBackendTest.php b/apps/dav/tests/unit/Files/FileSearchBackendTest.php new file mode 100644 index 00000000000..c6d6f85347b --- /dev/null +++ b/apps/dav/tests/unit/Files/FileSearchBackendTest.php @@ -0,0 +1,421 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\DAV\Tests\unit\Files; + +use OC\Files\Search\SearchComparison; +use OC\Files\Search\SearchQuery; +use OC\Files\View; +use OCA\DAV\Connector\Sabre\Directory; +use OCA\DAV\Connector\Sabre\File; +use OCA\DAV\Connector\Sabre\FilesPlugin; +use OCA\DAV\Connector\Sabre\ObjectTree; +use OCA\DAV\Connector\Sabre\Server; +use OCA\DAV\Files\FileSearchBackend; +use OCP\Files\FileInfo; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use OCP\Files\Search\ISearchQuery; +use OCP\FilesMetadata\IFilesMetadataManager; +use OCP\IUser; +use OCP\Share\IManager; +use PHPUnit\Framework\MockObject\MockObject; +use SearchDAV\Backend\SearchPropertyDefinition; +use SearchDAV\Query\Limit; +use SearchDAV\Query\Literal; +use SearchDAV\Query\Operator; +use SearchDAV\Query\Query; +use SearchDAV\Query\Scope; +use Test\TestCase; + +class FileSearchBackendTest extends TestCase { + private ObjectTree&MockObject $tree; + private Server&MockObject $server; + private IUser&MockObject $user; + private IRootFolder&MockObject $rootFolder; + private IManager&MockObject $shareManager; + private View&MockObject $view; + private Folder&MockObject $searchFolder; + private Directory&MockObject $davFolder; + private FileSearchBackend $search; + + protected function setUp(): void { + parent::setUp(); + + $this->user = $this->createMock(IUser::class); + $this->user->expects($this->any()) + ->method('getUID') + ->willReturn('test'); + + $this->tree = $this->createMock(ObjectTree::class); + $this->server = $this->createMock(Server::class); + $this->view = $this->createMock(View::class); + $this->rootFolder = $this->createMock(IRootFolder::class); + $this->shareManager = $this->createMock(IManager::class); + $this->searchFolder = $this->createMock(Folder::class); + $fileInfo = $this->createMock(FileInfo::class); + $this->davFolder = $this->createMock(Directory::class); + + $this->view->expects($this->any()) + ->method('getRoot') + ->willReturn(''); + + $this->view->expects($this->any()) + ->method('getRelativePath') + ->willReturnArgument(0); + + $this->davFolder->expects($this->any()) + ->method('getFileInfo') + ->willReturn($fileInfo); + + $this->rootFolder->expects($this->any()) + ->method('get') + ->willReturn($this->searchFolder); + + $filesMetadataManager = $this->createMock(IFilesMetadataManager::class); + + $this->search = new FileSearchBackend($this->server, $this->tree, $this->user, $this->rootFolder, $this->shareManager, $this->view, $filesMetadataManager); + } + + public function testSearchFilename(): void { + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($this->davFolder); + + $this->searchFolder->expects($this->once()) + ->method('search') + ->with(new SearchQuery( + new SearchComparison( + ISearchComparison::COMPARE_EQUAL, + 'name', + 'foo' + ), + 0, + 0, + [], + $this->user + )) + ->willReturn([ + new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path'), + ]); + + $query = $this->getBasicQuery(Operator::OPERATION_EQUAL, '{DAV:}displayname', 'foo'); + $result = $this->search->search($query); + + $this->assertCount(1, $result); + $this->assertEquals('/files/test/test/path', $result[0]->href); + } + + public function testSearchMimetype(): void { + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($this->davFolder); + + $this->searchFolder->expects($this->once()) + ->method('search') + ->with(new SearchQuery( + new SearchComparison( + ISearchComparison::COMPARE_EQUAL, + 'mimetype', + 'foo' + ), + 0, + 0, + [], + $this->user + )) + ->willReturn([ + new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path'), + ]); + + $query = $this->getBasicQuery(Operator::OPERATION_EQUAL, '{DAV:}getcontenttype', 'foo'); + $result = $this->search->search($query); + + $this->assertCount(1, $result); + $this->assertEquals('/files/test/test/path', $result[0]->href); + } + + public function testSearchSize(): void { + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($this->davFolder); + + $this->searchFolder->expects($this->once()) + ->method('search') + ->with(new SearchQuery( + new SearchComparison( + ISearchComparison::COMPARE_GREATER_THAN, + 'size', + 10 + ), + 0, + 0, + [], + $this->user + )) + ->willReturn([ + new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path'), + ]); + + $query = $this->getBasicQuery(Operator::OPERATION_GREATER_THAN, FilesPlugin::SIZE_PROPERTYNAME, 10); + $result = $this->search->search($query); + + $this->assertCount(1, $result); + $this->assertEquals('/files/test/test/path', $result[0]->href); + } + + public function testSearchMtime(): void { + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($this->davFolder); + + $this->searchFolder->expects($this->once()) + ->method('search') + ->with(new SearchQuery( + new SearchComparison( + ISearchComparison::COMPARE_GREATER_THAN, + 'mtime', + 10 + ), + 0, + 0, + [], + $this->user + )) + ->willReturn([ + new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path'), + ]); + + $query = $this->getBasicQuery(Operator::OPERATION_GREATER_THAN, '{DAV:}getlastmodified', 10); + $result = $this->search->search($query); + + $this->assertCount(1, $result); + $this->assertEquals('/files/test/test/path', $result[0]->href); + } + + public function testSearchIsCollection(): void { + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($this->davFolder); + + $this->searchFolder->expects($this->once()) + ->method('search') + ->with(new SearchQuery( + new SearchComparison( + ISearchComparison::COMPARE_EQUAL, + 'mimetype', + FileInfo::MIMETYPE_FOLDER + ), + 0, + 0, + [], + $this->user + )) + ->willReturn([ + new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path'), + ]); + + $query = $this->getBasicQuery(Operator::OPERATION_IS_COLLECTION, 'yes'); + $result = $this->search->search($query); + + $this->assertCount(1, $result); + $this->assertEquals('/files/test/test/path', $result[0]->href); + } + + + public function testSearchInvalidProp(): void { + $this->expectException(\InvalidArgumentException::class); + + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($this->davFolder); + + $this->searchFolder->expects($this->never()) + ->method('search'); + + $query = $this->getBasicQuery(Operator::OPERATION_EQUAL, '{DAV:}getetag', 'foo'); + $this->search->search($query); + } + + private function getBasicQuery(string $type, string $property, int|string|null $value = null) { + $scope = new Scope('/', 'infinite'); + $scope->path = '/'; + $from = [$scope]; + $orderBy = []; + $select = []; + if (is_null($value)) { + $where = new Operator( + $type, + [new Literal($property)] + ); + } else { + $where = new Operator( + $type, + [new SearchPropertyDefinition($property, true, true, true), new Literal($value)] + ); + } + $limit = new Limit(); + + return new Query($select, $from, $where, $orderBy, $limit); + } + + + public function testSearchNonFolder(): void { + $this->expectException(\InvalidArgumentException::class); + + $davNode = $this->createMock(File::class); + + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($davNode); + + $query = $this->getBasicQuery(Operator::OPERATION_EQUAL, '{DAV:}displayname', 'foo'); + $this->search->search($query); + } + + public function testSearchLimitOwnerBasic(): void { + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($this->davFolder); + + /** @var ISearchQuery|null $receivedQuery */ + $receivedQuery = null; + $this->searchFolder + ->method('search') + ->willReturnCallback(function ($query) use (&$receivedQuery) { + $receivedQuery = $query; + return [ + new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path'), + ]; + }); + + $query = $this->getBasicQuery(Operator::OPERATION_EQUAL, FilesPlugin::OWNER_ID_PROPERTYNAME, $this->user->getUID()); + $this->search->search($query); + + $this->assertNotNull($receivedQuery); + $this->assertTrue($receivedQuery->limitToHome()); + + /** @var ISearchBinaryOperator $operator */ + $operator = $receivedQuery->getSearchOperation(); + $this->assertInstanceOf(ISearchBinaryOperator::class, $operator); + $this->assertEquals(ISearchBinaryOperator::OPERATOR_AND, $operator->getType()); + $this->assertEmpty($operator->getArguments()); + } + + public function testSearchLimitOwnerNested(): void { + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($this->davFolder); + + /** @var ISearchQuery|null $receivedQuery */ + $receivedQuery = null; + $this->searchFolder + ->method('search') + ->willReturnCallback(function ($query) use (&$receivedQuery) { + $receivedQuery = $query; + return [ + new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path'), + ]; + }); + + $query = $this->getBasicQuery(Operator::OPERATION_EQUAL, FilesPlugin::OWNER_ID_PROPERTYNAME, $this->user->getUID()); + $query->where = new Operator( + Operator::OPERATION_AND, + [ + new Operator( + Operator::OPERATION_EQUAL, + [new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true), new Literal('image/png')] + ), + new Operator( + Operator::OPERATION_EQUAL, + [new SearchPropertyDefinition(FilesPlugin::OWNER_ID_PROPERTYNAME, true, true, true), new Literal($this->user->getUID())] + ), + ] + ); + $this->search->search($query); + + $this->assertNotNull($receivedQuery); + $this->assertTrue($receivedQuery->limitToHome()); + + /** @var ISearchBinaryOperator $operator */ + $operator = $receivedQuery->getSearchOperation(); + $this->assertInstanceOf(ISearchBinaryOperator::class, $operator); + $this->assertEquals(ISearchBinaryOperator::OPERATOR_AND, $operator->getType()); + $this->assertCount(2, $operator->getArguments()); + + /** @var ISearchBinaryOperator $operator */ + $operator = $operator->getArguments()[1]; + $this->assertInstanceOf(ISearchBinaryOperator::class, $operator); + $this->assertEquals(ISearchBinaryOperator::OPERATOR_AND, $operator->getType()); + $this->assertEmpty($operator->getArguments()); + } + + public function testSearchOperatorLimit(): void { + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($this->davFolder); + + $innerOperator = new Operator( + Operator::OPERATION_EQUAL, + [new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true), new Literal('image/png')] + ); + // 5 child operators + $level1Operator = new Operator( + Operator::OPERATION_AND, + [ + $innerOperator, + $innerOperator, + $innerOperator, + $innerOperator, + $innerOperator, + ] + ); + // 5^2 = 25 child operators + $level2Operator = new Operator( + Operator::OPERATION_AND, + [ + $level1Operator, + $level1Operator, + $level1Operator, + $level1Operator, + $level1Operator, + ] + ); + // 5^3 = 125 child operators + $level3Operator = new Operator( + Operator::OPERATION_AND, + [ + $level2Operator, + $level2Operator, + $level2Operator, + $level2Operator, + $level2Operator, + ] + ); + + $query = $this->getBasicQuery(Operator::OPERATION_EQUAL, FilesPlugin::OWNER_ID_PROPERTYNAME, $this->user->getUID()); + $query->where = $level3Operator; + $this->expectException(\InvalidArgumentException::class); + $this->search->search($query); + } + + public function testPreloadPropertyFor(): void { + $node1 = $this->createMock(File::class); + $node2 = $this->createMock(Directory::class); + $nodes = [$node1, $node2]; + $requestProperties = ['{DAV:}getcontenttype', '{DAV:}getlastmodified']; + + $this->server->expects($this->once()) + ->method('emit') + ->with('preloadProperties', [$nodes, $requestProperties]); + + $this->search->preloadPropertyFor($nodes, $requestProperties); + } +} |