diff options
Diffstat (limited to 'tests/lib/Files')
80 files changed, 5358 insertions, 2970 deletions
diff --git a/tests/lib/Files/AppData/AppDataTest.php b/tests/lib/Files/AppData/AppDataTest.php index d06d7a9a91e..ed006622504 100644 --- a/tests/lib/Files/AppData/AppDataTest.php +++ b/tests/lib/Files/AppData/AppDataTest.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\AppData; @@ -66,7 +50,7 @@ class AppDataTest extends \Test\TestCase { return $appFolder; } - public function testGetFolder() { + public function testGetFolder(): void { $folder = $this->createMock(Folder::class); $this->rootFolder->expects($this->once()) @@ -78,7 +62,7 @@ class AppDataTest extends \Test\TestCase { $this->assertInstanceOf(ISimpleFolder::class, $result); } - public function testNewFolder() { + public function testNewFolder(): void { $appFolder = $this->setupAppFolder(); $folder = $this->createMock(Folder::class); @@ -92,7 +76,7 @@ class AppDataTest extends \Test\TestCase { $this->assertInstanceOf(ISimpleFolder::class, $result); } - public function testGetDirectoryListing() { + public function testGetDirectoryListing(): void { $appFolder = $this->setupAppFolder(); $file = $this->createMock(File::class); diff --git a/tests/lib/Files/AppData/FactoryTest.php b/tests/lib/Files/AppData/FactoryTest.php index 8161bc515ec..6092c931091 100644 --- a/tests/lib/Files/AppData/FactoryTest.php +++ b/tests/lib/Files/AppData/FactoryTest.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\AppData; @@ -45,7 +29,7 @@ class FactoryTest extends \Test\TestCase { $this->factory = new Factory($this->rootFolder, $this->systemConfig); } - public function testGet() { + public function testGet(): void { $this->rootFolder->expects($this->never()) ->method($this->anything()); $this->systemConfig->expects($this->never()) diff --git a/tests/lib/Files/Cache/CacheTest.php b/tests/lib/Files/Cache/CacheTest.php index 7f8e1af1577..383962b7224 100644 --- a/tests/lib/Files/Cache/CacheTest.php +++ b/tests/lib/Files/Cache/CacheTest.php @@ -1,22 +1,30 @@ <?php + /** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Cache; -use Doctrine\DBAL\Platforms\MySqlPlatform; use OC\Files\Cache\Cache; +use OC\Files\Cache\CacheEntry; use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchQuery; +use OC\Files\Storage\Temporary; +use OC\User\User; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Cache\ICacheEntry; use OCP\Files\Search\ISearchComparison; +use OCP\IDBConnection; +use OCP\ITagManager; use OCP\IUser; +use OCP\IUserManager; +use OCP\Server; -class LongId extends \OC\Files\Storage\Temporary { - public function getId() { +class LongId extends Temporary { + public function getId(): string { return 'long:' . str_repeat('foo', 50) . parent::getId(); } } @@ -30,28 +38,47 @@ class LongId extends \OC\Files\Storage\Temporary { */ class CacheTest extends \Test\TestCase { /** - * @var \OC\Files\Storage\Temporary $storage ; + * @var Temporary $storage ; */ protected $storage; /** - * @var \OC\Files\Storage\Temporary $storage2 ; + * @var Temporary $storage2 ; */ protected $storage2; /** - * @var \OC\Files\Cache\Cache $cache + * @var Cache $cache */ protected $cache; /** - * @var \OC\Files\Cache\Cache $cache2 + * @var Cache $cache2 */ protected $cache2; - public function testGetNumericId() { + protected function setUp(): void { + parent::setUp(); + + $this->storage = new Temporary([]); + $this->storage2 = new Temporary([]); + $this->cache = new Cache($this->storage); + $this->cache2 = new Cache($this->storage2); + $this->cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $this->cache2->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + } + + protected function tearDown(): void { + if ($this->cache) { + $this->cache->clear(); + } + + parent::tearDown(); + } + + public function testGetNumericId(): void { $this->assertNotNull($this->cache->getNumericStorageId()); } - public function testSimple() { + public function testSimple(): void { $file1 = 'foo'; $file2 = 'foo/bar'; $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder']; @@ -97,33 +124,56 @@ class CacheTest extends \Test\TestCase { $this->assertEquals($cacheData1, $this->cache->get($id1)); } - public function testPartial() { + public function testCacheEntryGetters(): void { + $file1 = 'foo'; + $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/file']; + + $id1 = $this->cache->put($file1, $data1); + $entry = $this->cache->get($file1); + + $this->assertEquals($entry->getId(), $id1); + $this->assertEquals($entry->getStorageId(), $this->cache->getNumericStorageId()); + $this->assertEquals($entry->getPath(), 'foo'); + $this->assertEquals($entry->getName(), 'foo'); + $this->assertEquals($entry->getMimeType(), 'foo/file'); + $this->assertEquals($entry->getMimePart(), 'foo'); + $this->assertEquals($entry->getSize(), 100); + $this->assertEquals($entry->getMTime(), 50); + $this->assertEquals($entry->getStorageMTime(), 50); + $this->assertEquals($entry->getEtag(), null); + $this->assertEquals($entry->getPermissions(), 0); + $this->assertEquals($entry->isEncrypted(), false); + $this->assertEquals($entry->getMetadataEtag(), null); + $this->assertEquals($entry->getCreationTime(), null); + $this->assertEquals($entry->getUploadTime(), null); + $this->assertEquals($entry->getUnencryptedSize(), 100); + } + + public function testPartial(): void { $file1 = 'foo'; $this->cache->put($file1, ['size' => 10]); - $this->assertEquals(['size' => 10], $this->cache->get($file1)); + $this->assertEquals(new CacheEntry(['size' => 10]), $this->cache->get($file1)); $this->cache->put($file1, ['mtime' => 15]); - $this->assertEquals(['size' => 10, 'mtime' => 15], $this->cache->get($file1)); + $this->assertEquals(new CacheEntry(['size' => 10, 'mtime' => 15]), $this->cache->get($file1)); $this->cache->put($file1, ['size' => 12]); - $this->assertEquals(['size' => 12, 'mtime' => 15], $this->cache->get($file1)); + $this->assertEquals(new CacheEntry(['size' => 12, 'mtime' => 15]), $this->cache->get($file1)); } - /** - * @dataProvider folderDataProvider - */ - public function testFolder($folder) { + #[\PHPUnit\Framework\Attributes\DataProvider('folderDataProvider')] + public function testFolder($folder): void { if (strpos($folder, 'F09F9890')) { // 4 byte UTF doesn't work on mysql - $params = \OC::$server->get(\OC\DB\Connection::class)->getParams(); - if (\OC::$server->getDatabaseConnection()->getDatabasePlatform() instanceof MySqlPlatform && $params['charset'] !== 'utf8mb4') { + $params = Server::get(\OC\DB\Connection::class)->getParams(); + if (Server::get(IDBConnection::class)->getDatabaseProvider() === IDBConnection::PLATFORM_MYSQL && $params['charset'] !== 'utf8mb4') { $this->markTestSkipped('MySQL doesn\'t support 4 byte UTF-8'); } } $file2 = $folder . '/bar'; $file3 = $folder . '/foo'; - $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; + $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; $fileData = []; $fileData['bar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file']; $fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file']; @@ -162,8 +212,8 @@ class CacheTest extends \Test\TestCase { $this->assertFalse($this->cache->inCache($folder . '/bar')); } - public function testRemoveRecursive() { - $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; + public function testRemoveRecursive(): void { + $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; $fileData = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'text/plain']; $folders = ['folder', 'folder/subfolder', 'folder/sub2', 'folder/sub2/sub3']; $files = ['folder/foo.txt', 'folder/bar.txt', 'folder/subfolder/asd.txt', 'folder/sub2/qwerty.txt', 'folder/sub2/sub3/foo.txt']; @@ -181,7 +231,7 @@ class CacheTest extends \Test\TestCase { } } - public function folderDataProvider() { + public static function folderDataProvider(): array { return [ ['folder'], // that was too easy, try something harder @@ -195,11 +245,11 @@ class CacheTest extends \Test\TestCase { ]; } - public function testEncryptedFolder() { + public function testEncryptedFolder(): void { $file1 = 'folder'; $file2 = 'folder/bar'; $file3 = 'folder/foo'; - $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; + $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; $fileData = []; $fileData['bar'] = ['size' => 1000, 'encrypted' => 1, 'mtime' => 20, 'mimetype' => 'foo/file']; $fileData['foo'] = ['size' => 20, 'encrypted' => 1, 'mtime' => 25, 'mimetype' => 'foo/file']; @@ -238,15 +288,14 @@ class CacheTest extends \Test\TestCase { $this->assertFalse($this->cache->inCache('folder/bar')); } - public function testRootFolderSizeForNonHomeStorage() { + public function testRootFolderSizeForNonHomeStorage(): void { $dir1 = 'knownsize'; $dir2 = 'unknownsize'; $fileData = []; - $fileData[''] = ['size' => -1, 'mtime' => 20, 'mimetype' => 'httpd/unix-directory']; - $fileData[$dir1] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'httpd/unix-directory']; - $fileData[$dir2] = ['size' => -1, 'mtime' => 25, 'mimetype' => 'httpd/unix-directory']; + $fileData[''] = ['size' => -1, 'mtime' => 20, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; + $fileData[$dir1] = ['size' => 1000, 'mtime' => 20, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; + $fileData[$dir2] = ['size' => -1, 'mtime' => 25, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; - $this->cache->put('', $fileData['']); $this->cache->put($dir1, $fileData[$dir1]); $this->cache->put($dir2, $fileData[$dir2]); @@ -265,17 +314,17 @@ class CacheTest extends \Test\TestCase { $this->assertFalse($this->cache->inCache($dir2)); } - public function testStatus() { - $this->assertEquals(\OC\Files\Cache\Cache::NOT_FOUND, $this->cache->getStatus('foo')); + public function testStatus(): void { + $this->assertEquals(Cache::NOT_FOUND, $this->cache->getStatus('foo')); $this->cache->put('foo', ['size' => -1]); - $this->assertEquals(\OC\Files\Cache\Cache::PARTIAL, $this->cache->getStatus('foo')); + $this->assertEquals(Cache::PARTIAL, $this->cache->getStatus('foo')); $this->cache->put('foo', ['size' => -1, 'mtime' => 20, 'mimetype' => 'foo/file']); - $this->assertEquals(\OC\Files\Cache\Cache::SHALLOW, $this->cache->getStatus('foo')); + $this->assertEquals(Cache::SHALLOW, $this->cache->getStatus('foo')); $this->cache->put('foo', ['size' => 10]); - $this->assertEquals(\OC\Files\Cache\Cache::COMPLETE, $this->cache->getStatus('foo')); + $this->assertEquals(Cache::COMPLETE, $this->cache->getStatus('foo')); } - public function putWithAllKindOfQuotesData() { + public static function putWithAllKindOfQuotesData(): array { return [ ['`backtick`'], ['´forward´'], @@ -284,11 +333,11 @@ class CacheTest extends \Test\TestCase { } /** - * @dataProvider putWithAllKindOfQuotesData * @param $fileName */ - public function testPutWithAllKindOfQuotes($fileName) { - $this->assertEquals(\OC\Files\Cache\Cache::NOT_FOUND, $this->cache->get($fileName)); + #[\PHPUnit\Framework\Attributes\DataProvider('putWithAllKindOfQuotesData')] + public function testPutWithAllKindOfQuotes($fileName): void { + $this->assertEquals(Cache::NOT_FOUND, $this->cache->get($fileName)); $this->cache->put($fileName, ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file', 'etag' => $fileName]); $cacheEntry = $this->cache->get($fileName); @@ -296,7 +345,7 @@ class CacheTest extends \Test\TestCase { $this->assertEquals($fileName, $cacheEntry['path']); } - public function testSearch() { + public function testSearch(): void { $file1 = 'folder'; $file2 = 'folder/foobar'; $file3 = 'folder/foo'; @@ -313,7 +362,6 @@ class CacheTest extends \Test\TestCase { $this->assertEquals(1, count($this->cache->search('foo'))); $this->assertEquals(1, count($this->cache->search('%folder%'))); $this->assertEquals(1, count($this->cache->search('folder%'))); - $this->assertEquals(3, count($this->cache->search('%'))); // case insensitive search should match the same files $this->assertEquals(2, count($this->cache->search('%Foo%'))); @@ -325,11 +373,11 @@ class CacheTest extends \Test\TestCase { $this->assertEquals(2, count($this->cache->searchByMime('foo/file'))); } - public function testSearchQueryByTag() { + public function testSearchQueryByTag(): void { $userId = static::getUniqueID('user'); - \OC::$server->getUserManager()->createUser($userId, $userId); + Server::get(IUserManager::class)->createUser($userId, $userId); static::loginAsUser($userId); - $user = new \OC\User\User($userId, null, \OC::$server->getEventDispatcher()); + $user = new User($userId, null, Server::get(IEventDispatcher::class)); $file1 = 'folder'; $file2 = 'folder/foobar'; @@ -349,7 +397,7 @@ class CacheTest extends \Test\TestCase { $id4 = $this->cache->put($file4, $fileData['foo2']); $id5 = $this->cache->put($file5, $fileData['foo3']); - $tagManager = \OC::$server->getTagManager()->load('files', [], false, $userId); + $tagManager = Server::get(ITagManager::class)->load('files', [], false, $userId); $this->assertTrue($tagManager->tagAs($id1, 'tag1')); $this->assertTrue($tagManager->tagAs($id1, 'tag2')); $this->assertTrue($tagManager->tagAs($id2, 'tag2')); @@ -374,7 +422,7 @@ class CacheTest extends \Test\TestCase { $tagManager->delete('tag2'); static::logout(); - $user = \OC::$server->getUserManager()->get($userId); + $user = Server::get(IUserManager::class)->get($userId); if ($user !== null) { try { $user->delete(); @@ -383,7 +431,7 @@ class CacheTest extends \Test\TestCase { } } - public function testSearchByQuery() { + public function testSearchByQuery(): void { $file1 = 'folder'; $file2 = 'folder/foobar'; $file3 = 'folder/foo'; @@ -412,7 +460,7 @@ class CacheTest extends \Test\TestCase { new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN_EQUAL, 'size', 100), 10, 0, [], $user))); } - public function movePathProvider() { + public static function movePathProvider(): array { return [ ['folder/foo', 'folder/foobar', ['1', '2']], ['folder/foo', 'foo', ['1', '2']], @@ -420,12 +468,10 @@ class CacheTest extends \Test\TestCase { ]; } - /** - * @dataProvider movePathProvider - */ - public function testMove($sourceFolder, $targetFolder, $children) { + #[\PHPUnit\Framework\Attributes\DataProvider('movePathProvider')] + public function testMove($sourceFolder, $targetFolder, $children): void { $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/bar']; - $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; + $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; // create folders foreach ([$sourceFolder, $targetFolder] as $current) { @@ -458,7 +504,24 @@ class CacheTest extends \Test\TestCase { } } - public function testGetIncomplete() { + public function testMoveFromCache(): void { + $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/bar']; + $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; + + $this->cache2->put('folder', $folderData); + $this->cache2->put('folder/sub', $data); + + + $this->cache->moveFromCache($this->cache2, 'folder', 'targetfolder'); + + $this->assertFalse($this->cache2->inCache('folder')); + $this->assertFalse($this->cache2->inCache('folder/sub')); + + $this->assertTrue($this->cache->inCache('targetfolder')); + $this->assertTrue($this->cache->inCache('targetfolder/sub')); + } + + public function testGetIncomplete(): void { $file1 = 'folder1'; $file2 = 'folder2'; $file3 = 'folder3'; @@ -475,13 +538,13 @@ class CacheTest extends \Test\TestCase { $this->assertEquals($file3, $this->cache->getIncomplete()); } - public function testNonExisting() { + public function testNonExisting(): void { $this->assertFalse($this->cache->get('foo.txt')); $this->assertFalse($this->cache->get(-1)); $this->assertEquals([], $this->cache->getFolderContents('foo')); } - public function testGetById() { + public function testGetById(): void { $storageId = $this->storage->getId(); $data = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file']; $id = $this->cache->put('foo', $data); @@ -489,10 +552,10 @@ class CacheTest extends \Test\TestCase { if (strlen($storageId) > 64) { $storageId = md5($storageId); } - $this->assertEquals([$storageId, 'foo'], \OC\Files\Cache\Cache::getById($id)); + $this->assertEquals([$storageId, 'foo'], Cache::getById($id)); } - public function testStorageMTime() { + public function testStorageMTime(): void { $data = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file']; $this->cache->put('foo', $data); $cachedData = $this->cache->get('foo'); @@ -509,19 +572,20 @@ class CacheTest extends \Test\TestCase { $this->assertEquals(25, $cachedData['mtime']); } - public function testLongId() { + public function testLongId(): void { $storage = new LongId([]); $cache = $storage->getCache(); + $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); $storageId = $storage->getId(); $data = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file']; $id = $cache->put('foo', $data); - $this->assertEquals([md5($storageId), 'foo'], \OC\Files\Cache\Cache::getById($id)); + $this->assertEquals([md5($storageId), 'foo'], Cache::getById($id)); } /** * this test show the bug resulting if we have no normalizer installed */ - public function testWithoutNormalizer() { + public function testWithoutNormalizer(): void { // folder name "Schön" with U+00F6 (normalized) $folderWith00F6 = "\x53\x63\x68\xc3\xb6\x6e"; @@ -529,10 +593,10 @@ class CacheTest extends \Test\TestCase { $folderWith0308 = "\x53\x63\x68\x6f\xcc\x88\x6e"; /** - * @var \OC\Files\Cache\Cache | \PHPUnit\Framework\MockObject\MockObject $cacheMock + * @var Cache|\PHPUnit\Framework\MockObject\MockObject $cacheMock */ $cacheMock = $this->getMockBuilder(Cache::class) - ->setMethods(['normalize']) + ->onlyMethods(['normalize']) ->setConstructorArgs([$this->storage]) ->getMock(); @@ -540,7 +604,7 @@ class CacheTest extends \Test\TestCase { ->method('normalize') ->willReturnArgument(0); - $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; + $data = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; // put root folder $this->assertFalse($cacheMock->get('folder')); @@ -567,7 +631,7 @@ class CacheTest extends \Test\TestCase { /** * this test shows that there is no bug if we use the normalizer */ - public function testWithNormalizer() { + public function testWithNormalizer(): void { if (!class_exists('Patchwork\PHP\Shim\Normalizer')) { $this->markTestSkipped('The 3rdparty Normalizer extension is not available.'); return; @@ -579,7 +643,7 @@ class CacheTest extends \Test\TestCase { // folder name "Schön" with U+0308 (un-normalized) $folderWith0308 = "\x53\x63\x68\x6f\xcc\x88\x6e"; - $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; + $data = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; // put root folder $this->assertFalse($this->cache->get('folder')); @@ -603,7 +667,7 @@ class CacheTest extends \Test\TestCase { $this->assertEquals(1, count($this->cache->getFolderContents('folder'))); } - public function bogusPathNamesProvider() { + public static function bogusPathNamesProvider(): array { return [ ['/bogus.txt', 'bogus.txt'], ['//bogus.txt', 'bogus.txt'], @@ -614,16 +678,11 @@ class CacheTest extends \Test\TestCase { /** * Test bogus paths with leading or doubled slashes - * - * @dataProvider bogusPathNamesProvider */ - public function testBogusPaths($bogusPath, $fixedBogusPath) { - $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; - - // put root folder - $this->assertFalse($this->cache->get('')); - $parentId = $this->cache->put('', $data); - $this->assertGreaterThan(0, $parentId); + #[\PHPUnit\Framework\Attributes\DataProvider('bogusPathNamesProvider')] + public function testBogusPaths($bogusPath, $fixedBogusPath): void { + $data = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; + $parentId = $this->cache->getId(''); $this->assertGreaterThan(0, $this->cache->put($bogusPath, $data)); @@ -639,7 +698,7 @@ class CacheTest extends \Test\TestCase { $this->assertEquals($newData, $newDataFromBogus); } - public function testNoReuseOfFileId() { + public function testNoReuseOfFileId(): void { $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain']; $this->cache->put('somefile.txt', $data1); $info = $this->cache->get('somefile.txt'); @@ -652,7 +711,7 @@ class CacheTest extends \Test\TestCase { $this->assertNotEquals($fileId, $fileId2); } - public function escapingProvider() { + public static function escapingProvider(): array { return [ ['foo'], ['o%'], @@ -662,9 +721,9 @@ class CacheTest extends \Test\TestCase { /** * @param string $name - * @dataProvider escapingProvider */ - public function testEscaping($name) { + #[\PHPUnit\Framework\Attributes\DataProvider('escapingProvider')] + public function testEscaping($name): void { $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain']; $this->cache->put($name, $data); $this->assertTrue($this->cache->inCache($name)); @@ -677,7 +736,7 @@ class CacheTest extends \Test\TestCase { $this->assertTrue($this->cache->inCache($name . 'asd')); $this->cache->remove($name . 'asd'); $this->assertFalse($this->cache->inCache($name . 'asd')); - $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; + $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; $this->cache->put($name, $folderData); $this->cache->put('other', $folderData); $childs = ['asd', 'bar', 'foo', 'sub/folder']; @@ -700,31 +759,30 @@ class CacheTest extends \Test\TestCase { } } - public function testExtended() { - $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; - $this->cache->put("", $folderData); + public function testExtended(): void { + $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'creation_time' => 20]; - $id1 = $this->cache->put("foo1", $data); + $id1 = $this->cache->put('foo1', $data); $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'upload_time' => 30]; - $this->cache->put("foo2", $data); + $this->cache->put('foo2', $data); $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'metadata_etag' => 'foo']; - $this->cache->put("foo3", $data); + $this->cache->put('foo3', $data); $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain']; - $id4 = $this->cache->put("foo4", $data); + $id4 = $this->cache->put('foo4', $data); $entry = $this->cache->get($id1); $this->assertEquals(20, $entry->getCreationTime()); $this->assertEquals(0, $entry->getUploadTime()); $this->assertEquals(null, $entry->getMetadataEtag()); - $entries = $this->cache->getFolderContents(""); + $entries = $this->cache->getFolderContents(''); $this->assertCount(4, $entries); - $this->assertEquals("foo1", $entries[0]->getName()); - $this->assertEquals("foo2", $entries[1]->getName()); - $this->assertEquals("foo3", $entries[2]->getName()); - $this->assertEquals("foo4", $entries[3]->getName()); + $this->assertEquals('foo1', $entries[0]->getName()); + $this->assertEquals('foo2', $entries[1]->getName()); + $this->assertEquals('foo3', $entries[2]->getName()); + $this->assertEquals('foo4', $entries[3]->getName()); $this->assertEquals(20, $entries[0]->getCreationTime()); $this->assertEquals(0, $entries[0]->getUploadTime()); @@ -749,11 +807,11 @@ class CacheTest extends \Test\TestCase { $this->assertEquals(25, $entry->getUploadTime()); $this->assertEquals(null, $entry->getMetadataEtag()); - $this->cache->put("sub", $folderData); + $this->cache->put('sub', $folderData); - $this->cache->move("foo1", "sub/foo1"); + $this->cache->move('foo1', 'sub/foo1'); - $entries = $this->cache->getFolderContents("sub"); + $entries = $this->cache->getFolderContents('sub'); $this->assertCount(1, $entries); $this->assertEquals(20, $entries[0]->getCreationTime()); @@ -767,23 +825,6 @@ class CacheTest extends \Test\TestCase { $this->assertEquals(25, $entry->getUploadTime()); $this->assertEquals(null, $entry->getMetadataEtag()); - $this->cache->remove("sub"); - } - - protected function tearDown(): void { - if ($this->cache) { - $this->cache->clear(); - } - - parent::tearDown(); - } - - protected function setUp(): void { - parent::setUp(); - - $this->storage = new \OC\Files\Storage\Temporary([]); - $this->storage2 = new \OC\Files\Storage\Temporary([]); - $this->cache = new \OC\Files\Cache\Cache($this->storage); - $this->cache2 = new \OC\Files\Cache\Cache($this->storage2); + $this->cache->remove('sub'); } } diff --git a/tests/lib/Files/Cache/FileAccessTest.php b/tests/lib/Files/Cache/FileAccessTest.php new file mode 100644 index 00000000000..59fa2494ea8 --- /dev/null +++ b/tests/lib/Files/Cache/FileAccessTest.php @@ -0,0 +1,438 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Files\Cache; + +use OC\Files\Cache\CacheEntry; +use OC\Files\Cache\FileAccess; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +/** + * @group DB + */ +class FileAccessTest extends TestCase { + private IDBConnection $dbConnection; + private FileAccess $fileAccess; + + protected function setUp(): void { + parent::setUp(); + + // Setup the actual database connection (assume the database is configured properly in PHPUnit setup) + $this->dbConnection = \OCP\Server::get(IDBConnection::class); + + // Ensure FileAccess is instantiated with the real connection + $this->fileAccess = new FileAccess( + $this->dbConnection, + \OCP\Server::get(\OC\SystemConfig::class), + \OCP\Server::get(LoggerInterface::class), + \OCP\Server::get(\OC\FilesMetadata\FilesMetadataManager::class), + \OCP\Server::get(\OCP\Files\IMimeTypeLoader::class) + ); + + // Clear and prepare `filecache` table for tests + $queryBuilder = $this->dbConnection->getQueryBuilder()->runAcrossAllShards(); + $queryBuilder->delete('filecache')->executeStatement(); + + // Clean up potential leftovers from other tests + $queryBuilder = $this->dbConnection->getQueryBuilder(); + $queryBuilder->delete('mounts')->executeStatement(); + + + $this->setUpTestDatabaseForGetDistinctMounts(); + $this->setUpTestDatabaseForGetByAncestorInStorage(); + } + + private function setUpTestDatabaseForGetDistinctMounts(): void { + $queryBuilder = $this->dbConnection->getQueryBuilder(); + + // Insert test data + $queryBuilder->insert('mounts') + ->values([ + 'storage_id' => $queryBuilder->createNamedParameter(1, IQueryBuilder::PARAM_INT), + 'root_id' => $queryBuilder->createNamedParameter(10, IQueryBuilder::PARAM_INT), + 'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'), + 'mount_point' => $queryBuilder->createNamedParameter('/files'), + 'user_id' => $queryBuilder->createNamedParameter('test'), + ]) + ->executeStatement(); + + $queryBuilder->insert('mounts') + ->values([ + 'storage_id' => $queryBuilder->createNamedParameter(3, IQueryBuilder::PARAM_INT), + 'root_id' => $queryBuilder->createNamedParameter(30, IQueryBuilder::PARAM_INT), + 'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'), + 'mount_point' => $queryBuilder->createNamedParameter('/documents'), + 'user_id' => $queryBuilder->createNamedParameter('test'), + ]) + ->executeStatement(); + + $queryBuilder->insert('mounts') + ->values([ + 'storage_id' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT), + 'root_id' => $queryBuilder->createNamedParameter(31, IQueryBuilder::PARAM_INT), + 'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass2'), + 'mount_point' => $queryBuilder->createNamedParameter('/foobar'), + 'user_id' => $queryBuilder->createNamedParameter('test'), + ]) + ->executeStatement(); + } + + /** + * Test that getDistinctMounts returns all mounts without filters + */ + public function testGetDistinctMountsWithoutFilters(): void { + $result = iterator_to_array($this->fileAccess->getDistinctMounts([], false)); + + $this->assertCount(3, $result); + + $this->assertEquals([ + 'storage_id' => 1, + 'root_id' => 10, + 'overridden_root' => 10, + ], $result[0]); + + $this->assertEquals([ + 'storage_id' => 3, + 'root_id' => 30, + 'overridden_root' => 30, + ], $result[1]); + + $this->assertEquals([ + 'storage_id' => 4, + 'root_id' => 31, + 'overridden_root' => 31, + ], $result[2]); + } + + /** + * Test that getDistinctMounts applies filtering by mount providers + */ + public function testGetDistinctMountsWithMountProviderFilter(): void { + $result = iterator_to_array($this->fileAccess->getDistinctMounts(['TestProviderClass1'], false)); + + $this->assertCount(2, $result); + + $this->assertEquals([ + 'storage_id' => 1, + 'root_id' => 10, + 'overridden_root' => 10, + ], $result[0]); + + $this->assertEquals([ + 'storage_id' => 3, + 'root_id' => 30, + 'overridden_root' => 30, + ], $result[1]); + } + + /** + * Test that getDistinctMounts rewrites home directory paths + */ + public function testGetDistinctMountsWithRewriteHomeDirectories(): void { + // Add additional test data for a home directory mount + $queryBuilder = $this->dbConnection->getQueryBuilder(); + $queryBuilder->insert('mounts') + ->values([ + 'storage_id' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT), + 'root_id' => $queryBuilder->createNamedParameter(40, IQueryBuilder::PARAM_INT), + 'mount_provider_class' => $queryBuilder->createNamedParameter(\OC\Files\Mount\LocalHomeMountProvider::class), + 'mount_point' => $queryBuilder->createNamedParameter('/home/user'), + 'user_id' => $queryBuilder->createNamedParameter('test'), + ]) + ->executeStatement(); + + // Add a mount that is mounted in the home directory + $queryBuilder = $this->dbConnection->getQueryBuilder(); + $queryBuilder->insert('mounts') + ->values([ + 'storage_id' => $queryBuilder->createNamedParameter(5, IQueryBuilder::PARAM_INT), + 'root_id' => $queryBuilder->createNamedParameter(41, IQueryBuilder::PARAM_INT), + 'mount_provider_class' => $queryBuilder->createNamedParameter('TestMountProvider3'), + 'mount_point' => $queryBuilder->createNamedParameter('/test/files/foobar'), + 'user_id' => $queryBuilder->createNamedParameter('test'), + ]) + ->executeStatement(); + + // Simulate adding a "files" directory to the filecache table + $queryBuilder = $this->dbConnection->getQueryBuilder()->runAcrossAllShards(); + $queryBuilder->delete('filecache')->executeStatement(); + $queryBuilder = $this->dbConnection->getQueryBuilder(); + $queryBuilder->insert('filecache') + ->values([ + 'fileid' => $queryBuilder->createNamedParameter(99, IQueryBuilder::PARAM_INT), + 'storage' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT), + 'parent' => $queryBuilder->createNamedParameter(40), + 'name' => $queryBuilder->createNamedParameter('files'), + 'path' => $queryBuilder->createNamedParameter('files'), + 'path_hash' => $queryBuilder->createNamedParameter(md5('files')), + ]) + ->executeStatement(); + + $result = iterator_to_array($this->fileAccess->getDistinctMounts()); + + $this->assertCount(2, $result); + + $this->assertEquals([ + 'storage_id' => 4, + 'root_id' => 40, + 'overridden_root' => 99, + ], $result[0]); + + $this->assertEquals([ + 'storage_id' => 5, + 'root_id' => 41, + 'overridden_root' => 41, + ], $result[1]); + } + + private function setUpTestDatabaseForGetByAncestorInStorage(): void { + // prepare `filecache` table for tests + $queryBuilder = $this->dbConnection->getQueryBuilder(); + + $queryBuilder->insert('filecache') + ->values([ + 'fileid' => 1, + 'parent' => 0, + 'path' => $queryBuilder->createNamedParameter('files'), + 'path_hash' => $queryBuilder->createNamedParameter(md5('files')), + 'storage' => $queryBuilder->createNamedParameter(1), + 'name' => $queryBuilder->createNamedParameter('files'), + 'mimetype' => 1, + 'encrypted' => 0, + ]) + ->executeStatement(); + + $queryBuilder->insert('filecache') + ->values([ + 'fileid' => 2, + 'parent' => 1, + 'path' => $queryBuilder->createNamedParameter('files/documents'), + 'path_hash' => $queryBuilder->createNamedParameter(md5('files/documents')), + 'storage' => $queryBuilder->createNamedParameter(1), + 'name' => $queryBuilder->createNamedParameter('documents'), + 'mimetype' => 2, + 'encrypted' => 1, + ]) + ->executeStatement(); + + $queryBuilder->insert('filecache') + ->values([ + 'fileid' => 3, + 'parent' => 1, + 'path' => $queryBuilder->createNamedParameter('files/photos'), + 'path_hash' => $queryBuilder->createNamedParameter(md5('files/photos')), + 'storage' => $queryBuilder->createNamedParameter(1), + 'name' => $queryBuilder->createNamedParameter('photos'), + 'mimetype' => 3, + 'encrypted' => 1, + ]) + ->executeStatement(); + + $queryBuilder->insert('filecache') + ->values([ + 'fileid' => 4, + 'parent' => 3, + 'path' => $queryBuilder->createNamedParameter('files/photos/endtoendencrypted'), + 'path_hash' => $queryBuilder->createNamedParameter(md5('files/photos/endtoendencrypted')), + 'storage' => $queryBuilder->createNamedParameter(1), + 'name' => $queryBuilder->createNamedParameter('endtoendencrypted'), + 'mimetype' => 4, + 'encrypted' => 0, + ]) + ->executeStatement(); + + $queryBuilder->insert('filecache') + ->values([ + 'fileid' => 5, + 'parent' => 1, + 'path' => $queryBuilder->createNamedParameter('files/serversideencrypted'), + 'path_hash' => $queryBuilder->createNamedParameter(md5('files/serversideencrypted')), + 'storage' => $queryBuilder->createNamedParameter(1), + 'name' => $queryBuilder->createNamedParameter('serversideencrypted'), + 'mimetype' => 4, + 'encrypted' => 1, + ]) + ->executeStatement(); + + $queryBuilder->insert('filecache') + ->values([ + 'fileid' => 6, + 'parent' => 0, + 'path' => $queryBuilder->createNamedParameter('files/storage2'), + 'path_hash' => $queryBuilder->createNamedParameter(md5('files/storage2')), + 'storage' => $queryBuilder->createNamedParameter(2), + 'name' => $queryBuilder->createNamedParameter('storage2'), + 'mimetype' => 5, + 'encrypted' => 0, + ]) + ->executeStatement(); + + $queryBuilder->insert('filecache') + ->values([ + 'fileid' => 7, + 'parent' => 6, + 'path' => $queryBuilder->createNamedParameter('files/storage2/file'), + 'path_hash' => $queryBuilder->createNamedParameter(md5('files/storage2/file')), + 'storage' => $queryBuilder->createNamedParameter(2), + 'name' => $queryBuilder->createNamedParameter('file'), + 'mimetype' => 6, + 'encrypted' => 0, + ]) + ->executeStatement(); + } + + /** + * Test fetching files by ancestor in storage. + */ + public function testGetByAncestorInStorage(): void { + $generator = $this->fileAccess->getByAncestorInStorage( + 1, // storageId + 1, // rootId + 0, // lastFileId + 10, // maxResults + [], // mimeTypes + true, // include end-to-end encrypted files + true, // include server-side encrypted files + ); + + $result = iterator_to_array($generator); + + $this->assertCount(4, $result); + + $paths = array_map(fn (CacheEntry $entry) => $entry->getPath(), $result); + $this->assertEquals([ + 'files/documents', + 'files/photos', + 'files/photos/endtoendencrypted', + 'files/serversideencrypted', + ], $paths); + } + + /** + * Test filtering by mime types. + */ + public function testGetByAncestorInStorageWithMimeTypes(): void { + $generator = $this->fileAccess->getByAncestorInStorage( + 1, + 1, + 0, + 10, + [2], // Only include documents (mimetype=2) + true, + true, + ); + + $result = iterator_to_array($generator); + + $this->assertCount(1, $result); + $this->assertEquals('files/documents', $result[0]->getPath()); + } + + /** + * Test excluding end-to-end encrypted files. + */ + public function testGetByAncestorInStorageWithoutEndToEndEncrypted(): void { + $generator = $this->fileAccess->getByAncestorInStorage( + 1, + 1, + 0, + 10, + [], + false, // exclude end-to-end encrypted files + true, + ); + + $result = iterator_to_array($generator); + + $this->assertCount(3, $result); + $paths = array_map(fn (CacheEntry $entry) => $entry->getPath(), $result); + $this->assertEquals(['files/documents', 'files/photos', 'files/serversideencrypted'], $paths); + } + + /** + * Test excluding server-side encrypted files. + */ + public function testGetByAncestorInStorageWithoutServerSideEncrypted(): void { + $generator = $this->fileAccess->getByAncestorInStorage( + 1, + 1, + 0, + 10, + [], + true, + false, // exclude server-side encrypted files + ); + + $result = iterator_to_array($generator); + + $this->assertCount(1, $result); + $this->assertEquals('files/photos/endtoendencrypted', $result[0]->getPath()); + } + + /** + * Test max result limits. + */ + public function testGetByAncestorInStorageWithMaxResults(): void { + $generator = $this->fileAccess->getByAncestorInStorage( + 1, + 1, + 0, + 1, // Limit to 1 result + [], + true, + true, + ); + + $result = iterator_to_array($generator); + + $this->assertCount(1, $result); + $this->assertEquals('files/documents', $result[0]->getPath()); + } + + /** + * Test rootId filter + */ + public function testGetByAncestorInStorageWithRootIdFilter(): void { + $generator = $this->fileAccess->getByAncestorInStorage( + 1, + 3, // Filter by rootId + 0, + 10, + [], + true, + true, + ); + + $result = iterator_to_array($generator); + + $this->assertCount(1, $result); + $this->assertEquals('files/photos/endtoendencrypted', $result[0]->getPath()); + } + + /** + * Test rootId filter + */ + public function testGetByAncestorInStorageWithStorageFilter(): void { + $generator = $this->fileAccess->getByAncestorInStorage( + 2, // Filter by storage + 6, // and by rootId + 0, + 10, + [], + true, + true, + ); + + $result = iterator_to_array($generator); + + $this->assertCount(1, $result); + $this->assertEquals('files/storage2/file', $result[0]->getPath()); + } +} diff --git a/tests/lib/Files/Cache/HomeCacheTest.php b/tests/lib/Files/Cache/HomeCacheTest.php index aacb91c675e..86fda615fd7 100644 --- a/tests/lib/Files/Cache/HomeCacheTest.php +++ b/tests/lib/Files/Cache/HomeCacheTest.php @@ -1,31 +1,27 @@ <?php + /** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Cache; -class DummyUser extends \OC\User\User { - /** - * @var string $home - */ - private $home; - - /** - * @var string $uid - */ - private $uid; +use OC\Files\Storage\Home; +use OC\User\User; +use OCP\ITempManager; +use OCP\Server; +class DummyUser extends User { /** * @param string $uid * @param string $home */ - public function __construct($uid, $home) { - $this->home = $home; - $this->uid = $uid; + public function __construct( + private $uid, + private $home, + ) { } /** @@ -62,15 +58,15 @@ class HomeCacheTest extends \Test\TestCase { private $cache; /** - * @var \OC\User\User $user + * @var User $user */ private $user; protected function setUp(): void { parent::setUp(); - $this->user = new DummyUser('foo', \OC::$server->getTempManager()->getTemporaryFolder()); - $this->storage = new \OC\Files\Storage\Home(['user' => $this->user]); + $this->user = new DummyUser('foo', Server::get(ITempManager::class)->getTemporaryFolder()); + $this->storage = new Home(['user' => $this->user]); $this->cache = $this->storage->getCache(); } @@ -79,7 +75,7 @@ class HomeCacheTest extends \Test\TestCase { * that have an unknown size. This makes sure that quota calculation still * works as it's based on the "files" folder size. */ - public function testRootFolderSizeIgnoresUnknownUpdate() { + public function testRootFolderSizeIgnoresUnknownUpdate(): void { $dir1 = 'files/knownsize'; $dir2 = 'files/unknownsize'; $fileData = []; @@ -111,7 +107,7 @@ class HomeCacheTest extends \Test\TestCase { $this->assertFalse($this->cache->inCache($dir2)); } - public function testRootFolderSizeIsFilesSize() { + public function testRootFolderSizeIsFilesSize(): void { $dir1 = 'files'; $afile = 'test.txt'; $fileData = []; diff --git a/tests/lib/Files/Cache/LocalRootScannerTest.php b/tests/lib/Files/Cache/LocalRootScannerTest.php index 0a85f000dbc..727da2ed698 100644 --- a/tests/lib/Files/Cache/LocalRootScannerTest.php +++ b/tests/lib/Files/Cache/LocalRootScannerTest.php @@ -2,28 +2,15 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Cache; use OC\Files\Storage\LocalRootStorage; +use OCP\ITempManager; +use OCP\Server; use Test\TestCase; /** @@ -36,11 +23,11 @@ class LocalRootScannerTest extends TestCase { protected function setUp(): void { parent::setUp(); - $folder = \OC::$server->getTempManager()->getTemporaryFolder(); + $folder = Server::get(ITempManager::class)->getTemporaryFolder(); $this->storage = new LocalRootStorage(['datadir' => $folder]); } - public function testDontScanUsers() { + public function testDontScanUsers(): void { $this->storage->mkdir('foo'); $this->storage->mkdir('foo/bar'); @@ -48,7 +35,7 @@ class LocalRootScannerTest extends TestCase { $this->assertFalse($this->storage->getCache()->inCache('foo')); } - public function testDoScanAppData() { + public function testDoScanAppData(): void { $this->storage->mkdir('appdata_foo'); $this->storage->mkdir('appdata_foo/bar'); diff --git a/tests/lib/Files/Cache/MoveFromCacheTraitTest.php b/tests/lib/Files/Cache/MoveFromCacheTraitTest.php index 6e1430d146b..d580cdca4b9 100644 --- a/tests/lib/Files/Cache/MoveFromCacheTraitTest.php +++ b/tests/lib/Files/Cache/MoveFromCacheTraitTest.php @@ -1,16 +1,19 @@ <?php + /** - * Copyright (c) 2016 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Cache; +use OC\Files\Cache\Cache; use OC\Files\Cache\MoveFromCacheTrait; +use OC\Files\Storage\Temporary; +use OCP\Files\Cache\ICacheEntry; -class FallBackCrossCacheMoveCache extends \OC\Files\Cache\Cache { +class FallBackCrossCacheMoveCache extends Cache { use MoveFromCacheTrait; } @@ -23,9 +26,12 @@ class MoveFromCacheTraitTest extends CacheTest { protected function setUp(): void { parent::setUp(); - $this->storage = new \OC\Files\Storage\Temporary([]); - $this->storage2 = new \OC\Files\Storage\Temporary([]); + $this->storage = new Temporary([]); + $this->storage2 = new Temporary([]); $this->cache = new FallBackCrossCacheMoveCache($this->storage); $this->cache2 = new FallBackCrossCacheMoveCache($this->storage2); + + $this->cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); + $this->cache2->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); } } diff --git a/tests/lib/Files/Cache/PropagatorTest.php b/tests/lib/Files/Cache/PropagatorTest.php index c1822c90282..2ab213e9567 100644 --- a/tests/lib/Files/Cache/PropagatorTest.php +++ b/tests/lib/Files/Cache/PropagatorTest.php @@ -1,9 +1,9 @@ <?php + /** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Cache; @@ -17,7 +17,7 @@ use Test\TestCase; * @group DB */ class PropagatorTest extends TestCase { - /** @var IStorage */ + /** @var IStorage */ private $storage; protected function setUp(): void { @@ -39,7 +39,7 @@ class PropagatorTest extends TestCase { return array_combine($paths, $values); } - public function testEtagPropagation() { + public function testEtagPropagation(): void { $paths = ['', 'foo', 'foo/bar']; $oldInfos = $this->getFileInfos($paths); $this->storage->getPropagator()->propagateChange('foo/bar/file.txt', time()); @@ -50,7 +50,7 @@ class PropagatorTest extends TestCase { } } - public function testTimePropagation() { + public function testTimePropagation(): void { $paths = ['', 'foo', 'foo/bar']; $oldTime = time() - 200; $targetTime = time() - 100; @@ -70,7 +70,7 @@ class PropagatorTest extends TestCase { $this->assertEquals($now, $newInfos['']->getMTime()); } - public function testSizePropagation() { + public function testSizePropagation(): void { $paths = ['', 'foo', 'foo/bar']; $oldInfos = $this->getFileInfos($paths); $this->storage->getPropagator()->propagateChange('foo/bar/file.txt', time(), 10); @@ -81,7 +81,7 @@ class PropagatorTest extends TestCase { } } - public function testSizePropagationNoNegative() { + public function testSizePropagationNoNegative(): void { $paths = ['', 'foo', 'foo/bar']; $oldInfos = $this->getFileInfos($paths); $this->storage->getPropagator()->propagateChange('foo/bar/file.txt', time(), -100); @@ -92,7 +92,7 @@ class PropagatorTest extends TestCase { } } - public function testBatchedPropagation() { + public function testBatchedPropagation(): void { $this->storage->mkdir('foo/baz'); $this->storage->mkdir('asd'); $this->storage->file_put_contents('asd/file.txt', 'bar'); @@ -123,7 +123,7 @@ class PropagatorTest extends TestCase { foreach ($oldInfos as $i => $oldInfo) { if ($oldInfo->getPath() !== 'foo/baz') { - $this->assertNotEquals($oldInfo->getEtag(), $newInfos[$i]->getEtag()); + $this->assertNotEquals($oldInfo->getEtag(), $newInfos[$i]->getEtag(), "etag for {$oldInfo->getPath()} not updated"); } } diff --git a/tests/lib/Files/Cache/ScannerTest.php b/tests/lib/Files/Cache/ScannerTest.php index e4c052f6025..123c13893f7 100644 --- a/tests/lib/Files/Cache/ScannerTest.php +++ b/tests/lib/Files/Cache/ScannerTest.php @@ -1,9 +1,9 @@ <?php + /** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Cache; @@ -15,6 +15,8 @@ use OC\Files\Cache\Scanner; use OC\Files\Storage\Storage; use OC\Files\Storage\Temporary; use OCP\Files\Cache\IScanner; +use OCP\IDBConnection; +use OCP\Server; use Test\TestCase; /** @@ -43,7 +45,7 @@ class ScannerTest extends TestCase { parent::tearDown(); } - public function testFile() { + public function testFile(): void { $data = "dummy file data\n"; $this->storage->file_put_contents('foo.txt', $data); $this->scanner->scanFile('foo.txt'); @@ -64,11 +66,11 @@ class ScannerTest extends TestCase { $this->assertEquals($cachedData['mimetype'], 'image/png'); } - public function testFile4Byte() { + public function testFile4Byte(): void { $data = "dummy file data\n"; $this->storage->file_put_contents('foo🙈.txt', $data); - if (OC::$server->getDatabaseConnection()->supports4ByteText()) { + if (Server::get(IDBConnection::class)->supports4ByteText()) { $this->assertNotNull($this->scanner->scanFile('foo🙈.txt')); $this->assertTrue($this->cache->inCache('foo🙈.txt'), true); @@ -82,7 +84,7 @@ class ScannerTest extends TestCase { } } - public function testFileInvalidChars() { + public function testFileInvalidChars(): void { $data = "dummy file data\n"; $this->storage->file_put_contents("foo\nbar.txt", $data); @@ -99,7 +101,7 @@ class ScannerTest extends TestCase { $this->storage->file_put_contents('folder/bar.txt', $textData); } - public function testFolder() { + public function testFolder(): void { $this->fillTestFolders(); $this->scanner->scan(''); @@ -121,7 +123,7 @@ class ScannerTest extends TestCase { $this->assertEquals($cachedDataFolder2['size'], $cachedDataText2['size']); } - public function testShallow() { + public function testShallow(): void { $this->fillTestFolders(); $this->scanner->scan('', IScanner::SCAN_SHALLOW); @@ -149,7 +151,7 @@ class ScannerTest extends TestCase { $this->assertNotEquals($cachedDataFolder['size'], -1); } - public function testBackgroundScan() { + public function testBackgroundScan(): void { $this->fillTestFolders(); $this->storage->mkdir('folder2'); $this->storage->file_put_contents('folder2/bar.txt', 'foobar'); @@ -171,7 +173,7 @@ class ScannerTest extends TestCase { $this->assertFalse($this->cache->getIncomplete()); } - public function testBackgroundScanOnlyRecurseIncomplete() { + public function testBackgroundScanOnlyRecurseIncomplete(): void { $this->fillTestFolders(); $this->storage->mkdir('folder2'); $this->storage->file_put_contents('folder2/bar.txt', 'foobar'); @@ -197,7 +199,7 @@ class ScannerTest extends TestCase { $this->assertFalse($this->cache->getIncomplete()); } - public function testBackgroundScanNestedIncompleteFolders() { + public function testBackgroundScanNestedIncompleteFolders(): void { $this->storage->mkdir('folder'); $this->scanner->backgroundScan(); @@ -235,7 +237,7 @@ class ScannerTest extends TestCase { $this->assertEquals(6, $this->cache->get('folder/subfolder2')['size']); } - public function testReuseExisting() { + public function testReuseExisting(): void { $this->fillTestFolders(); $this->scanner->scan(''); @@ -274,7 +276,7 @@ class ScannerTest extends TestCase { $this->assertEquals($oldData['size'], $newData['size']); } - public function testRemovedFile() { + public function testRemovedFile(): void { $this->fillTestFolders(); $this->scanner->scan(''); @@ -284,7 +286,7 @@ class ScannerTest extends TestCase { $this->assertFalse($this->cache->inCache('foo.txt')); } - public function testRemovedFolder() { + public function testRemovedFolder(): void { $this->fillTestFolders(); $this->scanner->scan(''); @@ -295,7 +297,7 @@ class ScannerTest extends TestCase { $this->assertFalse($this->cache->inCache('folder/bar.txt')); } - public function testScanRemovedFile() { + public function testScanRemovedFile(): void { $this->fillTestFolders(); $this->scanner->scan(''); @@ -305,7 +307,7 @@ class ScannerTest extends TestCase { $this->assertFalse($this->cache->inCache('folder/bar.txt')); } - public function testETagRecreation() { + public function testETagRecreation(): void { $this->fillTestFolders(); $this->scanner->scan('folder/bar.txt'); @@ -331,14 +333,14 @@ class ScannerTest extends TestCase { $this->assertNotEmpty($newData0['etag']); } - public function testRepairParent() { + public function testRepairParent(): void { $this->fillTestFolders(); $this->scanner->scan(''); $this->assertTrue($this->cache->inCache('folder/bar.txt')); $oldFolderId = $this->cache->getId('folder'); // delete the folder without removing the children - $query = OC::$server->getDatabaseConnection()->getQueryBuilder(); + $query = Server::get(IDBConnection::class)->getQueryBuilder(); $query->delete('filecache') ->where($query->expr()->eq('fileid', $query->createNamedParameter($oldFolderId))); $query->execute(); @@ -357,14 +359,14 @@ class ScannerTest extends TestCase { $this->assertEquals($newFolderId, $cachedData['parent']); } - public function testRepairParentShallow() { + public function testRepairParentShallow(): void { $this->fillTestFolders(); $this->scanner->scan(''); $this->assertTrue($this->cache->inCache('folder/bar.txt')); $oldFolderId = $this->cache->getId('folder'); // delete the folder without removing the children - $query = OC::$server->getDatabaseConnection()->getQueryBuilder(); + $query = Server::get(IDBConnection::class)->getQueryBuilder(); $query->delete('filecache') ->where($query->expr()->eq('fileid', $query->createNamedParameter($oldFolderId))); $query->execute(); @@ -384,18 +386,18 @@ class ScannerTest extends TestCase { } /** - * @dataProvider dataTestIsPartialFile * * @param string $path * @param bool $expected */ - public function testIsPartialFile($path, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsPartialFile')] + public function testIsPartialFile($path, $expected): void { $this->assertSame($expected, $this->scanner->isPartialFile($path) ); } - public function dataTestIsPartialFile() { + public static function dataTestIsPartialFile(): array { return [ ['foo.txt.part', true], ['/sub/folder/foo.txt.part', true], @@ -404,4 +406,48 @@ class ScannerTest extends TestCase { ['/sub/folder/foo.txt', false], ]; } + + public function testNoETagUnscannedFolder(): void { + $this->fillTestFolders(); + + $this->scanner->scan(''); + + $oldFolderEntry = $this->cache->get('folder'); + // create a new file in a folder by keeping the mtime unchanged, but mark the folder as unscanned + $this->storage->file_put_contents('folder/new.txt', 'foo'); + $this->storage->touch('folder', $oldFolderEntry->getMTime()); + $this->cache->update($oldFolderEntry->getId(), ['size' => -1]); + + $this->scanner->scan(''); + + $this->cache->inCache('folder/new.txt'); + + $newFolderEntry = $this->cache->get('folder'); + $this->assertNotEquals($newFolderEntry->getEtag(), $oldFolderEntry->getEtag()); + } + + public function testNoETagUnscannedSubFolder(): void { + $this->fillTestFolders(); + $this->storage->mkdir('folder/sub'); + + $this->scanner->scan(''); + + $oldFolderEntry1 = $this->cache->get('folder'); + $oldFolderEntry2 = $this->cache->get('folder/sub'); + // create a new file in a folder by keeping the mtime unchanged, but mark the folder as unscanned + $this->storage->file_put_contents('folder/sub/new.txt', 'foo'); + $this->storage->touch('folder/sub', $oldFolderEntry1->getMTime()); + + // we only mark the direct parent as unscanned, which is the current "notify" behavior + $this->cache->update($oldFolderEntry2->getId(), ['size' => -1]); + + $this->scanner->scan(''); + + $this->cache->inCache('folder/new.txt'); + + $newFolderEntry1 = $this->cache->get('folder'); + $this->assertNotEquals($newFolderEntry1->getEtag(), $oldFolderEntry1->getEtag()); + $newFolderEntry2 = $this->cache->get('folder/sub'); + $this->assertNotEquals($newFolderEntry2->getEtag(), $oldFolderEntry2->getEtag()); + } } diff --git a/tests/lib/Files/Cache/SearchBuilderTest.php b/tests/lib/Files/Cache/SearchBuilderTest.php index 5eb1a0252f0..ee097044e3b 100644 --- a/tests/lib/Files/Cache/SearchBuilderTest.php +++ b/tests/lib/Files/Cache/SearchBuilderTest.php @@ -1,22 +1,8 @@ <?php + /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Cache; @@ -30,6 +16,9 @@ use OCP\Files\IMimeTypeLoader; use OCP\Files\Search\ISearchBinaryOperator; use OCP\Files\Search\ISearchComparison; use OCP\Files\Search\ISearchOperator; +use OCP\FilesMetadata\IFilesMetadataManager; +use OCP\IDBConnection; +use OCP\Server; use Test\TestCase; /** @@ -39,9 +28,12 @@ class SearchBuilderTest extends TestCase { /** @var IQueryBuilder */ private $builder; - /** @var IMimeTypeLoader|\PHPUnit\Framework\MockObject\MockObject */ + /** @var IMimeTypeLoader&\PHPUnit\Framework\MockObject\MockObject */ private $mimetypeLoader; + /** @var IFilesMetadataManager&\PHPUnit\Framework\MockObject\MockObject */ + private $filesMetadataManager; + /** @var SearchBuilder */ private $searchBuilder; @@ -50,8 +42,9 @@ class SearchBuilderTest extends TestCase { protected function setUp(): void { parent::setUp(); - $this->builder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); + $this->builder = Server::get(IDBConnection::class)->getQueryBuilder(); $this->mimetypeLoader = $this->createMock(IMimeTypeLoader::class); + $this->filesMetadataManager = $this->createMock(IFilesMetadataManager::class); $this->mimetypeLoader->expects($this->any()) ->method('getId') @@ -75,7 +68,7 @@ class SearchBuilderTest extends TestCase { [6, 'image'] ]); - $this->searchBuilder = new SearchBuilder($this->mimetypeLoader); + $this->searchBuilder = new SearchBuilder($this->mimetypeLoader, $this->filesMetadataManager); $this->numericStorageId = 10000; $this->builder->select(['fileid']) @@ -86,7 +79,7 @@ class SearchBuilderTest extends TestCase { protected function tearDown(): void { parent::tearDown(); - $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); + $builder = Server::get(IDBConnection::class)->getQueryBuilder(); $builder->delete('filecache') ->where($builder->expr()->eq('storage', $builder->createNamedParameter($this->numericStorageId, IQueryBuilder::PARAM_INT))); @@ -119,7 +112,7 @@ class SearchBuilderTest extends TestCase { $data['mimetype'] = 1; } - $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); + $builder = Server::get(IDBConnection::class)->getQueryBuilder(); $values = []; foreach ($data as $key => $value) { @@ -144,7 +137,7 @@ class SearchBuilderTest extends TestCase { return $rows; } - public function comparisonProvider() { + public static function comparisonProvider(): array { return [ [new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN, 'mtime', 125), [1]], [new SearchComparison(ISearchComparison::COMPARE_LESS_THAN, 'mtime', 125), [0]], @@ -154,6 +147,7 @@ class SearchBuilderTest extends TestCase { [new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', 'foo%'), [0, 1]], [new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'image/jpg'), [0]], [new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', 'image/%'), [0, 1]], + [new SearchComparison(ISearchComparison::COMPARE_IN, 'mimetype', ['image/jpg', 'image/png']), [0, 1]], [new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'size', 50), new SearchComparison(ISearchComparison::COMPARE_LESS_THAN, 'mtime', 125) @@ -184,12 +178,12 @@ class SearchBuilderTest extends TestCase { } /** - * @dataProvider comparisonProvider * * @param ISearchOperator $operator * @param array $fileIds */ - public function testComparison(ISearchOperator $operator, array $fileIds) { + #[\PHPUnit\Framework\Attributes\DataProvider('comparisonProvider')] + public function testComparison(ISearchOperator $operator, array $fileIds): void { $fileId = []; $fileId[] = $this->addCacheEntry([ 'path' => 'foobar', diff --git a/tests/lib/Files/Cache/UpdaterLegacyTest.php b/tests/lib/Files/Cache/UpdaterLegacyTest.php index be0390db15e..c71fac2d2dc 100644 --- a/tests/lib/Files/Cache/UpdaterLegacyTest.php +++ b/tests/lib/Files/Cache/UpdaterLegacyTest.php @@ -1,16 +1,22 @@ <?php + /** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Cache; +use OC\Files\Cache\Cache; +use OC\Files\Cache\Scanner; use OC\Files\Filesystem as Filesystem; +use OC\Files\Storage\Storage; +use OC\Files\Storage\Temporary; use OC\Files\View; use OCP\Files\Mount\IMountManager; +use OCP\IUserManager; +use OCP\Server; /** * Class UpdaterLegacyTest @@ -21,17 +27,17 @@ use OCP\Files\Mount\IMountManager; */ class UpdaterLegacyTest extends \Test\TestCase { /** - * @var \OC\Files\Storage\Storage $storage + * @var Storage $storage */ private $storage; /** - * @var \OC\Files\Cache\Scanner $scanner + * @var Scanner $scanner */ private $scanner; /** - * @var \OC\Files\Cache\Cache $cache + * @var Cache $cache */ private $cache; @@ -40,7 +46,7 @@ class UpdaterLegacyTest extends \Test\TestCase { protected function setUp(): void { parent::setUp(); - $this->storage = new \OC\Files\Storage\Temporary([]); + $this->storage = new Temporary([]); $textData = "dummy file data\n"; $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png'); $this->storage->mkdir('folder'); @@ -57,13 +63,13 @@ class UpdaterLegacyTest extends \Test\TestCase { self::$user = $this->getUniqueID(); } - \OC::$server->getUserManager()->createUser(self::$user, 'password'); + Server::get(IUserManager::class)->createUser(self::$user, 'NotAnEasyPassword123456+'); $this->loginAsUser(self::$user); Filesystem::init(self::$user, '/' . self::$user . '/files'); /** @var IMountManager $manager */ - $manager = \OC::$server->get(IMountManager::class); + $manager = Server::get(IMountManager::class); $manager->removeMount('/' . self::$user); Filesystem::mount($this->storage, [], '/' . self::$user . '/files'); @@ -77,7 +83,7 @@ class UpdaterLegacyTest extends \Test\TestCase { } $result = false; - $user = \OC::$server->getUserManager()->get(self::$user); + $user = Server::get(IUserManager::class)->get(self::$user); if ($user !== null) { $result = $user->delete(); } @@ -87,7 +93,7 @@ class UpdaterLegacyTest extends \Test\TestCase { parent::tearDown(); } - public function testWrite() { + public function testWrite(): void { $textSize = strlen("dummy file data\n"); $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo/logo.png'); $this->cache->put('foo.txt', ['mtime' => 100, 'storage_mtime' => 150]); @@ -122,8 +128,8 @@ class UpdaterLegacyTest extends \Test\TestCase { $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $mtime); } - public function testWriteWithMountPoints() { - $storage2 = new \OC\Files\Storage\Temporary([]); + public function testWriteWithMountPoints(): void { + $storage2 = new Temporary([]); $storage2->getScanner()->scan(''); //initialize etags $cache2 = $storage2->getCache(); Filesystem::mount($storage2, [], '/' . self::$user . '/files/folder/substorage'); @@ -148,7 +154,7 @@ class UpdaterLegacyTest extends \Test\TestCase { $this->assertNotSame($oldEtag, $cachedData['etag']); } - public function testDelete() { + public function testDelete(): void { $textSize = strlen("dummy file data\n"); $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo/logo.png'); $rootCachedData = $this->cache->get(''); @@ -183,8 +189,8 @@ class UpdaterLegacyTest extends \Test\TestCase { $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $cachedData['mtime']); } - public function testDeleteWithMountPoints() { - $storage2 = new \OC\Files\Storage\Temporary([]); + public function testDeleteWithMountPoints(): void { + $storage2 = new Temporary([]); $cache2 = $storage2->getCache(); Filesystem::mount($storage2, [], '/' . self::$user . '/files/folder/substorage'); Filesystem::file_put_contents('folder/substorage/foo.txt', 'asd'); @@ -209,7 +215,7 @@ class UpdaterLegacyTest extends \Test\TestCase { $this->assertGreaterThanOrEqual($folderCachedData['mtime'], $cachedData['mtime']); } - public function testRename() { + public function testRename(): void { $textSize = strlen("dummy file data\n"); $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo/logo.png'); $rootCachedData = $this->cache->get(''); @@ -231,7 +237,7 @@ class UpdaterLegacyTest extends \Test\TestCase { $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); } - public function testRenameExtension() { + public function testRenameExtension(): void { $fooCachedData = $this->cache->get('foo.txt'); $this->assertEquals('text/plain', $fooCachedData['mimetype']); Filesystem::rename('foo.txt', 'foo.abcd'); @@ -239,8 +245,8 @@ class UpdaterLegacyTest extends \Test\TestCase { $this->assertEquals('application/octet-stream', $fooCachedData['mimetype']); } - public function testRenameWithMountPoints() { - $storage2 = new \OC\Files\Storage\Temporary([]); + public function testRenameWithMountPoints(): void { + $storage2 = new Temporary([]); $cache2 = $storage2->getCache(); Filesystem::mount($storage2, [], '/' . self::$user . '/files/folder/substorage'); Filesystem::file_put_contents('folder/substorage/foo.txt', 'asd'); @@ -262,17 +268,17 @@ class UpdaterLegacyTest extends \Test\TestCase { $this->assertIsString($cachedData['etag']); $this->assertNotSame($oldEtag, $cachedData['etag']); // rename can cause mtime change - invalid assert -// $this->assertEquals($mtime, $cachedData['mtime']); + // $this->assertEquals($mtime, $cachedData['mtime']); $cachedData = $view->getFileInfo('folder'); $this->assertIsString($folderCachedData['etag']); $this->assertIsString($cachedData['etag']); $this->assertNotSame($oldEtag, $cachedData['etag']); // rename can cause mtime change - invalid assert -// $this->assertEquals($mtime, $cachedData['mtime']); + // $this->assertEquals($mtime, $cachedData['mtime']); } - public function testTouch() { + public function testTouch(): void { $rootCachedData = $this->cache->get(''); $fooCachedData = $this->cache->get('foo.txt'); Filesystem::touch('foo.txt'); diff --git a/tests/lib/Files/Cache/UpdaterTest.php b/tests/lib/Files/Cache/UpdaterTest.php index 7e0f6866793..65c47cb9ae6 100644 --- a/tests/lib/Files/Cache/UpdaterTest.php +++ b/tests/lib/Files/Cache/UpdaterTest.php @@ -1,15 +1,21 @@ <?php + /** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Cache; +use OC\Files\Cache\Cache; use OC\Files\Filesystem; +use OC\Files\ObjectStore\ObjectStoreStorage; +use OC\Files\ObjectStore\StorageObjectStore; +use OC\Files\Storage\Storage; use OC\Files\Storage\Temporary; +use OC\Files\View; +use OCP\Files\Storage\IStorage; /** * Class UpdaterTest @@ -20,17 +26,17 @@ use OC\Files\Storage\Temporary; */ class UpdaterTest extends \Test\TestCase { /** - * @var \OC\Files\Storage\Storage + * @var Storage */ protected $storage; /** - * @var \OC\Files\Cache\Cache + * @var Cache */ protected $cache; /** - * @var \OC\Files\View + * @var View */ protected $view; @@ -54,7 +60,7 @@ class UpdaterTest extends \Test\TestCase { parent::tearDown(); } - public function testNewFile() { + public function testNewFile(): void { $this->storage->file_put_contents('foo.txt', 'bar'); $this->assertFalse($this->cache->inCache('foo.txt')); @@ -66,7 +72,7 @@ class UpdaterTest extends \Test\TestCase { $this->assertEquals('text/plain', $cached['mimetype']); } - public function testUpdatedFile() { + public function testUpdatedFile(): void { $this->storage->file_put_contents('foo.txt', 'bar'); $this->updater->update('foo.txt'); @@ -85,7 +91,7 @@ class UpdaterTest extends \Test\TestCase { $this->assertEquals(6, $cached['size']); } - public function testParentSize() { + public function testParentSize(): void { $this->storage->getScanner()->scan(''); $parentCached = $this->cache->get(''); @@ -122,7 +128,7 @@ class UpdaterTest extends \Test\TestCase { $this->assertEquals(0, $parentCached['size']); } - public function testMove() { + public function testMove(): void { $this->storage->file_put_contents('foo.txt', 'qwerty'); $this->updater->update('foo.txt'); @@ -147,7 +153,7 @@ class UpdaterTest extends \Test\TestCase { $this->assertEquals($cached['fileid'], $cachedTarget['fileid']); } - public function testMoveNonExistingOverwrite() { + public function testMoveNonExistingOverwrite(): void { $this->storage->file_put_contents('bar.txt', 'qwerty'); $this->updater->update('bar.txt'); @@ -165,7 +171,7 @@ class UpdaterTest extends \Test\TestCase { $this->assertEquals($cached['fileid'], $cachedTarget['fileid']); } - public function testUpdateStorageMTime() { + public function testUpdateStorageMTime(): void { $this->storage->mkdir('sub'); $this->storage->mkdir('sub2'); $this->storage->file_put_contents('sub/foo.txt', 'qwerty'); @@ -206,7 +212,7 @@ class UpdaterTest extends \Test\TestCase { $this->assertNotEquals($testmtime, $cachedTargetParent['mtime'], 'target folder mtime changed, not from storage'); } - public function testNewFileDisabled() { + public function testNewFileDisabled(): void { $this->storage->file_put_contents('foo.txt', 'bar'); $this->assertFalse($this->cache->inCache('foo.txt')); @@ -216,7 +222,7 @@ class UpdaterTest extends \Test\TestCase { $this->assertFalse($this->cache->inCache('foo.txt')); } - public function testMoveCrossStorage() { + public function testMoveCrossStorage(): void { $storage2 = new Temporary([]); $cache2 = $storage2->getCache(); Filesystem::mount($storage2, [], '/bar'); @@ -247,7 +253,7 @@ class UpdaterTest extends \Test\TestCase { $this->assertEquals($cached['fileid'], $cachedTarget['fileid']); } - public function testMoveFolderCrossStorage() { + public function testMoveFolderCrossStorage(): void { $storage2 = new Temporary([]); $cache2 = $storage2->getCache(); Filesystem::mount($storage2, [], '/bar'); @@ -302,4 +308,34 @@ class UpdaterTest extends \Test\TestCase { $this->assertEquals($old['mimetype'], $new['mimetype']); } } + + public static function changeExtensionProvider(): array { + return [ + [new Temporary()], + [new ObjectStoreStorage(['objectstore' => new StorageObjectStore(new Temporary())])] + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('changeExtensionProvider')] + public function testChangeExtension(IStorage $storage) { + $updater = $storage->getUpdater(); + $cache = $storage->getCache(); + $storage->file_put_contents('foo', 'qwerty'); + $updater->update('foo'); + + $bareCached = $cache->get('foo'); + $this->assertEquals('application/octet-stream', $bareCached->getMimeType()); + + $storage->rename('foo', 'foo.txt'); + $updater->renameFromStorage($storage, 'foo', 'foo.txt'); + + $cached = $cache->get('foo.txt'); + $this->assertEquals('text/plain', $cached->getMimeType()); + + $storage->rename('foo.txt', 'foo.md'); + $updater->renameFromStorage($storage, 'foo.txt', 'foo.md'); + + $cachedTarget = $cache->get('foo.md'); + $this->assertEquals('text/markdown', $cachedTarget->getMimeType()); + } } diff --git a/tests/lib/Files/Cache/WatcherTest.php b/tests/lib/Files/Cache/WatcherTest.php index 509b9d6ba2a..6d0a8e0886b 100644 --- a/tests/lib/Files/Cache/WatcherTest.php +++ b/tests/lib/Files/Cache/WatcherTest.php @@ -1,13 +1,17 @@ <?php + /** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Cache; +use OC\Files\Cache\Watcher; +use OC\Files\Storage\Storage; +use OC\Files\Storage\Temporary; + /** * Class WatcherTest * @@ -17,7 +21,7 @@ namespace Test\Files\Cache; */ class WatcherTest extends \Test\TestCase { /** - * @var \OC\Files\Storage\Storage[] $storages + * @var Storage[] $storages */ private $storages = []; @@ -41,11 +45,11 @@ class WatcherTest extends \Test\TestCase { /** * @medium */ - public function testWatcher() { + public function testWatcher(): void { $storage = $this->getTestStorage(); $cache = $storage->getCache(); $updater = $storage->getWatcher(); - $updater->setPolicy(\OC\Files\Cache\Watcher::CHECK_ONCE); + $updater->setPolicy(Watcher::CHECK_ONCE); //set the mtime to the past so it can detect an mtime change $cache->put('', ['storage_mtime' => 10]); @@ -82,11 +86,11 @@ class WatcherTest extends \Test\TestCase { /** * @medium */ - public function testFileToFolder() { + public function testFileToFolder(): void { $storage = $this->getTestStorage(); $cache = $storage->getCache(); $updater = $storage->getWatcher(); - $updater->setPolicy(\OC\Files\Cache\Watcher::CHECK_ONCE); + $updater->setPolicy(Watcher::CHECK_ONCE); //set the mtime to the past so it can detect an mtime change $cache->put('', ['storage_mtime' => 10]); @@ -103,7 +107,7 @@ class WatcherTest extends \Test\TestCase { $storage = $this->getTestStorage(); $cache = $storage->getCache(); $updater = $storage->getWatcher(); - $updater->setPolicy(\OC\Files\Cache\Watcher::CHECK_ONCE); + $updater->setPolicy(Watcher::CHECK_ONCE); //set the mtime to the past so it can detect an mtime change $cache->put('foo.txt', ['storage_mtime' => 10]); @@ -117,7 +121,7 @@ class WatcherTest extends \Test\TestCase { $this->assertTrue($cache->inCache('foo.txt/bar.txt')); } - public function testPolicyNever() { + public function testPolicyNever(): void { $storage = $this->getTestStorage(); $cache = $storage->getCache(); $updater = $storage->getWatcher(); @@ -125,7 +129,7 @@ class WatcherTest extends \Test\TestCase { //set the mtime to the past so it can detect an mtime change $cache->put('foo.txt', ['storage_mtime' => 10]); - $updater->setPolicy(\OC\Files\Cache\Watcher::CHECK_NEVER); + $updater->setPolicy(Watcher::CHECK_NEVER); $storage->file_put_contents('foo.txt', 'q'); $this->assertFalse($updater->checkUpdate('foo.txt')); @@ -135,7 +139,7 @@ class WatcherTest extends \Test\TestCase { $this->assertFalse($updater->checkUpdate('foo.txt')); } - public function testPolicyOnce() { + public function testPolicyOnce(): void { $storage = $this->getTestStorage(); $cache = $storage->getCache(); $updater = $storage->getWatcher(); @@ -143,7 +147,7 @@ class WatcherTest extends \Test\TestCase { //set the mtime to the past so it can detect an mtime change $cache->put('foo.txt', ['storage_mtime' => 10]); - $updater->setPolicy(\OC\Files\Cache\Watcher::CHECK_ONCE); + $updater->setPolicy(Watcher::CHECK_ONCE); $storage->file_put_contents('foo.txt', 'q'); $this->assertTrue($updater->checkUpdate('foo.txt')); @@ -153,7 +157,7 @@ class WatcherTest extends \Test\TestCase { $this->assertFalse($updater->checkUpdate('foo.txt')); } - public function testPolicyAlways() { + public function testPolicyAlways(): void { $storage = $this->getTestStorage(); $cache = $storage->getCache(); $updater = $storage->getWatcher(); @@ -161,7 +165,7 @@ class WatcherTest extends \Test\TestCase { //set the mtime to the past so it can detect an mtime change $cache->put('foo.txt', ['storage_mtime' => 10]); - $updater->setPolicy(\OC\Files\Cache\Watcher::CHECK_ALWAYS); + $updater->setPolicy(Watcher::CHECK_ALWAYS); $storage->file_put_contents('foo.txt', 'q'); $this->assertTrue($updater->checkUpdate('foo.txt')); @@ -173,10 +177,10 @@ class WatcherTest extends \Test\TestCase { /** * @param bool $scan - * @return \OC\Files\Storage\Storage + * @return Storage */ private function getTestStorage($scan = true) { - $storage = new \OC\Files\Storage\Temporary([]); + $storage = new Temporary([]); $textData = "dummy file data\n"; $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png'); $storage->mkdir('folder'); diff --git a/tests/lib/Files/Cache/Wrapper/CacheJailTest.php b/tests/lib/Files/Cache/Wrapper/CacheJailTest.php index 4c3dce74087..8ac3492fbd2 100644 --- a/tests/lib/Files/Cache/Wrapper/CacheJailTest.php +++ b/tests/lib/Files/Cache/Wrapper/CacheJailTest.php @@ -1,19 +1,23 @@ <?php + /** - * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Cache\Wrapper; +use OC\Files\Cache\Cache; use OC\Files\Cache\Wrapper\CacheJail; +use OC\Files\Cache\Wrapper\CacheWrapper; use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchQuery; +use OC\Files\Storage\Wrapper\Jail; use OC\User\User; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Cache\ICacheEntry; use OCP\Files\Search\ISearchComparison; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Test\Files\Cache\CacheTest; /** @@ -25,23 +29,25 @@ use Test\Files\Cache\CacheTest; */ class CacheJailTest extends CacheTest { /** - * @var \OC\Files\Cache\Cache $sourceCache + * @var Cache $sourceCache */ protected $sourceCache; protected function setUp(): void { parent::setUp(); - $this->storage->mkdir('foo'); + $this->storage->mkdir('jail'); $this->sourceCache = $this->cache; - $this->cache = new \OC\Files\Cache\Wrapper\CacheJail($this->sourceCache, 'foo'); + $this->cache = new CacheJail($this->sourceCache, 'jail'); + $this->cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); } - public function testSearchOutsideJail() { + public function testSearchOutsideJail(): void { $this->storage->getScanner()->scan(''); - $file1 = 'foo/foobar'; + $file1 = 'jail/foobar'; $file2 = 'folder/foobar'; $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder']; + $this->sourceCache->insert('folder', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); $this->sourceCache->put($file1, $data1); $this->sourceCache->put($file2, $data1); @@ -52,20 +58,20 @@ class CacheJailTest extends CacheTest { $this->assertEquals('foobar', $result[0]['path']); $result = $this->cache->search('%foo%'); - $this->assertCount(2, $result); + $this->assertCount(1, $result); usort($result, function ($a, $b) { return $a['path'] <=> $b['path']; }); - $this->assertEquals('', $result[0]['path']); - $this->assertEquals('foobar', $result[1]['path']); + $this->assertEquals('foobar', $result[0]['path']); } - public function testSearchMimeOutsideJail() { + public function testSearchMimeOutsideJail(): void { $this->storage->getScanner()->scan(''); - $file1 = 'foo/foobar'; + $file1 = 'jail/foobar'; $file2 = 'folder/foobar'; $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder']; + $this->sourceCache->insert('folder', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); $this->sourceCache->put($file1, $data1); $this->sourceCache->put($file2, $data1); @@ -76,35 +82,37 @@ class CacheJailTest extends CacheTest { $this->assertEquals('foobar', $result[0]['path']); } - public function testSearchQueryOutsideJail() { + public function testSearchQueryOutsideJail(): void { $this->storage->getScanner()->scan(''); - $file1 = 'foo/foobar'; + $file1 = 'jail/foobar'; $file2 = 'folder/foobar'; $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder']; + + $this->sourceCache->insert('folder', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]); $this->sourceCache->put($file1, $data1); $this->sourceCache->put($file2, $data1); - $user = new User('foo', null, $this->createMock(EventDispatcherInterface::class)); + $user = new User('foo', null, $this->createMock(IEventDispatcher::class)); $query = new SearchQuery(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', 'foobar'), 10, 0, [], $user); $result = $this->cache->searchQuery($query); $this->assertCount(1, $result); $this->assertEquals('foobar', $result[0]['path']); - $query = new SearchQuery(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', 'foo'), 10, 0, [], $user); + $query = new SearchQuery(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', 'jail'), 10, 0, [], $user); $result = $this->cache->searchQuery($query); $this->assertCount(1, $result); $this->assertEquals('', $result[0]['path']); } - public function testClearKeepEntriesOutsideJail() { - $file1 = 'foo/foobar'; - $file2 = 'foo/foobar/asd'; + public function testClearKeepEntriesOutsideJail(): void { + $file1 = 'jail/foobar'; + $file2 = 'jail/foobar/asd'; $file3 = 'folder/foobar'; - $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; + $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; - $this->sourceCache->put('foo', $data1); + $this->sourceCache->put('folder', $data1); $this->sourceCache->put($file1, $data1); $this->sourceCache->put($file2, $data1); $this->sourceCache->put($file3, $data1); @@ -115,27 +123,27 @@ class CacheJailTest extends CacheTest { $this->assertTrue($this->sourceCache->inCache('folder/foobar')); } - public function testGetById() { - $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; - $id = $this->sourceCache->put('foo/bar', $data1); + public function testGetById(): void { + $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; + $id = $this->sourceCache->put('jail/bar', $data1); // path from jailed foo of foo/bar is bar $path = $this->cache->getPathById($id); $this->assertEquals('bar', $path); // path from jailed '' of foo/bar is foo/bar - $this->cache = new \OC\Files\Cache\Wrapper\CacheJail($this->sourceCache, ''); + $this->cache = new CacheJail($this->sourceCache, ''); $path = $this->cache->getPathById($id); - $this->assertEquals('foo/bar', $path); + $this->assertEquals('jail/bar', $path); } - public function testGetIncomplete() { + public function testGetIncomplete(): void { //not supported $this->addToAssertionCount(1); } - public function testMoveFromJail() { - $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; + public function testMoveFromJail(): void { + $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; $this->sourceCache->put('source', $folderData); $this->sourceCache->put('source/foo', $folderData); @@ -150,8 +158,8 @@ class CacheJailTest extends CacheTest { $this->assertTrue($this->sourceCache->inCache('target/foo/bar')); } - public function testMoveToJail() { - $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; + public function testMoveToJail(): void { + $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; $this->sourceCache->put('source', $folderData); $this->sourceCache->put('source/foo', $folderData); @@ -166,8 +174,8 @@ class CacheJailTest extends CacheTest { $this->assertTrue($this->sourceCache->inCache('target/foo/bar')); } - public function testMoveBetweenJail() { - $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; + public function testMoveBetweenJail(): void { + $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]; $this->sourceCache->put('source', $folderData); $this->sourceCache->put('source/foo', $folderData); @@ -183,39 +191,78 @@ class CacheJailTest extends CacheTest { $this->assertTrue($this->sourceCache->inCache('target/foo/bar')); } - public function testSearchNested() { + public function testSearchNested(): void { $this->storage->getScanner()->scan(''); - $file1 = 'foo'; - $file2 = 'foo/bar'; - $file3 = 'foo/bar/asd'; + $file1 = 'jail'; + $file2 = 'jail/bar'; + $file3 = 'jail/bar/asd'; $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder']; $this->sourceCache->put($file1, $data1); $this->sourceCache->put($file2, $data1); $this->sourceCache->put($file3, $data1); - $nested = new \OC\Files\Cache\Wrapper\CacheJail($this->cache, 'bar'); + $nested = new CacheJail($this->cache, 'bar'); $result = $nested->search('%asd%'); $this->assertCount(1, $result); $this->assertEquals('asd', $result[0]['path']); } - public function testRootJail() { + public function testRootJail(): void { $this->storage->getScanner()->scan(''); - $file1 = 'foo'; - $file2 = 'foo/bar'; - $file3 = 'foo/bar/asd'; + $file1 = 'jail'; + $file2 = 'jail/bar'; + $file3 = 'jail/bar/asd'; $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder']; $this->sourceCache->put($file1, $data1); $this->sourceCache->put($file2, $data1); $this->sourceCache->put($file3, $data1); - $nested = new \OC\Files\Cache\Wrapper\CacheJail($this->sourceCache, ''); + $nested = new CacheJail($this->sourceCache, ''); $result = $nested->search('%asd%'); $this->assertCount(1, $result); - $this->assertEquals('foo/bar/asd', $result[0]['path']); + $this->assertEquals('jail/bar/asd', $result[0]['path']); + } + + public function testWatcher(): void { + $storage = new Jail([ + 'storage' => $this->storage, + 'root' => 'jail' + ]); + $storage->getScanner()->scan(''); + $storage->file_put_contents('bar', 'asd'); + + $this->assertFalse($this->cache->inCache('bar')); + $storage->getWatcher()->update('bar', ['mimetype' => 'text/plain']); + $this->assertTrue($this->cache->inCache('bar')); + } + + public function testWatcherAfterInnerWatcher(): void { + $storage = new Jail([ + 'storage' => $this->storage, + 'root' => 'jail' + ]); + $storage->getScanner()->scan(''); + $storage->file_put_contents('bar', 'asd'); + + // let the underlying storage create it's watcher first + $this->storage->getWatcher(); + + $this->assertFalse($this->cache->inCache('bar')); + $storage->getWatcher()->update('bar', ['mimetype' => 'text/plain']); + $this->assertTrue($this->cache->inCache('bar')); + } + + public function testUnJailedRoot(): void { + $jail1 = new CacheJail($this->sourceCache, 'foo'); + $jail2 = new CacheJail($jail1, 'bar'); + $this->assertEquals('foo/bar', $jail2->getGetUnjailedRoot()); + + $middleWrapper = new CacheWrapper($jail1); + $jail3 = new CacheJail($middleWrapper, 'bar'); + $this->assertEquals('foo/bar', $jail3->getGetUnjailedRoot()); } } diff --git a/tests/lib/Files/Cache/Wrapper/CachePermissionsMaskTest.php b/tests/lib/Files/Cache/Wrapper/CachePermissionsMaskTest.php index 20c20974c07..4fbeafc9270 100644 --- a/tests/lib/Files/Cache/Wrapper/CachePermissionsMaskTest.php +++ b/tests/lib/Files/Cache/Wrapper/CachePermissionsMaskTest.php @@ -1,13 +1,15 @@ <?php + /** - * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Cache\Wrapper; +use OC\Files\Cache\Cache; +use OC\Files\Cache\Wrapper\CachePermissionsMask; use OCP\Constants; use Test\Files\Cache\CacheTest; @@ -20,7 +22,7 @@ use Test\Files\Cache\CacheTest; */ class CachePermissionsMaskTest extends CacheTest { /** - * @var \OC\Files\Cache\Cache $sourceCache + * @var Cache $sourceCache */ protected $sourceCache; @@ -32,10 +34,10 @@ class CachePermissionsMaskTest extends CacheTest { } protected function getMaskedCached($mask) { - return new \OC\Files\Cache\Wrapper\CachePermissionsMask($this->sourceCache, $mask); + return new CachePermissionsMask($this->sourceCache, $mask); } - public function maskProvider() { + public static function maskProvider(): array { return [ [Constants::PERMISSION_ALL], [Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE], @@ -45,10 +47,10 @@ class CachePermissionsMaskTest extends CacheTest { } /** - * @dataProvider maskProvider * @param int $mask */ - public function testGetMasked($mask) { + #[\PHPUnit\Framework\Attributes\DataProvider('maskProvider')] + public function testGetMasked($mask): void { $cache = $this->getMaskedCached($mask); $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'permissions' => Constants::PERMISSION_ALL]; $this->sourceCache->put('foo', $data); @@ -62,10 +64,10 @@ class CachePermissionsMaskTest extends CacheTest { } /** - * @dataProvider maskProvider * @param int $mask */ - public function testGetFolderContentMasked($mask) { + #[\PHPUnit\Framework\Attributes\DataProvider('maskProvider')] + public function testGetFolderContentMasked($mask): void { $this->storage->mkdir('foo'); $this->storage->file_put_contents('foo/bar', 'asd'); $this->storage->file_put_contents('foo/asd', 'bar'); @@ -81,10 +83,10 @@ class CachePermissionsMaskTest extends CacheTest { } /** - * @dataProvider maskProvider * @param int $mask */ - public function testSearchMasked($mask) { + #[\PHPUnit\Framework\Attributes\DataProvider('maskProvider')] + public function testSearchMasked($mask): void { $this->storage->mkdir('foo'); $this->storage->file_put_contents('foo/bar', 'asd'); $this->storage->file_put_contents('foo/foobar', 'bar'); diff --git a/tests/lib/Files/Config/UserMountCacheTest.php b/tests/lib/Files/Config/UserMountCacheTest.php index f1206781c5e..6a3dc6a6d7e 100644 --- a/tests/lib/Files/Config/UserMountCacheTest.php +++ b/tests/lib/Files/Config/UserMountCacheTest.php @@ -1,26 +1,34 @@ <?php + /** - * Copyright (c) 2015 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Config; +use OC\DB\Exceptions\DbalException; use OC\DB\QueryBuilder\Literal; +use OC\Files\Cache\Cache; +use OC\Files\Config\UserMountCache; use OC\Files\Mount\MountPoint; use OC\Files\Storage\Storage; -use OCP\Cache\CappedMemoryCache; use OC\User\Manager; +use OCP\Cache\CappedMemoryCache; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Config\Event\UserMountAddedEvent; +use OCP\Files\Config\Event\UserMountRemovedEvent; +use OCP\Files\Config\Event\UserMountUpdatedEvent; use OCP\Files\Config\ICachedMountInfo; use OCP\ICacheFactory; use OCP\IConfig; use OCP\IDBConnection; use OCP\IUserManager; +use OCP\Server; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Test\TestCase; use Test\Util\User\Dummy; @@ -28,28 +36,19 @@ use Test\Util\User\Dummy; * @group DB */ class UserMountCacheTest extends TestCase { - /** - * @var IDBConnection - */ - private $connection; - - /** - * @var IUserManager - */ - private $userManager; - - /** - * @var \OC\Files\Config\UserMountCache - */ - private $cache; - - private $fileIds = []; + private IDBConnection $connection; + private IUserManager $userManager; + private IEventDispatcher $eventDispatcher; + private UserMountCache $cache; + private array $fileIds = []; protected function setUp(): void { parent::setUp(); $this->fileIds = []; - $this->connection = \OC::$server->getDatabaseConnection(); + + $this->connection = Server::get(IDBConnection::class); + $config = $this->getMockBuilder(IConfig::class) ->disableOriginalConstructor() ->getMock(); @@ -61,13 +60,22 @@ class UserMountCacheTest extends TestCase { ->expects($this->any()) ->method('getAppValue') ->willReturnArgument(2); - $this->userManager = new Manager($config, $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class)); + + $this->userManager = new Manager($config, $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class), $this->createMock(LoggerInterface::class)); $userBackend = new Dummy(); $userBackend->createUser('u1', ''); $userBackend->createUser('u2', ''); $userBackend->createUser('u3', ''); $this->userManager->registerBackend($userBackend); - $this->cache = new \OC\Files\Config\UserMountCache($this->connection, $this->userManager, $this->createMock(LoggerInterface::class)); + + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + + $this->cache = new UserMountCache($this->connection, + $this->userManager, + $this->createMock(LoggerInterface::class), + $this->createMock(IEventLogger::class), + $this->eventDispatcher, + ); } protected function tearDown(): void { @@ -84,26 +92,22 @@ class UserMountCacheTest extends TestCase { } } - private function getStorage($storageId) { - $rootId = $this->createCacheEntry('', $storageId); + private function getStorage($storageId, $rootInternalPath = '') { + $rootId = $this->createCacheEntry($rootInternalPath, $storageId); - $storageCache = $this->getMockBuilder('\OC\Files\Cache\Storage') - ->disableOriginalConstructor() - ->getMock(); + $storageCache = $this->createMock(\OC\Files\Cache\Storage::class); $storageCache->expects($this->any()) ->method('getNumericId') ->willReturn($storageId); - $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') - ->disableOriginalConstructor() - ->getMock(); + $cache = $this->createMock(Cache::class); $cache->expects($this->any()) ->method('getId') ->willReturn($rootId); + $cache->method('getNumericStorageId') + ->willReturn($storageId); - $storage = $this->getMockBuilder('\OC\Files\Storage\Storage') - ->disableOriginalConstructor() - ->getMock(); + $storage = $this->createMock(Storage::class); $storage->expects($this->any()) ->method('getStorageCache') ->willReturn($storageCache); @@ -118,7 +122,16 @@ class UserMountCacheTest extends TestCase { $this->invokePrivate($this->cache, 'mountsForUsers', [new CappedMemoryCache()]); } - public function testNewMounts() { + private function keyForMount(MountPoint $mount): string { + return $mount->getStorageRootId() . '::' . $mount->getMountPoint(); + } + + public function testNewMounts(): void { + $this->eventDispatcher + ->expects($this->once()) + ->method('dispatchTyped') + ->with($this->callback(fn (UserMountAddedEvent $event) => $event->mountPoint->getMountPoint() === '/asd/')); + $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); @@ -131,14 +144,19 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(1, $cachedMounts); - $cachedMount = $cachedMounts[0]; + $cachedMount = $cachedMounts[$this->keyForMount($mount)]; $this->assertEquals('/asd/', $cachedMount->getMountPoint()); - $this->assertEquals($user, $cachedMount->getUser()); + $this->assertEquals($user->getUID(), $cachedMount->getUser()->getUID()); $this->assertEquals($storage->getCache()->getId(''), $cachedMount->getRootId()); $this->assertEquals($storage->getStorageCache()->getNumericId(), $cachedMount->getStorageId()); } - public function testSameMounts() { + public function testSameMounts(): void { + $this->eventDispatcher + ->expects($this->once()) + ->method('dispatchTyped') + ->with($this->callback(fn (UserMountAddedEvent $event) => $event->mountPoint->getMountPoint() === '/asd/')); + $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); @@ -155,14 +173,26 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(1, $cachedMounts); - $cachedMount = $cachedMounts[0]; + $cachedMount = $cachedMounts[$this->keyForMount($mount)]; $this->assertEquals('/asd/', $cachedMount->getMountPoint()); - $this->assertEquals($user, $cachedMount->getUser()); + $this->assertEquals($user->getUID(), $cachedMount->getUser()->getUID()); $this->assertEquals($storage->getCache()->getId(''), $cachedMount->getRootId()); $this->assertEquals($storage->getStorageCache()->getNumericId(), $cachedMount->getStorageId()); } - public function testRemoveMounts() { + public function testRemoveMounts(): void { + $operation = 0; + $this->eventDispatcher + ->expects($this->exactly(2)) + ->method('dispatchTyped') + ->with($this->callback(function (UserMountAddedEvent|UserMountRemovedEvent $event) use (&$operation) { + return match(++$operation) { + 1 => $event instanceof UserMountAddedEvent && $event->mountPoint->getMountPoint() === '/asd/', + 2 => $event instanceof UserMountRemovedEvent && $event->mountPoint->getMountPoint() === '/asd/', + default => false, + }; + })); + $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); @@ -181,7 +211,20 @@ class UserMountCacheTest extends TestCase { $this->assertCount(0, $cachedMounts); } - public function testChangeMounts() { + public function testChangeMounts(): void { + $operation = 0; + $this->eventDispatcher + ->expects($this->exactly(3)) + ->method('dispatchTyped') + ->with($this->callback(function (UserMountAddedEvent|UserMountRemovedEvent $event) use (&$operation) { + return match(++$operation) { + 1 => $event instanceof UserMountAddedEvent && $event->mountPoint->getMountPoint() === '/bar/', + 2 => $event instanceof UserMountAddedEvent && $event->mountPoint->getMountPoint() === '/foo/', + 3 => $event instanceof UserMountRemovedEvent && $event->mountPoint->getMountPoint() === '/bar/', + default => false, + }; + })); + $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); @@ -200,11 +243,23 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(1, $cachedMounts); - $cachedMount = $cachedMounts[0]; + $cachedMount = $cachedMounts[$this->keyForMount($mount)]; $this->assertEquals('/foo/', $cachedMount->getMountPoint()); } - public function testChangeMountId() { + public function testChangeMountId(): void { + $operation = 0; + $this->eventDispatcher + ->expects($this->exactly(2)) + ->method('dispatchTyped') + ->with($this->callback(function (UserMountAddedEvent|UserMountUpdatedEvent $event) use (&$operation) { + return match(++$operation) { + 1 => $event instanceof UserMountAddedEvent && $event->mountPoint->getMountPoint() === '/foo/', + 2 => $event instanceof UserMountUpdatedEvent && $event->oldMountPoint->getMountId() === null && $event->newMountPoint->getMountId() === 1, + default => false, + }; + })); + $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); @@ -223,17 +278,17 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(1, $cachedMounts); - $cachedMount = $cachedMounts[0]; + $cachedMount = $cachedMounts[$this->keyForMount($mount)]; $this->assertEquals(1, $cachedMount->getMountId()); } - public function testGetMountsForUser() { + public function testGetMountsForUser(): void { $user1 = $this->userManager->get('u1'); $user2 = $this->userManager->get('u2'); $user3 = $this->userManager->get('u3'); [$storage1, $id1] = $this->getStorage(1); - [$storage2, $id2] = $this->getStorage(2); + [$storage2, $id2] = $this->getStorage(2, 'foo/bar'); $mount1 = new MountPoint($storage1, '/foo/'); $mount2 = new MountPoint($storage2, '/bar/'); @@ -248,21 +303,23 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user1); $this->assertCount(2, $cachedMounts); - $this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[0]->getUser()); - $this->assertEquals($id1, $cachedMounts[0]->getRootId()); - $this->assertEquals(1, $cachedMounts[0]->getStorageId()); - - $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[1]->getUser()); - $this->assertEquals($id2, $cachedMounts[1]->getRootId()); - $this->assertEquals(2, $cachedMounts[1]->getStorageId()); + $this->assertEquals('/foo/', $cachedMounts[$this->keyForMount($mount1)]->getMountPoint()); + $this->assertEquals($user1->getUID(), $cachedMounts[$this->keyForMount($mount1)]->getUser()->getUID()); + $this->assertEquals($id1, $cachedMounts[$this->keyForMount($mount1)]->getRootId()); + $this->assertEquals(1, $cachedMounts[$this->keyForMount($mount1)]->getStorageId()); + $this->assertEquals('', $cachedMounts[$this->keyForMount($mount1)]->getRootInternalPath()); + + $this->assertEquals('/bar/', $cachedMounts[$this->keyForMount($mount2)]->getMountPoint()); + $this->assertEquals($user1->getUID(), $cachedMounts[$this->keyForMount($mount2)]->getUser()->getUID()); + $this->assertEquals($id2, $cachedMounts[$this->keyForMount($mount2)]->getRootId()); + $this->assertEquals(2, $cachedMounts[$this->keyForMount($mount2)]->getStorageId()); + $this->assertEquals('foo/bar', $cachedMounts[$this->keyForMount($mount2)]->getRootInternalPath()); $cachedMounts = $this->cache->getMountsForUser($user3); $this->assertEmpty($cachedMounts); } - public function testGetMountsByStorageId() { + public function testGetMountsByStorageId(): void { $user1 = $this->userManager->get('u1'); $user2 = $this->userManager->get('u2'); @@ -282,17 +339,17 @@ class UserMountCacheTest extends TestCase { $this->assertCount(2, $cachedMounts); $this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[0]->getUser()); + $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID()); $this->assertEquals($id2, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint()); - $this->assertEquals($user2, $cachedMounts[1]->getUser()); + $this->assertEquals($user2->getUID(), $cachedMounts[1]->getUser()->getUID()); $this->assertEquals($id2, $cachedMounts[1]->getRootId()); $this->assertEquals(2, $cachedMounts[1]->getStorageId()); } - public function testGetMountsByRootId() { + public function testGetMountsByRootId(): void { $user1 = $this->userManager->get('u1'); $user2 = $this->userManager->get('u2'); @@ -312,12 +369,12 @@ class UserMountCacheTest extends TestCase { $this->assertCount(2, $cachedMounts); $this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[0]->getUser()); + $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID()); $this->assertEquals($id2, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint()); - $this->assertEquals($user2, $cachedMounts[1]->getUser()); + $this->assertEquals($user2->getUID(), $cachedMounts[1]->getUser()->getUID()); $this->assertEquals($id2, $cachedMounts[1]->getRootId()); $this->assertEquals(2, $cachedMounts[1]->getStorageId()); } @@ -330,34 +387,43 @@ class UserMountCacheTest extends TestCase { private function createCacheEntry($internalPath, $storageId, $size = 0) { $internalPath = trim($internalPath, '/'); - $inserted = $this->connection->insertIfNotExist('*PREFIX*filecache', [ - 'storage' => $storageId, - 'path' => $internalPath, - 'path_hash' => md5($internalPath), - 'parent' => -1, - 'name' => basename($internalPath), - 'mimetype' => 0, - 'mimepart' => 0, - 'size' => $size, - 'storage_mtime' => 0, - 'encrypted' => 0, - 'unencrypted_size' => 0, - 'etag' => '', - 'permissions' => 31 - ], ['storage', 'path_hash']); - if ($inserted) { - $id = (int)$this->connection->lastInsertId('*PREFIX*filecache'); + try { + $query = $this->connection->getQueryBuilder(); + $query->insert('filecache') + ->values([ + 'storage' => $query->createNamedParameter($storageId), + 'path' => $query->createNamedParameter($internalPath), + 'path_hash' => $query->createNamedParameter(md5($internalPath)), + 'parent' => $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT), + 'name' => $query->createNamedParameter(basename($internalPath)), + 'mimetype' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT), + 'mimepart' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT), + 'size' => $query->createNamedParameter($size), + 'storage_mtime' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT), + 'encrypted' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT), + 'unencrypted_size' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT), + 'etag' => $query->createNamedParameter(''), + 'permissions' => $query->createNamedParameter(31, IQueryBuilder::PARAM_INT), + ]); + $query->executeStatement(); + $id = $query->getLastInsertId(); $this->fileIds[] = $id; - } else { - $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` =?'; - $query = $this->connection->prepare($sql); - $query->execute([$storageId, md5($internalPath)]); - return (int)$query->fetchOne(); + } catch (DbalException $e) { + if ($e->getReason() === DbalException::REASON_UNIQUE_CONSTRAINT_VIOLATION) { + $query = $this->connection->getQueryBuilder(); + $query->select('fileid') + ->from('filecache') + ->where($query->expr()->eq('storage', $query->createNamedParameter($storageId))) + ->andWhere($query->expr()->eq('path_hash', $query->createNamedParameter(md5($internalPath)))); + $id = (int)$query->execute()->fetchColumn(); + } else { + throw $e; + } } return $id; } - public function testGetMountsForFileIdRootId() { + public function testGetMountsForFileIdRootId(): void { $user1 = $this->userManager->get('u1'); [$storage1, $rootId] = $this->getStorage(2); @@ -372,12 +438,12 @@ class UserMountCacheTest extends TestCase { $this->assertCount(1, $cachedMounts); $this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[0]->getUser()); + $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID()); $this->assertEquals($rootId, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); } - public function testGetMountsForFileIdSubFolder() { + public function testGetMountsForFileIdSubFolder(): void { $user1 = $this->userManager->get('u1'); $fileId = $this->createCacheEntry('/foo/bar', 2); @@ -394,14 +460,14 @@ class UserMountCacheTest extends TestCase { $this->assertCount(1, $cachedMounts); $this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[0]->getUser()); + $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID()); $this->assertEquals($rootId, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('foo/bar', $cachedMounts[0]->getInternalPath()); $this->assertEquals('/foo/foo/bar', $cachedMounts[0]->getPath()); } - public function testGetMountsForFileIdSubFolderMount() { + public function testGetMountsForFileIdSubFolderMount(): void { $user1 = $this->userManager->get('u1'); [$storage1, $rootId] = $this->getStorage(2); @@ -409,9 +475,9 @@ class UserMountCacheTest extends TestCase { $fileId = $this->createCacheEntry('/foo/bar', 2); - $mount1 = $this->getMockBuilder('\OC\Files\Mount\MountPoint') + $mount1 = $this->getMockBuilder(MountPoint::class) ->setConstructorArgs([$storage1, '/']) - ->setMethods(['getStorageRootId']) + ->onlyMethods(['getStorageRootId']) ->getMock(); $mount1->expects($this->any()) @@ -427,7 +493,7 @@ class UserMountCacheTest extends TestCase { $this->assertCount(1, $cachedMounts); $this->assertEquals('/', $cachedMounts[0]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[0]->getUser()); + $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID()); $this->assertEquals($folderId, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('foo', $cachedMounts[0]->getRootInternalPath()); @@ -435,16 +501,16 @@ class UserMountCacheTest extends TestCase { $this->assertEquals('/bar', $cachedMounts[0]->getPath()); } - public function testGetMountsForFileIdSubFolderMountOutside() { + public function testGetMountsForFileIdSubFolderMountOutside(): void { $user1 = $this->userManager->get('u1'); [$storage1, $rootId] = $this->getStorage(2); $folderId = $this->createCacheEntry('/foo', 2); $fileId = $this->createCacheEntry('/bar/asd', 2); - $mount1 = $this->getMockBuilder('\OC\Files\Mount\MountPoint') + $mount1 = $this->getMockBuilder(MountPoint::class) ->setConstructorArgs([$storage1, '/foo/']) - ->setMethods(['getStorageRootId']) + ->onlyMethods(['getStorageRootId']) ->getMock(); $mount1->expects($this->any()) @@ -463,7 +529,7 @@ class UserMountCacheTest extends TestCase { } - public function testGetMountsForFileIdDeletedUser() { + public function testGetMountsForFileIdDeletedUser(): void { $user1 = $this->userManager->get('u1'); [$storage1, $rootId] = $this->getStorage(2); @@ -478,7 +544,7 @@ class UserMountCacheTest extends TestCase { $this->assertEmpty($cachedMounts); } - public function testGetUsedSpaceForUsers() { + public function testGetUsedSpaceForUsers(): void { $user1 = $this->userManager->get('u1'); $user2 = $this->userManager->get('u2'); @@ -491,7 +557,7 @@ class UserMountCacheTest extends TestCase { $mount1 = $this->getMockBuilder(MountPoint::class) ->setConstructorArgs([$storage1, '/u1/']) - ->setMethods(['getStorageRootId', 'getNumericStorageId']) + ->onlyMethods(['getStorageRootId', 'getNumericStorageId']) ->getMock(); $mount1->expects($this->any()) @@ -509,7 +575,7 @@ class UserMountCacheTest extends TestCase { } - public function testMigrateMountProvider() { + public function testMigrateMountProvider(): void { $user1 = $this->userManager->get('u1'); [$storage1, $rootId] = $this->getStorage(2); @@ -521,7 +587,7 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user1); $this->assertCount(1, $cachedMounts); - $this->assertEquals('', $cachedMounts[0]->getMountProvider()); + $this->assertEquals('', $cachedMounts[$this->keyForMount($mount1)]->getMountProvider()); $mount1 = new MountPoint($storage1, '/foo/', null, null, null, null, 'dummy'); $this->cache->registerMounts($user1, [$mount1], ['dummy']); @@ -530,6 +596,6 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user1); $this->assertCount(1, $cachedMounts); - $this->assertEquals('dummy', $cachedMounts[0]->getMountProvider()); + $this->assertEquals('dummy', $cachedMounts[$this->keyForMount($mount1)]->getMountProvider()); } } diff --git a/tests/lib/Files/EtagTest.php b/tests/lib/Files/EtagTest.php index 43b92c12391..d1b344ee997 100644 --- a/tests/lib/Files/EtagTest.php +++ b/tests/lib/Files/EtagTest.php @@ -1,16 +1,23 @@ <?php + /** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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; use OC\Files\Filesystem; -use OCP\EventDispatcher\IEventDispatcher; +use OC\Files\Utils\Scanner; +use OC\Share\Share; use OCA\Files_Sharing\AppInfo\Application; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\ITempManager; +use OCP\IUserManager; +use OCP\Server; use Psr\Log\LoggerInterface; /** @@ -37,26 +44,26 @@ class EtagTest extends \Test\TestCase { // init files sharing new Application(); - \OC\Share\Share::registerBackend('file', 'OCA\Files_Sharing\ShareBackend\File'); - \OC\Share\Share::registerBackend('folder', 'OCA\Files_Sharing\ShareBackend\Folder', 'file'); + Share::registerBackend('file', 'OCA\Files_Sharing\ShareBackend\File'); + Share::registerBackend('folder', 'OCA\Files_Sharing\ShareBackend\Folder', 'file'); - $config = \OC::$server->getConfig(); - $this->datadir = $config->getSystemValue('datadirectory'); - $this->tmpDir = \OC::$server->getTempManager()->getTemporaryFolder(); + $config = Server::get(IConfig::class); + $this->datadir = $config->getSystemValueString('datadirectory'); + $this->tmpDir = Server::get(ITempManager::class)->getTemporaryFolder(); $config->setSystemValue('datadirectory', $this->tmpDir); $this->userBackend = new \Test\Util\User\Dummy(); - \OC_User::useBackend($this->userBackend); + Server::get(IUserManager::class)->registerBackend($this->userBackend); } protected function tearDown(): void { - \OC::$server->getConfig()->setSystemValue('datadirectory', $this->datadir); + Server::get(IConfig::class)->setSystemValue('datadirectory', $this->datadir); $this->logout(); parent::tearDown(); } - public function testNewUser() { + public function testNewUser(): void { $user1 = $this->getUniqueID('user_'); $this->userBackend->createUser($user1, ''); @@ -70,7 +77,7 @@ class EtagTest extends \Test\TestCase { $files = ['/foo.txt', '/folder/bar.txt', '/folder/subfolder', '/folder/subfolder/qwerty.txt']; $originalEtags = $this->getEtags($files); - $scanner = new \OC\Files\Utils\Scanner($user1, \OC::$server->getDatabaseConnection(), \OC::$server->query(IEventDispatcher::class), \OC::$server->get(LoggerInterface::class)); + $scanner = new Scanner($user1, Server::get(IDBConnection::class), Server::get(IEventDispatcher::class), Server::get(LoggerInterface::class)); $scanner->backgroundScan('/'); $newEtags = $this->getEtags($files); diff --git a/tests/lib/Files/FileInfoTest.php b/tests/lib/Files/FileInfoTest.php index fd2b506beb9..b3d3c9f0fec 100644 --- a/tests/lib/Files/FileInfoTest.php +++ b/tests/lib/Files/FileInfoTest.php @@ -1,14 +1,15 @@ <?php + /** - * Copyright (c) 2016 Robin Appelman <robin@icewind.nl> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files; use OC\Files\FileInfo; +use OC\Files\Mount\HomeMountPoint; +use OC\Files\Mount\MountPoint; use OC\Files\Storage\Home; use OC\Files\Storage\Temporary; use OCP\IConfig; @@ -27,25 +28,33 @@ class FileInfoTest extends TestCase { $this->config = $this->getMockBuilder(IConfig::class)->getMock(); } - public function testIsMountedHomeStorage() { + public function testIsMountedHomeStorage(): void { $user = $this->createMock(IUser::class); $user->method('getUID') ->willReturn('foo'); $user->method('getHome') ->willReturn('foo'); + $storage = new Home(['user' => $user]); $fileInfo = new FileInfo( '', - new Home(['user' => $user]), - '', [], null); + $storage, + '', + [], + new HomeMountPoint($user, $storage, '/foo/files') + ); $this->assertFalse($fileInfo->isMounted()); } - public function testIsMountedNonHomeStorage() { + public function testIsMountedNonHomeStorage(): void { + $storage = new Temporary(); $fileInfo = new FileInfo( '', - new Temporary(), - '', [], null); + $storage, + '', + [], + new MountPoint($storage, '/foo/files/bar') + ); $this->assertTrue($fileInfo->isMounted()); } } diff --git a/tests/lib/Files/FilenameValidatorTest.php b/tests/lib/Files/FilenameValidatorTest.php new file mode 100644 index 00000000000..162275a2cf8 --- /dev/null +++ b/tests/lib/Files/FilenameValidatorTest.php @@ -0,0 +1,491 @@ +<?php + +declare(strict_types=1); + +/*! + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Files; + +use OC\Files\FilenameValidator; +use OCP\Files\EmptyFileNameException; +use OCP\Files\FileNameTooLongException; +use OCP\Files\InvalidCharacterInPathException; +use OCP\Files\InvalidDirectoryException; +use OCP\Files\InvalidPathException; +use OCP\Files\ReservedWordException; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\IL10N; +use OCP\L10N\IFactory; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class FilenameValidatorTest extends TestCase { + + protected IFactory&MockObject $l10n; + protected IConfig&MockObject $config; + protected IDBConnection&MockObject $database; + protected LoggerInterface&MockObject $logger; + + protected function setUp(): void { + parent::setUp(); + $l10n = $this->createMock(IL10N::class); + $l10n->method('t') + ->willReturnCallback(fn ($string, $params) => sprintf($string, ...$params)); + $this->l10n = $this->createMock(IFactory::class); + $this->l10n + ->method('get') + ->with('core') + ->willReturn($l10n); + + $this->config = $this->createMock(IConfig::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->database = $this->createMock(IDBConnection::class); + $this->database->method('supports4ByteText')->willReturn(true); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataValidateFilename')] + public function testValidateFilename( + string $filename, + array $forbiddenNames, + array $forbiddenBasenames, + array $forbiddenExtensions, + array $forbiddenCharacters, + ?string $exception, + ): void { + /** @var FilenameValidator&MockObject */ + $validator = $this->getMockBuilder(FilenameValidator::class) + ->onlyMethods([ + 'getForbiddenBasenames', + 'getForbiddenCharacters', + 'getForbiddenExtensions', + 'getForbiddenFilenames', + ]) + ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger]) + ->getMock(); + + $validator->method('getForbiddenBasenames') + ->willReturn($forbiddenBasenames); + $validator->method('getForbiddenCharacters') + ->willReturn($forbiddenCharacters); + $validator->method('getForbiddenExtensions') + ->willReturn($forbiddenExtensions); + $validator->method('getForbiddenFilenames') + ->willReturn($forbiddenNames); + + if ($exception !== null) { + $this->expectException($exception); + } else { + $this->expectNotToPerformAssertions(); + } + $validator->validateFilename($filename); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataValidateFilename')] + public function testIsFilenameValid( + string $filename, + array $forbiddenNames, + array $forbiddenBasenames, + array $forbiddenExtensions, + array $forbiddenCharacters, + ?string $exception, + ): void { + /** @var FilenameValidator&MockObject */ + $validator = $this->getMockBuilder(FilenameValidator::class) + ->onlyMethods([ + 'getForbiddenBasenames', + 'getForbiddenExtensions', + 'getForbiddenFilenames', + 'getForbiddenCharacters', + ]) + ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger]) + ->getMock(); + + $validator->method('getForbiddenBasenames') + ->willReturn($forbiddenBasenames); + $validator->method('getForbiddenCharacters') + ->willReturn($forbiddenCharacters); + $validator->method('getForbiddenExtensions') + ->willReturn($forbiddenExtensions); + $validator->method('getForbiddenFilenames') + ->willReturn($forbiddenNames); + + + $this->assertEquals($exception === null, $validator->isFilenameValid($filename)); + } + + public static function dataValidateFilename(): array { + return [ + 'valid name' => [ + 'a: b.txt', ['.htaccess'], [], [], [], null + ], + 'forbidden name in the middle is ok' => [ + 'a.htaccess.txt', ['.htaccess'], [], [], [], null + ], + 'valid name with some more parameters' => [ + 'a: b.txt', ['.htaccess'], [], ['exe'], ['~'], null + ], + 'valid name checks only the full name' => [ + '.htaccess.sample', ['.htaccess'], [], [], [], null + ], + 'forbidden name' => [ + '.htaccess', ['.htaccess'], [], [], [], ReservedWordException::class + ], + 'forbidden name - name is case insensitive' => [ + 'COM1', ['.htaccess', 'com1'], [], [], [], ReservedWordException::class + ], + 'forbidden basename' => [ + // needed for Windows namespaces + 'com1.suffix', ['.htaccess'], ['com1'], [], [], ReservedWordException::class + ], + 'forbidden basename case insensitive' => [ + // needed for Windows namespaces + 'COM1.suffix', ['.htaccess'], ['com1'], [], [], ReservedWordException::class + ], + 'forbidden basename for hidden files' => [ + // needed for Windows namespaces + '.thumbs.db', ['.htaccess'], ['.thumbs'], [], [], ReservedWordException::class + ], + 'invalid character' => [ + 'a: b.txt', ['.htaccess'], [], [], [':'], InvalidCharacterInPathException::class + ], + 'invalid path' => [ + '../../foo.bar', ['.htaccess'], [], [], ['/', '\\'], InvalidCharacterInPathException::class, + ], + 'invalid extension' => [ + 'a: b.txt', ['.htaccess'], [], ['.txt'], [], InvalidPathException::class + ], + 'invalid extension case insensitive' => [ + 'a: b.TXT', ['.htaccess'], [], ['.txt'], [], InvalidPathException::class + ], + 'empty filename' => [ + '', [], [], [], [], EmptyFileNameException::class + ], + 'reserved unix name "."' => [ + '.', [], [], [], [], InvalidDirectoryException::class + ], + 'reserved unix name ".."' => [ + '..', [], [], [], [], InvalidDirectoryException::class + ], + 'weird but valid tripple dot name' => [ + '...', [], [], [], [], null // is valid + ], + 'too long filename "."' => [ + str_repeat('a', 251), [], [], [], [], FileNameTooLongException::class + ], + // make sure to not split the list entries as they migh contain Unicode sequences + // in this example the "face in clouds" emoji contains the clouds emoji so only having clouds is ok + ['🌫️.txt', ['.htaccess'], [], [], ['😶🌫️'], null], + // This is the reverse: clouds are forbidden -> so is also the face in the clouds emoji + ['😶🌫️.txt', ['.htaccess'], [], [], ['🌫️'], InvalidCharacterInPathException::class], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('data4ByteUnicode')] + public function testDatabaseDoesNotSupport4ByteText($filename): void { + $database = $this->createMock(IDBConnection::class); + $database->expects($this->once()) + ->method('supports4ByteText') + ->willReturn(false); + $this->expectException(InvalidCharacterInPathException::class); + $validator = new FilenameValidator($this->l10n, $database, $this->config, $this->logger); + $validator->validateFilename($filename); + } + + public static function data4ByteUnicode(): array { + return [ + ['plane 1 𐪅'], + ['emoji 😶🌫️'], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataInvalidAsciiCharacters')] + public function testInvalidAsciiCharactersAreAlwaysForbidden(string $filename): void { + $this->expectException(InvalidPathException::class); + $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger); + $validator->validateFilename($filename); + } + + public static function dataInvalidAsciiCharacters(): array { + return [ + [\chr(0)], + [\chr(1)], + [\chr(2)], + [\chr(3)], + [\chr(4)], + [\chr(5)], + [\chr(6)], + [\chr(7)], + [\chr(8)], + [\chr(9)], + [\chr(10)], + [\chr(11)], + [\chr(12)], + [\chr(13)], + [\chr(14)], + [\chr(15)], + [\chr(16)], + [\chr(17)], + [\chr(18)], + [\chr(19)], + [\chr(20)], + [\chr(21)], + [\chr(22)], + [\chr(23)], + [\chr(24)], + [\chr(25)], + [\chr(26)], + [\chr(27)], + [\chr(28)], + [\chr(29)], + [\chr(30)], + [\chr(31)], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataIsForbidden')] + public function testIsForbidden(string $filename, array $forbiddenNames, bool $expected): void { + /** @var FilenameValidator&MockObject */ + $validator = $this->getMockBuilder(FilenameValidator::class) + ->onlyMethods(['getForbiddenFilenames']) + ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger]) + ->getMock(); + + $validator->method('getForbiddenFilenames') + ->willReturn($forbiddenNames); + + $this->assertEquals($expected, $validator->isForbidden($filename)); + } + + public static function dataIsForbidden(): array { + return [ + 'valid name' => [ + 'a: b.txt', ['.htaccess'], false + ], + 'valid name with some more parameters' => [ + 'a: b.txt', ['.htaccess'], false + ], + 'valid name as only full forbidden should be matched' => [ + '.htaccess.sample', ['.htaccess'], false, + ], + 'forbidden name' => [ + '.htaccess', ['.htaccess'], true + ], + 'forbidden name - name is case insensitive' => [ + 'COM1', ['.htaccess', 'com1'], true, + ], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetForbiddenExtensions')] + public function testGetForbiddenExtensions(array $configValue, array $expectedValue): void { + $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger); + $this->config + // only once - then cached + ->expects(self::once()) + ->method('getSystemValue') + ->with('forbidden_filename_extensions', ['.filepart']) + ->willReturn($configValue); + + self::assertEqualsCanonicalizing($expectedValue, $validator->getForbiddenExtensions()); + } + + public static function dataGetForbiddenExtensions(): array { + return [ + // default + [['.filepart'], ['.filepart', '.part']], + // always include .part + [[], ['.part']], + // handle case insensitivity + [['.TXT'], ['.txt', '.part']], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetForbiddenFilenames')] + public function testGetForbiddenFilenames(array $configValue, array $legacyValue, array $expectedValue): void { + $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger); + $this->config + // only once - then cached + ->expects(self::exactly(2)) + ->method('getSystemValue') + ->willReturnMap([ + ['forbidden_filenames', ['.htaccess'], $configValue], + ['blacklisted_files', [], $legacyValue], + ]); + + $this->logger + ->expects(empty($legacyValue) ? self::never() : self::once()) + ->method('warning'); + + self::assertEqualsCanonicalizing($expectedValue, $validator->getForbiddenFilenames()); + } + + public static function dataGetForbiddenFilenames(): array { + return [ + // default + [['.htaccess'], [], ['.htaccess']], + // with legacy values + [['.htaccess'], ['legacy'], ['.htaccess', 'legacy']], + // handle case insensitivity + [['FileName', '.htaccess'], ['LegAcy'], ['.htaccess', 'filename', 'legacy']], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetForbiddenBasenames')] + public function testGetForbiddenBasenames(array $configValue, array $expectedValue): void { + $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger); + $this->config + // only once - then cached + ->expects(self::once()) + ->method('getSystemValue') + ->with('forbidden_filename_basenames', []) + ->willReturn($configValue); + + self::assertEqualsCanonicalizing($expectedValue, $validator->getForbiddenBasenames()); + } + + public static function dataGetForbiddenBasenames(): array { + return [ + // default + [[], []], + // with values + [['aux', 'com0'], ['aux', 'com0']], + // handle case insensitivity + [['AuX', 'COM1'], ['aux', 'com1']], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataSanitizeFilename')] + public function testSanitizeFilename( + string $filename, + array $forbiddenNames, + array $forbiddenBasenames, + array $forbiddenExtensions, + array $forbiddenCharacters, + string $expected, + ): void { + /** @var FilenameValidator&MockObject */ + $validator = $this->getMockBuilder(FilenameValidator::class) + ->onlyMethods([ + 'getForbiddenBasenames', + 'getForbiddenExtensions', + 'getForbiddenFilenames', + 'getForbiddenCharacters', + ]) + ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger]) + ->getMock(); + + $validator->method('getForbiddenBasenames') + ->willReturn($forbiddenBasenames); + $validator->method('getForbiddenCharacters') + ->willReturn($forbiddenCharacters); + $validator->method('getForbiddenExtensions') + ->willReturn($forbiddenExtensions); + $validator->method('getForbiddenFilenames') + ->willReturn($forbiddenNames); + + $this->assertEquals($expected, $validator->sanitizeFilename($filename)); + } + + public static function dataSanitizeFilename(): array { + return [ + 'valid name' => [ + 'a * b.txt', ['.htaccess'], [], [], [], 'a * b.txt' + ], + 'forbidden name in the middle is ok' => [ + 'a.htaccess.txt', ['.htaccess'], [], [], [], 'a.htaccess.txt' + ], + 'forbidden name on the beginning' => [ + '.htaccess.sample', ['.htaccess'], [], [], [], '.htaccess.sample' + ], + 'forbidden name' => [ + '.htaccess', ['.htaccess'], [], [], [], '.htaccess (renamed)' + ], + 'forbidden name - name is case insensitive' => [ + 'COM1', ['.htaccess', 'com1'], [], [], [], 'COM1 (renamed)' + ], + 'forbidden basename' => [ + 'com1.suffix', ['.htaccess'], ['com1'], [], [], 'com1 (renamed).suffix' + ], + 'forbidden basename case insensitive' => [ + // needed for Windows namespaces + 'COM1.suffix', ['.htaccess'], ['com1'], [], [], 'COM1 (renamed).suffix' + ], + 'forbidden basename for hidden files' => [ + // needed for Windows namespaces + '.thumbs.db', ['.htaccess'], ['.thumbs'], [], [], '.thumbs (renamed).db' + ], + 'invalid character' => [ + 'a: b.txt', ['.htaccess'], [], [], [':'], 'a_ b.txt', + ], + 'invalid extension' => [ + 'a: b.txt', ['.htaccess'], [], ['.txt'], [], 'a: b' + ], + 'invalid extension case insensitive' => [ + 'a: b.TXT', ['.htaccess'], [], ['.txt'], [], 'a: b' + ], + 'empty filename' => [ + '', [], [], [], [], 'renamed file' + ], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataSanitizeFilenameCharacterReplacement')] + public function testSanitizeFilenameCharacterReplacement( + string $filename, + array $forbiddenCharacters, + ?string $characterReplacement, + ?string $expected, + ): void { + /** @var FilenameValidator&MockObject */ + $validator = $this->getMockBuilder(FilenameValidator::class) + ->onlyMethods([ + 'getForbiddenBasenames', + 'getForbiddenExtensions', + 'getForbiddenFilenames', + 'getForbiddenCharacters', + ]) + ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger]) + ->getMock(); + + $validator->method('getForbiddenBasenames') + ->willReturn([]); + $validator->method('getForbiddenCharacters') + ->willReturn($forbiddenCharacters); + $validator->method('getForbiddenExtensions') + ->willReturn([]); + $validator->method('getForbiddenFilenames') + ->willReturn([]); + + if ($expected === null) { + $this->expectException(\InvalidArgumentException::class); + $validator->sanitizeFilename($filename, $characterReplacement); + } else { + $this->assertEquals($expected, $validator->sanitizeFilename($filename, $characterReplacement)); + } + } + + public static function dataSanitizeFilenameCharacterReplacement(): array { + return [ + 'default' => [ + 'foo*bar', ['*'], null, 'foo_bar' + ], + 'default - underscore not allowed' => [ + 'foo*bar', ['*', '_'], null, 'foo-bar' + ], + 'default - dash and underscore not allowed' => [ + 'foo*bar', ['*', '-', '_'], null, 'foo bar' + ], + 'default - no replacement' => [ + 'foo*bar', ['*', ' ', '_', '-'], null, null + ], + 'custom replacement' => [ + 'foo*bar', ['*'], 'x', 'fooxbar' + ], + ]; + } +} diff --git a/tests/lib/Files/FilesystemTest.php b/tests/lib/Files/FilesystemTest.php index 8f34860d85a..a819acb1620 100644 --- a/tests/lib/Files/FilesystemTest.php +++ b/tests/lib/Files/FilesystemTest.php @@ -1,42 +1,37 @@ <?php + /** - * ownCloud - * - * @author Robin Appelman - * @copyright 2012 Robin Appelman icewind@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * 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; +use OC\Files\Filesystem; use OC\Files\Mount\MountPoint; use OC\Files\Storage\Temporary; +use OC\Files\View; use OC\User\NoUserException; +use OCP\Files; use OCP\Files\Config\IMountProvider; +use OCP\Files\Config\IMountProviderCollection; +use OCP\Files\Mount\IMountPoint; use OCP\Files\Storage\IStorageFactory; +use OCP\IConfig; +use OCP\ITempManager; use OCP\IUser; +use OCP\IUserManager; +use OCP\IUserSession; +use OCP\Server; class DummyMountProvider implements IMountProvider { - private $mounts = []; - /** * @param array $mounts */ - public function __construct(array $mounts) { - $this->mounts = $mounts; + public function __construct( + private array $mounts, + ) { } /** @@ -44,7 +39,7 @@ class DummyMountProvider implements IMountProvider { * * @param IUser $user * @param IStorageFactory $loader - * @return \OCP\Files\Mount\IMountPoint[] + * @return IMountPoint[] */ public function getMountsForUser(IUser $user, IStorageFactory $loader) { return isset($this->mounts[$user->getUID()]) ? $this->mounts[$user->getUID()] : []; @@ -59,8 +54,8 @@ class DummyMountProvider implements IMountProvider { * @package Test\Files */ class FilesystemTest extends \Test\TestCase { - public const TEST_FILESYSTEM_USER1 = "test-filesystem-user1"; - public const TEST_FILESYSTEM_USER2 = "test-filesystem-user1"; + public const TEST_FILESYSTEM_USER1 = 'test-filesystem-user1'; + public const TEST_FILESYSTEM_USER2 = 'test-filesystem-user1'; /** * @var array tmpDirs @@ -71,7 +66,7 @@ class FilesystemTest extends \Test\TestCase { * @return array */ private function getStorageData() { - $dir = \OC::$server->getTempManager()->getTemporaryFolder(); + $dir = Server::get(ITempManager::class)->getTemporaryFolder(); $this->tmpDirs[] = $dir; return ['datadir' => $dir]; } @@ -81,13 +76,13 @@ class FilesystemTest extends \Test\TestCase { $userBackend = new \Test\Util\User\Dummy(); $userBackend->createUser(self::TEST_FILESYSTEM_USER1, self::TEST_FILESYSTEM_USER1); $userBackend->createUser(self::TEST_FILESYSTEM_USER2, self::TEST_FILESYSTEM_USER2); - \OC::$server->getUserManager()->registerBackend($userBackend); + Server::get(IUserManager::class)->registerBackend($userBackend); $this->loginAsUser(); } protected function tearDown(): void { foreach ($this->tmpDirs as $dir) { - \OC_Helper::rmdirr($dir); + Files::rmdirr($dir); } $this->logout(); @@ -95,25 +90,25 @@ class FilesystemTest extends \Test\TestCase { parent::tearDown(); } - public function testMount() { - \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', self::getStorageData(), '/'); - $this->assertEquals('/', \OC\Files\Filesystem::getMountPoint('/')); - $this->assertEquals('/', \OC\Files\Filesystem::getMountPoint('/some/folder')); - [, $internalPath] = \OC\Files\Filesystem::resolvePath('/'); + public function testMount(): void { + Filesystem::mount('\OC\Files\Storage\Local', self::getStorageData(), '/'); + $this->assertEquals('/', Filesystem::getMountPoint('/')); + $this->assertEquals('/', Filesystem::getMountPoint('/some/folder')); + [, $internalPath] = Filesystem::resolvePath('/'); $this->assertEquals('', $internalPath); - [, $internalPath] = \OC\Files\Filesystem::resolvePath('/some/folder'); + [, $internalPath] = Filesystem::resolvePath('/some/folder'); $this->assertEquals('some/folder', $internalPath); - \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', self::getStorageData(), '/some'); - $this->assertEquals('/', \OC\Files\Filesystem::getMountPoint('/')); - $this->assertEquals('/some/', \OC\Files\Filesystem::getMountPoint('/some/folder')); - $this->assertEquals('/some/', \OC\Files\Filesystem::getMountPoint('/some/')); - $this->assertEquals('/some/', \OC\Files\Filesystem::getMountPoint('/some')); - [, $internalPath] = \OC\Files\Filesystem::resolvePath('/some/folder'); + Filesystem::mount('\OC\Files\Storage\Local', self::getStorageData(), '/some'); + $this->assertEquals('/', Filesystem::getMountPoint('/')); + $this->assertEquals('/some/', Filesystem::getMountPoint('/some/folder')); + $this->assertEquals('/some/', Filesystem::getMountPoint('/some/')); + $this->assertEquals('/some/', Filesystem::getMountPoint('/some')); + [, $internalPath] = Filesystem::resolvePath('/some/folder'); $this->assertEquals('folder', $internalPath); } - public function normalizePathData() { + public static function normalizePathData(): array { return [ ['/', ''], ['/', '/'], @@ -207,14 +202,12 @@ class FilesystemTest extends \Test\TestCase { ]; } - /** - * @dataProvider normalizePathData - */ - public function testNormalizePath($expected, $path, $stripTrailingSlash = true) { - $this->assertEquals($expected, \OC\Files\Filesystem::normalizePath($path, $stripTrailingSlash)); + #[\PHPUnit\Framework\Attributes\DataProvider('normalizePathData')] + public function testNormalizePath($expected, $path, $stripTrailingSlash = true): void { + $this->assertEquals($expected, Filesystem::normalizePath($path, $stripTrailingSlash)); } - public function normalizePathKeepUnicodeData() { + public static function normalizePathKeepUnicodeData(): array { $nfdName = 'ümlaut'; $nfcName = 'ümlaut'; return [ @@ -225,22 +218,20 @@ class FilesystemTest extends \Test\TestCase { ]; } - /** - * @dataProvider normalizePathKeepUnicodeData - */ - public function testNormalizePathKeepUnicode($expected, $path, $keepUnicode = false) { - $this->assertEquals($expected, \OC\Files\Filesystem::normalizePath($path, true, false, $keepUnicode)); + #[\PHPUnit\Framework\Attributes\DataProvider('normalizePathKeepUnicodeData')] + public function testNormalizePathKeepUnicode($expected, $path, $keepUnicode = false): void { + $this->assertEquals($expected, Filesystem::normalizePath($path, true, false, $keepUnicode)); } - public function testNormalizePathKeepUnicodeCache() { + public function testNormalizePathKeepUnicodeCache(): void { $nfdName = 'ümlaut'; $nfcName = 'ümlaut'; // call in succession due to cache - $this->assertEquals('/' . $nfcName, \OC\Files\Filesystem::normalizePath($nfdName, true, false, false)); - $this->assertEquals('/' . $nfdName, \OC\Files\Filesystem::normalizePath($nfdName, true, false, true)); + $this->assertEquals('/' . $nfcName, Filesystem::normalizePath($nfdName, true, false, false)); + $this->assertEquals('/' . $nfdName, Filesystem::normalizePath($nfdName, true, false, true)); } - public function isValidPathData() { + public static function isValidPathData(): array { return [ ['/', true], ['/path', true], @@ -266,14 +257,12 @@ class FilesystemTest extends \Test\TestCase { ]; } - /** - * @dataProvider isValidPathData - */ - public function testIsValidPath($path, $expected) { - $this->assertSame($expected, \OC\Files\Filesystem::isValidPath($path)); + #[\PHPUnit\Framework\Attributes\DataProvider('isValidPathData')] + public function testIsValidPath($path, $expected): void { + $this->assertSame($expected, Filesystem::isValidPath($path)); } - public function isFileBlacklistedData() { + public static function isFileBlacklistedData(): array { return [ ['/etc/foo/bar/foo.txt', false], ['\etc\foo/bar\foo.txt', false], @@ -288,81 +277,79 @@ class FilesystemTest extends \Test\TestCase { ]; } - /** - * @dataProvider isFileBlacklistedData - */ - public function testIsFileBlacklisted($path, $expected) { - $this->assertSame($expected, \OC\Files\Filesystem::isFileBlacklisted($path)); + #[\PHPUnit\Framework\Attributes\DataProvider('isFileBlacklistedData')] + public function testIsFileBlacklisted($path, $expected): void { + $this->assertSame($expected, Filesystem::isFileBlacklisted($path)); } - public function testNormalizePathUTF8() { + public function testNormalizePathUTF8(): void { if (!class_exists('Patchwork\PHP\Shim\Normalizer')) { $this->markTestSkipped('UTF8 normalizer Patchwork was not found'); } - $this->assertEquals("/foo/bar\xC3\xBC", \OC\Files\Filesystem::normalizePath("/foo/baru\xCC\x88")); - $this->assertEquals("/foo/bar\xC3\xBC", \OC\Files\Filesystem::normalizePath("\\foo\\baru\xCC\x88")); + $this->assertEquals("/foo/bar\xC3\xBC", Filesystem::normalizePath("/foo/baru\xCC\x88")); + $this->assertEquals("/foo/bar\xC3\xBC", Filesystem::normalizePath("\\foo\\baru\xCC\x88")); } - public function testHooks() { - if (\OC\Files\Filesystem::getView()) { + public function testHooks(): void { + if (Filesystem::getView()) { $user = \OC_User::getUser(); } else { $user = self::TEST_FILESYSTEM_USER1; $backend = new \Test\Util\User\Dummy(); - \OC_User::useBackend($backend); + Server::get(IUserManager::class)->registerBackend($backend); $backend->createUser($user, $user); - $userObj = \OC::$server->getUserManager()->get($user); - \OC::$server->getUserSession()->setUser($userObj); - \OC\Files\Filesystem::init($user, '/' . $user . '/files'); + $userObj = Server::get(IUserManager::class)->get($user); + Server::get(IUserSession::class)->setUser($userObj); + Filesystem::init($user, '/' . $user . '/files'); } \OC_Hook::clear('OC_Filesystem'); \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook'); - \OC\Files\Filesystem::mount('OC\Files\Storage\Temporary', [], '/'); + Filesystem::mount('OC\Files\Storage\Temporary', [], '/'); - $rootView = new \OC\Files\View(''); + $rootView = new View(''); $rootView->mkdir('/' . $user); $rootView->mkdir('/' . $user . '/files'); -// \OC\Files\Filesystem::file_put_contents('/foo', 'foo'); - \OC\Files\Filesystem::mkdir('/bar'); -// \OC\Files\Filesystem::file_put_contents('/bar//foo', 'foo'); + // \OC\Files\Filesystem::file_put_contents('/foo', 'foo'); + Filesystem::mkdir('/bar'); + // \OC\Files\Filesystem::file_put_contents('/bar//foo', 'foo'); - $tmpFile = \OC::$server->getTempManager()->getTemporaryFile(); + $tmpFile = Server::get(ITempManager::class)->getTemporaryFile(); file_put_contents($tmpFile, 'foo'); $fh = fopen($tmpFile, 'r'); -// \OC\Files\Filesystem::file_put_contents('/bar//foo', $fh); + // \OC\Files\Filesystem::file_put_contents('/bar//foo', $fh); } /** * Tests that an exception is thrown when passed user does not exist. * */ - public function testLocalMountWhenUserDoesNotExist() { - $this->expectException(\OC\User\NoUserException::class); + public function testLocalMountWhenUserDoesNotExist(): void { + $this->expectException(NoUserException::class); $userId = $this->getUniqueID('user_'); - \OC\Files\Filesystem::initMountPoints($userId); + Filesystem::initMountPoints($userId); } - - public function testNullUserThrows() { - $this->expectException(\OC\User\NoUserException::class); - \OC\Files\Filesystem::initMountPoints(null); + public function testNullUserThrows(): void { + $this->expectException(NoUserException::class); + + Filesystem::initMountPoints(null); } - public function testNullUserThrowsTwice() { + public function testNullUserThrowsTwice(): void { $thrown = 0; try { - \OC\Files\Filesystem::initMountPoints(null); + Filesystem::initMountPoints(null); } catch (NoUserException $e) { $thrown++; } try { - \OC\Files\Filesystem::initMountPoints(null); + Filesystem::initMountPoints(null); } catch (NoUserException $e) { $thrown++; } @@ -372,18 +359,18 @@ class FilesystemTest extends \Test\TestCase { /** * Tests that an exception is thrown when passed user does not exist. */ - public function testLocalMountWhenUserDoesNotExistTwice() { + public function testLocalMountWhenUserDoesNotExistTwice(): void { $thrown = 0; $userId = $this->getUniqueID('user_'); try { - \OC\Files\Filesystem::initMountPoints($userId); + Filesystem::initMountPoints($userId); } catch (NoUserException $e) { $thrown++; } try { - \OC\Files\Filesystem::initMountPoints($userId); + Filesystem::initMountPoints($userId); } catch (NoUserException $e) { $thrown++; } @@ -394,14 +381,14 @@ class FilesystemTest extends \Test\TestCase { /** * Tests that the home storage is used for the user's mount point */ - public function testHomeMount() { + public function testHomeMount(): void { $userId = $this->getUniqueID('user_'); - \OC::$server->getUserManager()->createUser($userId, $userId); + Server::get(IUserManager::class)->createUser($userId, $userId); - \OC\Files\Filesystem::initMountPoints($userId); + Filesystem::initMountPoints($userId); - $homeMount = \OC\Files\Filesystem::getStorage('/' . $userId . '/'); + $homeMount = Filesystem::getStorage('/' . $userId . '/'); $this->assertTrue($homeMount->instanceOfStorage('\OCP\Files\IHomeStorage')); if ($homeMount->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')) { @@ -410,7 +397,7 @@ class FilesystemTest extends \Test\TestCase { $this->assertEquals('home::' . $userId, $homeMount->getId()); } - $user = \OC::$server->getUserManager()->get($userId); + $user = Server::get(IUserManager::class)->get($userId); if ($user !== null) { $user->delete(); } @@ -418,30 +405,30 @@ class FilesystemTest extends \Test\TestCase { public function dummyHook($arguments) { $path = $arguments['path']; - $this->assertEquals($path, \OC\Files\Filesystem::normalizePath($path)); //the path passed to the hook should already be normalized + $this->assertEquals($path, Filesystem::normalizePath($path)); //the path passed to the hook should already be normalized } /** * Test that the default cache dir is part of the user's home */ - public function testMountDefaultCacheDir() { + public function testMountDefaultCacheDir(): void { $userId = $this->getUniqueID('user_'); - $config = \OC::$server->getConfig(); - $oldCachePath = $config->getSystemValue('cache_path', ''); + $config = Server::get(IConfig::class); + $oldCachePath = $config->getSystemValueString('cache_path', ''); // no cache path configured $config->setSystemValue('cache_path', ''); - \OC::$server->getUserManager()->createUser($userId, $userId); - \OC\Files\Filesystem::initMountPoints($userId); + Server::get(IUserManager::class)->createUser($userId, $userId); + Filesystem::initMountPoints($userId); $this->assertEquals( '/' . $userId . '/', - \OC\Files\Filesystem::getMountPoint('/' . $userId . '/cache') + Filesystem::getMountPoint('/' . $userId . '/cache') ); - [$storage, $internalPath] = \OC\Files\Filesystem::resolvePath('/' . $userId . '/cache'); + [$storage, $internalPath] = Filesystem::resolvePath('/' . $userId . '/cache'); $this->assertTrue($storage->instanceOfStorage('\OCP\Files\IHomeStorage')); $this->assertEquals('cache', $internalPath); - $user = \OC::$server->getUserManager()->get($userId); + $user = Server::get(IUserManager::class)->get($userId); if ($user !== null) { $user->delete(); } @@ -453,26 +440,26 @@ class FilesystemTest extends \Test\TestCase { * Test that an external cache is mounted into * the user's home */ - public function testMountExternalCacheDir() { + public function testMountExternalCacheDir(): void { $userId = $this->getUniqueID('user_'); - $config = \OC::$server->getConfig(); - $oldCachePath = $config->getSystemValue('cache_path', ''); + $config = Server::get(IConfig::class); + $oldCachePath = $config->getSystemValueString('cache_path', ''); // set cache path to temp dir - $cachePath = \OC::$server->getTempManager()->getTemporaryFolder() . '/extcache'; + $cachePath = Server::get(ITempManager::class)->getTemporaryFolder() . '/extcache'; $config->setSystemValue('cache_path', $cachePath); - \OC::$server->getUserManager()->createUser($userId, $userId); - \OC\Files\Filesystem::initMountPoints($userId); + Server::get(IUserManager::class)->createUser($userId, $userId); + Filesystem::initMountPoints($userId); $this->assertEquals( '/' . $userId . '/cache/', - \OC\Files\Filesystem::getMountPoint('/' . $userId . '/cache') + Filesystem::getMountPoint('/' . $userId . '/cache') ); - [$storage, $internalPath] = \OC\Files\Filesystem::resolvePath('/' . $userId . '/cache'); + [$storage, $internalPath] = Filesystem::resolvePath('/' . $userId . '/cache'); $this->assertTrue($storage->instanceOfStorage('\OC\Files\Storage\Local')); $this->assertEquals('', $internalPath); - $user = \OC::$server->getUserManager()->get($userId); + $user = Server::get(IUserManager::class)->get($userId); if ($user !== null) { $user->delete(); } @@ -480,12 +467,12 @@ class FilesystemTest extends \Test\TestCase { $config->setSystemValue('cache_path', $oldCachePath); } - public function testRegisterMountProviderAfterSetup() { - \OC\Files\Filesystem::initMountPoints(self::TEST_FILESYSTEM_USER2); - $this->assertEquals('/', \OC\Files\Filesystem::getMountPoint('/foo/bar')); + public function testRegisterMountProviderAfterSetup(): void { + Filesystem::initMountPoints(self::TEST_FILESYSTEM_USER2); + $this->assertEquals('/', Filesystem::getMountPoint('/foo/bar')); $mount = new MountPoint(new Temporary([]), '/foo/bar'); $mountProvider = new DummyMountProvider([self::TEST_FILESYSTEM_USER2 => [$mount]]); - \OC::$server->getMountProviderCollection()->registerProvider($mountProvider); - $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::getMountPoint('/foo/bar')); + Server::get(IMountProviderCollection::class)->registerProvider($mountProvider); + $this->assertEquals('/foo/bar/', Filesystem::getMountPoint('/foo/bar')); } } diff --git a/tests/lib/Files/Mount/CacheMountProviderTest.php b/tests/lib/Files/Mount/CacheMountProviderTest.php new file mode 100644 index 00000000000..003fc40eafc --- /dev/null +++ b/tests/lib/Files/Mount/CacheMountProviderTest.php @@ -0,0 +1,92 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Files\Mount; + +use OC\Files\Mount\CacheMountProvider; +use OC\Files\Storage\StorageFactory; +use OCP\Files\Storage\IStorageFactory; +use OCP\IConfig; +use OCP\IUser; +use Test\TestCase; + +class CacheMountProviderTestStream { + public static $statCounter = 0; + public static $mkdirCounter = 0; + + public $context; + + public function mkdir(string $path, int $mode, int $options): bool { + self::$mkdirCounter++; + return true; + } + + public function url_stat(string $path, int $flags): array|false { + self::$statCounter++; + return false; + } +} + +class CacheMountProviderTest extends TestCase { + private IConfig $config; + private IUser $user; + private IStorageFactory $storageFactory; + + protected function setUp(): void { + $this->config = $this->createMock(IConfig::class); + $this->user = $this->createMock(IUser::class); + $this->storageFactory = new StorageFactory(); + stream_wrapper_register('cachemountprovidertest', CacheMountProviderTestStream::class); + } + + protected function tearDown(): void { + stream_wrapper_unregister('cachemountprovidertest'); + } + + public function testGetMountsForUser(): void { + $provider = new CacheMountProvider($this->config); + + $this->assertCount(0, $provider->getMountsForUser($this->user, $this->storageFactory)); + } + + public function testGetMountsForUserCacheDir(): void { + $this->config->expects($this->exactly(1)) + ->method('getSystemValueString') + ->willReturnMap([ + ['cache_path', '', 'cachemountprovidertest:////cache_path'], + ]); + $this->user->method('getUID') + ->willReturn('bob'); + + $provider = new CacheMountProvider($this->config); + $mounts = $provider->getMountsForUser($this->user, $this->storageFactory); + + $this->assertCount(2, $mounts); + $this->assertEquals(1, CacheMountProviderTestStream::$statCounter); + $this->assertEquals(2, CacheMountProviderTestStream::$mkdirCounter); + + $cacheMountProvider = $mounts[0]; + $this->assertEquals('/bob/cache/', $cacheMountProvider->getMountPoint()); + + $cacheStorage = $cacheMountProvider->getStorage(); + $this->assertEquals('local::cachemountprovidertest://cache_path/bob/', $cacheStorage->getId()); + + $uploadsMountProvider = $mounts[1]; + $this->assertEquals('/bob/uploads/', $uploadsMountProvider->getMountPoint()); + + $uploadsStorage = $uploadsMountProvider->getStorage(); + $this->assertEquals('local::cachemountprovidertest://cache_path/bob/uploads/', $uploadsStorage->getId()); + + $cacheStorage->mkdir('foobar'); + $this->assertEquals(3, CacheMountProviderTestStream::$mkdirCounter); + + $uploadsStorage->mkdir('foobar'); + $this->assertEquals(4, CacheMountProviderTestStream::$mkdirCounter); + } +} diff --git a/tests/lib/Files/Mount/ManagerTest.php b/tests/lib/Files/Mount/ManagerTest.php index f69f8b239bb..e6cf3348664 100644 --- a/tests/lib/Files/Mount/ManagerTest.php +++ b/tests/lib/Files/Mount/ManagerTest.php @@ -1,18 +1,19 @@ <?php + /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Mount; +use OC\Files\Mount\MountPoint; use OC\Files\SetupManagerFactory; use OC\Files\Storage\Temporary; class LongId extends Temporary { - public function getId() { + public function getId(): string { return 'long:' . str_repeat('foo', 50) . parent::getId(); } } @@ -28,34 +29,34 @@ class ManagerTest extends \Test\TestCase { $this->manager = new \OC\Files\Mount\Manager($this->createMock(SetupManagerFactory::class)); } - public function testFind() { - $rootMount = new \OC\Files\Mount\MountPoint(new Temporary([]), '/'); + public function testFind(): void { + $rootMount = new MountPoint(new Temporary([]), '/'); $this->manager->addMount($rootMount); $this->assertEquals($rootMount, $this->manager->find('/')); $this->assertEquals($rootMount, $this->manager->find('/foo/bar')); $storage = new Temporary([]); - $mount1 = new \OC\Files\Mount\MountPoint($storage, '/foo'); + $mount1 = new MountPoint($storage, '/foo'); $this->manager->addMount($mount1); $this->assertEquals($rootMount, $this->manager->find('/')); $this->assertEquals($mount1, $this->manager->find('/foo/bar')); $this->assertEquals(1, count($this->manager->findIn('/'))); - $mount2 = new \OC\Files\Mount\MountPoint(new Temporary([]), '/bar'); + $mount2 = new MountPoint(new Temporary([]), '/bar'); $this->manager->addMount($mount2); $this->assertEquals(2, count($this->manager->findIn('/'))); $id = $mount1->getStorageId(); $this->assertEquals([$mount1], $this->manager->findByStorageId($id)); - $mount3 = new \OC\Files\Mount\MountPoint($storage, '/foo/bar'); + $mount3 = new MountPoint($storage, '/foo/bar'); $this->manager->addMount($mount3); $this->assertEquals([$mount1, $mount3], $this->manager->findByStorageId($id)); } - public function testLong() { + public function testLong(): void { $storage = new LongId([]); - $mount = new \OC\Files\Mount\MountPoint($storage, '/foo'); + $mount = new MountPoint($storage, '/foo'); $this->manager->addMount($mount); $id = $mount->getStorageId(); diff --git a/tests/lib/Files/Mount/MountPointTest.php b/tests/lib/Files/Mount/MountPointTest.php index 106a8f9a932..bcbcc96e3a3 100644 --- a/tests/lib/Files/Mount/MountPointTest.php +++ b/tests/lib/Files/Mount/MountPointTest.php @@ -1,22 +1,21 @@ <?php + /** - * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Mount; +use OC\Files\Mount\MountPoint; use OC\Files\Storage\StorageFactory; -use OCP\Files\Storage; - -class DummyStorage { -} +use OC\Lockdown\Filesystem\NullStorage; +use OCP\Files\Storage\IStorage; class MountPointTest extends \Test\TestCase { - public function testGetStorage() { - $storage = $this->createMock(Storage::class); + public function testGetStorage(): void { + $storage = $this->createMock(IStorage::class); $storage->expects($this->once()) ->method('getId') ->willReturn(123); @@ -26,9 +25,9 @@ class MountPointTest extends \Test\TestCase { ->method('wrap') ->willReturn($storage); - $mountPoint = new \OC\Files\Mount\MountPoint( + $mountPoint = new MountPoint( // just use this because a real class is needed - '\Test\Files\Mount\DummyStorage', + NullStorage::class, '/mountpoint', null, $loader @@ -42,20 +41,20 @@ class MountPointTest extends \Test\TestCase { $this->assertEquals('/another/', $mountPoint->getMountPoint()); } - public function testInvalidStorage() { + public function testInvalidStorage(): void { $loader = $this->createMock(StorageFactory::class); $loader->expects($this->once()) ->method('wrap') - ->will($this->throwException(new \Exception('Test storage init exception'))); + ->willThrowException(new \Exception('Test storage init exception')); $called = false; - $wrapper = function ($mountPoint, $storage) use ($called) { + $wrapper = function ($mountPoint, $storage) use ($called): void { $called = true; }; - $mountPoint = new \OC\Files\Mount\MountPoint( + $mountPoint = new MountPoint( // just use this because a real class is needed - '\Test\Files\Mount\DummyStorage', + NullStorage::class, '/mountpoint', null, $loader diff --git a/tests/lib/Files/Mount/MountTest.php b/tests/lib/Files/Mount/MountTest.php index 340e6931c1d..05c8a7d58e7 100644 --- a/tests/lib/Files/Mount/MountTest.php +++ b/tests/lib/Files/Mount/MountTest.php @@ -1,31 +1,32 @@ <?php + /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2020-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Mount; +use OC\Files\Mount\MountPoint; use OC\Files\Storage\StorageFactory; use OC\Files\Storage\Wrapper\Wrapper; class MountTest extends \Test\TestCase { - public function testFromStorageObject() { + public function testFromStorageObject(): void { $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary') ->disableOriginalConstructor() ->getMock(); - $mount = new \OC\Files\Mount\MountPoint($storage, '/foo'); + $mount = new MountPoint($storage, '/foo'); $this->assertInstanceOf('\OC\Files\Storage\Temporary', $mount->getStorage()); } - public function testFromStorageClassname() { - $mount = new \OC\Files\Mount\MountPoint('\OC\Files\Storage\Temporary', '/foo'); + public function testFromStorageClassname(): void { + $mount = new MountPoint('\OC\Files\Storage\Temporary', '/foo'); $this->assertInstanceOf('\OC\Files\Storage\Temporary', $mount->getStorage()); } - public function testWrapper() { + public function testWrapper(): void { $test = $this; $wrapper = function ($mountPoint, $storage) use (&$test) { $test->assertEquals('/foo/', $mountPoint); @@ -39,7 +40,7 @@ class MountTest extends \Test\TestCase { $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary') ->disableOriginalConstructor() ->getMock(); - $mount = new \OC\Files\Mount\MountPoint($storage, '/foo', [], $loader); + $mount = new MountPoint($storage, '/foo', [], $loader); $this->assertInstanceOf('\OC\Files\Storage\Wrapper\Wrapper', $mount->getStorage()); } } diff --git a/tests/lib/Files/Mount/ObjectHomeMountProviderTest.php b/tests/lib/Files/Mount/ObjectHomeMountProviderTest.php index e53069ee2b8..ae0a53f2cc0 100644 --- a/tests/lib/Files/Mount/ObjectHomeMountProviderTest.php +++ b/tests/lib/Files/Mount/ObjectHomeMountProviderTest.php @@ -1,8 +1,16 @@ <?php +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ namespace Test\Files\Mount; use OC\Files\Mount\ObjectHomeMountProvider; +use OC\Files\ObjectStore\PrimaryObjectStoreConfig; +use OCP\App\IAppManager; +use OCP\Files\ObjectStore\IObjectStore; use OCP\Files\Storage\IStorageFactory; use OCP\IConfig; use OCP\IUser; @@ -27,53 +35,56 @@ class ObjectHomeMountProviderTest extends \Test\TestCase { $this->user = $this->createMock(IUser::class); $this->loader = $this->createMock(IStorageFactory::class); - $this->provider = new ObjectHomeMountProvider($this->config); + $objectStoreConfig = new PrimaryObjectStoreConfig($this->config, $this->createMock(IAppManager::class)); + $this->provider = new ObjectHomeMountProvider($objectStoreConfig); } - public function testSingleBucket() { - $this->config->expects($this->once()) - ->method('getSystemValue') - ->with($this->equalTo('objectstore'), '') - ->willReturn([ - 'class' => 'Test\Files\Mount\FakeObjectStore', - ]); - - $this->user->expects($this->never())->method($this->anything()); - $this->loader->expects($this->never())->method($this->anything()); + public function testSingleBucket(): void { + $this->config->method('getSystemValue') + ->willReturnCallback(function ($key, $default) { + if ($key === 'objectstore') { + return [ + 'class' => 'Test\Files\Mount\FakeObjectStore', + 'arguments' => [ + 'foo' => 'bar' + ], + ]; + } else { + return $default; + } + }); - $config = $this->invokePrivate($this->provider, 'getSingleBucketObjectStoreConfig', [$this->user, $this->loader]); + $mount = $this->provider->getHomeMountForUser($this->user, $this->loader); + $arguments = $this->invokePrivate($mount, 'arguments'); - $this->assertArrayHasKey('class', $config); - $this->assertEquals($config['class'], 'Test\Files\Mount\FakeObjectStore'); - $this->assertArrayHasKey('arguments', $config); - $this->assertArrayHasKey('user', $config['arguments']); - $this->assertSame($this->user, $config['arguments']['user']); - $this->assertArrayHasKey('objectstore', $config['arguments']); - $this->assertInstanceOf('Test\Files\Mount\FakeObjectStore', $config['arguments']['objectstore']); + $objectStore = $arguments['objectstore']; + $this->assertInstanceOf(FakeObjectStore::class, $objectStore); + $this->assertEquals(['foo' => 'bar', 'multibucket' => false], $objectStore->getArguments()); } - public function testMultiBucket() { - $this->config->expects($this->exactly(2)) - ->method('getSystemValue') - ->with($this->equalTo('objectstore_multibucket'), '') - ->willReturn([ - 'class' => 'Test\Files\Mount\FakeObjectStore', - ]); + public function testMultiBucket(): void { + $this->config->method('getSystemValue') + ->willReturnCallback(function ($key, $default) { + if ($key === 'objectstore_multibucket') { + return [ + 'class' => 'Test\Files\Mount\FakeObjectStore', + 'arguments' => [ + 'foo' => 'bar' + ], + ]; + } else { + return $default; + } + }); $this->user->method('getUID') ->willReturn('uid'); $this->loader->expects($this->never())->method($this->anything()); - $this->config->expects($this->once()) - ->method('getUserValue') - ->with( - $this->equalTo('uid'), - $this->equalTo('homeobjectstore'), - $this->equalTo('bucket'), - $this->equalTo(null) - )->willReturn(null); + $this->config->method('getUserValue') + ->willReturn(null); - $this->config->expects($this->once()) + $this->config ->method('setUserValue') ->with( $this->equalTo('uid'), @@ -83,42 +94,37 @@ class ObjectHomeMountProviderTest extends \Test\TestCase { $this->equalTo(null) ); - $config = $this->invokePrivate($this->provider, 'getMultiBucketObjectStoreConfig', [$this->user, $this->loader]); - - $this->assertArrayHasKey('class', $config); - $this->assertEquals($config['class'], 'Test\Files\Mount\FakeObjectStore'); - $this->assertArrayHasKey('arguments', $config); - $this->assertArrayHasKey('user', $config['arguments']); - $this->assertSame($this->user, $config['arguments']['user']); - $this->assertArrayHasKey('objectstore', $config['arguments']); - $this->assertInstanceOf('Test\Files\Mount\FakeObjectStore', $config['arguments']['objectstore']); - $this->assertArrayHasKey('bucket', $config['arguments']); - $this->assertEquals('49', $config['arguments']['bucket']); - } - - public function testMultiBucketWithPrefix() { - $this->config->expects($this->exactly(2)) - ->method('getSystemValue') - ->with('objectstore_multibucket') - ->willReturn([ - 'class' => 'Test\Files\Mount\FakeObjectStore', - 'arguments' => [ - 'bucket' => 'myBucketPrefix', - ], - ]); + $mount = $this->provider->getHomeMountForUser($this->user, $this->loader); + $arguments = $this->invokePrivate($mount, 'arguments'); + + $objectStore = $arguments['objectstore']; + $this->assertInstanceOf(FakeObjectStore::class, $objectStore); + $this->assertEquals(['foo' => 'bar', 'bucket' => 49, 'multibucket' => true], $objectStore->getArguments()); + } + + public function testMultiBucketWithPrefix(): void { + $this->config->method('getSystemValue') + ->willReturnCallback(function ($key, $default) { + if ($key === 'objectstore_multibucket') { + return [ + 'class' => 'Test\Files\Mount\FakeObjectStore', + 'arguments' => [ + 'foo' => 'bar', + 'bucket' => 'myBucketPrefix', + ], + ]; + } else { + return $default; + } + }); $this->user->method('getUID') ->willReturn('uid'); $this->loader->expects($this->never())->method($this->anything()); - $this->config->expects($this->once()) + $this->config ->method('getUserValue') - ->with( - $this->equalTo('uid'), - $this->equalTo('homeobjectstore'), - $this->equalTo('bucket'), - $this->equalTo(null) - )->willReturn(null); + ->willReturn(null); $this->config->expects($this->once()) ->method('setUserValue') @@ -130,66 +136,70 @@ class ObjectHomeMountProviderTest extends \Test\TestCase { $this->equalTo(null) ); - $config = $this->invokePrivate($this->provider, 'getMultiBucketObjectStoreConfig', [$this->user, $this->loader]); + $mount = $this->provider->getHomeMountForUser($this->user, $this->loader); + $arguments = $this->invokePrivate($mount, 'arguments'); - $this->assertArrayHasKey('class', $config); - $this->assertEquals($config['class'], 'Test\Files\Mount\FakeObjectStore'); - $this->assertArrayHasKey('arguments', $config); - $this->assertArrayHasKey('user', $config['arguments']); - $this->assertSame($this->user, $config['arguments']['user']); - $this->assertArrayHasKey('objectstore', $config['arguments']); - $this->assertInstanceOf('Test\Files\Mount\FakeObjectStore', $config['arguments']['objectstore']); - $this->assertArrayHasKey('bucket', $config['arguments']); - $this->assertEquals('myBucketPrefix49', $config['arguments']['bucket']); + $objectStore = $arguments['objectstore']; + $this->assertInstanceOf(FakeObjectStore::class, $objectStore); + $this->assertEquals(['foo' => 'bar', 'bucket' => 'myBucketPrefix49', 'multibucket' => true], $objectStore->getArguments()); } - public function testMultiBucketBucketAlreadySet() { - $this->config->expects($this->once()) - ->method('getSystemValue') - ->with('objectstore_multibucket') - ->willReturn([ - 'class' => 'Test\Files\Mount\FakeObjectStore', - 'arguments' => [ - 'bucket' => 'myBucketPrefix', - ], - ]); + public function testMultiBucketBucketAlreadySet(): void { + $this->config->method('getSystemValue') + ->willReturnCallback(function ($key, $default) { + if ($key === 'objectstore_multibucket') { + return [ + 'class' => 'Test\Files\Mount\FakeObjectStore', + 'arguments' => [ + 'foo' => 'bar', + 'bucket' => 'myBucketPrefix', + ], + ]; + } else { + return $default; + } + }); $this->user->method('getUID') ->willReturn('uid'); $this->loader->expects($this->never())->method($this->anything()); - $this->config->expects($this->once()) + $this->config ->method('getUserValue') - ->with( - $this->equalTo('uid'), - $this->equalTo('homeobjectstore'), - $this->equalTo('bucket'), - $this->equalTo(null) - )->willReturn('awesomeBucket1'); + ->willReturnCallback(function ($uid, $app, $key, $default) { + if ($uid === 'uid' && $app === 'homeobjectstore' && $key === 'bucket') { + return 'awesomeBucket1'; + } else { + return $default; + } + }); $this->config->expects($this->never()) ->method('setUserValue'); - $config = $this->invokePrivate($this->provider, 'getMultiBucketObjectStoreConfig', [$this->user, $this->loader]); - - $this->assertArrayHasKey('class', $config); - $this->assertEquals($config['class'], 'Test\Files\Mount\FakeObjectStore'); - $this->assertArrayHasKey('arguments', $config); - $this->assertArrayHasKey('user', $config['arguments']); - $this->assertSame($this->user, $config['arguments']['user']); - $this->assertArrayHasKey('objectstore', $config['arguments']); - $this->assertInstanceOf('Test\Files\Mount\FakeObjectStore', $config['arguments']['objectstore']); - $this->assertArrayHasKey('bucket', $config['arguments']); - $this->assertEquals('awesomeBucket1', $config['arguments']['bucket']); - } - - public function testMultiBucketConfigFirst() { - $this->config->expects($this->exactly(2)) - ->method('getSystemValue') - ->with('objectstore_multibucket') - ->willReturn([ - 'class' => 'Test\Files\Mount\FakeObjectStore', - ]); + $mount = $this->provider->getHomeMountForUser($this->user, $this->loader); + $arguments = $this->invokePrivate($mount, 'arguments'); + + $objectStore = $arguments['objectstore']; + $this->assertInstanceOf(FakeObjectStore::class, $objectStore); + $this->assertEquals(['foo' => 'bar', 'bucket' => 'awesomeBucket1', 'multibucket' => true], $objectStore->getArguments()); + } + + public function testMultiBucketConfigFirst(): void { + $this->config->method('getSystemValue') + ->willReturnCallback(function ($key, $default) { + if ($key === 'objectstore_multibucket') { + return [ + 'class' => 'Test\Files\Mount\FakeObjectStore', + 'arguments' => [ + 'foo' => 'bar', + 'bucket' => 'myBucketPrefix', + ], + ]; + } else { + return $default; + } + }); $this->user->method('getUID') ->willReturn('uid'); @@ -199,18 +209,18 @@ class ObjectHomeMountProviderTest extends \Test\TestCase { $this->assertInstanceOf('OC\Files\Mount\MountPoint', $mount); } - public function testMultiBucketConfigFirstFallBackSingle() { - $this->config->expects($this->exactly(2)) - ->method('getSystemValue') - ->withConsecutive( - [$this->equalTo('objectstore_multibucket')], - [$this->equalTo('objectstore')], - )->willReturnOnConsecutiveCalls( - '', - [ + public function testMultiBucketConfigFirstFallBackSingle(): void { + $this->config + ->method('getSystemValue')->willReturnMap([ + ['objectstore_multibucket', null, null], + ['objectstore', null, [ 'class' => 'Test\Files\Mount\FakeObjectStore', - ], - ); + 'arguments' => [ + 'foo' => 'bar', + 'bucket' => 'myBucketPrefix', + ], + ]], + ]); $this->user->method('getUID') ->willReturn('uid'); @@ -220,24 +230,42 @@ class ObjectHomeMountProviderTest extends \Test\TestCase { $this->assertInstanceOf('OC\Files\Mount\MountPoint', $mount); } - public function testNoObjectStore() { - $this->config->expects($this->exactly(2)) - ->method('getSystemValue') - ->willReturn(''); + public function testNoObjectStore(): void { + $this->config->method('getSystemValue') + ->willReturnCallback(function ($key, $default) { + return $default; + }); $mount = $this->provider->getHomeMountForUser($this->user, $this->loader); $this->assertNull($mount); } } -class FakeObjectStore { - private $arguments; - - public function __construct(array $arguments) { - $this->arguments = $arguments; +class FakeObjectStore implements IObjectStore { + public function __construct( + private array $arguments, + ) { } public function getArguments() { return $this->arguments; } + + public function getStorageId() { + } + + public function readObject($urn) { + } + + public function writeObject($urn, $stream, ?string $mimetype = null) { + } + + public function deleteObject($urn) { + } + + public function objectExists($urn) { + } + + public function copyObject($from, $to) { + } } diff --git a/tests/lib/Files/Mount/ObjectStorePreviewCacheMountProviderTest.php b/tests/lib/Files/Mount/ObjectStorePreviewCacheMountProviderTest.php index da9ba8bacaf..9060bf0d5f5 100644 --- a/tests/lib/Files/Mount/ObjectStorePreviewCacheMountProviderTest.php +++ b/tests/lib/Files/Mount/ObjectStorePreviewCacheMountProviderTest.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Morris Jobke <hey@morrisjobke.de> - * - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Mount; @@ -60,7 +43,7 @@ class ObjectStorePreviewCacheMountProviderTest extends \Test\TestCase { $this->provider = new ObjectStorePreviewCacheMountProvider($this->logger, $this->config); } - public function testNoMultibucketObjectStorage() { + public function testNoMultibucketObjectStorage(): void { $this->config->expects($this->once()) ->method('getSystemValue') ->with('objectstore_multibucket') @@ -69,7 +52,7 @@ class ObjectStorePreviewCacheMountProviderTest extends \Test\TestCase { $this->assertEquals([], $this->provider->getRootMounts($this->loader)); } - public function testMultibucketObjectStorage() { + public function testMultibucketObjectStorage(): void { $objectstoreConfig = [ 'class' => S3::class, 'arguments' => [ diff --git a/tests/lib/Files/Mount/RootMountProviderTest.php b/tests/lib/Files/Mount/RootMountProviderTest.php index e5eaabf93be..bf29bfa070a 100644 --- a/tests/lib/Files/Mount/RootMountProviderTest.php +++ b/tests/lib/Files/Mount/RootMountProviderTest.php @@ -2,34 +2,20 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Mount; use OC\Files\Mount\RootMountProvider; use OC\Files\ObjectStore\ObjectStoreStorage; +use OC\Files\ObjectStore\PrimaryObjectStoreConfig; use OC\Files\ObjectStore\S3; use OC\Files\Storage\LocalRootStorage; use OC\Files\Storage\StorageFactory; +use OCP\App\IAppManager; use OCP\IConfig; -use Psr\Log\LoggerInterface; use Test\TestCase; /** @@ -55,11 +41,11 @@ class RootMountProviderTest extends TestCase { private function getProvider(array $systemConfig): RootMountProvider { $config = $this->getConfig($systemConfig); - $provider = new RootMountProvider($config, $this->createMock(LoggerInterface::class)); - return $provider; + $objectStoreConfig = new PrimaryObjectStoreConfig($config, $this->createMock(IAppManager::class)); + return new RootMountProvider($objectStoreConfig, $config); } - public function testLocal() { + public function testLocal(): void { $provider = $this->getProvider([ 'datadirectory' => '/data', ]); @@ -73,20 +59,20 @@ class RootMountProviderTest extends TestCase { $this->assertEquals('/data/', $storage->getSourcePath('')); } - public function testObjectStore() { + public function testObjectStore(): void { $provider = $this->getProvider([ 'objectstore' => [ - "class" => "OC\Files\ObjectStore\S3", - "arguments" => [ - "bucket" => "nextcloud", - "autocreate" => true, - "key" => "minio", - "secret" => "minio123", - "hostname" => "localhost", - "port" => 9000, - "use_ssl" => false, - "use_path_style" => true, - "uploadPartSize" => 52428800, + 'class' => "OC\Files\ObjectStore\S3", + 'arguments' => [ + 'bucket' => 'nextcloud', + 'autocreate' => true, + 'key' => 'minio', + 'secret' => 'minio123', + 'hostname' => 'localhost', + 'port' => 9000, + 'use_ssl' => false, + 'use_path_style' => true, + 'uploadPartSize' => 52428800, ], ], ]); @@ -106,20 +92,20 @@ class RootMountProviderTest extends TestCase { $this->assertEquals('nextcloud', $objectStore->getBucket()); } - public function testObjectStoreMultiBucket() { + public function testObjectStoreMultiBucket(): void { $provider = $this->getProvider([ 'objectstore_multibucket' => [ - "class" => "OC\Files\ObjectStore\S3", - "arguments" => [ - "bucket" => "nextcloud", - "autocreate" => true, - "key" => "minio", - "secret" => "minio123", - "hostname" => "localhost", - "port" => 9000, - "use_ssl" => false, - "use_path_style" => true, - "uploadPartSize" => 52428800, + 'class' => "OC\Files\ObjectStore\S3", + 'arguments' => [ + 'bucket' => 'nextcloud', + 'autocreate' => true, + 'key' => 'minio', + 'secret' => 'minio123', + 'hostname' => 'localhost', + 'port' => 9000, + 'use_ssl' => false, + 'use_path_style' => true, + 'uploadPartSize' => 52428800, ], ], ]); diff --git a/tests/lib/Files/Node/FileTest.php b/tests/lib/Files/Node/FileTest.php index 3305f9ac170..eec34d156ad 100644 --- a/tests/lib/Files/Node/FileTest.php +++ b/tests/lib/Files/Node/FileTest.php @@ -1,13 +1,18 @@ <?php + /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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 * @@ -15,12 +20,12 @@ namespace Test\Files\Node; * * @package Test\Files\Node */ -class FileTest extends NodeTest { +class FileTest extends NodeTestCase { protected function createTestNode($root, $view, $path, array $data = [], $internalPath = '', $storage = null) { if ($data || $internalPath || $storage) { - return new \OC\Files\Node\File($root, $view, $path, $this->getFileInfo($data, $internalPath, $storage)); + return new File($root, $view, $path, $this->getFileInfo($data, $internalPath, $storage)); } else { - return new \OC\Files\Node\File($root, $view, $path); + return new File($root, $view, $path); } } @@ -36,13 +41,13 @@ class FileTest extends NodeTest { return 'unlink'; } - public function testGetContent() { + public function testGetContent(): void { /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ - $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + $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) { + $hook = function ($file): void { throw new \Exception('Hooks are not supposed to be called'); }; @@ -57,19 +62,19 @@ class FileTest extends NodeTest { $this->view->expects($this->once()) ->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_READ])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); - $node = new \OC\Files\Node\File($root, $this->view, '/bar/foo'); + $node = new File($root, $this->view, '/bar/foo'); $this->assertEquals('bar', $node->getContent()); } - public function testGetContentNotPermitted() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testGetContentNotPermitted(): void { + $this->expectException(NotPermittedException::class); /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ - $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + $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()) @@ -81,14 +86,14 @@ class FileTest extends NodeTest { ->with('/bar/foo') ->willReturn($this->getFileInfo(['permissions' => 0])); - $node = new \OC\Files\Node\File($root, $this->view, '/bar/foo'); + $node = new File($root, $this->view, '/bar/foo'); $node->getContent(); } - public function testPutContent() { + public function testPutContent(): void { /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ - $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + $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()) @@ -98,39 +103,39 @@ class FileTest extends NodeTest { $this->view->expects($this->once()) ->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); $this->view->expects($this->once()) ->method('file_put_contents') ->with('/bar/foo', 'bar') ->willReturn(true); - $node = new \OC\Files\Node\File($root, $this->view, '/bar/foo'); + $node = new File($root, $this->view, '/bar/foo'); $node->putContent('bar'); } - public function testPutContentNotPermitted() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testPutContentNotPermitted(): void { + $this->expectException(NotPermittedException::class); /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ - $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + $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' => \OCP\Constants::PERMISSION_READ])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); - $node = new \OC\Files\Node\File($root, $this->view, '/bar/foo'); + $node = new File($root, $this->view, '/bar/foo'); $node->putContent('bar'); } - public function testGetMimeType() { + public function testGetMimeType(): void { /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ - $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + $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()) @@ -138,26 +143,27 @@ class FileTest extends NodeTest { ->with('/bar/foo') ->willReturn($this->getFileInfo(['mimetype' => 'text/plain'])); - $node = new \OC\Files\Node\File($root, $this->view, '/bar/foo'); + $node = new File($root, $this->view, '/bar/foo'); $this->assertEquals('text/plain', $node->getMimeType()); } - public function testFOpenRead() { + public function testFOpenRead(): void { $stream = fopen('php://memory', 'w+'); fwrite($stream, 'bar'); rewind($stream); - $root = new \OC\Files\Node\Root( + $root = new Root( $this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); - $hook = function ($file) { + $hook = function ($file): void { throw new \Exception('Hooks are not supposed to be called'); }; @@ -172,28 +178,29 @@ class FileTest extends NodeTest { $this->view->expects($this->once()) ->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); - $node = new \OC\Files\Node\File($root, $this->view, '/bar/foo'); + $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() { + public function testFOpenWrite(): void { $stream = fopen('php://memory', 'w+'); - $root = new \OC\Files\Node\Root( + $root = new Root( $this->manager, - new $this->view, + $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $hooksCalled = 0; - $hook = function ($file) use (&$hooksCalled) { + $hook = function ($file) use (&$hooksCalled): void { $hooksCalled++; }; @@ -208,9 +215,9 @@ class FileTest extends NodeTest { $this->view->expects($this->once()) ->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); - $node = new \OC\Files\Node\File($root, $this->view, '/bar/foo'); + $node = new File($root, $this->view, '/bar/foo'); $fh = $node->fopen('w'); $this->assertEquals($stream, $fh); fwrite($fh, 'bar'); @@ -220,19 +227,20 @@ class FileTest extends NodeTest { } - public function testFOpenReadNotPermitted() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testFOpenReadNotPermitted(): void { + $this->expectException(NotPermittedException::class); - $root = new \OC\Files\Node\Root( + $root = new Root( $this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); - $hook = function ($file) { + $hook = function ($file): void { throw new \Exception('Hooks are not supposed to be called'); }; @@ -241,59 +249,61 @@ class FileTest extends NodeTest { ->with('/bar/foo') ->willReturn($this->getFileInfo(['permissions' => 0])); - $node = new \OC\Files\Node\File($root, $this->view, '/bar/foo'); + $node = new File($root, $this->view, '/bar/foo'); $node->fopen('r'); } - public function testFOpenReadWriteNoReadPermissions() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testFOpenReadWriteNoReadPermissions(): void { + $this->expectException(NotPermittedException::class); - $root = new \OC\Files\Node\Root( + $root = new Root( $this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); - $hook = function () { + $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' => \OCP\Constants::PERMISSION_UPDATE])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_UPDATE])); - $node = new \OC\Files\Node\File($root, $this->view, '/bar/foo'); + $node = new File($root, $this->view, '/bar/foo'); $node->fopen('w'); } - public function testFOpenReadWriteNoWritePermissions() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testFOpenReadWriteNoWritePermissions(): void { + $this->expectException(NotPermittedException::class); - $root = new \OC\Files\Node\Root( + $root = new Root( $this->manager, - new $this->view, + $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); - $hook = function () { + $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' => \OCP\Constants::PERMISSION_READ])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); - $node = new \OC\Files\Node\File($root, $this->view, '/bar/foo'); + $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 index 8a604af3846..439535cf2c1 100644 --- a/tests/lib/Files/Node/FolderTest.php +++ b/tests/lib/Files/Node/FolderTest.php @@ -1,9 +1,9 @@ <?php + /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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; @@ -18,18 +18,26 @@ 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; +use OCP\Files\Storage\IStorage; +use PHPUnit\Framework\MockObject\MockObject; /** * Class FolderTest @@ -38,8 +46,11 @@ use OCP\Files\Storage; * * @package Test\Files\Node */ -class FolderTest extends NodeTest { +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 { @@ -59,30 +70,31 @@ class FolderTest extends NodeTest { return 'rmdir'; } - public function testGetDirectoryContent() { + public function testGetDirectoryContent(): void { $manager = $this->createMock(Manager::class); /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view + * @var View|\PHPUnit\Framework\MockObject\MockObject $view */ - $view = $this->createMock(View::class); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->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); - $view->expects($this->any()) + $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), ]); - $view->method('getFileInfo') + $this->view->method('getFileInfo') ->willReturn($this->createMock(FileInfo::class)); + $this->view->method('getRelativePath') + ->willReturn('/bar/foo'); - $node = new Folder($root, $view, '/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]); @@ -93,14 +105,11 @@ class FolderTest extends NodeTest { $this->assertEquals(3, $children[1]->getId()); } - public function testGet() { + public function testGet(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -115,14 +124,11 @@ class FolderTest extends NodeTest { self::assertEquals($node, $parentNode->get('asd')); } - public function testNodeExists() { + public function testNodeExists(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -138,14 +144,11 @@ class FolderTest extends NodeTest { $this->assertTrue($node->nodeExists('asd')); } - public function testNodeExistsNotExists() { + public function testNodeExistsNotExists(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -153,20 +156,17 @@ class FolderTest extends NodeTest { $root->method('get') ->with('/bar/foo/asd') - ->will($this->throwException(new NotFoundException())); + ->willThrowException(new NotFoundException()); $node = new Folder($root, $view, '/bar/foo'); $this->assertFalse($node->nodeExists('asd')); } - public function testNewFolder() { + public function testNewFolder(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -174,7 +174,7 @@ class FolderTest extends NodeTest { $view->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); $view->method('mkdir') ->with('/bar/foo/asd') @@ -186,14 +186,11 @@ class FolderTest extends NodeTest { $this->assertEquals($child, $result); } - public function testNewFolderDeepParent() { + public function testNewFolderDeepParent(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -201,7 +198,7 @@ class FolderTest extends NodeTest { $view->method('getFileInfo') ->with('/foobar') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); $view->method('mkdir') ->with('/foobar/asd/sdf') @@ -214,36 +211,30 @@ class FolderTest extends NodeTest { } - public function testNewFolderNotPermitted() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testNewFolderNotPermitted(): void { + $this->expectException(NotPermittedException::class); $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->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' => \OCP\Constants::PERMISSION_READ])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); $node = new Folder($root, $view, '/bar/foo'); $node->newFolder('asd'); } - public function testNewFile() { + public function testNewFile(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -251,49 +242,43 @@ class FolderTest extends NodeTest { $view->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL])); + ->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 \OC\Files\Node\File($root, $view, '/bar/foo/asd', null, $node); + $child = new File($root, $view, '/bar/foo/asd', null, $node); $result = $node->newFile('asd'); $this->assertEquals($child, $result); } - public function testNewFileNotPermitted() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testNewFileNotPermitted(): void { + $this->expectException(NotPermittedException::class); $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->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' => \OCP\Constants::PERMISSION_READ])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); $node = new Folder($root, $view, '/bar/foo'); $node->newFile('asd'); } - public function testGetFreeSpace() { + public function testGetFreeSpace(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->method('getUser') ->willReturn($this->user); @@ -306,31 +291,36 @@ class FolderTest extends NodeTest { $this->assertEquals(100, $node->getFreeSpace()); } - public function testSearch() { + public function testSearch(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->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 $storage */ - $storage = $this->createMock(Storage\IStorage::class); + /** @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->method('getStorage') + $mount->expects($this->atLeastOnce()) + ->method('getStorage') ->willReturn($storage); - $mount->method('getInternalPath') + $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']); @@ -349,21 +339,18 @@ class FolderTest extends NodeTest { $this->assertEquals('/bar/foo/qwerty', $result[0]->getPath()); } - public function testSearchInRoot() { + public function testSearchInRoot(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setMethods(['getUser', 'getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->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(Storage::class); + $storage = $this->createMock(IStorage::class); $storage->method('getId')->willReturn('test::2'); $cache = new Cache($storage); @@ -375,7 +362,10 @@ class FolderTest extends NodeTest { $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']); @@ -393,18 +383,15 @@ class FolderTest extends NodeTest { $this->assertEquals('/foo', $result[0]->getPath()); } - public function testSearchInStorageRoot() { + public function testSearchInStorageRoot(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->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(Storage::class); + $storage = $this->createMock(IStorage::class); $storage->method('getId')->willReturn('test::1'); $cache = new Cache($storage); @@ -416,7 +403,10 @@ class FolderTest extends NodeTest { $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']); @@ -436,22 +426,19 @@ class FolderTest extends NodeTest { $this->assertEquals('/bar/foo/qwerty', $result[0]->getPath()); } - public function testSearchSubStorages() { + public function testSearchSubStorages(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->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(Storage::class); + $storage = $this->createMock(IStorage::class); $storage->method('getId')->willReturn('test::1'); $cache = new Cache($storage); - $subStorage = $this->createMock(Storage::class); + $subStorage = $this->createMock(IStorage::class); $subStorage->method('getId')->willReturn('test::2'); $subCache = new Cache($subStorage); $subMount = $this->getMockBuilder(MountPoint::class)->setConstructorArgs([Temporary::class, ''])->getMock(); @@ -470,13 +457,19 @@ class FolderTest extends NodeTest { $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']); @@ -497,27 +490,25 @@ class FolderTest extends NodeTest { $this->assertEquals(2, count($result)); } - public function testIsSubNode() { - $file = new Node(null, null, '/foo/bar'); - $folder = new Folder(null, null, '/foo'); + 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(null, null, '/foobar'); + $file = new Node($rootFolderMock, $this->view, '/foobar'); $this->assertFalse($folder->isSubNode($file)); } - public function testGetById() { + public function testGetById(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setMethods(['getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->onlyMethods(['getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); - $storage = $this->createMock(\OC\Files\Storage\Storage::class); + $storage = $this->createMock(Storage::class); $mount = new MountPoint($storage, '/bar'); $storage->method('getId')->willReturn(''); $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([$storage])->getMock(); @@ -526,6 +517,8 @@ class FolderTest extends NodeTest { $storage->method('getCache') ->willReturn($cache); + $storage->method('getOwner') + ->willReturn('owner'); $this->userMountCache->expects($this->any()) ->method('getMountsForFileId') @@ -557,17 +550,14 @@ class FolderTest extends NodeTest { $this->assertEquals('/bar/foo/qwerty', $result[0]->getPath()); } - public function testGetByIdMountRoot() { + public function testGetByIdMountRoot(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setMethods(['getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->onlyMethods(['getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); - $storage = $this->createMock(\OC\Files\Storage\Storage::class); + $storage = $this->createMock(Storage::class); $mount = new MountPoint($storage, '/bar'); $storage->method('getId')->willReturn(''); $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([$storage])->getMock(); @@ -576,6 +566,8 @@ class FolderTest extends NodeTest { $storage->method('getCache') ->willReturn($cache); + $storage->method('getOwner') + ->willReturn('owner'); $this->userMountCache->expects($this->any()) ->method('getMountsForFileId') @@ -603,17 +595,14 @@ class FolderTest extends NodeTest { $this->assertEquals('/bar', $result[0]->getPath()); } - public function testGetByIdOutsideFolder() { + public function testGetByIdOutsideFolder(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setMethods(['getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->onlyMethods(['getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); - $storage = $this->createMock(\OC\Files\Storage\Storage::class); + $storage = $this->createMock(Storage::class); $mount = new MountPoint($storage, '/bar'); $storage->method('getId')->willReturn(''); $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([$storage])->getMock(); @@ -622,6 +611,8 @@ class FolderTest extends NodeTest { $storage->method('getCache') ->willReturn($cache); + $storage->method('getOwner') + ->willReturn('owner'); $this->userMountCache->expects($this->any()) ->method('getMountsForFileId') @@ -648,17 +639,14 @@ class FolderTest extends NodeTest { $this->assertEquals(0, count($result)); } - public function testGetByIdMultipleStorages() { + public function testGetByIdMultipleStorages(): void { $manager = $this->createMock(Manager::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setMethods(['getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->onlyMethods(['getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); - $storage = $this->createMock(\OC\Files\Storage\Storage::class); + $storage = $this->createMock(Storage::class); $mount1 = new MountPoint($storage, '/bar'); $mount2 = new MountPoint($storage, '/bar/foo/asd'); $storage->method('getId')->willReturn(''); @@ -668,6 +656,8 @@ class FolderTest extends NodeTest { $storage->method('getCache') ->willReturn($cache); + $storage->method('getOwner') + ->willReturn('owner'); $this->userMountCache->method('getMountsForFileId') ->with(1) @@ -683,9 +673,6 @@ class FolderTest extends NodeTest { ), ]); - $storage->method('getCache') - ->willReturn($cache); - $cache->method('get') ->with(1) ->willReturn($fileInfo); @@ -700,7 +687,7 @@ class FolderTest extends NodeTest { $this->assertEquals('/bar/foo/asd/foo/qwerty', $result[1]->getPath()); } - public function uniqueNameProvider() { + public static function uniqueNameProvider(): array { return [ // input, existing, expected ['foo', [], 'foo'], @@ -709,19 +696,14 @@ class FolderTest extends NodeTest { ]; } - /** - * @dataProvider uniqueNameProvider - */ - public function testGetUniqueName($name, $existingFiles, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('uniqueNameProvider')] + public function testGetUniqueName($name, $existingFiles, $expected): void { $manager = $this->createMock(Manager::class); $folderPath = '/bar/foo'; - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setMethods(['getUser', 'getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->onlyMethods(['getUser', 'getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $view->expects($this->any()) @@ -742,16 +724,13 @@ class FolderTest extends NodeTest { public function testRecent(): void { $manager = $this->createMock(Manager::class); $folderPath = '/bar/foo'; - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Files\Node\Root $root */ $root = $this->getMockBuilder(Root::class) - ->setMethods(['getUser', 'getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->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|\OC\Files\FileInfo $folderInfo */ + /** @var \PHPUnit\Framework\MockObject\MockObject|FileInfo $folderInfo */ $folderInfo = $this->getMockBuilder(FileInfo::class) ->disableOriginalConstructor()->getMock(); @@ -769,19 +748,23 @@ class FolderTest extends NodeTest { $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' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, ]); $id2 = $cache->put('bar/foo/old.txt', [ 'storage_mtime' => $baseTime - 100, 'mtime' => $baseTime - 100, 'mimetype' => 'text/plain', 'size' => 3, - 'permissions' => \OCP\Constants::PERMISSION_READ, + 'permissions' => Constants::PERMISSION_READ, ]); $cache->put('bar/asd/outside.txt', [ 'storage_mtime' => $baseTime, @@ -794,7 +777,7 @@ class FolderTest extends NodeTest { 'mtime' => $baseTime - 600, 'mimetype' => 'text/plain', 'size' => 3, - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, ]); $node = new Folder($root, $view, $folderPath, $folderInfo); @@ -807,19 +790,16 @@ class FolderTest extends NodeTest { $this->assertEquals([$id1, $id2, $id3], $ids); } - public function testRecentFolder() { + public function testRecentFolder(): void { $manager = $this->createMock(Manager::class); $folderPath = '/bar/foo'; - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Files\Node\Root $root */ $root = $this->getMockBuilder(Root::class) - ->setMethods(['getUser', 'getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->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|\OC\Files\FileInfo $folderInfo */ + /** @var \PHPUnit\Framework\MockObject\MockObject|FileInfo $folderInfo */ $folderInfo = $this->getMockBuilder(FileInfo::class) ->disableOriginalConstructor()->getMock(); @@ -838,6 +818,9 @@ class FolderTest extends NodeTest { $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, @@ -851,7 +834,7 @@ class FolderTest extends NodeTest { 'mimetype' => 'text/plain', 'size' => 3, 'parent' => $id1, - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, ]); $id3 = $cache->put('bar/foo/folder/asd.txt', [ 'storage_mtime' => $baseTime - 100, @@ -859,7 +842,7 @@ class FolderTest extends NodeTest { 'mimetype' => 'text/plain', 'size' => 3, 'parent' => $id1, - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, ]); $node = new Folder($root, $view, $folderPath, $folderInfo); @@ -874,19 +857,16 @@ class FolderTest extends NodeTest { $this->assertEquals($baseTime - 100, $nodes[1]->getMTime()); } - public function testRecentJail() { + public function testRecentJail(): void { $manager = $this->createMock(Manager::class); $folderPath = '/bar/foo'; - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Files\Node\Root $root */ $root = $this->getMockBuilder(Root::class) - ->setMethods(['getUser', 'getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->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|\OC\Files\FileInfo $folderInfo */ + /** @var \PHPUnit\Framework\MockObject\MockObject|FileInfo $folderInfo */ $folderInfo = $this->getMockBuilder(FileInfo::class) ->disableOriginalConstructor()->getMock(); @@ -908,13 +888,16 @@ class FolderTest extends NodeTest { $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' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, ]); + $cache->put('outside.txt', [ 'storage_mtime' => $baseTime - 100, 'mtime' => $baseTime - 100, @@ -931,7 +914,7 @@ class FolderTest extends NodeTest { $this->assertEquals([$id1], $ids); } - public function offsetLimitProvider() { + 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'], []], @@ -952,38 +935,35 @@ class FolderTest extends NodeTest { } /** - * @dataProvider offsetLimitProvider * @param int $offset * @param int $limit * @param string[] $expectedPaths * @param ISearchOrder[] $ordering * @throws NotFoundException - * @throws \OCP\Files\InvalidPathException + * @throws InvalidPathException */ - public function testSearchSubStoragesLimitOffset(int $offset, int $limit, array $expectedPaths, array $ordering) { + #[\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); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->createMock(View::class); + $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->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(Storage::class); + $storage = $this->createMock(IStorage::class); $storage->method('getId')->willReturn('test::1'); $cache = new Cache($storage); - $subStorage1 = $this->createMock(Storage::class); + $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(Storage::class); + $subStorage2 = $this->createMock(IStorage::class); $subStorage2->method('getId')->willReturn('test::3'); $subCache2 = new Cache($subStorage2); $subMount2 = $this->getMockBuilder(MountPoint::class)->setConstructorArgs([Temporary::class, ''])->getMock(); @@ -1002,9 +982,13 @@ class FolderTest extends NodeTest { $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); @@ -1014,15 +998,22 @@ class FolderTest extends NodeTest { $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']); @@ -1036,7 +1027,11 @@ class FolderTest extends NodeTest { $node = new Folder($root, $view, '/bar/foo'); $comparison = new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%foo%'); - $query = new SearchQuery($comparison, $limit, $offset, $ordering); + $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(); diff --git a/tests/lib/Files/Node/HookConnectorTest.php b/tests/lib/Files/Node/HookConnectorTest.php index 9704c7b89f1..3f3957bab1d 100644 --- a/tests/lib/Files/Node/HookConnectorTest.php +++ b/tests/lib/Files/Node/HookConnectorTest.php @@ -1,9 +1,9 @@ <?php + /** - * Copyright (c) 2015 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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; @@ -13,8 +13,10 @@ 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; @@ -30,10 +32,10 @@ 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 PHPUnit\Framework\MockObject\MockObject; +use OCP\Server; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; use Test\TestCase; use Test\Traits\MountProviderTrait; @@ -50,12 +52,11 @@ class HookConnectorTest extends TestCase { use UserTrait; use MountProviderTrait; - /** @var EventDispatcherInterface|MockObject */ - protected $legacyDispatcher; - - /** @var IEventDispatcher */ + /** @var IEventDispatcher */ protected $eventDispatcher; + private LoggerInterface $logger; + /** @var View */ private $view; @@ -72,18 +73,24 @@ class HookConnectorTest extends TestCase { // 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, - \OC::$server->getUserManager()->get($this->userId), - \OC::$server->getUserMountCache(), + Server::get(IUserManager::class)->get($this->userId), + Server::get(IUserMountCache::class), $this->createMock(LoggerInterface::class), $this->createMock(IUserManager::class), - $this->createMock(IEventDispatcher::class) + $this->createMock(IEventDispatcher::class), + $cacheFactory, ); - $this->legacyDispatcher = \OC::$server->getEventDispatcher(); - $this->eventDispatcher = \OC::$server->query(IEventDispatcher::class); + $this->eventDispatcher = Server::get(IEventDispatcher::class); + $this->logger = Server::get(LoggerInterface::class); } protected function tearDown(): void { @@ -92,51 +99,51 @@ class HookConnectorTest extends TestCase { \OC_Util::tearDownFS(); } - public function viewToNodeProvider() { + public static function viewToNodeProvider(): array { return [ - [function () { + [function (): void { Filesystem::file_put_contents('test.txt', 'asd'); }, 'preWrite', '\OCP\Files::preWrite', BeforeNodeWrittenEvent::class], - [function () { + [function (): void { Filesystem::file_put_contents('test.txt', 'asd'); }, 'postWrite', '\OCP\Files::postWrite', NodeWrittenEvent::class], - [function () { + [function (): void { Filesystem::file_put_contents('test.txt', 'asd'); }, 'preCreate', '\OCP\Files::preCreate', BeforeNodeCreatedEvent::class], - [function () { + [function (): void { Filesystem::file_put_contents('test.txt', 'asd'); }, 'postCreate', '\OCP\Files::postCreate', NodeCreatedEvent::class], - [function () { + [function (): void { Filesystem::mkdir('test.txt'); }, 'preCreate', '\OCP\Files::preCreate', BeforeNodeCreatedEvent::class], - [function () { + [function (): void { Filesystem::mkdir('test.txt'); }, 'postCreate', '\OCP\Files::postCreate', NodeCreatedEvent::class], - [function () { + [function (): void { Filesystem::touch('test.txt'); }, 'preTouch', '\OCP\Files::preTouch', BeforeNodeTouchedEvent::class], - [function () { + [function (): void { Filesystem::touch('test.txt'); }, 'postTouch', '\OCP\Files::postTouch', NodeTouchedEvent::class], - [function () { + [function (): void { Filesystem::touch('test.txt'); }, 'preCreate', '\OCP\Files::preCreate', BeforeNodeCreatedEvent::class], - [function () { + [function (): void { Filesystem::touch('test.txt'); }, 'postCreate', '\OCP\Files::postCreate', NodeCreatedEvent::class], - [function () { + [function (): void { Filesystem::file_put_contents('test.txt', 'asd'); Filesystem::unlink('test.txt'); }, 'preDelete', '\OCP\Files::preDelete', BeforeNodeDeletedEvent::class], - [function () { + [function (): void { Filesystem::file_put_contents('test.txt', 'asd'); Filesystem::unlink('test.txt'); }, 'postDelete', '\OCP\Files::postDelete', NodeDeletedEvent::class], - [function () { + [function (): void { Filesystem::mkdir('test.txt'); Filesystem::rmdir('test.txt'); }, 'preDelete', '\OCP\Files::preDelete', BeforeNodeDeletedEvent::class], - [function () { + [function (): void { Filesystem::mkdir('test.txt'); Filesystem::rmdir('test.txt'); }, 'postDelete', '\OCP\Files::postDelete', NodeDeletedEvent::class], @@ -146,16 +153,16 @@ class HookConnectorTest extends TestCase { /** * @param callable $operation * @param string $expectedHook - * @dataProvider viewToNodeProvider */ - public function testViewToNode(callable $operation, $expectedHook, $expectedLegacyEvent, $expectedEvent) { - $connector = new HookConnector($this->root, $this->view, $this->legacyDispatcher, $this->eventDispatcher); + #[\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) { + $this->root->listen('\OC\Files', $expectedHook, function ($node) use (&$hookNode, &$hookCalled): void { $hookCalled = true; $hookNode = $node; }); @@ -163,7 +170,7 @@ class HookConnectorTest extends TestCase { $dispatcherCalled = false; /** @var Node $dispatcherNode */ $dispatcherNode = null; - $this->legacyDispatcher->addListener($expectedLegacyEvent, function ($event) use (&$dispatcherCalled, &$dispatcherNode) { + $this->eventDispatcher->addListener($expectedLegacyEvent, function ($event) use (&$dispatcherCalled, &$dispatcherNode): void { /** @var GenericEvent|APIGenericEvent $event */ $dispatcherCalled = true; $dispatcherNode = $event->getSubject(); @@ -171,7 +178,7 @@ class HookConnectorTest extends TestCase { $newDispatcherCalled = false; $newDispatcherNode = null; - $this->eventDispatcher->addListener($expectedEvent, function ($event) use ($expectedEvent, &$newDispatcherCalled, &$newDispatcherNode) { + $this->eventDispatcher->addListener($expectedEvent, function ($event) use ($expectedEvent, &$newDispatcherCalled, &$newDispatcherNode): void { if ($event instanceof $expectedEvent) { /** @var AbstractNodeEvent $event */ $newDispatcherCalled = true; @@ -191,21 +198,21 @@ class HookConnectorTest extends TestCase { $this->assertEquals('/' . $this->userId . '/files/test.txt', $newDispatcherNode->getPath()); } - public function viewToNodeProviderCopyRename() { + public static function viewToNodeProviderCopyRename(): array { return [ - [function () { + [function (): void { Filesystem::file_put_contents('source', 'asd'); Filesystem::rename('source', 'target'); }, 'preRename', '\OCP\Files::preRename', BeforeNodeRenamedEvent::class], - [function () { + [function (): void { Filesystem::file_put_contents('source', 'asd'); Filesystem::rename('source', 'target'); }, 'postRename', '\OCP\Files::postRename', NodeRenamedEvent::class], - [function () { + [function (): void { Filesystem::file_put_contents('source', 'asd'); Filesystem::copy('source', 'target'); }, 'preCopy', '\OCP\Files::preCopy', BeforeNodeCopiedEvent::class], - [function () { + [function (): void { Filesystem::file_put_contents('source', 'asd'); Filesystem::copy('source', 'target'); }, 'postCopy', '\OCP\Files::postCopy', NodeCopiedEvent::class], @@ -215,10 +222,10 @@ class HookConnectorTest extends TestCase { /** * @param callable $operation * @param string $expectedHook - * @dataProvider viewToNodeProviderCopyRename */ - public function testViewToNodeCopyRename(callable $operation, $expectedHook, $expectedLegacyEvent, $expectedEvent) { - $connector = new HookConnector($this->root, $this->view, $this->legacyDispatcher, $this->eventDispatcher); + #[\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 */ @@ -226,7 +233,7 @@ class HookConnectorTest extends TestCase { /** @var Node $hookTargetNode */ $hookTargetNode = null; - $this->root->listen('\OC\Files', $expectedHook, function ($sourceNode, $targetNode) use (&$hookCalled, &$hookSourceNode, &$hookTargetNode) { + $this->root->listen('\OC\Files', $expectedHook, function ($sourceNode, $targetNode) use (&$hookCalled, &$hookSourceNode, &$hookTargetNode): void { $hookCalled = true; $hookSourceNode = $sourceNode; $hookTargetNode = $targetNode; @@ -237,7 +244,7 @@ class HookConnectorTest extends TestCase { $dispatcherSourceNode = null; /** @var Node $dispatcherTargetNode */ $dispatcherTargetNode = null; - $this->legacyDispatcher->addListener($expectedLegacyEvent, function ($event) use (&$dispatcherSourceNode, &$dispatcherTargetNode, &$dispatcherCalled) { + $this->eventDispatcher->addListener($expectedLegacyEvent, function ($event) use (&$dispatcherSourceNode, &$dispatcherTargetNode, &$dispatcherCalled): void { /** @var GenericEvent|APIGenericEvent $event */ $dispatcherCalled = true; [$dispatcherSourceNode, $dispatcherTargetNode] = $event->getSubject(); @@ -248,7 +255,7 @@ class HookConnectorTest extends TestCase { $newDispatcherSourceNode = null; /** @var Node $dispatcherTargetNode */ $newDispatcherTargetNode = null; - $this->eventDispatcher->addListener($expectedEvent, function ($event) use ($expectedEvent, &$newDispatcherSourceNode, &$newDispatcherTargetNode, &$newDispatcherCalled) { + $this->eventDispatcher->addListener($expectedEvent, function ($event) use ($expectedEvent, &$newDispatcherSourceNode, &$newDispatcherTargetNode, &$newDispatcherCalled): void { if ($event instanceof $expectedEvent) { /** @var AbstractNodesEvent$event */ $newDispatcherCalled = true; @@ -272,14 +279,14 @@ class HookConnectorTest extends TestCase { $this->assertEquals('/' . $this->userId . '/files/target', $newDispatcherTargetNode->getPath()); } - public function testPostDeleteMeta() { - $connector = new HookConnector($this->root, $this->view, $this->legacyDispatcher, $this->eventDispatcher); + 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) { + $this->root->listen('\OC\Files', 'postDelete', function ($node) use (&$hookNode, &$hookCalled): void { $hookCalled = true; $hookNode = $node; }); @@ -287,7 +294,7 @@ class HookConnectorTest extends TestCase { $dispatcherCalled = false; /** @var Node $dispatcherNode */ $dispatcherNode = null; - $this->legacyDispatcher->addListener('\OCP\Files::postDelete', function ($event) use (&$dispatcherCalled, &$dispatcherNode) { + $this->eventDispatcher->addListener('\OCP\Files::postDelete', function ($event) use (&$dispatcherCalled, &$dispatcherNode): void { /** @var GenericEvent|APIGenericEvent $event */ $dispatcherCalled = true; $dispatcherNode = $event->getSubject(); @@ -296,7 +303,7 @@ class HookConnectorTest extends TestCase { $newDispatcherCalled = false; /** @var Node $dispatcherNode */ $newDispatcherNode = null; - $this->eventDispatcher->addListener(NodeDeletedEvent::class, function ($event) use (&$newDispatcherCalled, &$newDispatcherNode) { + $this->eventDispatcher->addListener(NodeDeletedEvent::class, function ($event) use (&$newDispatcherCalled, &$newDispatcherNode): void { if ($event instanceof NodeDeletedEvent) { /** @var AbstractNodeEvent $event */ $newDispatcherCalled = true; diff --git a/tests/lib/Files/Node/IntegrationTest.php b/tests/lib/Files/Node/IntegrationTest.php index 5ef5a134e1b..f059afa1625 100644 --- a/tests/lib/Files/Node/IntegrationTest.php +++ b/tests/lib/Files/Node/IntegrationTest.php @@ -1,19 +1,24 @@ <?php + /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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; @@ -33,35 +38,40 @@ class IntegrationTest extends \Test\TestCase { private $root; /** - * @var \OC\Files\Storage\Storage[] + * @var Storage[] */ private $storages; /** - * @var \OC\Files\View $view + * @var View $view */ private $view; protected function setUp(): void { parent::setUp(); - /** @var IMountManager $manager */ - $manager = \OC::$server->get(IMountManager::class); + $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, - \OC::$server->getUserMountCache(), + Server::get(IUserMountCache::class), $this->createMock(LoggerInterface::class), $this->createMock(IUserManager::class), - $this->createMock(IEventDispatcher::class) + $this->createMock(IEventDispatcher::class), + $cacheFactory, ); $storage = new Temporary([]); $subStorage = new Temporary([]); @@ -81,7 +91,7 @@ class IntegrationTest extends \Test\TestCase { parent::tearDown(); } - public function testBasicFile() { + public function testBasicFile(): void { $file = $this->root->newFile('/foo.txt'); $this->assertCount(2, $this->root->getDirectoryListing()); $this->assertTrue($this->root->nodeExists('/foo.txt')); @@ -104,7 +114,7 @@ class IntegrationTest extends \Test\TestCase { $this->assertEquals('qwerty', $file->getContent()); } - public function testBasicFolder() { + public function testBasicFolder(): void { $folder = $this->root->newFolder('/foo'); $this->assertTrue($this->root->nodeExists('/foo')); $file = $folder->newFile('/bar'); diff --git a/tests/lib/Files/Node/NodeTest.php b/tests/lib/Files/Node/NodeTestCase.php index 8c0d4cdb293..4aecd0fef11 100644 --- a/tests/lib/Files/Node/NodeTest.php +++ b/tests/lib/Files/Node/NodeTestCase.php @@ -1,22 +1,33 @@ <?php + /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Storage; +use OCP\Files\NotPermittedException; +use OCP\Files\Storage\IStorage; +use OCP\ICacheFactory; use OCP\IUser; use OCP\IUserManager; use Psr\Log\LoggerInterface; @@ -26,16 +37,16 @@ use Psr\Log\LoggerInterface; * * @package Test\Files\Node */ -abstract class NodeTest extends \Test\TestCase { - /** @var \OC\User\User */ +abstract class NodeTestCase extends \Test\TestCase { + /** @var User */ protected $user; /** @var \OC\Files\Mount\Manager */ protected $manager; - /** @var \OC\Files\View|\PHPUnit\Framework\MockObject\MockObject */ + /** @var View|\PHPUnit\Framework\MockObject\MockObject */ protected $view; /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject */ protected $root; - /** @var \OCP\Files\Config\IUserMountCache|\PHPUnit\Framework\MockObject\MockObject */ + /** @var IUserMountCache|\PHPUnit\Framework\MockObject\MockObject */ protected $userMountCache; /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $logger; @@ -43,6 +54,8 @@ abstract class NodeTest extends \Test\TestCase { protected $userManager; /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */ protected $eventDispatcher; + /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ + protected $cacheFactory; protected function setUp(): void { parent::setUp(); @@ -54,18 +67,37 @@ abstract class NodeTest extends \Test\TestCase { $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->root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->eventDispatcher]) + $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 @@ -89,7 +121,7 @@ abstract class NodeTest extends \Test\TestCase { abstract protected function getViewDeleteMethod(); protected function getMockStorage() { - $storage = $this->getMockBuilder(Storage::class) + $storage = $this->getMockBuilder(IStorage::class) ->disableOriginalConstructor() ->getMock(); $storage->expects($this->any()) @@ -105,7 +137,7 @@ abstract class NodeTest extends \Test\TestCase { return new FileInfo('', $this->getMockStorage(), $internalPath, $data, $mount); } - public function testDelete() { + public function testDelete(): void { $this->root->expects($this->exactly(2)) ->method('emit') ->willReturn(true); @@ -116,7 +148,7 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->once()) ->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL])); $this->view->expects($this->once()) ->method($this->getViewDeleteMethod()) @@ -127,13 +159,13 @@ abstract class NodeTest extends \Test\TestCase { $node->delete(); } - public function testDeleteHooks() { + public function testDeleteHooks(): void { $test = $this; $hooksRun = 0; /** * @param \OC\Files\Node\File $node */ - $preListener = function ($node) use (&$test, &$hooksRun) { + $preListener = function ($node) use (&$test, &$hooksRun): void { $test->assertInstanceOf($this->getNodeClass(), $node); $test->assertEquals('foo', $node->getInternalPath()); $test->assertEquals('/bar/foo', $node->getPath()); @@ -144,7 +176,7 @@ abstract class NodeTest extends \Test\TestCase { /** * @param \OC\Files\Node\File $node */ - $postListener = function ($node) use (&$test, &$hooksRun) { + $postListener = function ($node) use (&$test, &$hooksRun): void { $test->assertInstanceOf($this->getNonExistingNodeClass(), $node); $test->assertEquals('foo', $node->getInternalPath()); $test->assertEquals('/bar/foo', $node->getPath()); @@ -153,14 +185,15 @@ abstract class NodeTest extends \Test\TestCase { $hooksRun++; }; - $root = new \OC\Files\Node\Root( + $root = new Root( $this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $root->listen('\OC\Files', 'preDelete', $preListener); @@ -169,7 +202,7 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->any()) ->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL, 'fileid' => 1, 'mimetype' => 'text/plain'], 'foo')); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1, 'mimetype' => 'text/plain'], 'foo')); $this->view->expects($this->once()) ->method($this->getViewDeleteMethod()) @@ -182,8 +215,8 @@ abstract class NodeTest extends \Test\TestCase { } - public function testDeleteNotPermitted() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testDeleteNotPermitted(): void { + $this->expectException(NotPermittedException::class); $this->root->expects($this->any()) ->method('getUser') @@ -192,14 +225,14 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->once()) ->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_READ])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); $node->delete(); } - public function testStat() { + public function testStat(): void { $this->root->expects($this->any()) ->method('getUser') ->willReturn($this->user); @@ -221,7 +254,7 @@ abstract class NodeTest extends \Test\TestCase { $this->assertEquals($stat, $node->stat()); } - public function testGetId() { + public function testGetId(): void { $this->root->expects($this->any()) ->method('getUser') ->willReturn($this->user); @@ -242,7 +275,7 @@ abstract class NodeTest extends \Test\TestCase { $this->assertEquals(1, $node->getId()); } - public function testGetSize() { + public function testGetSize(): void { $this->root->expects($this->any()) ->method('getUser') ->willReturn($this->user); @@ -264,7 +297,7 @@ abstract class NodeTest extends \Test\TestCase { $this->assertEquals(100, $node->getSize()); } - public function testGetEtag() { + public function testGetEtag(): void { $this->root->expects($this->any()) ->method('getUser') ->willReturn($this->user); @@ -285,7 +318,7 @@ abstract class NodeTest extends \Test\TestCase { $this->assertEquals('qwerty', $node->getEtag()); } - public function testGetMTime() { + public function testGetMTime(): void { $this->root->expects($this->any()) ->method('getUser') ->willReturn($this->user); @@ -306,12 +339,12 @@ abstract class NodeTest extends \Test\TestCase { $this->assertEquals(50, $node->getMTime()); } - public function testGetStorage() { + public function testGetStorage(): void { $this->root->expects($this->any()) ->method('getUser') ->willReturn($this->user); /** - * @var \OC\Files\Storage\Storage | \PHPUnit\Framework\MockObject\MockObject $storage + * @var Storage|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder('\OC\Files\Storage\Storage') ->disableOriginalConstructor() @@ -321,7 +354,7 @@ abstract class NodeTest extends \Test\TestCase { $this->assertEquals($storage, $node->getStorage()); } - public function testGetPath() { + public function testGetPath(): void { $this->root->expects($this->any()) ->method('getUser') ->willReturn($this->user); @@ -330,12 +363,12 @@ abstract class NodeTest extends \Test\TestCase { $this->assertEquals('/bar/foo', $node->getPath()); } - public function testGetInternalPath() { + public function testGetInternalPath(): void { $this->root->expects($this->any()) ->method('getUser') ->willReturn($this->user); /** - * @var \OC\Files\Storage\Storage | \PHPUnit\Framework\MockObject\MockObject $storage + * @var Storage|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder('\OC\Files\Storage\Storage') ->disableOriginalConstructor() @@ -351,7 +384,7 @@ abstract class NodeTest extends \Test\TestCase { $this->assertEquals('foo', $node->getInternalPath()); } - public function testGetName() { + public function testGetName(): void { $this->root->expects($this->any()) ->method('getUser') ->willReturn($this->user); @@ -360,7 +393,7 @@ abstract class NodeTest extends \Test\TestCase { $this->assertEquals('foo', $node->getName()); } - public function testTouchSetMTime() { + public function testTouchSetMTime(): void { $this->root->expects($this->any()) ->method('getUser') ->willReturn($this->user); @@ -373,20 +406,20 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->once()) ->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL])); + ->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() { + public function testTouchHooks(): void { $test = $this; $hooksRun = 0; /** * @param \OC\Files\Node\File $node */ - $preListener = function ($node) use (&$test, &$hooksRun) { + $preListener = function ($node) use (&$test, &$hooksRun): void { $test->assertEquals('foo', $node->getInternalPath()); $test->assertEquals('/bar/foo', $node->getPath()); $hooksRun++; @@ -395,20 +428,21 @@ abstract class NodeTest extends \Test\TestCase { /** * @param \OC\Files\Node\File $node */ - $postListener = function ($node) use (&$test, &$hooksRun) { + $postListener = function ($node) use (&$test, &$hooksRun): void { $test->assertEquals('foo', $node->getInternalPath()); $test->assertEquals('/bar/foo', $node->getPath()); $hooksRun++; }; - $root = new \OC\Files\Node\Root( + $root = new Root( $this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $root->listen('\OC\Files', 'preTouch', $preListener); $root->listen('\OC\Files', 'postTouch', $postListener); @@ -421,7 +455,7 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->any()) ->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL], 'foo')); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL], 'foo')); $node = $this->createTestNode($root, $this->view, '/bar/foo'); $node->touch(100); @@ -429,8 +463,8 @@ abstract class NodeTest extends \Test\TestCase { } - public function testTouchNotPermitted() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testTouchNotPermitted(): void { + $this->expectException(NotPermittedException::class); $this->root->expects($this->any()) ->method('getUser') @@ -439,21 +473,21 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->any()) ->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_READ])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ])); $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); $node->touch(100); } - public function testInvalidPath() { - $this->expectException(\OCP\Files\InvalidPathException::class); + public function testInvalidPath(): void { + $this->expectException(InvalidPathException::class); $node = $this->createTestNode($this->root, $this->view, '/../foo'); $node->getFileInfo(); } - public function testCopySameStorage() { + public function testCopySameStorage(): void { $this->view->expects($this->any()) ->method('copy') ->with('/bar/foo', '/bar/asd') @@ -461,14 +495,13 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->any()) ->method('getFileInfo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL, 'fileid' => 3])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 3])); $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); - $parentNode = new \OC\Files\Node\Folder($this->root, $this->view, '/bar'); + $parentNode = new Folder($this->root, $this->view, '/bar'); $newNode = $this->createTestNode($this->root, $this->view, '/bar/asd'); - $this->root->expects($this->exactly(2)) - ->method('get') + $this->root->method('get') ->willReturnMap([ ['/bar/asd', $newNode], ['/bar', $parentNode] @@ -480,11 +513,11 @@ abstract class NodeTest extends \Test\TestCase { } - public function testCopyNotPermitted() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testCopyNotPermitted(): void { + $this->expectException(NotPermittedException::class); /** - * @var \OC\Files\Storage\Storage | \PHPUnit\Framework\MockObject\MockObject $storage + * @var Storage|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->createMock('\OC\Files\Storage\Storage'); @@ -496,10 +529,10 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->any()) ->method('getFileInfo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_READ, 'fileid' => 3])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ, 'fileid' => 3])); $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); - $parentNode = new \OC\Files\Node\Folder($this->root, $this->view, '/bar'); + $parentNode = new Folder($this->root, $this->view, '/bar'); $this->root->expects($this->once()) ->method('get') @@ -511,8 +544,8 @@ abstract class NodeTest extends \Test\TestCase { } - public function testCopyNoParent() { - $this->expectException(\OCP\Files\NotFoundException::class); + public function testCopyNoParent(): void { + $this->expectException(NotFoundException::class); $this->view->expects($this->never()) ->method('copy'); @@ -522,20 +555,20 @@ abstract class NodeTest extends \Test\TestCase { $this->root->expects($this->once()) ->method('get') ->with('/bar/asd') - ->will($this->throwException(new NotFoundException())); + ->willThrowException(new NotFoundException()); $node->copy('/bar/asd/foo'); } - public function testCopyParentIsFile() { - $this->expectException(\OCP\Files\NotPermittedException::class); + 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 \OC\Files\Node\File($this->root, $this->view, '/bar'); + $parentNode = new File($this->root, $this->view, '/bar'); $this->root->expects($this->once()) ->method('get') @@ -546,7 +579,7 @@ abstract class NodeTest extends \Test\TestCase { $node->copy('/bar/asd'); } - public function testMoveSameStorage() { + public function testMoveSameStorage(): void { $this->view->expects($this->any()) ->method('rename') ->with('/bar/foo', '/bar/asd') @@ -554,10 +587,10 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->any()) ->method('getFileInfo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL, 'fileid' => 1])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1])); $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); - $parentNode = new \OC\Files\Node\Folder($this->root, $this->view, '/bar'); + $parentNode = new Folder($this->root, $this->view, '/bar'); $this->root->expects($this->any()) ->method('get') @@ -569,7 +602,7 @@ abstract class NodeTest extends \Test\TestCase { $this->assertEquals('/bar/asd', $node->getPath()); } - public function moveOrCopyProvider() { + public static function moveOrCopyProvider(): array { return [ ['move', 'rename', 'preRename', 'postRename'], ['copy', 'copy', 'preCopy', 'postCopy'], @@ -577,17 +610,17 @@ abstract class NodeTest extends \Test\TestCase { } /** - * @dataProvider moveOrCopyProvider * @param string $operationMethod * @param string $viewMethod * @param string $preHookName * @param string $postHookName */ - public function testMoveCopyHooks($operationMethod, $viewMethod, $preHookName, $postHookName) { + #[\PHPUnit\Framework\Attributes\DataProvider('moveOrCopyProvider')] + public function testMoveCopyHooks($operationMethod, $viewMethod, $preHookName, $postHookName): void { /** @var IRootFolder|\PHPUnit\Framework\MockObject\MockObject $root */ - $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) - ->setMethods(['get']) + $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()) @@ -597,13 +630,13 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->any()) ->method('getFileInfo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL, 'fileid' => 1])); + ->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 \OC\Files\Node\Folder($root, $this->view, '/bar'); + $parentNode = new Folder($root, $this->view, '/bar'); $targetTestNode = $this->createTestNode($root, $this->view, '/bar/asd'); $root->expects($this->any()) @@ -612,7 +645,7 @@ abstract class NodeTest extends \Test\TestCase { $hooksRun = 0; - $preListener = function (Node $sourceNode, Node $targetNode) use (&$hooksRun, $node) { + $preListener = function (Node $sourceNode, Node $targetNode) use (&$hooksRun, $node): void { $this->assertSame($node, $sourceNode); $this->assertInstanceOf($this->getNodeClass(), $sourceNode); $this->assertInstanceOf($this->getNonExistingNodeClass(), $targetNode); @@ -620,7 +653,7 @@ abstract class NodeTest extends \Test\TestCase { $hooksRun++; }; - $postListener = function (Node $sourceNode, Node $targetNode) use (&$hooksRun, $node, $targetTestNode) { + $postListener = function (Node $sourceNode, Node $targetNode) use (&$hooksRun, $node, $targetTestNode): void { $this->assertSame($node, $sourceNode); $this->assertNotSame($node, $targetNode); $this->assertSame($targetTestNode, $targetNode); @@ -629,13 +662,13 @@ abstract class NodeTest extends \Test\TestCase { $hooksRun++; }; - $preWriteListener = function (Node $targetNode) use (&$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) { + $postWriteListener = function (Node $targetNode) use (&$hooksRun, $targetTestNode): void { $this->assertSame($targetTestNode, $targetNode); $hooksRun++; }; @@ -651,18 +684,18 @@ abstract class NodeTest extends \Test\TestCase { } - public function testMoveNotPermitted() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testMoveNotPermitted(): void { + $this->expectException(NotPermittedException::class); $this->view->expects($this->any()) ->method('getFileInfo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_READ])); + ->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 \OC\Files\Node\Folder($this->root, $this->view, '/bar'); + $parentNode = new Folder($this->root, $this->view, '/bar'); $this->root->expects($this->once()) ->method('get') @@ -673,11 +706,11 @@ abstract class NodeTest extends \Test\TestCase { } - public function testMoveNoParent() { - $this->expectException(\OCP\Files\NotFoundException::class); + public function testMoveNoParent(): void { + $this->expectException(NotFoundException::class); /** - * @var \OC\Files\Storage\Storage | \PHPUnit\Framework\MockObject\MockObject $storage + * @var Storage|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->createMock('\OC\Files\Storage\Storage'); @@ -689,20 +722,20 @@ abstract class NodeTest extends \Test\TestCase { $this->root->expects($this->once()) ->method('get') ->with('/bar') - ->will($this->throwException(new NotFoundException())); + ->willThrowException(new NotFoundException()); $node->move('/bar/asd'); } - public function testMoveParentIsFile() { - $this->expectException(\OCP\Files\NotPermittedException::class); + 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 \OC\Files\Node\File($this->root, $this->view, '/bar'); + $parentNode = new File($this->root, $this->view, '/bar'); $this->root->expects($this->once()) ->method('get') @@ -713,8 +746,8 @@ abstract class NodeTest extends \Test\TestCase { } - public function testMoveFailed() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testMoveFailed(): void { + $this->expectException(NotPermittedException::class); $this->view->expects($this->any()) ->method('rename') @@ -723,10 +756,10 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->any()) ->method('getFileInfo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL, 'fileid' => 1])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1])); $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); - $parentNode = new \OC\Files\Node\Folder($this->root, $this->view, '/bar'); + $parentNode = new Folder($this->root, $this->view, '/bar'); $this->root->expects($this->any()) ->method('get') @@ -736,8 +769,8 @@ abstract class NodeTest extends \Test\TestCase { } - public function testCopyFailed() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testCopyFailed(): void { + $this->expectException(NotPermittedException::class); $this->view->expects($this->any()) ->method('copy') @@ -746,10 +779,10 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->any()) ->method('getFileInfo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL, 'fileid' => 1])); + ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1])); $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); - $parentNode = new \OC\Files\Node\Folder($this->root, $this->view, '/bar'); + $parentNode = new Folder($this->root, $this->view, '/bar'); $this->root->expects($this->any()) ->method('get') diff --git a/tests/lib/Files/Node/RootTest.php b/tests/lib/Files/Node/RootTest.php index 5d8e2a4ac62..d90e6a2cc6e 100644 --- a/tests/lib/Files/Node/RootTest.php +++ b/tests/lib/Files/Node/RootTest.php @@ -1,19 +1,28 @@ <?php + /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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 OCP\Cache\CappedMemoryCache; 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; @@ -24,11 +33,11 @@ use Psr\Log\LoggerInterface; * @package Test\Files\Node */ class RootTest extends \Test\TestCase { - /** @var \OC\User\User */ + /** @var User */ private $user; /** @var \OC\Files\Mount\Manager */ private $manager; - /** @var \OCP\Files\Config\IUserMountCache|\PHPUnit\Framework\MockObject\MockObject */ + /** @var IUserMountCache|\PHPUnit\Framework\MockObject\MockObject */ private $userMountCache; /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */ private $logger; @@ -36,6 +45,8 @@ class RootTest extends \Test\TestCase { private $userManager; /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */ private $eventDispatcher; + /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ + protected $cacheFactory; protected function setUp(): void { parent::setUp(); @@ -50,33 +61,45 @@ class RootTest extends \Test\TestCase { $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() { + public function testGet(): void { /** - * @var \OC\Files\Storage\Storage $storage + * @var Storage $storage */ $storage = $this->getMockBuilder('\OC\Files\Storage\Storage') ->disableOriginalConstructor() ->getMock(); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->getMockBuilder(View::class) - ->disableOriginalConstructor() - ->getMock(); - $root = new \OC\Files\Node\Root( + $view = $this->getRootViewMock(); + $root = new Root( $this->manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $view->expects($this->once()) @@ -91,29 +114,25 @@ class RootTest extends \Test\TestCase { } - public function testGetNotFound() { - $this->expectException(\OCP\Files\NotFoundException::class); + public function testGetNotFound(): void { + $this->expectException(NotFoundException::class); /** - * @var \OC\Files\Storage\Storage $storage + * @var Storage $storage */ $storage = $this->getMockBuilder('\OC\Files\Storage\Storage') ->disableOriginalConstructor() ->getMock(); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->getMockBuilder(View::class) - ->disableOriginalConstructor() - ->getMock(); - $root = new \OC\Files\Node\Root( + $view = $this->getRootViewMock(); + $root = new Root( $this->manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $view->expects($this->once()) @@ -126,60 +145,53 @@ class RootTest extends \Test\TestCase { } - public function testGetInvalidPath() { - $this->expectException(\OCP\Files\NotPermittedException::class); + public function testGetInvalidPath(): void { + $this->expectException(NotPermittedException::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->getMockBuilder(View::class) - ->disableOriginalConstructor() - ->getMock(); - $root = new \OC\Files\Node\Root( + $view = $this->getRootViewMock(); + $root = new Root( $this->manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $root->get('/../foo'); } - public function testGetNoStorages() { - $this->expectException(\OCP\Files\NotFoundException::class); + public function testGetNoStorages(): void { + $this->expectException(NotFoundException::class); - /** - * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view - */ - $view = $this->getMockBuilder(View::class) - ->disableOriginalConstructor() - ->getMock(); - $root = new \OC\Files\Node\Root( + $view = $this->getRootViewMock(); + $root = new Root( $this->manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $root->get('/bar/foo'); } - public function testGetUserFolder() { - $root = new \OC\Files\Node\Root( + public function testGetUserFolder(): void { + $root = new Root( $this->manager, - $this->createMock(View::class), + $this->getRootViewMock(), $this->user, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $user = $this->createMock(IUser::class); $user @@ -209,18 +221,19 @@ class RootTest extends \Test\TestCase { } - public function testGetUserFolderWithNoUserObj() { - $this->expectException(\OC\User\NoUserException::class); + public function testGetUserFolderWithNoUserObj(): void { + $this->expectException(NoUserException::class); $this->expectExceptionMessage('Backends provided no user object'); - $root = new \OC\Files\Node\Root( + $root = new Root( $this->createMock(Manager::class), - $this->createMock(View::class), + $this->getRootViewMock(), null, $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $this->userManager ->expects($this->once()) diff --git a/tests/lib/Files/ObjectStore/AzureTest.php b/tests/lib/Files/ObjectStore/AzureTest.php index 054dc36cce4..52d2b9e8657 100644 --- a/tests/lib/Files/ObjectStore/AzureTest.php +++ b/tests/lib/Files/ObjectStore/AzureTest.php @@ -1,34 +1,22 @@ <?php + /** - * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\ObjectStore; use OC\Files\ObjectStore\Azure; +use OCP\IConfig; +use OCP\Server; /** * @group PRIMARY-azure */ -class AzureTest extends ObjectStoreTest { +class AzureTest extends ObjectStoreTestCase { protected function getInstance() { - $config = \OC::$server->getConfig()->getSystemValue('objectstore'); + $config = Server::get(IConfig::class)->getSystemValue('objectstore'); if (!is_array($config) || $config['class'] !== 'OC\\Files\\ObjectStore\\Azure') { $this->markTestSkipped('objectstore not configured for azure'); } @@ -36,7 +24,7 @@ class AzureTest extends ObjectStoreTest { return new Azure($config['arguments']); } - public function testFseekSize() { + public function testFseekSize(): void { $this->markTestSkipped('azure does not support seeking at the moment'); } } diff --git a/tests/lib/Files/ObjectStore/FailDeleteObjectStore.php b/tests/lib/Files/ObjectStore/FailDeleteObjectStore.php index 5160abe574f..767125d42aa 100644 --- a/tests/lib/Files/ObjectStore/FailDeleteObjectStore.php +++ b/tests/lib/Files/ObjectStore/FailDeleteObjectStore.php @@ -2,23 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\ObjectStore; @@ -26,10 +11,9 @@ namespace Test\Files\ObjectStore; use OCP\Files\ObjectStore\IObjectStore; class FailDeleteObjectStore implements IObjectStore { - private $objectStore; - - public function __construct(IObjectStore $objectStore) { - $this->objectStore = $objectStore; + public function __construct( + private IObjectStore $objectStore, + ) { } public function getStorageId() { @@ -40,7 +24,7 @@ class FailDeleteObjectStore implements IObjectStore { return $this->objectStore->readObject($urn); } - public function writeObject($urn, $stream, string $mimetype = null) { + public function writeObject($urn, $stream, ?string $mimetype = null) { return $this->objectStore->writeObject($urn, $stream, $mimetype); } diff --git a/tests/lib/Files/ObjectStore/FailWriteObjectStore.php b/tests/lib/Files/ObjectStore/FailWriteObjectStore.php index 559d004cd0c..924bbdada4f 100644 --- a/tests/lib/Files/ObjectStore/FailWriteObjectStore.php +++ b/tests/lib/Files/ObjectStore/FailWriteObjectStore.php @@ -2,23 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\ObjectStore; @@ -26,10 +11,9 @@ namespace Test\Files\ObjectStore; use OCP\Files\ObjectStore\IObjectStore; class FailWriteObjectStore implements IObjectStore { - private $objectStore; - - public function __construct(IObjectStore $objectStore) { - $this->objectStore = $objectStore; + public function __construct( + private IObjectStore $objectStore, + ) { } public function getStorageId() { @@ -40,7 +24,7 @@ class FailWriteObjectStore implements IObjectStore { return $this->objectStore->readObject($urn); } - public function writeObject($urn, $stream, string $mimetype = null) { + public function writeObject($urn, $stream, ?string $mimetype = null) { // emulate a failed write that didn't throw an error return true; } diff --git a/tests/lib/Files/ObjectStore/LocalTest.php b/tests/lib/Files/ObjectStore/LocalTest.php index ceb21428e4b..d3e9ad56164 100644 --- a/tests/lib/Files/ObjectStore/LocalTest.php +++ b/tests/lib/Files/ObjectStore/LocalTest.php @@ -1,32 +1,19 @@ <?php + /** - * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\ObjectStore; use OC\Files\ObjectStore\StorageObjectStore; use OC\Files\Storage\Temporary; +use OCP\Files\ObjectStore\IObjectStore; -class LocalTest extends ObjectStoreTest { +class LocalTest extends ObjectStoreTestCase { /** - * @return \OCP\Files\ObjectStore\IObjectStore + * @return IObjectStore */ protected function getInstance() { $storage = new Temporary(); diff --git a/tests/lib/Files/ObjectStore/MapperTest.php b/tests/lib/Files/ObjectStore/MapperTest.php index dbe5fe5d32f..6448d5ce1f5 100644 --- a/tests/lib/Files/ObjectStore/MapperTest.php +++ b/tests/lib/Files/ObjectStore/MapperTest.php @@ -1,22 +1,9 @@ <?php + /** - * @author Roeland Jago Douma <rullzer@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace Test\Files\ObjectStore; @@ -43,7 +30,7 @@ class MapperTest extends \Test\TestCase { $this->mapper = new Mapper($this->user, $this->config); } - public function dataGetBucket() { + public static function dataGetBucket(): array { return [ ['user', 64, 0, '17'], ['USER', 64, 0, '0'], @@ -56,12 +43,12 @@ class MapperTest extends \Test\TestCase { } /** - * @dataProvider dataGetBucket * @param string $username * @param int $numBuckets * @param string $expectedBucket */ - public function testGetBucket($username, $numBuckets, $bucketShift, $expectedBucket) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetBucket')] + public function testGetBucket($username, $numBuckets, $bucketShift, $expectedBucket): void { $this->user->expects($this->once()) ->method('getUID') ->willReturn($username); diff --git a/tests/lib/Files/ObjectStore/NoopScannerTest.php b/tests/lib/Files/ObjectStore/NoopScannerTest.php deleted file mode 100644 index 7142fb6daf9..00000000000 --- a/tests/lib/Files/ObjectStore/NoopScannerTest.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -/** - * ownCloud - * - * @author Joas Schilling - * @copyright 2015 Joas Schilling nickvergessen@owncloud.com - * - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -namespace Test\Files\ObjectStore; - -class NoopScannerTest extends \Test\TestCase { - /** @var \OC\Files\Storage\Storage $storage */ - private $storage; - - /** @var \OC\Files\ObjectStore\NoopScanner $scanner */ - private $scanner; - - protected function setUp(): void { - parent::setUp(); - - $this->storage = new \OC\Files\Storage\Temporary([]); - $this->scanner = new \OC\Files\ObjectStore\NoopScanner($this->storage); - } - - public function testFile() { - $data = "dummy file data\n"; - $this->storage->file_put_contents('foo.txt', $data); - - $this->assertEquals( - [], - $this->scanner->scanFile('foo.txt'), - 'Asserting that no error occurred while scanFile()' - ); - } - - private function fillTestFolders() { - $textData = "dummy file data\n"; - $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png'); - $this->storage->mkdir('folder'); - $this->storage->file_put_contents('foo.txt', $textData); - $this->storage->file_put_contents('foo.png', $imgData); - $this->storage->file_put_contents('folder/bar.txt', $textData); - } - - public function testFolder() { - $this->fillTestFolders(); - - $this->assertEquals( - [], - $this->scanner->scan(''), - 'Asserting that no error occurred while scan()' - ); - } - - public function testBackgroundScan() { - $this->fillTestFolders(); - $this->storage->mkdir('folder2'); - $this->storage->file_put_contents('folder2/bar.txt', 'foobar'); - - $this->assertEquals( - [], - $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW), - 'Asserting that no error occurred while scan(SCAN_SHALLOW)' - ); - - $this->scanner->backgroundScan(); - - $this->assertTrue( - true, - 'Asserting that no error occurred while backgroundScan()' - ); - } -} diff --git a/tests/lib/Files/ObjectStore/ObjectStoreScannerTest.php b/tests/lib/Files/ObjectStore/ObjectStoreScannerTest.php new file mode 100644 index 00000000000..ea6ac682c70 --- /dev/null +++ b/tests/lib/Files/ObjectStore/ObjectStoreScannerTest.php @@ -0,0 +1,79 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Files\ObjectStore; + +use OC\Files\Cache\Scanner; +use OC\Files\ObjectStore\ObjectStoreScanner; +use OC\Files\Storage\Temporary; +use OCP\Files\Cache\ICache; +use OCP\Files\Storage\IStorage; +use Test\TestCase; + +/** + * @group DB + */ +class ObjectStoreScannerTest extends TestCase { + private IStorage $storage; + private ICache $cache; + private ObjectStoreScanner $scanner; + private Scanner $realScanner; + + protected function setUp(): void { + parent::setUp(); + + $this->storage = new Temporary([]); + $this->cache = $this->storage->getCache(); + $this->scanner = new ObjectStoreScanner($this->storage); + $this->realScanner = new Scanner($this->storage); + } + + public function testFile(): void { + $data = "dummy file data\n"; + $this->storage->file_put_contents('foo.txt', $data); + + $this->assertNull($this->scanner->scanFile('foo.txt'), + 'Asserting that no error occurred while scanFile()' + ); + } + + private function fillTestFolders() { + $textData = "dummy file data\n"; + $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png'); + $this->storage->mkdir('folder'); + $this->storage->file_put_contents('foo.txt', $textData); + $this->storage->file_put_contents('foo.png', $imgData); + $this->storage->file_put_contents('folder/bar.txt', $textData); + } + + public function testFolder(): void { + $this->fillTestFolders(); + + $this->assertNull( + $this->scanner->scan(''), + 'Asserting that no error occurred while scan()' + ); + } + + public function testBackgroundScan(): void { + $this->fillTestFolders(); + $this->storage->mkdir('folder2'); + $this->storage->file_put_contents('folder2/bar.txt', 'foobar'); + + $this->realScanner->scan(''); + + $this->assertEquals(6, $this->cache->get('folder2')->getSize()); + + $this->cache->put('folder2', ['size' => -1]); + + $this->assertEquals(-1, $this->cache->get('folder2')->getSize()); + + $this->scanner->backgroundScan(); + + $this->assertEquals(6, $this->cache->get('folder2')->getSize()); + } +} diff --git a/tests/lib/Files/ObjectStore/ObjectStoreStorageOverwrite.php b/tests/lib/Files/ObjectStore/ObjectStoreStorageOverwrite.php index b85f6289c94..6eaffb6961c 100644 --- a/tests/lib/Files/ObjectStore/ObjectStoreStorageOverwrite.php +++ b/tests/lib/Files/ObjectStore/ObjectStoreStorageOverwrite.php @@ -2,23 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\ObjectStore; @@ -30,7 +15,7 @@ use OCP\Files\ObjectStore\IObjectStore; * Allow overwriting the object store instance for test purposes */ class ObjectStoreStorageOverwrite extends ObjectStoreStorage { - public function setObjectStore(IObjectStore $objectStore) { + public function setObjectStore(IObjectStore $objectStore): void { $this->objectStore = $objectStore; } @@ -38,7 +23,7 @@ class ObjectStoreStorageOverwrite extends ObjectStoreStorage { return $this->objectStore; } - public function setValidateWrites(bool $validate) { + public function setValidateWrites(bool $validate): void { $this->validateWrites = $validate; } } diff --git a/tests/lib/Files/ObjectStore/ObjectStoreStorageTest.php b/tests/lib/Files/ObjectStore/ObjectStoreStorageTest.php index 1bebaf6c4ba..3387808445a 100644 --- a/tests/lib/Files/ObjectStore/ObjectStoreStorageTest.php +++ b/tests/lib/Files/ObjectStore/ObjectStoreStorageTest.php @@ -1,21 +1,9 @@ <?php + /** - * @author Jörn Friedrich Dreyer - * @copyright (c) 2014 Jörn Friedrich Dreyer <jfd@owncloud.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * 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\ObjectStore; @@ -23,6 +11,7 @@ namespace Test\Files\ObjectStore; use OC\Files\ObjectStore\StorageObjectStore; use OC\Files\Storage\Temporary; use OC\Files\Storage\Wrapper\Jail; +use OCP\Constants; use OCP\Files\ObjectStore\IObjectStore; use Test\Files\Storage\Storage; @@ -56,7 +45,7 @@ class ObjectStoreStorageTest extends Storage { parent::tearDown(); } - public function testStat() { + public function testStat(): void { $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt'; $ctimeStart = time(); $this->instance->file_put_contents('/lorem.txt', file_get_contents($textFile)); @@ -80,29 +69,27 @@ class ObjectStoreStorageTest extends Storage { } } - public function testCheckUpdate() { + public function testCheckUpdate(): void { $this->markTestSkipped('Detecting external changes is not supported on object storages'); } - /** - * @dataProvider copyAndMoveProvider - */ - public function testMove($source, $target) { + #[\PHPUnit\Framework\Attributes\DataProvider('copyAndMoveProvider')] + public function testMove($source, $target): void { $this->initSourceAndTarget($source); - $sourceId = $this->instance->getCache()->getId(ltrim('/', $source)); + $sourceId = $this->instance->getCache()->getId(ltrim($source, '/')); $this->assertNotEquals(-1, $sourceId); $this->instance->rename($source, $target); - $this->assertTrue($this->instance->file_exists($target), $target.' was not created'); - $this->assertFalse($this->instance->file_exists($source), $source.' still exists'); + $this->assertTrue($this->instance->file_exists($target), $target . ' was not created'); + $this->assertFalse($this->instance->file_exists($source), $source . ' still exists'); $this->assertSameAsLorem($target); - $targetId = $this->instance->getCache()->getId(ltrim('/', $target)); + $targetId = $this->instance->getCache()->getId(ltrim($target, '/')); $this->assertSame($sourceId, $targetId, 'fileid must be stable on move or shares will break'); } - public function testRenameDirectory() { + public function testRenameDirectory(): void { $this->instance->mkdir('source'); $this->instance->file_put_contents('source/test1.txt', 'foo'); $this->instance->file_put_contents('source/test2.txt', 'qwerty'); @@ -131,7 +118,7 @@ class ObjectStoreStorageTest extends Storage { $this->assertSame($sourceId, $targetId, 'fileid must be stable on move or shares will break'); } - public function testRenameOverWriteDirectory() { + public function testRenameOverWriteDirectory(): void { $this->instance->mkdir('source'); $this->instance->file_put_contents('source/test1.txt', 'foo'); $sourceId = $this->instance->getCache()->getId('source'); @@ -151,7 +138,7 @@ class ObjectStoreStorageTest extends Storage { $this->assertSame($sourceId, $targetId, 'fileid must be stable on move or shares will break'); } - public function testRenameOverWriteDirectoryOverFile() { + public function testRenameOverWriteDirectoryOverFile(): void { $this->instance->mkdir('source'); $this->instance->file_put_contents('source/test1.txt', 'foo'); $sourceId = $this->instance->getCache()->getId('source'); @@ -168,7 +155,7 @@ class ObjectStoreStorageTest extends Storage { $this->assertSame($sourceId, $targetId, 'fileid must be stable on move or shares will break'); } - public function testWriteObjectSilentFailure() { + public function testWriteObjectSilentFailure(): void { $objectStore = $this->instance->getObjectStore(); $this->instance->setObjectStore(new FailWriteObjectStore($objectStore)); @@ -181,7 +168,7 @@ class ObjectStoreStorageTest extends Storage { $this->assertFalse($this->instance->file_exists('test.txt')); } - public function testWriteObjectSilentFailureNoCheck() { + public function testWriteObjectSilentFailureNoCheck(): void { $objectStore = $this->instance->getObjectStore(); $this->instance->setObjectStore(new FailWriteObjectStore($objectStore)); $this->instance->setValidateWrites(false); @@ -190,7 +177,7 @@ class ObjectStoreStorageTest extends Storage { $this->assertTrue($this->instance->file_exists('test.txt')); } - public function testDeleteObjectFailureKeepCache() { + public function testDeleteObjectFailureKeepCache(): void { $objectStore = $this->instance->getObjectStore(); $this->instance->setObjectStore(new FailDeleteObjectStore($objectStore)); $cache = $this->instance->getCache(); @@ -215,7 +202,7 @@ class ObjectStoreStorageTest extends Storage { $this->assertTrue($cache->inCache('foo/test.txt')); } - public function testCopyBetweenJails() { + public function testCopyBetweenJails(): void { $this->instance->mkdir('a'); $this->instance->mkdir('b'); $jailA = new Jail([ @@ -237,4 +224,55 @@ class ObjectStoreStorageTest extends Storage { $this->assertEquals('2', $this->instance->file_get_contents('b/target/sub/2.txt')); $this->assertEquals('3', $this->instance->file_get_contents('b/target/sub/3.txt')); } + + public function testCopyPreservesPermissions(): void { + $cache = $this->instance->getCache(); + + $this->instance->file_put_contents('test.txt', 'foo'); + $this->assertTrue($cache->inCache('test.txt')); + + $cache->update($cache->getId('test.txt'), ['permissions' => Constants::PERMISSION_READ]); + $this->assertEquals(Constants::PERMISSION_READ, $this->instance->getPermissions('test.txt')); + + $this->assertTrue($this->instance->copy('test.txt', 'new.txt')); + + $this->assertTrue($cache->inCache('new.txt')); + $this->assertEquals(Constants::PERMISSION_READ, $this->instance->getPermissions('new.txt')); + } + + /** + * Test that copying files will drop permissions like local storage does + * TODO: Drop this and fix local storage + */ + public function testCopyGrantsPermissions(): void { + $config['objectstore'] = $this->objectStorage; + $config['handleCopiesAsOwned'] = true; + $instance = new ObjectStoreStorageOverwrite($config); + + $cache = $instance->getCache(); + + $instance->file_put_contents('test.txt', 'foo'); + $this->assertTrue($cache->inCache('test.txt')); + + $cache->update($cache->getId('test.txt'), ['permissions' => Constants::PERMISSION_READ]); + $this->assertEquals(Constants::PERMISSION_READ, $instance->getPermissions('test.txt')); + + $this->assertTrue($instance->copy('test.txt', 'new.txt')); + + $this->assertTrue($cache->inCache('new.txt')); + $this->assertEquals(Constants::PERMISSION_ALL, $instance->getPermissions('new.txt')); + } + + public function testCopyFolderSize(): void { + $cache = $this->instance->getCache(); + + $this->instance->mkdir('source'); + $this->instance->file_put_contents('source/test.txt', 'foo'); + $this->instance->getUpdater()->update('source/test.txt'); + $this->assertEquals(3, $cache->get('source')->getSize()); + + $this->assertTrue($this->instance->copy('source', 'target')); + + $this->assertEquals(3, $cache->get('target')->getSize()); + } } diff --git a/tests/lib/Files/ObjectStore/ObjectStoreStoragesDifferentBucketTest.php b/tests/lib/Files/ObjectStore/ObjectStoreStoragesDifferentBucketTest.php new file mode 100644 index 00000000000..d39426ee821 --- /dev/null +++ b/tests/lib/Files/ObjectStore/ObjectStoreStoragesDifferentBucketTest.php @@ -0,0 +1,43 @@ +<?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\ObjectStore; + +use OC\Files\ObjectStore\StorageObjectStore; +use OC\Files\Storage\Temporary; +use OCP\Files\ObjectStore\IObjectStore; +use Test\Files\Storage\StoragesTestCase; + +/** + * @group DB + */ +class ObjectStoreStoragesDifferentBucketTest extends StoragesTestCase { + /** + * @var IObjectStore + */ + private $objectStore1; + + /** + * @var IObjectStore + */ + private $objectStore2; + + protected function setUp(): void { + parent::setUp(); + + $baseStorage1 = new Temporary(); + $this->objectStore1 = new StorageObjectStore($baseStorage1); + $config['objectstore'] = $this->objectStore1; + $this->storage1 = new ObjectStoreStorageOverwrite($config); + + $baseStorage2 = new Temporary(); + $this->objectStore2 = new StorageObjectStore($baseStorage2); + $config['objectstore'] = $this->objectStore2; + $this->storage2 = new ObjectStoreStorageOverwrite($config); + } +} diff --git a/tests/lib/Files/ObjectStore/ObjectStoreStoragesSameBucketTest.php b/tests/lib/Files/ObjectStore/ObjectStoreStoragesSameBucketTest.php new file mode 100644 index 00000000000..4e42668cd3f --- /dev/null +++ b/tests/lib/Files/ObjectStore/ObjectStoreStoragesSameBucketTest.php @@ -0,0 +1,35 @@ +<?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\ObjectStore; + +use OC\Files\ObjectStore\StorageObjectStore; +use OC\Files\Storage\Temporary; +use OCP\Files\ObjectStore\IObjectStore; +use Test\Files\Storage\StoragesTestCase; + +/** + * @group DB + */ +class ObjectStoreStoragesSameBucketTest extends StoragesTestCase { + /** + * @var IObjectStore + */ + private $objectStore; + + protected function setUp(): void { + parent::setUp(); + + $baseStorage = new Temporary(); + $this->objectStore = new StorageObjectStore($baseStorage); + $config['objectstore'] = $this->objectStore; + // storage1 and storage2 share the same object store. + $this->storage1 = new ObjectStoreStorageOverwrite($config); + $this->storage2 = new ObjectStoreStorageOverwrite($config); + } +} diff --git a/tests/lib/Files/ObjectStore/ObjectStoreTest.php b/tests/lib/Files/ObjectStore/ObjectStoreTestCase.php index c5580e4a2bf..03e7b9545e0 100644 --- a/tests/lib/Files/ObjectStore/ObjectStoreTest.php +++ b/tests/lib/Files/ObjectStore/ObjectStoreTestCase.php @@ -1,35 +1,23 @@ <?php /** - * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\ObjectStore; +use OCP\Files\ObjectStore\IObjectStore; use Test\TestCase; -abstract class ObjectStoreTest extends TestCase { +abstract class ObjectStoreTestCase extends TestCase { /** @var string[] */ private $cleanup = []; + private $instance = null; + /** - * @return \OCP\Files\ObjectStore\IObjectStore + * @return IObjectStore */ abstract protected function getInstance(); @@ -37,13 +25,20 @@ abstract class ObjectStoreTest extends TestCase { $this->cleanup[] = $urn; } - public function tearDown(): void { - parent::tearDown(); + public function setUp(): void { + parent::setUp(); - $instance = $this->getInstance(); - foreach ($this->cleanup as $urn) { - $instance->deleteObject($urn); + $this->instance = $this->getInstance(); + } + + public function tearDown(): void { + if ($this->instance) { + foreach ($this->cleanup as $urn) { + $this->instance->deleteObject($urn); + } } + + parent::tearDown(); } protected function stringToStream($data) { @@ -53,7 +48,7 @@ abstract class ObjectStoreTest extends TestCase { return $stream; } - public function testWriteRead() { + public function testWriteRead(): void { $stream = $this->stringToStream('foobar'); $instance = $this->getInstance(); @@ -66,7 +61,7 @@ abstract class ObjectStoreTest extends TestCase { $this->assertEquals('foobar', stream_get_contents($result)); } - public function testDelete() { + public function testDelete(): void { $stream = $this->stringToStream('foobar'); $instance = $this->getInstance(); @@ -85,7 +80,7 @@ abstract class ObjectStoreTest extends TestCase { } } - public function testReadNonExisting() { + public function testReadNonExisting(): void { $instance = $this->getInstance(); try { @@ -97,7 +92,7 @@ abstract class ObjectStoreTest extends TestCase { } } - public function testDeleteNonExisting() { + public function testDeleteNonExisting(): void { $instance = $this->getInstance(); try { @@ -109,7 +104,7 @@ abstract class ObjectStoreTest extends TestCase { } } - public function testExists() { + public function testExists(): void { $stream = $this->stringToStream('foobar'); $instance = $this->getInstance(); @@ -124,7 +119,7 @@ abstract class ObjectStoreTest extends TestCase { $this->assertFalse($instance->objectExists('2')); } - public function testCopy() { + public function testCopy(): void { $this->cleanupAfter('source'); $this->cleanupAfter('target'); @@ -143,7 +138,7 @@ abstract class ObjectStoreTest extends TestCase { $this->assertEquals('foobar', stream_get_contents($instance->readObject('target'))); } - public function testFseekSize() { + public function testFseekSize(): void { $instance = $this->getInstance(); $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt'; diff --git a/tests/lib/Files/ObjectStore/PrimaryObjectStoreConfigTest.php b/tests/lib/Files/ObjectStore/PrimaryObjectStoreConfigTest.php new file mode 100644 index 00000000000..b60b7ca4f83 --- /dev/null +++ b/tests/lib/Files/ObjectStore/PrimaryObjectStoreConfigTest.php @@ -0,0 +1,285 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2025 Robin Appelman <robin@icewind.nl> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace lib\Files\ObjectStore; + +use OC\Files\ObjectStore\PrimaryObjectStoreConfig; +use OC\Files\ObjectStore\StorageObjectStore; +use OCP\App\IAppManager; +use OCP\IConfig; +use OCP\IUser; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class PrimaryObjectStoreConfigTest extends TestCase { + private array $systemConfig = []; + private array $userConfig = []; + private IConfig&MockObject $config; + private IAppManager&MockObject $appManager; + private PrimaryObjectStoreConfig $objectStoreConfig; + + protected function setUp(): void { + parent::setUp(); + + $this->systemConfig = []; + $this->config = $this->createMock(IConfig::class); + $this->appManager = $this->createMock(IAppManager::class); + $this->config->method('getSystemValue') + ->willReturnCallback(function ($key, $default = '') { + if (isset($this->systemConfig[$key])) { + return $this->systemConfig[$key]; + } else { + return $default; + } + }); + $this->config->method('getUserValue') + ->willReturnCallback(function ($userId, $appName, $key, $default = '') { + if (isset($this->userConfig[$userId][$appName][$key])) { + return $this->userConfig[$userId][$appName][$key]; + } else { + return $default; + } + }); + $this->config->method('setUserValue') + ->willReturnCallback(function ($userId, $appName, $key, $value) { + $this->userConfig[$userId][$appName][$key] = $value; + }); + + $this->objectStoreConfig = new PrimaryObjectStoreConfig($this->config, $this->appManager); + } + + private function getUser(string $uid): IUser { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn($uid); + return $user; + } + + private function setConfig(string $key, $value) { + $this->systemConfig[$key] = $value; + } + + public function testNewUserGetsDefault() { + $this->setConfig('objectstore', [ + 'default' => 'server1', + 'server1' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server1', + ], + ], + ]); + + $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test')); + $this->assertEquals('server1', $result['arguments']['host']); + + $this->assertEquals('server1', $this->config->getUserValue('test', 'homeobjectstore', 'objectstore', null)); + } + + public function testExistingUserKeepsStorage() { + // setup user with `server1` as storage + $this->testNewUserGetsDefault(); + + $this->setConfig('objectstore', [ + 'default' => 'server2', + 'server1' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server1', + ], + ], + 'server2' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server2', + ], + ], + ]); + + $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test')); + $this->assertEquals('server1', $result['arguments']['host']); + + $this->assertEquals('server1', $this->config->getUserValue('test', 'homeobjectstore', 'objectstore', null)); + + $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('other-user')); + $this->assertEquals('server2', $result['arguments']['host']); + } + + public function testNestedAliases() { + $this->setConfig('objectstore', [ + 'default' => 'a1', + 'a1' => 'a2', + 'a2' => 'server1', + 'server1' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server1', + ], + ], + ]); + $this->assertEquals('server1', $this->objectStoreConfig->resolveAlias('default')); + } + + public function testMultibucketChangedConfig() { + $this->setConfig('objectstore', [ + 'default' => 'server1', + 'server1' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server1', + 'multibucket' => true, + 'num_buckets' => 8, + 'bucket' => 'bucket-' + ], + ], + ]); + + $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test')); + $this->assertEquals('server1', $result['arguments']['host']); + $this->assertEquals('bucket-7', $result['arguments']['bucket']); + + $this->setConfig('objectstore', [ + 'default' => 'server1', + 'server1' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server1', + 'multibucket' => true, + 'num_buckets' => 64, + 'bucket' => 'bucket-' + ], + ], + ]); + + $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test')); + $this->assertEquals('server1', $result['arguments']['host']); + $this->assertEquals('bucket-7', $result['arguments']['bucket']); + + $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test-foo')); + $this->assertEquals('server1', $result['arguments']['host']); + $this->assertEquals('bucket-40', $result['arguments']['bucket']); + + $this->setConfig('objectstore', [ + 'default' => 'server2', + 'server1' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server1', + 'multibucket' => true, + 'num_buckets' => 64, + 'bucket' => 'bucket-' + ], + ], + 'server2' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server2', + 'multibucket' => true, + 'num_buckets' => 16, + 'bucket' => 'bucket-' + ], + ], + ]); + + $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test')); + $this->assertEquals('server1', $result['arguments']['host']); + $this->assertEquals('bucket-7', $result['arguments']['bucket']); + + $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test-bar')); + $this->assertEquals('server2', $result['arguments']['host']); + $this->assertEquals('bucket-4', $result['arguments']['bucket']); + } + + public function testMultibucketOldConfig() { + $this->setConfig('objectstore_multibucket', [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server1', + 'multibucket' => true, + 'num_buckets' => 8, + 'bucket' => 'bucket-' + ], + ]); + $configs = $this->objectStoreConfig->getObjectStoreConfigs(); + $this->assertEquals([ + 'default' => 'server1', + 'root' => 'server1', + 'server1' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server1', + 'multibucket' => true, + 'num_buckets' => 8, + 'bucket' => 'bucket-' + ], + ], + ], $configs); + } + + public function testSingleObjectStore() { + $this->setConfig('objectstore', [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server1', + ], + ]); + $configs = $this->objectStoreConfig->getObjectStoreConfigs(); + $this->assertEquals([ + 'default' => 'server1', + 'root' => 'server1', + 'server1' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server1', + 'multibucket' => false, + ], + ], + ], $configs); + } + + public function testRoot() { + $this->setConfig('objectstore', [ + 'default' => 'server1', + 'server1' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server1', + ], + ], + 'server2' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server2', + ], + ], + ]); + + $result = $this->objectStoreConfig->getObjectStoreConfigForRoot(); + $this->assertEquals('server1', $result['arguments']['host']); + + $this->setConfig('objectstore', [ + 'default' => 'server1', + 'root' => 'server2', + 'server1' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server1', + ], + ], + 'server2' => [ + 'class' => StorageObjectStore::class, + 'arguments' => [ + 'host' => 'server2', + ], + ], + ]); + + $result = $this->objectStoreConfig->getObjectStoreConfigForRoot(); + $this->assertEquals('server2', $result['arguments']['host']); + } +} diff --git a/tests/lib/Files/ObjectStore/S3Test.php b/tests/lib/Files/ObjectStore/S3Test.php index fd451dc3c01..2915ada0aab 100644 --- a/tests/lib/Files/ObjectStore/S3Test.php +++ b/tests/lib/Files/ObjectStore/S3Test.php @@ -1,31 +1,19 @@ <?php + /** - * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\ObjectStore; use Icewind\Streams\Wrapper; use OC\Files\ObjectStore\S3; +use OCP\IConfig; +use OCP\Server; class MultiPartUploadS3 extends S3 { - public function writeObject($urn, $stream, string $mimetype = null) { + public function writeObject($urn, $stream, ?string $mimetype = null) { $this->getConnection()->upload($this->bucket, $urn, $stream, 'private', [ 'mup_threshold' => 1, ]); @@ -59,7 +47,7 @@ class NonSeekableStream extends Wrapper { /** * @group PRIMARY-s3 */ -class S3Test extends ObjectStoreTest { +class S3Test extends ObjectStoreTestCase { public function setUp(): void { parent::setUp(); $s3 = $this->getInstance(); @@ -67,7 +55,7 @@ class S3Test extends ObjectStoreTest { } protected function getInstance() { - $config = \OC::$server->getConfig()->getSystemValue('objectstore'); + $config = Server::get(IConfig::class)->getSystemValue('objectstore'); if (!is_array($config) || $config['class'] !== S3::class) { $this->markTestSkipped('objectstore not configured for s3'); } @@ -75,7 +63,7 @@ class S3Test extends ObjectStoreTest { return new S3($config['arguments']); } - public function testUploadNonSeekable() { + public function testUploadNonSeekable(): void { $this->cleanupAfter('multiparttest'); $s3 = $this->getInstance(); @@ -87,7 +75,7 @@ class S3Test extends ObjectStoreTest { $this->assertEquals(file_get_contents(__FILE__), stream_get_contents($result)); } - public function testSeek() { + public function testSeek(): void { $this->cleanupAfter('seek'); $data = file_get_contents(__FILE__); @@ -106,29 +94,30 @@ class S3Test extends ObjectStoreTest { } public function assertNoUpload($objectUrn) { + /** @var \OC\Files\ObjectStore\S3 */ $s3 = $this->getInstance(); $s3client = $s3->getConnection(); $uploads = $s3client->listMultipartUploads([ 'Bucket' => $s3->getBucket(), 'Prefix' => $objectUrn, ]); - $this->assertArrayNotHasKey('Uploads', $uploads); + $this->assertArrayNotHasKey('Uploads', $uploads, 'Assert is not uploaded'); } - public function testEmptyUpload() { + public function testEmptyUpload(): void { $s3 = $this->getInstance(); - $emptyStream = fopen("php://memory", "r"); - fwrite($emptyStream, null); + $emptyStream = fopen('php://memory', 'r'); + fwrite($emptyStream, ''); $s3->writeObject('emptystream', $emptyStream); $this->assertNoUpload('emptystream'); - $this->assertTrue($s3->objectExists('emptystream')); + $this->assertTrue($s3->objectExists('emptystream'), 'Object exists on S3'); $thrown = false; try { - self::assertFalse($s3->readObject('emptystream')); + self::assertFalse($s3->readObject('emptystream'), 'Reading empty stream object should return false'); } catch (\Exception $e) { // An exception is expected here since 0 byte files are wrapped // to be read from an empty memory stream in the ObjectStoreStorage @@ -140,14 +129,18 @@ class S3Test extends ObjectStoreTest { } /** File size to upload in bytes */ - public function dataFileSizes() { + public static function dataFileSizes(): array { return [ [1000000], [2000000], [5242879], [5242880], [5242881], [10000000] ]; } - /** @dataProvider dataFileSizes */ - public function testFileSizes($size) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataFileSizes')] + public function testFileSizes($size): void { + if (str_starts_with(PHP_VERSION, '8.3') && getenv('CI')) { + $this->markTestSkipped('Test is unreliable and skipped on 8.3'); + } + $this->cleanupAfter('testfilesizes'); $s3 = $this->getInstance(); @@ -163,20 +156,20 @@ class S3Test extends ObjectStoreTest { $s3->writeObject('testfilesizes', $sourceStream); $this->assertNoUpload('testfilesizes'); - self::assertTrue($s3->objectExists('testfilesizes')); + self::assertTrue($s3->objectExists('testfilesizes'), 'Object exists on S3'); $result = $s3->readObject('testfilesizes'); // compare first 100 bytes - self::assertEquals(str_repeat('A', 100), fread($result, 100)); + self::assertEquals(str_repeat('A', 100), fread($result, 100), 'Compare first 100 bytes'); - // compare 100 bytes + // compare last 100 bytes fseek($result, $size - 100); - self::assertEquals(str_repeat('A', 100), fread($result, 100)); + self::assertEquals(str_repeat('A', 100), fread($result, 100), 'Compare last 100 bytes'); // end of file reached fseek($result, $size); - self::assertTrue(feof($result)); + self::assertTrue(feof($result), 'End of file reached'); $this->assertNoUpload('testfilesizes'); } diff --git a/tests/lib/Files/ObjectStore/SwiftTest.php b/tests/lib/Files/ObjectStore/SwiftTest.php index 1ea55a84628..3f919c0dd48 100644 --- a/tests/lib/Files/ObjectStore/SwiftTest.php +++ b/tests/lib/Files/ObjectStore/SwiftTest.php @@ -1,41 +1,35 @@ <?php + /** - * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * 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\ObjectStore; use OC\Files\ObjectStore\Swift; +use OCP\Files\ObjectStore\IObjectStore; +use OCP\IConfig; +use OCP\Server; /** * @group PRIMARY-swift */ -class SwiftTest extends ObjectStoreTest { +class SwiftTest extends ObjectStoreTestCase { /** - * @return \OCP\Files\ObjectStore\IObjectStore + * @return IObjectStore */ protected function getInstance() { - $config = \OC::$server->getConfig()->getSystemValue('objectstore'); + $config = Server::get(IConfig::class)->getSystemValue('objectstore'); if (!is_array($config) || $config['class'] !== 'OC\\Files\\ObjectStore\\Swift') { $this->markTestSkipped('objectstore not configured for swift'); } return new Swift($config['arguments']); } + + public function testFseekSize(): void { + $this->markTestSkipped('Swift does not support seeking at the moment'); + } } diff --git a/tests/lib/Files/PathVerificationTest.php b/tests/lib/Files/PathVerificationTest.php index ededab14d0b..e13dbe4f207 100644 --- a/tests/lib/Files/PathVerificationTest.php +++ b/tests/lib/Files/PathVerificationTest.php @@ -1,15 +1,18 @@ <?php + /** - * Copyright (c) 2015 Thomas Müller <deepdiver@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. */ + * 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; use OC\Files\Storage\Local; use OC\Files\View; use OCP\Files\InvalidPathException; +use OCP\IDBConnection; +use OCP\Server; /** * Class PathVerificationTest @@ -20,7 +23,7 @@ use OCP\Files\InvalidPathException; */ class PathVerificationTest extends \Test\TestCase { /** - * @var \OC\Files\View + * @var View */ private $view; @@ -30,43 +33,39 @@ class PathVerificationTest extends \Test\TestCase { } - public function testPathVerificationFileNameTooLong() { - $this->expectException(\OCP\Files\InvalidPathException::class); - $this->expectExceptionMessage('File name is too long'); + public function testPathVerificationFileNameTooLong(): void { + $this->expectException(InvalidPathException::class); + $this->expectExceptionMessage('Filename is too long'); $fileName = str_repeat('a', 500); $this->view->verifyPath('', $fileName); } - /** - * @dataProvider providesEmptyFiles - */ - public function testPathVerificationEmptyFileName($fileName) { - $this->expectException(\OCP\Files\InvalidPathException::class); + #[\PHPUnit\Framework\Attributes\DataProvider('providesEmptyFiles')] + public function testPathVerificationEmptyFileName($fileName): void { + $this->expectException(InvalidPathException::class); $this->expectExceptionMessage('Empty filename is not allowed'); $this->view->verifyPath('', $fileName); } - public function providesEmptyFiles() { + public static function providesEmptyFiles(): array { return [ [''], [' '], ]; } - /** - * @dataProvider providesDotFiles - */ - public function testPathVerificationDotFiles($fileName) { - $this->expectException(\OCP\Files\InvalidPathException::class); + #[\PHPUnit\Framework\Attributes\DataProvider('providesDotFiles')] + public function testPathVerificationDotFiles($fileName): void { + $this->expectException(InvalidPathException::class); $this->expectExceptionMessage('Dot files are not allowed'); $this->view->verifyPath('', $fileName); } - public function providesDotFiles() { + public static function providesDotFiles(): array { return [ ['.'], ['..'], @@ -79,11 +78,9 @@ class PathVerificationTest extends \Test\TestCase { ]; } - /** - * @dataProvider providesAstralPlane - */ - public function testPathVerificationAstralPlane($fileName) { - $connection = \OC::$server->getDatabaseConnection(); + #[\PHPUnit\Framework\Attributes\DataProvider('providesAstralPlane')] + public function testPathVerificationAstralPlane($fileName): void { + $connection = Server::get(IDBConnection::class); if (!$connection->supports4ByteText()) { $this->expectException(InvalidPathException::class); @@ -95,7 +92,7 @@ class PathVerificationTest extends \Test\TestCase { $this->view->verifyPath('', $fileName); } - public function providesAstralPlane() { + public static function providesAstralPlane(): array { return [ // this is the monkey emoji - http://en.wikipedia.org/w/index.php?title=%F0%9F%90%B5&redirect=no ['🐵'], @@ -106,61 +103,8 @@ class PathVerificationTest extends \Test\TestCase { ]; } - /** - * @dataProvider providesInvalidCharsPosix - */ - public function testPathVerificationInvalidCharsPosix($fileName) { - $this->expectException(\OCP\Files\InvalidCharacterInPathException::class); - - $storage = new Local(['datadir' => '']); - - $fileName = " 123{$fileName}456 "; - self::invokePrivate($storage, 'verifyPosixPath', [$fileName]); - } - - public function providesInvalidCharsPosix() { - return [ - [\chr(0)], - [\chr(1)], - [\chr(2)], - [\chr(3)], - [\chr(4)], - [\chr(5)], - [\chr(6)], - [\chr(7)], - [\chr(8)], - [\chr(9)], - [\chr(10)], - [\chr(11)], - [\chr(12)], - [\chr(13)], - [\chr(14)], - [\chr(15)], - [\chr(16)], - [\chr(17)], - [\chr(18)], - [\chr(19)], - [\chr(20)], - [\chr(21)], - [\chr(22)], - [\chr(23)], - [\chr(24)], - [\chr(25)], - [\chr(26)], - [\chr(27)], - [\chr(28)], - [\chr(29)], - [\chr(30)], - [\chr(31)], - ['/'], - ['\\'], - ]; - } - - /** - * @dataProvider providesValidPosixPaths - */ - public function testPathVerificationValidPaths($fileName) { + #[\PHPUnit\Framework\Attributes\DataProvider('providesValidPosixPaths')] + public function testPathVerificationValidPaths($fileName): void { $storage = new Local(['datadir' => '']); self::invokePrivate($storage, 'verifyPosixPath', [$fileName]); @@ -168,7 +112,7 @@ class PathVerificationTest extends \Test\TestCase { $this->addToAssertionCount(1); } - public function providesValidPosixPaths() { + public static function providesValidPosixPaths(): array { return [ ['simple'], ['simple.txt'], diff --git a/tests/lib/Files/Search/QueryOptimizer/CombinedTests.php b/tests/lib/Files/Search/QueryOptimizer/CombinedTests.php new file mode 100644 index 00000000000..665224cb63e --- /dev/null +++ b/tests/lib/Files/Search/QueryOptimizer/CombinedTests.php @@ -0,0 +1,195 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace Test\Files\Search\QueryOptimizer; + +use OC\Files\Search\QueryOptimizer\QueryOptimizer; +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use Test\TestCase; + +class CombinedTests extends TestCase { + private QueryOptimizer $optimizer; + + protected function setUp(): void { + parent::setUp(); + + $this->optimizer = new QueryOptimizer(); + } + + public function testBasicOrOfAnds(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'), + ]) + ] + ); + $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 1 and path eq "asd"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + + $this->assertEquals('(storage eq 1 and path in ["foo","bar","asd"])', $operator->__toString()); + } + + public function testComplexSearchPattern1(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 2), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '201'), + new SearchComparison(ISearchComparison::COMPARE_LIKE, 'path', '201/%'), + ]), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '301'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 4), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '401'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '302'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 4), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '402'), + ]), + ] + ); + $this->assertEquals('((storage eq 1) or (storage eq 2 and (path eq "201" or path like "201\/%")) or (storage eq 3 and path eq "301") or (storage eq 4 and path eq "401") or (storage eq 3 and path eq "302") or (storage eq 4 and path eq "402"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + + $this->assertEquals('(storage eq 1 or (storage eq 2 and (path eq "201" or path like "201\/%")) or (storage eq 3 and path in ["301","302"]) or (storage eq 4 and path in ["401","402"]))', $operator->__toString()); + } + + public function testComplexSearchPattern2(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 2), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '201'), + new SearchComparison(ISearchComparison::COMPARE_LIKE, 'path', '201/%'), + ]), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '301'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 4), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '401'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '302'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 4), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '402'), + ]), + ] + ); + $this->assertEquals('(storage eq 1 or (storage eq 2 and (path eq "201" or path like "201\/%")) or (storage eq 3 and path eq "301") or (storage eq 4 and path eq "401") or (storage eq 3 and path eq "302") or (storage eq 4 and path eq "402"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + + $this->assertEquals('(storage eq 1 or (storage eq 2 and (path eq "201" or path like "201\/%")) or (storage eq 3 and path in ["301","302"]) or (storage eq 4 and path in ["401","402"]))', $operator->__toString()); + } + + public function testApplySearchConstraints1(): void { + $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'image/png'), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'image/jpeg'), + ]), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'files'), + new SearchComparison(ISearchComparison::COMPARE_LIKE, 'path', 'files/%'), + ]), + ]), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 2), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'files/301'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'files/302'), + ]), + ]), + ]); + $this->assertEquals('(((mimetype eq "image\/png" or mimetype eq "image\/jpeg")) and ((storage eq 1 and (path eq "files" or path like "files\/%")) or storage eq 2 or (storage eq 3 and path eq "files\/301") or (storage eq 3 and path eq "files\/302")))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + + $this->assertEquals('(mimetype in ["image\/png","image\/jpeg"] and ((storage eq 1 and (path eq "files" or path like "files\/%")) or storage eq 2 or (storage eq 3 and path in ["files\/301","files\/302"])))', $operator->__toString()); + } + + public function testApplySearchConstraints2(): void { + $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'image/png'), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'image/jpeg'), + ]), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'files'), + new SearchComparison(ISearchComparison::COMPARE_LIKE, 'path', 'files/%'), + ]), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 2), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'files/301'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'files/302'), + ]), + ]), + ]); + $this->assertEquals('(((mimetype eq "image\/png" or mimetype eq "image\/jpeg")) and ((storage eq 1 and (path eq "files" or path like "files\/%")) or (storage eq 2) or (storage eq 3 and path eq "files\/301") or (storage eq 3 and path eq "files\/302")))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + + $this->assertEquals('(mimetype in ["image\/png","image\/jpeg"] and ((storage eq 1 and (path eq "files" or path like "files\/%")) or storage eq 2 or (storage eq 3 and path in ["files\/301","files\/302"])))', $operator->__toString()); + } +} diff --git a/tests/lib/Files/Search/QueryOptimizer/FlattenNestedBoolTest.php b/tests/lib/Files/Search/QueryOptimizer/FlattenNestedBoolTest.php new file mode 100644 index 00000000000..1d43541a5a0 --- /dev/null +++ b/tests/lib/Files/Search/QueryOptimizer/FlattenNestedBoolTest.php @@ -0,0 +1,46 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace Test\Files\Search\QueryOptimizer; + +use OC\Files\Search\QueryOptimizer\FlattenNestedBool; +use OC\Files\Search\QueryOptimizer\FlattenSingleArgumentBinaryOperation; +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use Test\TestCase; + +class FlattenNestedBoolTest extends TestCase { + private $optimizer; + private $simplifier; + + protected function setUp(): void { + parent::setUp(); + + $this->optimizer = new FlattenNestedBool(); + $this->simplifier = new FlattenSingleArgumentBinaryOperation(); + } + + public function testOrs(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'), + ]) + ] + ); + $this->assertEquals('(path eq "foo" or (path eq "bar" or path eq "asd"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('(path eq "foo" or path eq "bar" or path eq "asd")', $operator->__toString()); + } +} diff --git a/tests/lib/Files/Search/QueryOptimizer/MergeDistributiveOperationsTest.php b/tests/lib/Files/Search/QueryOptimizer/MergeDistributiveOperationsTest.php new file mode 100644 index 00000000000..9aaa7030aac --- /dev/null +++ b/tests/lib/Files/Search/QueryOptimizer/MergeDistributiveOperationsTest.php @@ -0,0 +1,164 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace Test\Files\Search\QueryOptimizer; + +use OC\Files\Search\QueryOptimizer\FlattenSingleArgumentBinaryOperation; +use OC\Files\Search\QueryOptimizer\MergeDistributiveOperations; +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use Test\TestCase; + +class MergeDistributiveOperationsTest extends TestCase { + private $optimizer; + private $simplifier; + + protected function setUp(): void { + parent::setUp(); + + $this->optimizer = new MergeDistributiveOperations(); + $this->simplifier = new FlattenSingleArgumentBinaryOperation(); + } + + public function testBasicOrOfAnds(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'), + ]) + ] + ); + $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 1 and path eq "asd"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('(storage eq 1 and (path eq "foo" or path eq "bar" or path eq "asd"))', $operator->__toString()); + } + + public function testDontTouchIfNotSame(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 2), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'), + ]) + ] + ); + $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 2 and path eq "bar") or (storage eq 3 and path eq "asd"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 2 and path eq "bar") or (storage eq 3 and path eq "asd"))', $operator->__toString()); + } + + public function testMergePartial(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 2), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'), + ]) + ] + ); + $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 2 and path eq "asd"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('((storage eq 1 and (path eq "foo" or path eq "bar")) or (storage eq 2 and path eq "asd"))', $operator->__toString()); + } + + public function testOptimizeInside(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_AND, + [ + new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'), + ]) + ] + ), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'text') + ] + ); + $this->assertEquals('(((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 1 and path eq "asd")) and mimetype eq "text")', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('((storage eq 1 and (path eq "foo" or path eq "bar" or path eq "asd")) and mimetype eq "text")', $operator->__toString()); + } + + public function testMoveInnerOperations(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'), + new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN, 'size', '100'), + ]) + ] + ); + $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 1 and path eq "asd" and size gt "100"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('(storage eq 1 and (path eq "foo" or path eq "bar" or (path eq "asd" and size gt "100")))', $operator->__toString()); + } +} diff --git a/tests/lib/Files/Search/QueryOptimizer/OrEqualsToInTest.php b/tests/lib/Files/Search/QueryOptimizer/OrEqualsToInTest.php new file mode 100644 index 00000000000..d51fe3d0d16 --- /dev/null +++ b/tests/lib/Files/Search/QueryOptimizer/OrEqualsToInTest.php @@ -0,0 +1,124 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace Test\Files\Search\QueryOptimizer; + +use OC\Files\Search\QueryOptimizer\FlattenSingleArgumentBinaryOperation; +use OC\Files\Search\QueryOptimizer\OrEqualsToIn; +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use Test\TestCase; + +class OrEqualsToInTest extends TestCase { + private $optimizer; + private $simplifier; + + protected function setUp(): void { + parent::setUp(); + + $this->optimizer = new OrEqualsToIn(); + $this->simplifier = new FlattenSingleArgumentBinaryOperation(); + } + + public function testOrs(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'), + ] + ); + $this->assertEquals('(path eq "foo" or path eq "bar" or path eq "asd")', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('path in ["foo","bar","asd"]', $operator->__toString()); + } + + public function testOrsMultipleFields(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'fileid', 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'fileid', 2), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'asd'), + ] + ); + $this->assertEquals('(path eq "foo" or path eq "bar" or fileid eq 1 or fileid eq 2 or mimetype eq "asd")', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('(path in ["foo","bar"] or fileid in [1,2] or mimetype eq "asd")', $operator->__toString()); + } + + public function testPreserveHints(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'), + ] + ); + foreach ($operator->getArguments() as $argument) { + $argument->setQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, false); + } + $this->assertEquals('(path eq "foo" or path eq "bar" or path eq "asd")', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('path in ["foo","bar","asd"]', $operator->__toString()); + $this->assertEquals(false, $operator->getQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, true)); + } + + public function testOrSomeEq(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'), + new SearchComparison(ISearchComparison::COMPARE_LIKE, 'path', 'foo%'), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'), + ] + ); + $this->assertEquals('(path eq "foo" or path like "foo%" or path eq "bar")', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('(path in ["foo","bar"] or path like "foo%")', $operator->__toString()); + } + + public function testOrsInside(): void { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_AND, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'text'), + new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'), + ] + ) + ] + ); + $this->assertEquals('(mimetype eq "text" and (path eq "foo" or path eq "bar" or path eq "asd"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('(mimetype eq "text" and path in ["foo","bar","asd"])', $operator->__toString()); + } +} diff --git a/tests/lib/Files/Search/SearchIntegrationTest.php b/tests/lib/Files/Search/SearchIntegrationTest.php new file mode 100644 index 00000000000..e3e99bbfadd --- /dev/null +++ b/tests/lib/Files/Search/SearchIntegrationTest.php @@ -0,0 +1,48 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace Test\Files\Search; + +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OC\Files\Search\SearchQuery; +use OC\Files\Storage\Temporary; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use Test\TestCase; + +/** + * @group DB + */ +class SearchIntegrationTest extends TestCase { + private $cache; + private $storage; + + protected function setUp(): void { + parent::setUp(); + + $this->storage = new Temporary([]); + $this->cache = $this->storage->getCache(); + $this->storage->getScanner()->scan(''); + } + + + public function testThousandAndOneFilters(): void { + $id = $this->cache->put('file10', ['size' => 1, 'mtime' => 50, 'mimetype' => 'foo/folder']); + + $comparisons = []; + for ($i = 1; $i <= 1001; $i++) { + $comparisons[] = new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', "file$i"); + } + $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $comparisons); + $query = new SearchQuery($operator, 10, 0, []); + + $results = $this->cache->searchQuery($query); + + $this->assertCount(1, $results); + $this->assertEquals($id, $results[0]->getId()); + } +} diff --git a/tests/lib/Files/SimpleFS/InMemoryFileTest.php b/tests/lib/Files/SimpleFS/InMemoryFileTest.php index 0ba1a9ddc9f..43587acdce9 100644 --- a/tests/lib/Files/SimpleFS/InMemoryFileTest.php +++ b/tests/lib/Files/SimpleFS/InMemoryFileTest.php @@ -3,23 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu> - * - * @author Michael Weimann <mail@michael-weimann.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace Test\File\SimpleFS; @@ -59,7 +44,7 @@ class InMemoryFileTest extends TestCase { * * @return void */ - public function testPutContent() { + public function testPutContent(): void { $this->testPdf->putContent('test'); self::assertEquals('test', $this->testPdf->getContent()); } @@ -69,7 +54,7 @@ class InMemoryFileTest extends TestCase { * * @return void */ - public function testDelete() { + public function testDelete(): void { $this->testPdf->delete(); // assert true, otherwise phpunit complains about not doing any assert self::assertTrue(true); @@ -80,7 +65,7 @@ class InMemoryFileTest extends TestCase { * * @return void */ - public function testGetName() { + public function testGetName(): void { self::assertEquals('test.pdf', $this->testPdf->getName()); } @@ -89,7 +74,7 @@ class InMemoryFileTest extends TestCase { * * @return void */ - public function testGetSize() { + public function testGetSize(): void { self::assertEquals(7083, $this->testPdf->getSize()); } @@ -98,7 +83,7 @@ class InMemoryFileTest extends TestCase { * * @return void */ - public function testGetContent() { + public function testGetContent(): void { self::assertEquals( file_get_contents(__DIR__ . '/../../../data/test.pdf'), $this->testPdf->getContent() @@ -110,7 +95,7 @@ class InMemoryFileTest extends TestCase { * * @return void */ - public function testGetMTime() { + public function testGetMTime(): void { self::assertTrue(is_int($this->testPdf->getMTime())); } @@ -119,7 +104,7 @@ class InMemoryFileTest extends TestCase { * * @return void */ - public function testGetMimeType() { + public function testGetMimeType(): void { self::assertEquals('application/pdf', $this->testPdf->getMimeType()); } @@ -129,7 +114,7 @@ class InMemoryFileTest extends TestCase { * * @return void */ - public function testRead() { + public function testRead(): void { self::expectException(NotPermittedException::class); $this->testPdf->read(); } @@ -139,7 +124,7 @@ class InMemoryFileTest extends TestCase { * * @return void */ - public function testWrite() { + public function testWrite(): void { self::expectException(NotPermittedException::class); $this->testPdf->write(); } diff --git a/tests/lib/Files/SimpleFS/SimpleFileTest.php b/tests/lib/Files/SimpleFS/SimpleFileTest.php index def570edf99..6ce5ddad351 100644 --- a/tests/lib/Files/SimpleFS/SimpleFileTest.php +++ b/tests/lib/Files/SimpleFS/SimpleFileTest.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\File\SimpleFS; @@ -42,7 +26,7 @@ class SimpleFileTest extends \Test\TestCase { $this->simpleFile = new SimpleFile($this->file); } - public function testGetName() { + public function testGetName(): void { $this->file->expects($this->once()) ->method('getName') ->willReturn('myname'); @@ -50,7 +34,7 @@ class SimpleFileTest extends \Test\TestCase { $this->assertEquals('myname', $this->simpleFile->getName()); } - public function testGetSize() { + public function testGetSize(): void { $this->file->expects($this->once()) ->method('getSize') ->willReturn(42); @@ -58,7 +42,7 @@ class SimpleFileTest extends \Test\TestCase { $this->assertEquals(42, $this->simpleFile->getSize()); } - public function testGetETag() { + public function testGetETag(): void { $this->file->expects($this->once()) ->method('getETag') ->willReturn('etag'); @@ -66,7 +50,7 @@ class SimpleFileTest extends \Test\TestCase { $this->assertEquals('etag', $this->simpleFile->getETag()); } - public function testGetMTime() { + public function testGetMTime(): void { $this->file->expects($this->once()) ->method('getMTime') ->willReturn(101); @@ -74,7 +58,7 @@ class SimpleFileTest extends \Test\TestCase { $this->assertEquals(101, $this->simpleFile->getMTime()); } - public function testGetContent() { + public function testGetContent(): void { $this->file->expects($this->once()) ->method('getContent') ->willReturn('foo'); @@ -82,7 +66,7 @@ class SimpleFileTest extends \Test\TestCase { $this->assertEquals('foo', $this->simpleFile->getContent()); } - public function testPutContent() { + public function testPutContent(): void { $this->file->expects($this->once()) ->method('putContent') ->with($this->equalTo('bar')); @@ -90,14 +74,14 @@ class SimpleFileTest extends \Test\TestCase { $this->simpleFile->putContent('bar'); } - public function testDelete() { + public function testDelete(): void { $this->file->expects($this->once()) ->method('delete'); $this->simpleFile->delete(); } - public function testGetMimeType() { + public function testGetMimeType(): void { $this->file->expects($this->once()) ->method('getMimeType') ->willReturn('app/awesome'); @@ -105,7 +89,7 @@ class SimpleFileTest extends \Test\TestCase { $this->assertEquals('app/awesome', $this->simpleFile->getMimeType()); } - public function testGetContentInvalidAppData() { + public function testGetContentInvalidAppData(): void { $this->file->method('getContent') ->willReturn(false); $this->file->method('stat')->willReturn(false); @@ -124,7 +108,7 @@ class SimpleFileTest extends \Test\TestCase { $this->simpleFile->getContent(); } - public function testRead() { + public function testRead(): void { $this->file->expects($this->once()) ->method('fopen') ->with('r'); @@ -132,7 +116,7 @@ class SimpleFileTest extends \Test\TestCase { $this->simpleFile->read(); } - public function testWrite() { + public function testWrite(): void { $this->file->expects($this->once()) ->method('fopen') ->with('w'); diff --git a/tests/lib/Files/SimpleFS/SimpleFolderTest.php b/tests/lib/Files/SimpleFS/SimpleFolderTest.php index 9710b6f438b..50038b286a9 100644 --- a/tests/lib/Files/SimpleFS/SimpleFolderTest.php +++ b/tests/lib/Files/SimpleFS/SimpleFolderTest.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\File\SimpleFS; @@ -64,24 +48,24 @@ class SimpleFolderTest extends \Test\TestCase { $this->simpleFolder = new SimpleFolder($this->folder); } - public function testGetName() { + public function testGetName(): void { $this->assertEquals('test', $this->simpleFolder->getName()); } - public function testDelete() { + public function testDelete(): void { $this->assertTrue($this->parentFolder->nodeExists('test')); $this->simpleFolder->delete(); $this->assertFalse($this->parentFolder->nodeExists('test')); } - public function testFileExists() { + public function testFileExists(): void { $this->folder->newFile('exists'); $this->assertFalse($this->simpleFolder->fileExists('not-exists')); $this->assertTrue($this->simpleFolder->fileExists('exists')); } - public function testGetFile() { + public function testGetFile(): void { $this->folder->newFile('exists'); $result = $this->simpleFolder->getFile('exists'); @@ -91,7 +75,7 @@ class SimpleFolderTest extends \Test\TestCase { $this->simpleFolder->getFile('not-exists'); } - public function testNewFile() { + public function testNewFile(): void { $result = $this->simpleFolder->newFile('file'); $this->assertInstanceOf(ISimpleFile::class, $result); $this->assertFalse($this->folder->nodeExists('file')); @@ -101,7 +85,7 @@ class SimpleFolderTest extends \Test\TestCase { $this->assertEquals('bar', $result->getContent()); } - public function testGetDirectoryListing() { + public function testGetDirectoryListing(): void { $this->folder->newFile('file1'); $this->folder->newFile('file2'); @@ -111,7 +95,7 @@ class SimpleFolderTest extends \Test\TestCase { $this->assertInstanceOf(ISimpleFile::class, $result[1]); } - public function testGetFolder() { + public function testGetFolder(): void { $this->folder->newFolder('exists'); $result = $this->simpleFolder->getFolder('exists'); @@ -121,7 +105,7 @@ class SimpleFolderTest extends \Test\TestCase { $this->simpleFolder->getFolder('not-exists'); } - public function testNewFolder() { + public function testNewFolder(): void { $result = $this->simpleFolder->newFolder('folder'); $this->assertInstanceOf(ISimpleFolder::class, $result); $result->newFile('file'); diff --git a/tests/lib/Files/Storage/CommonTest.php b/tests/lib/Files/Storage/CommonTest.php index 0900765c510..c5ff6bb0b5f 100644 --- a/tests/lib/Files/Storage/CommonTest.php +++ b/tests/lib/Files/Storage/CommonTest.php @@ -1,29 +1,21 @@ <?php + /** - * ownCloud - * - * @author Robin Appelman - * @copyright 2012 Robin Appelman icewind@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Storage; use OC\Files\Storage\Wrapper\Jail; use OC\Files\Storage\Wrapper\Wrapper; +use OCP\Files; +use OCP\Files\IFilenameValidator; +use OCP\Files\InvalidCharacterInPathException; +use OCP\Files\InvalidPathException; +use OCP\ITempManager; +use OCP\Server; use PHPUnit\Framework\MockObject\MockObject; /** @@ -34,27 +26,49 @@ use PHPUnit\Framework\MockObject\MockObject; * @package Test\Files\Storage */ class CommonTest extends Storage { - /** - * @var string tmpDir - */ - private $tmpDir; + + private string $tmpDir; + private IFilenameValidator&MockObject $filenameValidator; protected function setUp(): void { parent::setUp(); - $this->tmpDir = \OC::$server->getTempManager()->getTemporaryFolder(); + $this->filenameValidator = $this->createMock(IFilenameValidator::class); + $this->overwriteService(IFilenameValidator::class, $this->filenameValidator); + $this->tmpDir = Server::get(ITempManager::class)->getTemporaryFolder(); $this->instance = new \OC\Files\Storage\CommonTest(['datadir' => $this->tmpDir]); } protected function tearDown(): void { - \OC_Helper::rmdirr($this->tmpDir); + Files::rmdirr($this->tmpDir); + $this->restoreService(IFilenameValidator::class); parent::tearDown(); } - public function testMoveFromStorageWrapped() { + public function testVerifyPath(): void { + $this->filenameValidator + ->expects($this->once()) + ->method('validateFilename') + ->with('invalid:char.txt') + ->willThrowException(new InvalidCharacterInPathException()); + $this->expectException(InvalidPathException::class); + + $this->instance->verifyPath('/', 'invalid:char.txt'); + } + + public function testVerifyPathSucceed(): void { + $this->filenameValidator + ->expects($this->once()) + ->method('validateFilename') + ->with('valid-char.txt'); + + $this->instance->verifyPath('/', 'valid-char.txt'); + } + + public function testMoveFromStorageWrapped(): void { /** @var \OC\Files\Storage\CommonTest|MockObject $instance */ $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class) - ->setMethods(['copyFromStorage', 'rmdir', 'unlink']) + ->onlyMethods(['copyFromStorage', 'rmdir', 'unlink']) ->setConstructorArgs([['datadir' => $this->tmpDir]]) ->getMock(); $instance->method('copyFromStorage') @@ -69,10 +83,10 @@ class CommonTest extends Storage { $this->assertTrue($instance->file_exists('bar.txt')); } - public function testMoveFromStorageJailed() { + public function testMoveFromStorageJailed(): void { /** @var \OC\Files\Storage\CommonTest|MockObject $instance */ $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class) - ->setMethods(['copyFromStorage', 'rmdir', 'unlink']) + ->onlyMethods(['copyFromStorage', 'rmdir', 'unlink']) ->setConstructorArgs([['datadir' => $this->tmpDir]]) ->getMock(); $instance->method('copyFromStorage') @@ -92,10 +106,10 @@ class CommonTest extends Storage { $this->assertTrue($instance->file_exists('bar.txt')); } - public function testMoveFromStorageNestedJail() { + public function testMoveFromStorageNestedJail(): void { /** @var \OC\Files\Storage\CommonTest|MockObject $instance */ $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class) - ->setMethods(['copyFromStorage', 'rmdir', 'unlink']) + ->onlyMethods(['copyFromStorage', 'rmdir', 'unlink']) ->setConstructorArgs([['datadir' => $this->tmpDir]]) ->getMock(); $instance->method('copyFromStorage') diff --git a/tests/lib/Files/Storage/CopyDirectoryTest.php b/tests/lib/Files/Storage/CopyDirectoryTest.php index 25bdb016ead..b593b7c984f 100644 --- a/tests/lib/Files/Storage/CopyDirectoryTest.php +++ b/tests/lib/Files/Storage/CopyDirectoryTest.php @@ -1,39 +1,27 @@ <?php + /** - * @author Robin Appelman <icewind@owncloud.com> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace Test\Files\Storage; +use OC\Files\Storage\PolyFill\CopyDirectory; use OC\Files\Storage\Temporary; class StorageNoRecursiveCopy extends Temporary { - public function copy($path1, $path2) { - if ($this->is_dir($path1)) { + public function copy(string $source, string $target): bool { + if ($this->is_dir($source)) { return false; } - return copy($this->getSourcePath($path1), $this->getSourcePath($path2)); + return copy($this->getSourcePath($source), $this->getSourcePath($target)); } } class CopyDirectoryStorage extends StorageNoRecursiveCopy { - use \OC\Files\Storage\PolyFill\CopyDirectory; + use CopyDirectory; } /** diff --git a/tests/lib/Files/Storage/HomeTest.php b/tests/lib/Files/Storage/HomeTest.php index d5feb40840f..84a9816cb0c 100644 --- a/tests/lib/Files/Storage/HomeTest.php +++ b/tests/lib/Files/Storage/HomeTest.php @@ -1,41 +1,28 @@ <?php + /** - * ownCloud - * - * @author Robin Appelman - * @copyright 2012 Robin Appelman icewind@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * 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\Storage; +use OC\Files\Storage\Home; use OC\User\User; +use OCP\Files; +use OCP\ITempManager; +use OCP\Server; class DummyUser extends User { - private $home; - - private $uid; - /** * @param string $uid * @param string $home */ - public function __construct($uid, $home) { - $this->uid = $uid; - $this->home = $home; + public function __construct( + private $uid, + private $home, + ) { } public function getHome() { @@ -63,39 +50,39 @@ class HomeTest extends Storage { private $userId; /** - * @var \OC\User\User $user + * @var User $user */ private $user; protected function setUp(): void { parent::setUp(); - $this->tmpDir = \OC::$server->getTempManager()->getTemporaryFolder(); + $this->tmpDir = Server::get(ITempManager::class)->getTemporaryFolder(); $this->userId = $this->getUniqueID('user_'); $this->user = new DummyUser($this->userId, $this->tmpDir); - $this->instance = new \OC\Files\Storage\Home(['user' => $this->user]); + $this->instance = new Home(['user' => $this->user]); } protected function tearDown(): void { - \OC_Helper::rmdirr($this->tmpDir); + Files::rmdirr($this->tmpDir); parent::tearDown(); } /** * Tests that the home id is in the format home::user1 */ - public function testId() { + public function testId(): void { $this->assertEquals('home::' . $this->userId, $this->instance->getId()); } /** * Tests that getCache() returns an instance of HomeCache */ - public function testGetCacheReturnsHomeCache() { + public function testGetCacheReturnsHomeCache(): void { $this->assertInstanceOf('\OC\Files\Cache\HomeCache', $this->instance->getCache()); } - public function testGetOwner() { + public function testGetOwner(): void { $this->assertEquals($this->userId, $this->instance->getOwner('')); } } diff --git a/tests/lib/Files/Storage/LocalTest.php b/tests/lib/Files/Storage/LocalTest.php index e324d2b28db..89449a51351 100644 --- a/tests/lib/Files/Storage/LocalTest.php +++ b/tests/lib/Files/Storage/LocalTest.php @@ -1,27 +1,21 @@ <?php + /** - * ownCloud - * - * @author Robin Appelman - * @copyright 2012 Robin Appelman icewind@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * 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\Storage; +use OC\Files\Storage\Local; +use OC\Files\Storage\Wrapper\Jail; +use OCP\Files; +use OCP\Files\ForbiddenException; +use OCP\Files\StorageNotAvailableException; +use OCP\ITempManager; +use OCP\Server; + /** * Class LocalTest * @@ -38,23 +32,23 @@ class LocalTest extends Storage { protected function setUp(): void { parent::setUp(); - $this->tmpDir = \OC::$server->getTempManager()->getTemporaryFolder(); - $this->instance = new \OC\Files\Storage\Local(['datadir' => $this->tmpDir]); + $this->tmpDir = Server::get(ITempManager::class)->getTemporaryFolder(); + $this->instance = new Local(['datadir' => $this->tmpDir]); } protected function tearDown(): void { - \OC_Helper::rmdirr($this->tmpDir); + Files::rmdirr($this->tmpDir); parent::tearDown(); } - public function testStableEtag() { + public function testStableEtag(): void { $this->instance->file_put_contents('test.txt', 'foobar'); $etag1 = $this->instance->getETag('test.txt'); $etag2 = $this->instance->getETag('test.txt'); $this->assertEquals($etag1, $etag2); } - public function testEtagChange() { + public function testEtagChange(): void { $this->instance->file_put_contents('test.txt', 'foo'); $this->instance->touch('test.txt', time() - 2); $etag1 = $this->instance->getETag('test.txt'); @@ -64,22 +58,22 @@ class LocalTest extends Storage { } - public function testInvalidArgumentsEmptyArray() { + public function testInvalidArgumentsEmptyArray(): void { $this->expectException(\InvalidArgumentException::class); - new \OC\Files\Storage\Local([]); + new Local([]); } - public function testInvalidArgumentsNoArray() { + public function testInvalidArgumentsNoArray(): void { $this->expectException(\InvalidArgumentException::class); - new \OC\Files\Storage\Local(null); + new Local([]); } - public function testDisallowSymlinksOutsideDatadir() { - $this->expectException(\OCP\Files\ForbiddenException::class); + public function testDisallowSymlinksOutsideDatadir(): void { + $this->expectException(ForbiddenException::class); $subDir1 = $this->tmpDir . 'sub1'; $subDir2 = $this->tmpDir . 'sub2'; @@ -89,12 +83,12 @@ class LocalTest extends Storage { symlink($subDir2, $sym); - $storage = new \OC\Files\Storage\Local(['datadir' => $subDir1]); + $storage = new Local(['datadir' => $subDir1]); $storage->file_put_contents('sym/foo', 'bar'); } - public function testDisallowSymlinksInsideDatadir() { + public function testDisallowSymlinksInsideDatadir(): void { $subDir1 = $this->tmpDir . 'sub1'; $subDir2 = $this->tmpDir . 'sub1/sub2'; $sym = $this->tmpDir . 'sub1/sym'; @@ -103,27 +97,27 @@ class LocalTest extends Storage { symlink($subDir2, $sym); - $storage = new \OC\Files\Storage\Local(['datadir' => $subDir1]); + $storage = new Local(['datadir' => $subDir1]); $storage->file_put_contents('sym/foo', 'bar'); $this->addToAssertionCount(1); } - public function testWriteUmaskFilePutContents() { + public function testWriteUmaskFilePutContents(): void { $oldMask = umask(0333); $this->instance->file_put_contents('test.txt', 'sad'); umask($oldMask); $this->assertTrue($this->instance->isUpdatable('test.txt')); } - public function testWriteUmaskMkdir() { + public function testWriteUmaskMkdir(): void { $oldMask = umask(0333); $this->instance->mkdir('test.txt'); umask($oldMask); $this->assertTrue($this->instance->isUpdatable('test.txt')); } - public function testWriteUmaskFopen() { + public function testWriteUmaskFopen(): void { $oldMask = umask(0333); $handle = $this->instance->fopen('test.txt', 'w'); fwrite($handle, 'foo'); @@ -132,11 +126,43 @@ class LocalTest extends Storage { $this->assertTrue($this->instance->isUpdatable('test.txt')); } - public function testWriteUmaskCopy() { + public function testWriteUmaskCopy(): void { $this->instance->file_put_contents('source.txt', 'sad'); $oldMask = umask(0333); $this->instance->copy('source.txt', 'test.txt'); umask($oldMask); $this->assertTrue($this->instance->isUpdatable('test.txt')); } + + public function testUnavailableExternal(): void { + $this->expectException(StorageNotAvailableException::class); + $this->instance = new Local(['datadir' => $this->tmpDir . '/unexist', 'isExternal' => true]); + } + + public function testUnavailableNonExternal(): void { + $this->instance = new Local(['datadir' => $this->tmpDir . '/unexist']); + // no exception thrown + $this->assertNotNull($this->instance); + } + + public function testMoveNestedJail(): void { + $this->instance->mkdir('foo'); + $this->instance->mkdir('foo/bar'); + $this->instance->mkdir('target'); + $this->instance->file_put_contents('foo/bar/file.txt', 'foo'); + $jail1 = new Jail([ + 'storage' => $this->instance, + 'root' => 'foo' + ]); + $jail2 = new Jail([ + 'storage' => $jail1, + 'root' => 'bar' + ]); + $jail3 = new Jail([ + 'storage' => $this->instance, + 'root' => 'target' + ]); + $jail3->moveFromStorage($jail2, 'file.txt', 'file.txt'); + $this->assertTrue($this->instance->file_exists('target/file.txt')); + } } diff --git a/tests/lib/Files/Storage/Storage.php b/tests/lib/Files/Storage/Storage.php index a646fd5fd0b..51bb5b7c8ad 100644 --- a/tests/lib/Files/Storage/Storage.php +++ b/tests/lib/Files/Storage/Storage.php @@ -1,28 +1,16 @@ <?php + /** - * ownCloud - * - * @author Robin Appelman - * @copyright 2012 Robin Appelman icewind@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * 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\Storage; use OC\Files\Cache\Watcher; +use OC\Files\Storage\Wrapper\Wrapper; +use OCP\Files\Storage\IStorage; use OCP\Files\Storage\IWriteStreamStorage; abstract class Storage extends \Test\TestCase { @@ -45,7 +33,7 @@ abstract class Storage extends \Test\TestCase { /** * the root folder of the storage should always exist, be readable and be recognized as a directory */ - public function testRoot() { + public function testRoot(): void { $this->assertTrue($this->instance->file_exists('/'), 'Root folder does not exist'); $this->assertTrue($this->instance->isReadable('/'), 'Root folder is not readable'); $this->assertTrue($this->instance->is_dir('/'), 'Root folder is not a directory'); @@ -59,14 +47,12 @@ abstract class Storage extends \Test\TestCase { /** * Check that the test() function works */ - public function testTestFunction() { + public function testTestFunction(): void { $this->assertTrue($this->instance->test()); } - /** - * @dataProvider directoryProvider - */ - public function testDirectories($directory) { + #[\PHPUnit\Framework\Attributes\DataProvider('directoryProvider')] + public function testDirectories($directory): void { $this->assertFalse($this->instance->file_exists('/' . $directory)); $this->assertTrue($this->instance->mkdir('/' . $directory)); @@ -81,7 +67,7 @@ abstract class Storage extends \Test\TestCase { $dh = $this->instance->opendir('/'); $content = []; - while ($file = readdir($dh)) { + while (($file = readdir($dh)) !== false) { if ($file != '.' and $file != '..') { $content[] = $file; } @@ -94,12 +80,13 @@ abstract class Storage extends \Test\TestCase { $dirEntry = $content[0]; unset($dirEntry['scan_permissions']); unset($dirEntry['etag']); + $this->assertLessThanOrEqual(1, abs($dirEntry['mtime'] - $this->instance->filemtime($directory))); + unset($dirEntry['mtime']); + unset($dirEntry['storage_mtime']); $this->assertEquals([ 'name' => $directory, 'mimetype' => $this->instance->getMimeType($directory), - 'mtime' => $this->instance->filemtime($directory), 'size' => -1, - 'storage_mtime' => $this->instance->filemtime($directory), 'permissions' => $this->instance->getPermissions($directory), ], $dirEntry); @@ -113,7 +100,7 @@ abstract class Storage extends \Test\TestCase { $dh = $this->instance->opendir('/'); $content = []; - while ($file = readdir($dh)) { + while (($file = readdir($dh)) !== false) { if ($file != '.' and $file != '..') { $content[] = $file; } @@ -121,7 +108,7 @@ abstract class Storage extends \Test\TestCase { $this->assertEquals([], $content); } - public function fileNameProvider() { + public static function fileNameProvider(): array { return [ ['file.txt'], [' file.txt'], @@ -132,7 +119,7 @@ abstract class Storage extends \Test\TestCase { ]; } - public function directoryProvider() { + public static function directoryProvider(): array { return [ ['folder'], [' folder'], @@ -143,7 +130,7 @@ abstract class Storage extends \Test\TestCase { ]; } - public function loremFileProvider() { + public static function loremFileProvider(): array { $root = \OC::$SERVERROOT . '/tests/data/'; return [ // small file @@ -155,10 +142,9 @@ abstract class Storage extends \Test\TestCase { /** * test the various uses of file_get_contents and file_put_contents - * - * @dataProvider loremFileProvider */ - public function testGetPutContents($sourceFile) { + #[\PHPUnit\Framework\Attributes\DataProvider('loremFileProvider')] + public function testGetPutContents($sourceFile): void { $sourceText = file_get_contents($sourceFile); //fill a file with string data @@ -174,7 +160,7 @@ abstract class Storage extends \Test\TestCase { /** * test various known mimetypes */ - public function testMimeType() { + public function testMimeType(): void { $this->assertEquals('httpd/unix-directory', $this->instance->getMimeType('/')); $this->assertEquals(false, $this->instance->getMimeType('/non/existing/file')); @@ -192,7 +178,7 @@ abstract class Storage extends \Test\TestCase { } - public function copyAndMoveProvider() { + public static function copyAndMoveProvider(): array { return [ ['/source.txt', '/target.txt'], ['/source.txt', '/target with space.txt'], @@ -223,10 +209,8 @@ abstract class Storage extends \Test\TestCase { ); } - /** - * @dataProvider copyAndMoveProvider - */ - public function testCopy($source, $target) { + #[\PHPUnit\Framework\Attributes\DataProvider('copyAndMoveProvider')] + public function testCopy($source, $target): void { $this->initSourceAndTarget($source); $this->instance->copy($source, $target); @@ -236,10 +220,8 @@ abstract class Storage extends \Test\TestCase { $this->assertTrue($this->instance->file_exists($source), $source . ' was deleted'); } - /** - * @dataProvider copyAndMoveProvider - */ - public function testMove($source, $target) { + #[\PHPUnit\Framework\Attributes\DataProvider('copyAndMoveProvider')] + public function testMove($source, $target): void { $this->initSourceAndTarget($source); $this->instance->rename($source, $target); @@ -250,10 +232,8 @@ abstract class Storage extends \Test\TestCase { $this->assertSameAsLorem($target); } - /** - * @dataProvider copyAndMoveProvider - */ - public function testCopyOverwrite($source, $target) { + #[\PHPUnit\Framework\Attributes\DataProvider('copyAndMoveProvider')] + public function testCopyOverwrite($source, $target): void { $this->initSourceAndTarget($source, $target); $this->instance->copy($source, $target); @@ -264,10 +244,8 @@ abstract class Storage extends \Test\TestCase { $this->assertSameAsLorem($source); } - /** - * @dataProvider copyAndMoveProvider - */ - public function testMoveOverwrite($source, $target) { + #[\PHPUnit\Framework\Attributes\DataProvider('copyAndMoveProvider')] + public function testMoveOverwrite($source, $target): void { $this->initSourceAndTarget($source, $target); $this->instance->rename($source, $target); @@ -277,7 +255,7 @@ abstract class Storage extends \Test\TestCase { $this->assertSameAsLorem($target); } - public function testLocal() { + public function testLocal(): void { $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt'; $this->instance->file_put_contents('/lorem.txt', file_get_contents($textFile)); $localFile = $this->instance->getLocalFile('/lorem.txt'); @@ -304,7 +282,7 @@ abstract class Storage extends \Test\TestCase { $this->assertEquals(file_get_contents($localFile), 'foo'); } - public function testStat() { + public function testStat(): void { $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt'; $ctimeStart = time(); $this->instance->file_put_contents('/lorem.txt', file_get_contents($textFile)); @@ -339,8 +317,8 @@ abstract class Storage extends \Test\TestCase { * Test whether checkUpdate properly returns false when there was * no change. */ - public function testCheckUpdate() { - if ($this->instance instanceof \OC\Files\Storage\Wrapper\Wrapper) { + public function testCheckUpdate(): void { + if ($this->instance instanceof Wrapper) { $this->markTestSkipped('Cannot test update check on wrappers'); } $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt'; @@ -351,7 +329,7 @@ abstract class Storage extends \Test\TestCase { $this->assertFalse($watcher->checkUpdate('/lorem.txt'), 'No update'); } - public function testUnlink() { + public function testUnlink(): void { $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt'; $this->instance->file_put_contents('/lorem.txt', file_get_contents($textFile)); @@ -363,10 +341,8 @@ abstract class Storage extends \Test\TestCase { $this->assertFalse($this->instance->file_exists('/lorem.txt')); } - /** - * @dataProvider fileNameProvider - */ - public function testFOpen($fileName) { + #[\PHPUnit\Framework\Attributes\DataProvider('fileNameProvider')] + public function testFOpen($fileName): void { $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt'; $fh = @$this->instance->fopen($fileName, 'r'); @@ -386,14 +362,14 @@ abstract class Storage extends \Test\TestCase { $this->assertEquals(file_get_contents($textFile), $content); } - public function testTouchCreateFile() { + public function testTouchCreateFile(): void { $this->assertFalse($this->instance->file_exists('touch')); // returns true on success $this->assertTrue($this->instance->touch('touch')); $this->assertTrue($this->instance->file_exists('touch')); } - public function testRecursiveRmdir() { + public function testRecursiveRmdir(): void { $this->instance->mkdir('folder'); $this->instance->mkdir('folder/bar'); $this->wait(); @@ -407,14 +383,14 @@ abstract class Storage extends \Test\TestCase { $this->assertFalse($this->instance->file_exists('folder')); } - public function testRmdirEmptyFolder() { + public function testRmdirEmptyFolder(): void { $this->assertTrue($this->instance->mkdir('empty')); $this->wait(); $this->assertTrue($this->instance->rmdir('empty')); $this->assertFalse($this->instance->file_exists('empty')); } - public function testRecursiveUnlink() { + public function testRecursiveUnlink(): void { $this->instance->mkdir('folder'); $this->instance->mkdir('folder/bar'); $this->instance->file_put_contents('folder/asd.txt', 'foobar'); @@ -427,7 +403,7 @@ abstract class Storage extends \Test\TestCase { $this->assertFalse($this->instance->file_exists('folder')); } - public function hashProvider() { + public static function hashProvider(): array { return [ ['Foobar', 'md5'], ['Foobar', 'sha1'], @@ -435,16 +411,14 @@ abstract class Storage extends \Test\TestCase { ]; } - /** - * @dataProvider hashProvider - */ - public function testHash($data, $type) { + #[\PHPUnit\Framework\Attributes\DataProvider('hashProvider')] + public function testHash($data, $type): void { $this->instance->file_put_contents('hash.txt', $data); $this->assertEquals(hash($type, $data), $this->instance->hash($type, 'hash.txt')); $this->assertEquals(hash($type, $data, true), $this->instance->hash($type, 'hash.txt', true)); } - public function testHashInFileName() { + public function testHashInFileName(): void { $this->instance->file_put_contents('#test.txt', 'data'); $this->assertEquals('data', $this->instance->file_get_contents('#test.txt')); @@ -463,14 +437,14 @@ abstract class Storage extends \Test\TestCase { $this->assertEquals(['test.txt'], $content); } - public function testCopyOverWriteFile() { + public function testCopyOverWriteFile(): void { $this->instance->file_put_contents('target.txt', 'foo'); $this->instance->file_put_contents('source.txt', 'bar'); $this->instance->copy('source.txt', 'target.txt'); $this->assertEquals('bar', $this->instance->file_get_contents('target.txt')); } - public function testRenameOverWriteFile() { + public function testRenameOverWriteFile(): void { $this->instance->file_put_contents('target.txt', 'foo'); $this->instance->file_put_contents('source.txt', 'bar'); $this->instance->rename('source.txt', 'target.txt'); @@ -478,7 +452,7 @@ abstract class Storage extends \Test\TestCase { $this->assertFalse($this->instance->file_exists('source.txt')); } - public function testRenameDirectory() { + public function testRenameDirectory(): void { $this->instance->mkdir('source'); $this->instance->file_put_contents('source/test1.txt', 'foo'); $this->instance->file_put_contents('source/test2.txt', 'qwerty'); @@ -506,7 +480,7 @@ abstract class Storage extends \Test\TestCase { $this->assertEquals('bar', $this->instance->file_get_contents('target/subfolder/test.txt')); } - public function testRenameOverWriteDirectory() { + public function testRenameOverWriteDirectory(): void { $this->instance->mkdir('source'); $this->instance->file_put_contents('source/test1.txt', 'foo'); @@ -522,7 +496,7 @@ abstract class Storage extends \Test\TestCase { $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt'), 'target/test1.txt has not been overwritten'); } - public function testRenameOverWriteDirectoryOverFile() { + public function testRenameOverWriteDirectoryOverFile(): void { $this->instance->mkdir('source'); $this->instance->file_put_contents('source/test1.txt', 'foo'); @@ -535,7 +509,7 @@ abstract class Storage extends \Test\TestCase { $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt')); } - public function testCopyDirectory() { + public function testCopyDirectory(): void { $this->instance->mkdir('source'); $this->instance->file_put_contents('source/test1.txt', 'foo'); $this->instance->file_put_contents('source/test2.txt', 'qwerty'); @@ -560,7 +534,7 @@ abstract class Storage extends \Test\TestCase { $this->assertEquals('bar', $this->instance->file_get_contents('target/subfolder/test.txt')); } - public function testCopyOverWriteDirectory() { + public function testCopyOverWriteDirectory(): void { $this->instance->mkdir('source'); $this->instance->file_put_contents('source/test1.txt', 'foo'); @@ -570,11 +544,11 @@ abstract class Storage extends \Test\TestCase { $this->instance->copy('source', 'target'); - $this->assertFalse($this->instance->file_exists('target/test2.txt')); + $this->assertFalse($this->instance->file_exists('target/test2.txt'), 'File target/test2.txt should no longer exist, but does'); $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt')); } - public function testCopyOverWriteDirectoryOverFile() { + public function testCopyOverWriteDirectoryOverFile(): void { $this->instance->mkdir('source'); $this->instance->file_put_contents('source/test1.txt', 'foo'); @@ -585,16 +559,14 @@ abstract class Storage extends \Test\TestCase { $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt')); } - public function testInstanceOfStorage() { - $this->assertTrue($this->instance->instanceOfStorage('\OCP\Files\Storage')); + public function testInstanceOfStorage(): void { + $this->assertTrue($this->instance->instanceOfStorage(IStorage::class)); $this->assertTrue($this->instance->instanceOfStorage(get_class($this->instance))); $this->assertFalse($this->instance->instanceOfStorage('\OC')); } - /** - * @dataProvider copyAndMoveProvider - */ - public function testCopyFromSameStorage($source, $target) { + #[\PHPUnit\Framework\Attributes\DataProvider('copyAndMoveProvider')] + public function testCopyFromSameStorage($source, $target): void { $this->initSourceAndTarget($source); $this->instance->copyFromStorage($this->instance, $source, $target); @@ -604,32 +576,32 @@ abstract class Storage extends \Test\TestCase { $this->assertTrue($this->instance->file_exists($source), $source . ' was deleted'); } - public function testIsCreatable() { + public function testIsCreatable(): void { $this->instance->mkdir('source'); $this->assertTrue($this->instance->isCreatable('source')); } - public function testIsReadable() { + public function testIsReadable(): void { $this->instance->mkdir('source'); $this->assertTrue($this->instance->isReadable('source')); } - public function testIsUpdatable() { + public function testIsUpdatable(): void { $this->instance->mkdir('source'); $this->assertTrue($this->instance->isUpdatable('source')); } - public function testIsDeletable() { + public function testIsDeletable(): void { $this->instance->mkdir('source'); $this->assertTrue($this->instance->isDeletable('source')); } - public function testIsShareable() { + public function testIsShareable(): void { $this->instance->mkdir('source'); $this->assertTrue($this->instance->isSharable('source')); } - public function testStatAfterWrite() { + public function testStatAfterWrite(): void { $this->instance->file_put_contents('foo.txt', 'bar'); $stat = $this->instance->stat('foo.txt'); $this->assertEquals(3, $stat['size']); @@ -642,13 +614,13 @@ abstract class Storage extends \Test\TestCase { $this->assertEquals(6, $stat['size']); } - public function testPartFile() { + public function testPartFile(): void { $this->instance->file_put_contents('bar.txt.part', 'bar'); $this->instance->rename('bar.txt.part', 'bar.txt'); $this->assertEquals('bar', $this->instance->file_get_contents('bar.txt')); } - public function testWriteStream() { + public function testWriteStream(): void { $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt'; if (!$this->instance->instanceOfStorage(IWriteStreamStorage::class)) { @@ -665,7 +637,7 @@ abstract class Storage extends \Test\TestCase { $this->assertEquals('resource (closed)', gettype($source)); } - public function testFseekSize() { + public function testFseekSize(): void { $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt'; $this->instance->file_put_contents('bar.txt', file_get_contents($textFile)); diff --git a/tests/lib/Files/Storage/StorageFactoryTest.php b/tests/lib/Files/Storage/StorageFactoryTest.php index 20b51cdda22..0bb9cbf5824 100644 --- a/tests/lib/Files/Storage/StorageFactoryTest.php +++ b/tests/lib/Files/Storage/StorageFactoryTest.php @@ -1,33 +1,34 @@ <?php + /** - * Copyright (c) 2015 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Storage; use OC\Files\Mount\MountPoint; +use OC\Files\Storage\StorageFactory; use OC\Files\Storage\Wrapper\Wrapper; use OCP\Files\Mount\IMountPoint; -use OCP\Files\Storage as IStorage; +use OCP\Files\Storage\IStorage; use Test\TestCase; class DummyWrapper extends Wrapper { public $data; - public function __construct($arguments) { - parent::__construct($arguments); - if (isset($arguments['data'])) { - $this->data = $arguments['data']; + public function __construct(array $parameters) { + parent::__construct($parameters); + if (isset($parameters['data'])) { + $this->data = $parameters['data']; } } } class StorageFactoryTest extends TestCase { - public function testSimpleWrapper() { - $instance = new \OC\Files\Storage\StorageFactory(); + public function testSimpleWrapper(): void { + $instance = new StorageFactory(); $mount = new MountPoint('\OC\Files\Storage\Temporary', '/foo', [[]], $instance); $instance->addStorageWrapper('dummy', function ($mountPoint, IStorage $storage, IMountPoint $mount) { $this->assertInstanceOf('\OC\Files\Storage\Temporary', $storage); @@ -39,8 +40,8 @@ class StorageFactoryTest extends TestCase { $this->assertInstanceOf('\Test\Files\Storage\DummyWrapper', $wrapped); } - public function testRemoveWrapper() { - $instance = new \OC\Files\Storage\StorageFactory(); + public function testRemoveWrapper(): void { + $instance = new StorageFactory(); $mount = new MountPoint('\OC\Files\Storage\Temporary', '/foo', [[]], $instance); $instance->addStorageWrapper('dummy', function ($mountPoint, IStorage $storage) { return new DummyWrapper(['storage' => $storage]); @@ -50,8 +51,8 @@ class StorageFactoryTest extends TestCase { $this->assertInstanceOf('\OC\Files\Storage\Temporary', $wrapped); } - public function testWrapperPriority() { - $instance = new \OC\Files\Storage\StorageFactory(); + public function testWrapperPriority(): void { + $instance = new StorageFactory(); $mount = new MountPoint('\OC\Files\Storage\Temporary', '/foo', [[]], $instance); $instance->addStorageWrapper('dummy1', function ($mountPoint, IStorage $storage) { return new DummyWrapper(['storage' => $storage, 'data' => 1]); diff --git a/tests/lib/Files/Storage/StoragesTestCase.php b/tests/lib/Files/Storage/StoragesTestCase.php new file mode 100644 index 00000000000..565ff1ddfda --- /dev/null +++ b/tests/lib/Files/Storage/StoragesTestCase.php @@ -0,0 +1,128 @@ +<?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\Storage; + +use OC\Files\Storage\Storage; +use Test\TestCase; + +abstract class StoragesTestCase extends TestCase { + /** + * @var Storage + */ + protected $storage1; + + /** + * @var Storage + */ + protected $storage2; + + protected function tearDown(): void { + if (is_null($this->storage1) && is_null($this->storage2)) { + return; + } + $this->storage1->getCache()->clear(); + $this->storage2->getCache()->clear(); + + parent::tearDown(); + } + + public function testMoveFileFromStorage() { + $source = 'source.txt'; + $target = 'target.txt'; + $this->storage2->file_put_contents($source, 'foo'); + + $this->storage1->moveFromStorage($this->storage2, $source, $target); + + $this->assertTrue($this->storage1->file_exists($target), $target . ' was not created'); + $this->assertFalse($this->storage2->file_exists($source), $source . ' still exists'); + $this->assertEquals('foo', $this->storage1->file_get_contents($target)); + } + + public function testMoveFileFromStorageWithExistingTarget() { + $source = 'source.txt'; + $target = 'target.txt'; + $this->storage1->file_put_contents($target, 'bar'); + $this->storage2->file_put_contents($source, 'foo'); + + $targetURN = $this->storage1->getURN($this->storage1->getCache()->get($target)->getID()); + $sourceURN = $this->storage2->getURN($this->storage2->getCache()->get($source)->getID()); + + $this->storage1->moveFromStorage($this->storage2, $source, $target); + + $this->assertTrue($this->storage1->file_exists($target), $target . ' was not created in DB'); + $this->assertFalse($this->storage2->file_exists($source), $source . ' still exists in DB'); + $this->assertTrue($this->storage1->getObjectStore()->objectExists($sourceURN), $sourceURN . ' was not created in bucket'); + $this->assertFalse($this->storage1->getObjectStore()->objectExists($targetURN), $targetURN . ' still exists in bucket'); + $this->assertEquals('foo', $this->storage1->file_get_contents($target)); + } + + public function testMoveDirectoryFromStorage() { + $this->storage2->mkdir('source'); + $this->storage2->file_put_contents('source/test1.txt', 'foo'); + $this->storage2->file_put_contents('source/test2.txt', 'qwerty'); + $this->storage2->mkdir('source/subfolder'); + $this->storage2->file_put_contents('source/subfolder/test.txt', 'bar'); + + $this->storage1->moveFromStorage($this->storage2, 'source', 'target'); + + $this->assertTrue($this->storage1->file_exists('target')); + $this->assertTrue($this->storage1->file_exists('target/test1.txt')); + $this->assertTrue($this->storage1->file_exists('target/test2.txt')); + $this->assertTrue($this->storage1->file_exists('target/subfolder')); + $this->assertTrue($this->storage1->file_exists('target/subfolder/test.txt')); + + $this->assertFalse($this->storage2->file_exists('source')); + $this->assertFalse($this->storage2->file_exists('source/test1.txt')); + $this->assertFalse($this->storage2->file_exists('source/test2.txt')); + $this->assertFalse($this->storage2->file_exists('source/subfolder')); + $this->assertFalse($this->storage2->file_exists('source/subfolder/test.txt')); + + $this->assertEquals('foo', $this->storage1->file_get_contents('target/test1.txt')); + $this->assertEquals('qwerty', $this->storage1->file_get_contents('target/test2.txt')); + $this->assertEquals('bar', $this->storage1->file_get_contents('target/subfolder/test.txt')); + } + + public function testCopyFileFromStorage() { + $source = 'source.txt'; + $target = 'target.txt'; + $this->storage2->file_put_contents($source, 'foo'); + + $this->storage1->copyFromStorage($this->storage2, $source, $target); + + $this->assertTrue($this->storage1->file_exists($target), $target . ' was not created'); + $this->assertTrue($this->storage2->file_exists($source), $source . ' was deleted'); + $this->assertEquals('foo', $this->storage1->file_get_contents($target)); + } + + public function testCopyDirectoryFromStorage() { + $this->storage2->mkdir('source'); + $this->storage2->file_put_contents('source/test1.txt', 'foo'); + $this->storage2->file_put_contents('source/test2.txt', 'qwerty'); + $this->storage2->mkdir('source/subfolder'); + $this->storage2->file_put_contents('source/subfolder/test.txt', 'bar'); + + $this->storage1->copyFromStorage($this->storage2, 'source', 'target'); + + $this->assertTrue($this->storage1->file_exists('target')); + $this->assertTrue($this->storage1->file_exists('target/test1.txt')); + $this->assertTrue($this->storage1->file_exists('target/test2.txt')); + $this->assertTrue($this->storage1->file_exists('target/subfolder')); + $this->assertTrue($this->storage1->file_exists('target/subfolder/test.txt')); + + $this->assertTrue($this->storage2->file_exists('source')); + $this->assertTrue($this->storage2->file_exists('source/test1.txt')); + $this->assertTrue($this->storage2->file_exists('source/test2.txt')); + $this->assertTrue($this->storage2->file_exists('source/subfolder')); + $this->assertTrue($this->storage2->file_exists('source/subfolder/test.txt')); + + $this->assertEquals('foo', $this->storage1->file_get_contents('target/test1.txt')); + $this->assertEquals('qwerty', $this->storage1->file_get_contents('target/test2.txt')); + $this->assertEquals('bar', $this->storage1->file_get_contents('target/subfolder/test.txt')); + } +} diff --git a/tests/lib/Files/Storage/Wrapper/AvailabilityTest.php b/tests/lib/Files/Storage/Wrapper/AvailabilityTest.php index 7d5d7d31808..d890081cbb6 100644 --- a/tests/lib/Files/Storage/Wrapper/AvailabilityTest.php +++ b/tests/lib/Files/Storage/Wrapper/AvailabilityTest.php @@ -1,22 +1,9 @@ <?php + /** - * @author Robin McCorkell <rmccorkell@karoshi.org.uk> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace Test\Files\Storage\Wrapper; @@ -31,7 +18,7 @@ class AvailabilityTest extends \Test\TestCase { protected $storageCache; /** @var \PHPUnit\Framework\MockObject\MockObject|Temporary */ protected $storage; - /** @var Availability */ + /** @var Availability */ protected $wrapper; protected function setUp(): void { @@ -50,7 +37,7 @@ class AvailabilityTest extends \Test\TestCase { /** * Storage is available */ - public function testAvailable() { + public function testAvailable(): void { $this->storage->expects($this->once()) ->method('getAvailability') ->willReturn(['available' => true, 'last_checked' => 0]); @@ -66,8 +53,8 @@ class AvailabilityTest extends \Test\TestCase { * Storage marked unavailable, TTL not expired * */ - public function testUnavailable() { - $this->expectException(\OCP\Files\StorageNotAvailableException::class); + public function testUnavailable(): void { + $this->expectException(StorageNotAvailableException::class); $this->storage->expects($this->once()) ->method('getAvailability') @@ -83,19 +70,23 @@ class AvailabilityTest extends \Test\TestCase { /** * Storage marked unavailable, TTL expired */ - public function testUnavailableRecheck() { + public function testUnavailableRecheck(): void { $this->storage->expects($this->once()) ->method('getAvailability') ->willReturn(['available' => false, 'last_checked' => 0]); $this->storage->expects($this->once()) ->method('test') ->willReturn(true); + $calls = [ + false, // prevents concurrent rechecks + true, // sets correct availability + ]; $this->storage->expects($this->exactly(2)) ->method('setAvailability') - ->withConsecutive( - [$this->equalTo(false)], // prevents concurrent rechecks - [$this->equalTo(true)] // sets correct availability - ); + ->willReturnCallback(function ($value) use (&$calls): void { + $expected = array_shift($calls); + $this->assertEquals($expected, $value); + }); $this->storage->expects($this->once()) ->method('mkdir'); @@ -106,8 +97,8 @@ class AvailabilityTest extends \Test\TestCase { * Storage marked available, but throws StorageNotAvailableException * */ - public function testAvailableThrowStorageNotAvailable() { - $this->expectException(\OCP\Files\StorageNotAvailableException::class); + public function testAvailableThrowStorageNotAvailable(): void { + $this->expectException(StorageNotAvailableException::class); $this->storage->expects($this->once()) ->method('getAvailability') @@ -116,7 +107,7 @@ class AvailabilityTest extends \Test\TestCase { ->method('test'); $this->storage->expects($this->once()) ->method('mkdir') - ->will($this->throwException(new StorageNotAvailableException())); + ->willThrowException(new StorageNotAvailableException()); $this->storageCache->expects($this->once()) ->method('setAvailability') ->with($this->equalTo(false)); @@ -128,7 +119,7 @@ class AvailabilityTest extends \Test\TestCase { * Storage available, but call fails * Method failure does not indicate storage unavailability */ - public function testAvailableFailure() { + public function testAvailableFailure(): void { $this->storage->expects($this->once()) ->method('getAvailability') ->willReturn(['available' => true, 'last_checked' => 0]); @@ -148,7 +139,7 @@ class AvailabilityTest extends \Test\TestCase { * Standard exception does not indicate storage unavailability * */ - public function testAvailableThrow() { + public function testAvailableThrow(): void { $this->expectException(\Exception::class); $this->storage->expects($this->once()) @@ -158,7 +149,7 @@ class AvailabilityTest extends \Test\TestCase { ->method('test'); $this->storage->expects($this->once()) ->method('mkdir') - ->will($this->throwException(new \Exception())); + ->willThrowException(new \Exception()); $this->storage->expects($this->never()) ->method('setAvailability'); diff --git a/tests/lib/Files/Storage/Wrapper/EncodingTest.php b/tests/lib/Files/Storage/Wrapper/EncodingTest.php index 0901edf83fa..cb6b6de0fb7 100644 --- a/tests/lib/Files/Storage/Wrapper/EncodingTest.php +++ b/tests/lib/Files/Storage/Wrapper/EncodingTest.php @@ -1,26 +1,29 @@ <?php + /** - * Copyright (c) 2016 Vincent Petry <pvince81@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Storage\Wrapper; +use OC\Files\Storage\Temporary; +use OC\Files\Storage\Wrapper\Encoding; + class EncodingTest extends \Test\Files\Storage\Storage { public const NFD_NAME = 'ümlaut'; public const NFC_NAME = 'ümlaut'; /** - * @var \OC\Files\Storage\Temporary + * @var Temporary */ private $sourceStorage; protected function setUp(): void { parent::setUp(); - $this->sourceStorage = new \OC\Files\Storage\Temporary([]); - $this->instance = new \OC\Files\Storage\Wrapper\Encoding([ + $this->sourceStorage = new Temporary([]); + $this->instance = new Encoding([ 'storage' => $this->sourceStorage ]); } @@ -30,43 +33,39 @@ class EncodingTest extends \Test\Files\Storage\Storage { parent::tearDown(); } - public function directoryProvider() { + public static function directoryProvider(): array { $a = parent::directoryProvider(); $a[] = [self::NFC_NAME]; return $a; } - public function fileNameProvider() { + public static function fileNameProvider(): array { $a = parent::fileNameProvider(); $a[] = [self::NFD_NAME . '.txt']; return $a; } - public function copyAndMoveProvider() { + public static function copyAndMoveProvider(): array { $a = parent::copyAndMoveProvider(); $a[] = [self::NFD_NAME . '.txt', self::NFC_NAME . '-renamed.txt']; return $a; } - public function accessNameProvider() { + public static function accessNameProvider(): array { return [ [self::NFD_NAME], [self::NFC_NAME], ]; } - /** - * @dataProvider accessNameProvider - */ - public function testFputEncoding($accessName) { + #[\PHPUnit\Framework\Attributes\DataProvider('accessNameProvider')] + public function testFputEncoding($accessName): void { $this->sourceStorage->file_put_contents(self::NFD_NAME, 'bar'); $this->assertEquals('bar', $this->instance->file_get_contents($accessName)); } - /** - * @dataProvider accessNameProvider - */ - public function testFopenReadEncoding($accessName) { + #[\PHPUnit\Framework\Attributes\DataProvider('accessNameProvider')] + public function testFopenReadEncoding($accessName): void { $this->sourceStorage->file_put_contents(self::NFD_NAME, 'bar'); $fh = $this->instance->fopen($accessName, 'r'); $data = fgets($fh); @@ -74,10 +73,8 @@ class EncodingTest extends \Test\Files\Storage\Storage { $this->assertEquals('bar', $data); } - /** - * @dataProvider accessNameProvider - */ - public function testFopenOverwriteEncoding($accessName) { + #[\PHPUnit\Framework\Attributes\DataProvider('accessNameProvider')] + public function testFopenOverwriteEncoding($accessName): void { $this->sourceStorage->file_put_contents(self::NFD_NAME, 'bar'); $fh = $this->instance->fopen($accessName, 'w'); $data = fputs($fh, 'test'); @@ -87,42 +84,36 @@ class EncodingTest extends \Test\Files\Storage\Storage { $this->assertFalse($this->sourceStorage->file_exists(self::NFC_NAME)); } - /** - * @dataProvider accessNameProvider - */ - public function testFileExistsEncoding($accessName) { + #[\PHPUnit\Framework\Attributes\DataProvider('accessNameProvider')] + public function testFileExistsEncoding($accessName): void { $this->sourceStorage->file_put_contents(self::NFD_NAME, 'bar'); $this->assertTrue($this->instance->file_exists($accessName)); } - /** - * @dataProvider accessNameProvider - */ - public function testUnlinkEncoding($accessName) { + #[\PHPUnit\Framework\Attributes\DataProvider('accessNameProvider')] + public function testUnlinkEncoding($accessName): void { $this->sourceStorage->file_put_contents(self::NFD_NAME, 'bar'); $this->assertTrue($this->instance->unlink($accessName)); $this->assertFalse($this->sourceStorage->file_exists(self::NFC_NAME)); $this->assertFalse($this->sourceStorage->file_exists(self::NFD_NAME)); } - public function testNfcHigherPriority() { + public function testNfcHigherPriority(): void { $this->sourceStorage->file_put_contents(self::NFC_NAME, 'nfc'); $this->sourceStorage->file_put_contents(self::NFD_NAME, 'nfd'); $this->assertEquals('nfc', $this->instance->file_get_contents(self::NFC_NAME)); } - public function encodedDirectoriesProvider() { + public static function encodedDirectoriesProvider(): array { return [ [self::NFD_NAME, self::NFC_NAME], [self::NFD_NAME . '/' . self::NFD_NAME, self::NFC_NAME . '/' . self::NFC_NAME], - [self::NFD_NAME . '/' . self::NFC_NAME . '/' .self::NFD_NAME, self::NFC_NAME . '/' . self::NFC_NAME . '/' . self::NFC_NAME], + [self::NFD_NAME . '/' . self::NFC_NAME . '/' . self::NFD_NAME, self::NFC_NAME . '/' . self::NFC_NAME . '/' . self::NFC_NAME], ]; } - /** - * @dataProvider encodedDirectoriesProvider - */ - public function testOperationInsideDirectory($sourceDir, $accessDir) { + #[\PHPUnit\Framework\Attributes\DataProvider('encodedDirectoriesProvider')] + public function testOperationInsideDirectory($sourceDir, $accessDir): void { $this->sourceStorage->mkdir($sourceDir); $this->instance->file_put_contents($accessDir . '/test.txt', 'bar'); $this->assertTrue($this->instance->file_exists($accessDir . '/test.txt')); @@ -139,7 +130,7 @@ class EncodingTest extends \Test\Files\Storage\Storage { $this->assertTrue($this->instance->file_exists($accessDir . '/' . self::NFC_NAME)); } - public function testCacheExtraSlash() { + public function testCacheExtraSlash(): void { $this->sourceStorage->file_put_contents(self::NFD_NAME, 'foo'); $this->assertEquals(3, $this->instance->file_put_contents(self::NFC_NAME, 'bar')); $this->assertEquals('bar', $this->instance->file_get_contents(self::NFC_NAME)); @@ -151,7 +142,7 @@ class EncodingTest extends \Test\Files\Storage\Storage { $this->assertEquals('barbaric', $this->instance->file_get_contents('//' . self::NFC_NAME)); } - public function sourceAndTargetDirectoryProvider() { + public static function sourceAndTargetDirectoryProvider(): array { return [ [self::NFC_NAME . '1', self::NFC_NAME . '2'], [self::NFD_NAME . '1', self::NFC_NAME . '2'], @@ -160,10 +151,8 @@ class EncodingTest extends \Test\Files\Storage\Storage { ]; } - /** - * @dataProvider sourceAndTargetDirectoryProvider - */ - public function testCopyAndMoveEncodedFolder($sourceDir, $targetDir) { + #[\PHPUnit\Framework\Attributes\DataProvider('sourceAndTargetDirectoryProvider')] + public function testCopyAndMoveEncodedFolder($sourceDir, $targetDir): void { $this->sourceStorage->mkdir($sourceDir); $this->sourceStorage->mkdir($targetDir); $this->sourceStorage->file_put_contents($sourceDir . '/test.txt', 'bar'); @@ -180,10 +169,8 @@ class EncodingTest extends \Test\Files\Storage\Storage { $this->assertEquals('bar', $this->instance->file_get_contents(self::NFC_NAME . '2/test2.txt')); } - /** - * @dataProvider sourceAndTargetDirectoryProvider - */ - public function testCopyAndMoveFromStorageEncodedFolder($sourceDir, $targetDir) { + #[\PHPUnit\Framework\Attributes\DataProvider('sourceAndTargetDirectoryProvider')] + public function testCopyAndMoveFromStorageEncodedFolder($sourceDir, $targetDir): void { $this->sourceStorage->mkdir($sourceDir); $this->sourceStorage->mkdir($targetDir); $this->sourceStorage->file_put_contents($sourceDir . '/test.txt', 'bar'); @@ -200,7 +187,7 @@ class EncodingTest extends \Test\Files\Storage\Storage { $this->assertEquals('bar', $this->instance->file_get_contents(self::NFC_NAME . '2/test2.txt')); } - public function testNormalizedDirectoryEntriesOpenDir() { + public function testNormalizedDirectoryEntriesOpenDir(): void { $this->sourceStorage->mkdir('/test'); $this->sourceStorage->mkdir('/test/' . self::NFD_NAME); @@ -209,7 +196,7 @@ class EncodingTest extends \Test\Files\Storage\Storage { $dh = $this->instance->opendir('/test'); $content = []; - while ($file = readdir($dh)) { + while (($file = readdir($dh)) !== false) { if ($file != '.' and $file != '..') { $content[] = $file; } @@ -219,7 +206,7 @@ class EncodingTest extends \Test\Files\Storage\Storage { $this->assertEquals(self::NFC_NAME, $content[0]); } - public function testNormalizedDirectoryEntriesGetDirectoryContent() { + public function testNormalizedDirectoryEntriesGetDirectoryContent(): void { $this->sourceStorage->mkdir('/test'); $this->sourceStorage->mkdir('/test/' . self::NFD_NAME); @@ -231,7 +218,7 @@ class EncodingTest extends \Test\Files\Storage\Storage { $this->assertEquals(self::NFC_NAME, $content[0]['name']); } - public function testNormalizedGetMetaData() { + public function testNormalizedGetMetaData(): void { $this->sourceStorage->mkdir('/test'); $this->sourceStorage->mkdir('/test/' . self::NFD_NAME); @@ -241,4 +228,12 @@ class EncodingTest extends \Test\Files\Storage\Storage { $entry = $this->instance->getMetaData('/test/' . self::NFD_NAME); $this->assertEquals(self::NFC_NAME, $entry['name']); } + + /** + * Regression test of https://github.com/nextcloud/server/issues/50431 + */ + public function testNoMetadata() { + $this->assertNull($this->instance->getMetaData('/test/null')); + } + } diff --git a/tests/lib/Files/Storage/Wrapper/EncryptionTest.php b/tests/lib/Files/Storage/Wrapper/EncryptionTest.php index ae776e40666..3e643714300 100644 --- a/tests/lib/Files/Storage/Wrapper/EncryptionTest.php +++ b/tests/lib/Files/Storage/Wrapper/EncryptionTest.php @@ -1,11 +1,20 @@ <?php +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ + namespace Test\Files\Storage\Wrapper; +use Exception; use OC\Encryption\Exceptions\ModuleDoesNotExistsException; -use OC\Encryption\Update; +use OC\Encryption\File; use OC\Encryption\Util; +use OC\Files\Cache\Cache; use OC\Files\Cache\CacheEntry; +use OC\Files\Mount\MountPoint; use OC\Files\Storage\Temporary; use OC\Files\Storage\Wrapper\Encryption; use OC\Files\View; @@ -19,103 +28,43 @@ use OCP\Files\Cache\ICache; use OCP\Files\Mount\IMountPoint; use OCP\ICacheFactory; use OCP\IConfig; +use OCP\ITempManager; +use OCP\Server; +use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Test\Files\Storage\Storage; class EncryptionTest extends Storage { /** * block size will always be 8192 for a PHP stream * @see https://bugs.php.net/bug.php?id=21641 - * @var integer - */ - protected $headerSize = 8192; - - /** - * @var Temporary - */ - private $sourceStorage; - - /** - * @var \OC\Files\Storage\Wrapper\Encryption | \PHPUnit\Framework\MockObject\MockObject */ + protected int $headerSize = 8192; + private Temporary $sourceStorage; + /** @var Encryption&MockObject */ protected $instance; - - /** - * @var \OC\Encryption\Keys\Storage | \PHPUnit\Framework\MockObject\MockObject - */ - private $keyStore; - - /** - * @var \OC\Encryption\Util | \PHPUnit\Framework\MockObject\MockObject - */ - private $util; - - /** - * @var \OC\Encryption\Manager | \PHPUnit\Framework\MockObject\MockObject - */ - private $encryptionManager; - - /** - * @var \OCP\Encryption\IEncryptionModule | \PHPUnit\Framework\MockObject\MockObject - */ - private $encryptionModule; - - /** - * @var \OC\Encryption\Update | \PHPUnit\Framework\MockObject\MockObject - */ - private $update; - - /** - * @var \OC\Files\Cache\Cache | \PHPUnit\Framework\MockObject\MockObject - */ - private $cache; - - /** - * @var \OC\Log | \PHPUnit\Framework\MockObject\MockObject - */ - private $logger; - - /** - * @var \OC\Encryption\File | \PHPUnit\Framework\MockObject\MockObject - */ - private $file; - - - /** - * @var \OC\Files\Mount\MountPoint | \PHPUnit\Framework\MockObject\MockObject - */ - private $mount; - - /** - * @var \OC\Files\Mount\Manager | \PHPUnit\Framework\MockObject\MockObject - */ - private $mountManager; - - /** - * @var \OC\Group\Manager | \PHPUnit\Framework\MockObject\MockObject - */ - private $groupManager; - - /** - * @var \OCP\IConfig | \PHPUnit\Framework\MockObject\MockObject - */ - private $config; - - /** @var \OC\Memcache\ArrayCache | \PHPUnit\Framework\MockObject\MockObject */ - private $arrayCache; - - - /** @var integer dummy unencrypted size */ - private $dummySize = -1; + private \OC\Encryption\Keys\Storage&MockObject $keyStore; + private Util&MockObject $util; + private \OC\Encryption\Manager&MockObject $encryptionManager; + private IEncryptionModule&MockObject $encryptionModule; + private Cache&MockObject $cache; + private LoggerInterface&MockObject $logger; + private File&MockObject $file; + private MountPoint&MockObject $mount; + private \OC\Files\Mount\Manager&MockObject $mountManager; + private \OC\Group\Manager&MockObject $groupManager; + private IConfig&MockObject $config; + private ArrayCache&MockObject $arrayCache; + /** dummy unencrypted size */ + private int $dummySize = -1; protected function setUp(): void { parent::setUp(); $mockModule = $this->buildMockModule(); - $this->encryptionManager = $this->getMockBuilder('\OC\Encryption\Manager') + $this->encryptionManager = $this->getMockBuilder(\OC\Encryption\Manager::class) ->disableOriginalConstructor() - ->setMethods(['getEncryptionModule', 'isEnabled']) + ->onlyMethods(['getEncryptionModule', 'isEnabled']) ->getMock(); $this->encryptionManager->expects($this->any()) ->method('getEncryptionModule') @@ -129,13 +78,13 @@ class EncryptionTest extends Storage { ->disableOriginalConstructor() ->getMock(); - $this->util = $this->getMockBuilder('\OC\Encryption\Util') - ->setMethods(['getUidAndFilename', 'isFile', 'isExcluded']) + $this->util = $this->getMockBuilder(Util::class) + ->onlyMethods(['getUidAndFilename', 'isFile', 'isExcluded', 'stripPartialFileExtension']) ->setConstructorArgs([new View(), new Manager( $this->config, - $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), - $this->createMock(IEventDispatcher::class) + $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), ), $this->groupManager, $this->config, $this->arrayCache]) ->getMock(); $this->util->expects($this->any()) @@ -143,10 +92,15 @@ class EncryptionTest extends Storage { ->willReturnCallback(function ($path) { return ['user1', $path]; }); + $this->util->expects($this->any()) + ->method('stripPartialFileExtension') + ->willReturnCallback(function ($path) { + return $path; + }); - $this->file = $this->getMockBuilder('\OC\Encryption\File') + $this->file = $this->getMockBuilder(File::class) ->disableOriginalConstructor() - ->setMethods(['getAccessList']) + ->onlyMethods(['getAccessList']) ->getMock(); $this->file->expects($this->any())->method('getAccessList')->willReturn([]); @@ -154,15 +108,11 @@ class EncryptionTest extends Storage { $this->sourceStorage = new Temporary([]); - $this->keyStore = $this->getMockBuilder('\OC\Encryption\Keys\Storage') - ->disableOriginalConstructor()->getMock(); - - $this->update = $this->getMockBuilder('\OC\Encryption\Update') - ->disableOriginalConstructor()->getMock(); + $this->keyStore = $this->createMock(\OC\Encryption\Keys\Storage::class); - $this->mount = $this->getMockBuilder('\OC\Files\Mount\MountPoint') + $this->mount = $this->getMockBuilder(MountPoint::class) ->disableOriginalConstructor() - ->setMethods(['getOption']) + ->onlyMethods(['getOption']) ->getMock(); $this->mount->expects($this->any())->method('getOption')->willReturnCallback(function ($option, $default) { if ($option === 'encrypt' && $default === true) { @@ -186,7 +136,7 @@ class EncryptionTest extends Storage { $this->mountManager->method('findByStorageId') ->willReturn([]); - $this->instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption') + $this->instance = $this->getMockBuilder(Encryption::class) ->setConstructorArgs( [ [ @@ -195,10 +145,17 @@ class EncryptionTest extends Storage { 'mountPoint' => '/', 'mount' => $this->mount ], - $this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager, $this->arrayCache + $this->encryptionManager, + $this->util, + $this->logger, + $this->file, + null, + $this->keyStore, + $this->mountManager, + $this->arrayCache ] ) - ->setMethods(['getMetaData', 'getCache', 'getEncryptionModule']) + ->onlyMethods(['getMetaData', 'getCache', 'getEncryptionModule']) ->getMock(); $this->instance->expects($this->any()) @@ -216,13 +173,10 @@ class EncryptionTest extends Storage { ->willReturn($mockModule); } - /** - * @return \PHPUnit\Framework\MockObject\MockObject - */ - protected function buildMockModule() { + protected function buildMockModule(): IEncryptionModule&MockObject { $this->encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule') ->disableOriginalConstructor() - ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList']) + ->onlyMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList']) ->getMock(); $this->encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE'); @@ -240,7 +194,6 @@ class EncryptionTest extends Storage { } /** - * @dataProvider dataTestGetMetaData * * @param string $path * @param array $metaData @@ -249,7 +202,8 @@ class EncryptionTest extends Storage { * @param int $storedUnencryptedSize * @param array $expected */ - public function testGetMetaData($path, $metaData, $encrypted, $unencryptedSizeSet, $storedUnencryptedSize, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetMetaData')] + public function testGetMetaData($path, $metaData, $encrypted, $unencryptedSizeSet, $storedUnencryptedSize, $expected): void { $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') ->disableOriginalConstructor()->getMock(); @@ -263,7 +217,7 @@ class EncryptionTest extends Storage { } ); - $this->instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption') + $this->instance = $this->getMockBuilder(Encryption::class) ->setConstructorArgs( [ [ @@ -272,10 +226,17 @@ class EncryptionTest extends Storage { 'mountPoint' => '/', 'mount' => $this->mount ], - $this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager, $this->arrayCache + $this->encryptionManager, + $this->util, + $this->logger, + $this->file, + null, + $this->keyStore, + $this->mountManager, + $this->arrayCache, ] ) - ->setMethods(['getCache', 'verifyUnencryptedSize']) + ->onlyMethods(['getCache', 'verifyUnencryptedSize']) ->getMock(); if ($unencryptedSizeSet) { @@ -318,7 +279,7 @@ class EncryptionTest extends Storage { } } - public function dataTestGetMetaData() { + public static function dataTestGetMetaData(): array { return [ ['/test.txt', ['size' => 42, 'encrypted' => 2, 'encryptedVersion' => 2, 'fileid' => 1], true, true, 12, ['size' => 12, 'encrypted' => true, 'encryptedVersion' => 2]], ['/test.txt', null, true, true, 12, null], @@ -327,14 +288,14 @@ class EncryptionTest extends Storage { ]; } - public function testFilesize() { + public function testFilesize(): void { $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') ->disableOriginalConstructor()->getMock(); $cache->expects($this->any()) ->method('get') ->willReturn(new CacheEntry(['encrypted' => true, 'path' => '/test.txt', 'size' => 0, 'fileid' => 1])); - $this->instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption') + $this->instance = $this->getMockBuilder(Encryption::class) ->setConstructorArgs( [ [ @@ -343,10 +304,17 @@ class EncryptionTest extends Storage { 'mountPoint' => '/', 'mount' => $this->mount ], - $this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager, $this->arrayCache + $this->encryptionManager, + $this->util, + $this->logger, + $this->file, + null, + $this->keyStore, + $this->mountManager, + $this->arrayCache, ] ) - ->setMethods(['getCache', 'verifyUnencryptedSize']) + ->onlyMethods(['getCache', 'verifyUnencryptedSize']) ->getMock(); $this->instance->expects($this->any())->method('getCache')->willReturn($cache); @@ -360,18 +328,18 @@ class EncryptionTest extends Storage { } /** - * @dataProvider dataTestVerifyUnencryptedSize * * @param int $encryptedSize * @param int $unencryptedSize * @param bool $failure * @param int $expected */ - public function testVerifyUnencryptedSize($encryptedSize, $unencryptedSize, $failure, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestVerifyUnencryptedSize')] + public function testVerifyUnencryptedSize($encryptedSize, $unencryptedSize, $failure, $expected): void { $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') ->disableOriginalConstructor()->getMock(); - $this->instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption') + $this->instance = $this->getMockBuilder(Encryption::class) ->setConstructorArgs( [ [ @@ -380,10 +348,17 @@ class EncryptionTest extends Storage { 'mountPoint' => '/', 'mount' => $this->mount ], - $this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager, $this->arrayCache + $this->encryptionManager, + $this->util, + $this->logger, + $this->file, + null, + $this->keyStore, + $this->mountManager, + $this->arrayCache, ] ) - ->setMethods(['fixUnencryptedSize']) + ->onlyMethods(['fixUnencryptedSize']) ->getMock(); $sourceStorage->expects($this->once())->method('filesize')->willReturn($encryptedSize); @@ -393,7 +368,7 @@ class EncryptionTest extends Storage { ->willReturnCallback( function () use ($failure, $expected) { if ($failure) { - throw new \Exception(); + throw new Exception(); } else { return $expected; } @@ -406,7 +381,7 @@ class EncryptionTest extends Storage { ); } - public function dataTestVerifyUnencryptedSize() { + public static function dataTestVerifyUnencryptedSize(): array { return [ [120, 80, false, 80], [120, 120, false, 80], @@ -416,17 +391,17 @@ class EncryptionTest extends Storage { } /** - * @dataProvider dataTestCopyAndRename * * @param string $source * @param string $target * @param $encryptionEnabled * @param boolean $renameKeysReturn */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCopyAndRename')] public function testRename($source, - $target, - $encryptionEnabled, - $renameKeysReturn) { + $target, + $encryptionEnabled, + $renameKeysReturn): void { if ($encryptionEnabled) { $this->keyStore ->expects($this->once()) @@ -446,7 +421,7 @@ class EncryptionTest extends Storage { $this->instance->rename($source, $target); } - public function testCopyEncryption() { + public function testCopyEncryption(): void { $this->instance->file_put_contents('source.txt', 'bar'); $this->instance->copy('source.txt', 'target.txt'); $this->assertSame('bar', $this->instance->file_get_contents('target.txt')); @@ -461,7 +436,7 @@ class EncryptionTest extends Storage { * * @return array */ - public function dataTestCopyAndRename() { + public static function dataTestCopyAndRename(): array { return [ ['source', 'target', true, false, false], ['source', 'target', true, true, false], @@ -471,38 +446,45 @@ class EncryptionTest extends Storage { ]; } - public function testIsLocal() { + public function testIsLocal(): void { $this->encryptionManager->expects($this->once()) ->method('isEnabled')->willReturn(true); $this->assertFalse($this->instance->isLocal()); } /** - * @dataProvider dataTestRmdir * * @param string $path * @param boolean $rmdirResult * @param boolean $isExcluded * @param boolean $encryptionEnabled */ - public function testRmdir($path, $rmdirResult, $isExcluded, $encryptionEnabled) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestRmdir')] + public function testRmdir($path, $rmdirResult, $isExcluded, $encryptionEnabled): void { $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') ->disableOriginalConstructor()->getMock(); $util = $this->getMockBuilder('\OC\Encryption\Util')->disableOriginalConstructor()->getMock(); $sourceStorage->expects($this->once())->method('rmdir')->willReturn($rmdirResult); - $util->expects($this->any())->method('isExcluded')-> willReturn($isExcluded); + $util->expects($this->any())->method('isExcluded')->willReturn($isExcluded); $this->encryptionManager->expects($this->any())->method('isEnabled')->willReturn($encryptionEnabled); - $encryptionStorage = new \OC\Files\Storage\Wrapper\Encryption( + $encryptionStorage = new Encryption( [ 'storage' => $sourceStorage, 'root' => 'foo', 'mountPoint' => '/mountPoint', 'mount' => $this->mount ], - $this->encryptionManager, $util, $this->logger, $this->file, null, $this->keyStore, $this->update + $this->encryptionManager, + $util, + $this->logger, + $this->file, + null, + $this->keyStore, + $this->mountManager, + $this->arrayCache, ); @@ -515,7 +497,7 @@ class EncryptionTest extends Storage { $encryptionStorage->rmdir($path); } - public function dataTestRmdir() { + public static function dataTestRmdir(): array { return [ ['/file.txt', true, true, true], ['/file.txt', false, true, true], @@ -529,12 +511,12 @@ class EncryptionTest extends Storage { } /** - * @dataProvider dataTestCopyKeys * * @param boolean $excluded * @param boolean $expected */ - public function testCopyKeys($excluded, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCopyKeys')] + public function testCopyKeys($excluded, $expected): void { $this->util->expects($this->once()) ->method('isExcluded') ->willReturn($excluded); @@ -550,7 +532,7 @@ class EncryptionTest extends Storage { ); } - public function dataTestCopyKeys() { + public static function dataTestCopyKeys(): array { return [ [true, false], [false, true], @@ -558,13 +540,13 @@ class EncryptionTest extends Storage { } /** - * @dataProvider dataTestGetHeader * * @param string $path * @param bool $strippedPathExists * @param string $strippedPath */ - public function testGetHeader($path, $strippedPathExists, $strippedPath) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetHeader')] + public function testGetHeader($path, $strippedPathExists, $strippedPath): void { $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') ->disableOriginalConstructor()->getMock(); @@ -574,9 +556,9 @@ class EncryptionTest extends Storage { new View(), new Manager( $this->config, - $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), - $this->createMock(IEventDispatcher::class) + $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), ), $this->groupManager, $this->config, @@ -592,7 +574,7 @@ class EncryptionTest extends Storage { return ['encrypted' => true, 'path' => $path]; }); - $instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption') + $instance = $this->getMockBuilder(Encryption::class) ->setConstructorArgs( [ [ @@ -601,22 +583,29 @@ class EncryptionTest extends Storage { 'mountPoint' => '/', 'mount' => $this->mount ], - $this->encryptionManager, $util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager, $this->arrayCache + $this->encryptionManager, + $util, + $this->logger, + $this->file, + null, + $this->keyStore, + $this->mountManager, + $this->arrayCache, ] ) - ->setMethods(['getCache','readFirstBlock', 'parseRawHeader']) + ->onlyMethods(['getCache', 'readFirstBlock']) ->getMock(); - $instance->expects($this->once())->method('getCache')->willReturn($cache); + $instance->method('getCache')->willReturn($cache); - $instance->expects($this->once())->method(('parseRawHeader')) + $util->method('parseRawHeader') ->willReturn([Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']); if ($strippedPathExists) { - $instance->expects($this->once())->method('readFirstBlock') + $instance->method('readFirstBlock') ->with($strippedPath)->willReturn(''); } else { - $instance->expects($this->once())->method('readFirstBlock') + $instance->method('readFirstBlock') ->with($path)->willReturn(''); } @@ -630,7 +619,7 @@ class EncryptionTest extends Storage { $this->invokePrivate($instance, 'getHeader', [$path]); } - public function dataTestGetHeader() { + public static function dataTestGetHeader(): array { return [ ['/foo/bar.txt', false, '/foo/bar.txt'], ['/foo/bar.txt.part', false, '/foo/bar.txt'], @@ -643,35 +632,40 @@ class EncryptionTest extends Storage { /** * test if getHeader adds the default module correctly to the header for * legacy files - * - * @dataProvider dataTestGetHeaderAddLegacyModule */ - public function testGetHeaderAddLegacyModule($header, $isEncrypted, $exists, $expected) { - $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetHeaderAddLegacyModule')] + public function testGetHeaderAddLegacyModule($header, $isEncrypted, $strippedPathExists, $expected): void { + $sourceStorage = $this->getMockBuilder(\OC\Files\Storage\Storage::class) ->disableOriginalConstructor()->getMock(); $sourceStorage->expects($this->once()) ->method('is_file') - ->willReturn($exists); + ->with('test.txt') + ->willReturn($strippedPathExists); - $util = $this->getMockBuilder('\OC\Encryption\Util') + $util = $this->getMockBuilder(Util::class) + ->onlyMethods(['stripPartialFileExtension', 'parseRawHeader']) ->setConstructorArgs([new View(), new Manager( $this->config, - $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), - $this->createMock(IEventDispatcher::class) + $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), ), $this->groupManager, $this->config, $this->arrayCache]) ->getMock(); + $util->expects($this->any()) + ->method('stripPartialFileExtension') + ->willReturnCallback(function ($path) { + return $path; + }); - $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') - ->disableOriginalConstructor()->getMock(); + $cache = $this->createMock(Cache::class); $cache->expects($this->any()) ->method('get') ->willReturnCallback(function ($path) use ($isEncrypted) { return ['encrypted' => $isEncrypted, 'path' => $path]; }); - $instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption') + $instance = $this->getMockBuilder(Encryption::class) ->setConstructorArgs( [ [ @@ -680,14 +674,23 @@ class EncryptionTest extends Storage { 'mountPoint' => '/', 'mount' => $this->mount ], - $this->encryptionManager, $util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager, $this->arrayCache + $this->encryptionManager, + $util, + $this->logger, + $this->file, + null, + $this->keyStore, + $this->mountManager, + $this->arrayCache, ] ) - ->setMethods(['readFirstBlock', 'parseRawHeader', 'getCache']) + ->onlyMethods(['readFirstBlock', 'getCache']) ->getMock(); - $instance->expects($this->any())->method(('parseRawHeader'))->willReturn($header); - $instance->expects($this->once())->method('getCache')->willReturn($cache); + $instance->method('readFirstBlock')->willReturn(''); + + $util->method(('parseRawHeader'))->willReturn($header); + $instance->method('getCache')->willReturn($cache); $result = $this->invokePrivate($instance, 'getHeader', ['test.txt']); $this->assertSameSize($expected, $result); @@ -697,7 +700,7 @@ class EncryptionTest extends Storage { } } - public function dataTestGetHeaderAddLegacyModule() { + public static function dataTestGetHeaderAddLegacyModule(): array { return [ [['cipher' => 'AES-128'], true, true, ['cipher' => 'AES-128', Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']], [[], true, false, []], @@ -706,45 +709,7 @@ class EncryptionTest extends Storage { ]; } - /** - * @dataProvider dataTestParseRawHeader - */ - public function testParseRawHeader($rawHeader, $expected) { - $instance = new \OC\Files\Storage\Wrapper\Encryption( - [ - 'storage' => $this->sourceStorage, - 'root' => 'foo', - 'mountPoint' => '/', - 'mount' => $this->mount - ], - $this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager, $this->arrayCache - - ); - - $result = $this->invokePrivate($instance, 'parseRawHeader', [$rawHeader]); - $this->assertSameSize($expected, $result); - foreach ($result as $key => $value) { - $this->assertArrayHasKey($key, $expected); - $this->assertSame($expected[$key], $value); - } - } - - public function dataTestParseRawHeader() { - return [ - [str_pad('HBEGIN:oc_encryption_module:0:HEND', $this->headerSize, '-', STR_PAD_RIGHT) - , [Util::HEADER_ENCRYPTION_MODULE_KEY => '0']], - [str_pad('HBEGIN:oc_encryption_module:0:custom_header:foo:HEND', $this->headerSize, '-', STR_PAD_RIGHT) - , ['custom_header' => 'foo', Util::HEADER_ENCRYPTION_MODULE_KEY => '0']], - [str_pad('HelloWorld', $this->headerSize, '-', STR_PAD_RIGHT), []], - ['', []], - [str_pad('HBEGIN:oc_encryption_module:0', $this->headerSize, '-', STR_PAD_RIGHT) - , []], - [str_pad('oc_encryption_module:0:HEND', $this->headerSize, '-', STR_PAD_RIGHT) - , []], - ]; - } - - public function dataCopyBetweenStorage() { + public static function dataCopyBetweenStorage(): array { return [ [true, true, true], [true, false, false], @@ -753,7 +718,7 @@ class EncryptionTest extends Storage { ]; } - public function testCopyBetweenStorageMinimumEncryptedVersion() { + public function testCopyBetweenStorageMinimumEncryptedVersion(): void { $storage2 = $this->createMock(\OC\Files\Storage\Storage::class); $sourceInternalPath = $targetInternalPath = 'file.txt'; @@ -762,7 +727,7 @@ class EncryptionTest extends Storage { $storage2->expects($this->any()) ->method('fopen') ->willReturnCallback(function ($path, $mode) { - $temp = \OC::$server->getTempManager(); + $temp = Server::get(ITempManager::class); return fopen($temp->getTemporaryFile(), $mode); }); $storage2->method('getId') @@ -796,13 +761,13 @@ class EncryptionTest extends Storage { } /** - * @dataProvider dataCopyBetweenStorage * * @param bool $encryptionEnabled * @param bool $mountPointEncryptionEnabled * @param bool $expectedEncrypted */ - public function testCopyBetweenStorage($encryptionEnabled, $mountPointEncryptionEnabled, $expectedEncrypted) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataCopyBetweenStorage')] + public function testCopyBetweenStorage($encryptionEnabled, $mountPointEncryptionEnabled, $expectedEncrypted): void { $storage2 = $this->createMock(\OC\Files\Storage\Storage::class); $sourceInternalPath = $targetInternalPath = 'file.txt'; @@ -811,7 +776,7 @@ class EncryptionTest extends Storage { $storage2->expects($this->any()) ->method('fopen') ->willReturnCallback(function ($path, $mode) { - $temp = \OC::$server->getTempManager(); + $temp = Server::get(ITempManager::class); return fopen($temp->getTemporaryFile(), $mode); }); $storage2->method('getId') @@ -830,10 +795,10 @@ class EncryptionTest extends Storage { ->method('isEnabled') ->willReturn($encryptionEnabled); // FIXME can not overwrite the return after definition -// $this->mount->expects($this->at(0)) -// ->method('getOption') -// ->with('encrypt', true) -// ->willReturn($mountPointEncryptionEnabled); + // $this->mount->expects($this->at(0)) + // ->method('getOption') + // ->with('encrypt', true) + // ->willReturn($mountPointEncryptionEnabled); global $mockedMountPointEncryptionEnabled; $mockedMountPointEncryptionEnabled = $mountPointEncryptionEnabled; @@ -856,14 +821,14 @@ class EncryptionTest extends Storage { } /** - * @dataProvider dataTestCopyBetweenStorageVersions * * @param string $sourceInternalPath * @param string $targetInternalPath * @param bool $copyResult * @param bool $encrypted */ - public function testCopyBetweenStorageVersions($sourceInternalPath, $targetInternalPath, $copyResult, $encrypted) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCopyBetweenStorageVersions')] + public function testCopyBetweenStorageVersions($sourceInternalPath, $targetInternalPath, $copyResult, $encrypted): void { $sourceStorage = $this->createMock(\OC\Files\Storage\Storage::class); $targetStorage = $this->createMock(\OC\Files\Storage\Storage::class); @@ -873,8 +838,8 @@ class EncryptionTest extends Storage { $mountPoint = '/mountPoint'; - /** @var \OC\Files\Storage\Wrapper\Encryption |\PHPUnit\Framework\MockObject\MockObject $instance */ - $instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption') + /** @var Encryption |MockObject $instance */ + $instance = $this->getMockBuilder(Encryption::class) ->setConstructorArgs( [ [ @@ -889,12 +854,11 @@ class EncryptionTest extends Storage { $this->file, null, $this->keyStore, - $this->update, $this->mountManager, $this->arrayCache ] ) - ->setMethods(['updateUnencryptedSize', 'getCache']) + ->onlyMethods(['updateUnencryptedSize', 'getCache']) ->getMock(); $targetStorage->expects($this->once())->method('copyFromStorage') @@ -936,7 +900,7 @@ class EncryptionTest extends Storage { $this->assertSame($copyResult, $result); } - public function dataTestCopyBetweenStorageVersions() { + public static function dataTestCopyBetweenStorageVersions(): array { return [ ['/files/foo.txt', '/files_versions/foo.txt.768743', true, true], ['/files/foo.txt', '/files_versions/foo.txt.768743', true, false], @@ -951,17 +915,17 @@ class EncryptionTest extends Storage { } /** - * @dataProvider dataTestIsVersion * @param string $path * @param bool $expected */ - public function testIsVersion($path, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsVersion')] + public function testIsVersion($path, $expected): void { $this->assertSame($expected, $this->invokePrivate($this->instance, 'isVersion', [$path]) ); } - public function dataTestIsVersion() { + public static function dataTestIsVersion(): array { return [ ['files_versions/foo', true], ['/files_versions/foo', true], @@ -973,25 +937,23 @@ class EncryptionTest extends Storage { } /** - * @dataProvider dataTestShouldEncrypt * * @param bool $encryptMountPoint * @param mixed $encryptionModule * @param bool $encryptionModuleShouldEncrypt * @param bool $expected */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestShouldEncrypt')] public function testShouldEncrypt( $encryptMountPoint, $encryptionModule, $encryptionModuleShouldEncrypt, - $expected - ) { + $expected, + ): void { $encryptionManager = $this->createMock(\OC\Encryption\Manager::class); $util = $this->createMock(Util::class); $fileHelper = $this->createMock(IFile::class); - $uid = null; $keyStorage = $this->createMock(IStorage::class); - $update = $this->createMock(Update::class); $mountManager = $this->createMock(\OC\Files\Mount\Manager::class); $mount = $this->createMock(IMountPoint::class); $arrayCache = $this->createMock(ArrayCache::class); @@ -1007,18 +969,17 @@ class EncryptionTest extends Storage { $util, $this->logger, $fileHelper, - $uid, + null, $keyStorage, - $update, $mountManager, $arrayCache ] ) - ->setMethods(['getFullPath', 'getEncryptionModule']) + ->onlyMethods(['getFullPath', 'getEncryptionModule']) ->getMock(); if ($encryptionModule === true) { - /** @var IEncryptionModule|\PHPUnit\Framework\MockObject\MockObject $encryptionModule */ + /** @var IEncryptionModule|MockObject $encryptionModule */ $encryptionModule = $this->createMock(IEncryptionModule::class); } @@ -1056,7 +1017,7 @@ class EncryptionTest extends Storage { $this->assertSame($expected, $result); } - public function dataTestShouldEncrypt() { + public static function dataTestShouldEncrypt(): array { return [ [false, false, false, false], [true, false, false, false], diff --git a/tests/lib/Files/Storage/Wrapper/JailTest.php b/tests/lib/Files/Storage/Wrapper/JailTest.php index 148bd3a4f30..0043e37ba33 100644 --- a/tests/lib/Files/Storage/Wrapper/JailTest.php +++ b/tests/lib/Files/Storage/Wrapper/JailTest.php @@ -1,24 +1,28 @@ <?php + /** - * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Storage\Wrapper; +use OC\Files\Filesystem; +use OC\Files\Storage\Temporary; +use OC\Files\Storage\Wrapper\Jail; + class JailTest extends \Test\Files\Storage\Storage { /** - * @var \OC\Files\Storage\Temporary + * @var Temporary */ private $sourceStorage; protected function setUp(): void { parent::setUp(); - $this->sourceStorage = new \OC\Files\Storage\Temporary([]); + $this->sourceStorage = new Temporary([]); $this->sourceStorage->mkdir('foo'); - $this->instance = new \OC\Files\Storage\Wrapper\Jail([ + $this->instance = new Jail([ 'storage' => $this->sourceStorage, 'root' => 'foo' ]); @@ -28,8 +32,8 @@ class JailTest extends \Test\Files\Storage\Storage { // test that nothing outside our jail is touched $contents = []; $dh = $this->sourceStorage->opendir(''); - while ($file = readdir($dh)) { - if (!\OC\Files\Filesystem::isIgnoredDir($file)) { + while (($file = readdir($dh)) !== false) { + if (!Filesystem::isIgnoredDir($file)) { $contents[] = $file; } } @@ -38,12 +42,12 @@ class JailTest extends \Test\Files\Storage\Storage { parent::tearDown(); } - public function testMkDirRooted() { + public function testMkDirRooted(): void { $this->instance->mkdir('bar'); $this->assertTrue($this->sourceStorage->is_dir('foo/bar')); } - public function testFilePutContentsRooted() { + public function testFilePutContentsRooted(): void { $this->instance->file_put_contents('bar', 'asd'); $this->assertEquals('asd', $this->sourceStorage->file_get_contents('foo/bar')); } diff --git a/tests/lib/Files/Storage/Wrapper/KnownMtimeTest.php b/tests/lib/Files/Storage/Wrapper/KnownMtimeTest.php new file mode 100644 index 00000000000..b1b5582b4ed --- /dev/null +++ b/tests/lib/Files/Storage/Wrapper/KnownMtimeTest.php @@ -0,0 +1,69 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace lib\Files\Storage\Wrapper; + +use OC\Files\Storage\Temporary; +use OC\Files\Storage\Wrapper\KnownMtime; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Clock\ClockInterface; +use Test\Files\Storage\Storage; + +/** + * @group DB + */ +class KnownMtimeTest extends Storage { + /** @var Temporary */ + private $sourceStorage; + + /** @var ClockInterface|MockObject */ + private $clock; + private int $fakeTime = 0; + + protected function setUp(): void { + parent::setUp(); + $this->fakeTime = 0; + $this->sourceStorage = new Temporary([]); + $this->clock = $this->createMock(ClockInterface::class); + $this->clock->method('now')->willReturnCallback(function () { + if ($this->fakeTime) { + return new \DateTimeImmutable("@{$this->fakeTime}"); + } else { + return new \DateTimeImmutable(); + } + }); + $this->instance = $this->getWrappedStorage(); + } + + protected function tearDown(): void { + $this->sourceStorage->cleanUp(); + parent::tearDown(); + } + + protected function getWrappedStorage() { + return new KnownMtime([ + 'storage' => $this->sourceStorage, + 'clock' => $this->clock, + ]); + } + + public function testNewerKnownMtime(): void { + $future = time() + 1000; + $this->fakeTime = $future; + + $this->instance->file_put_contents('foo.txt', 'bar'); + + // fuzzy match since the clock might have ticked + $this->assertLessThan(2, abs(time() - $this->sourceStorage->filemtime('foo.txt'))); + $this->assertEquals($this->sourceStorage->filemtime('foo.txt'), $this->sourceStorage->stat('foo.txt')['mtime']); + $this->assertEquals($this->sourceStorage->filemtime('foo.txt'), $this->sourceStorage->getMetaData('foo.txt')['mtime']); + + $this->assertEquals($future, $this->instance->filemtime('foo.txt')); + $this->assertEquals($future, $this->instance->stat('foo.txt')['mtime']); + $this->assertEquals($future, $this->instance->getMetaData('foo.txt')['mtime']); + } +} diff --git a/tests/lib/Files/Storage/Wrapper/PermissionsMaskTest.php b/tests/lib/Files/Storage/Wrapper/PermissionsMaskTest.php index ea01a0e3e94..a2f3460c58c 100644 --- a/tests/lib/Files/Storage/Wrapper/PermissionsMaskTest.php +++ b/tests/lib/Files/Storage/Wrapper/PermissionsMaskTest.php @@ -1,13 +1,15 @@ <?php + /** - * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Storage\Wrapper; +use OC\Files\Storage\Temporary; +use OC\Files\Storage\Wrapper\PermissionsMask; use OC\Files\Storage\Wrapper\Wrapper; use OCP\Constants; use OCP\Files\Cache\IScanner; @@ -17,13 +19,13 @@ use OCP\Files\Cache\IScanner; */ class PermissionsMaskTest extends \Test\Files\Storage\Storage { /** - * @var \OC\Files\Storage\Temporary + * @var Temporary */ private $sourceStorage; protected function setUp(): void { parent::setUp(); - $this->sourceStorage = new \OC\Files\Storage\Temporary([]); + $this->sourceStorage = new Temporary([]); $this->instance = $this->getMaskedStorage(Constants::PERMISSION_ALL); } @@ -33,19 +35,19 @@ class PermissionsMaskTest extends \Test\Files\Storage\Storage { } protected function getMaskedStorage($mask) { - return new \OC\Files\Storage\Wrapper\PermissionsMask([ + return new PermissionsMask([ 'storage' => $this->sourceStorage, 'mask' => $mask ]); } - public function testMkdirNoCreate() { + public function testMkdirNoCreate(): void { $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE); $this->assertFalse($storage->mkdir('foo')); $this->assertFalse($storage->file_exists('foo')); } - public function testRmdirNoDelete() { + public function testRmdirNoDelete(): void { $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE); $this->assertTrue($storage->mkdir('foo')); $this->assertTrue($storage->file_exists('foo')); @@ -53,25 +55,25 @@ class PermissionsMaskTest extends \Test\Files\Storage\Storage { $this->assertTrue($storage->file_exists('foo')); } - public function testTouchNewFileNoCreate() { + public function testTouchNewFileNoCreate(): void { $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE); $this->assertFalse($storage->touch('foo')); $this->assertFalse($storage->file_exists('foo')); } - public function testTouchNewFileNoUpdate() { + public function testTouchNewFileNoUpdate(): void { $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE); $this->assertTrue($storage->touch('foo')); $this->assertTrue($storage->file_exists('foo')); } - public function testTouchExistingFileNoUpdate() { + public function testTouchExistingFileNoUpdate(): void { $this->sourceStorage->touch('foo'); $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE); $this->assertFalse($storage->touch('foo')); } - public function testUnlinkNoDelete() { + public function testUnlinkNoDelete(): void { $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE); $this->assertTrue($storage->touch('foo')); $this->assertTrue($storage->file_exists('foo')); @@ -79,35 +81,35 @@ class PermissionsMaskTest extends \Test\Files\Storage\Storage { $this->assertTrue($storage->file_exists('foo')); } - public function testPutContentsNewFileNoUpdate() { + public function testPutContentsNewFileNoUpdate(): void { $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE); $this->assertEquals(3, $storage->file_put_contents('foo', 'bar')); $this->assertEquals('bar', $storage->file_get_contents('foo')); } - public function testPutContentsNewFileNoCreate() { + public function testPutContentsNewFileNoCreate(): void { $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE); $this->assertFalse($storage->file_put_contents('foo', 'bar')); } - public function testPutContentsExistingFileNoUpdate() { + public function testPutContentsExistingFileNoUpdate(): void { $this->sourceStorage->touch('foo'); $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE); $this->assertFalse($storage->file_put_contents('foo', 'bar')); } - public function testFopenExistingFileNoUpdate() { + public function testFopenExistingFileNoUpdate(): void { $this->sourceStorage->touch('foo'); $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE); $this->assertFalse($storage->fopen('foo', 'w')); } - public function testFopenNewFileNoCreate() { + public function testFopenNewFileNoCreate(): void { $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE); $this->assertFalse($storage->fopen('foo', 'w')); } - public function testScanNewFiles() { + public function testScanNewFiles(): void { $storage = $this->getMaskedStorage(Constants::PERMISSION_READ + Constants::PERMISSION_CREATE); $storage->file_put_contents('foo', 'bar'); $storage->getScanner()->scan(''); @@ -116,7 +118,7 @@ class PermissionsMaskTest extends \Test\Files\Storage\Storage { $this->assertEquals(Constants::PERMISSION_READ, $storage->getCache()->get('foo')->getPermissions()); } - public function testScanNewWrappedFiles() { + public function testScanNewWrappedFiles(): void { $storage = $this->getMaskedStorage(Constants::PERMISSION_READ + Constants::PERMISSION_CREATE); $wrappedStorage = new Wrapper(['storage' => $storage]); $wrappedStorage->file_put_contents('foo', 'bar'); @@ -126,9 +128,9 @@ class PermissionsMaskTest extends \Test\Files\Storage\Storage { $this->assertEquals(Constants::PERMISSION_READ, $storage->getCache()->get('foo')->getPermissions()); } - public function testScanNewFilesNested() { + public function testScanNewFilesNested(): void { $storage = $this->getMaskedStorage(Constants::PERMISSION_READ + Constants::PERMISSION_CREATE + Constants::PERMISSION_UPDATE); - $nestedStorage = new \OC\Files\Storage\Wrapper\PermissionsMask([ + $nestedStorage = new PermissionsMask([ 'storage' => $storage, 'mask' => Constants::PERMISSION_READ + Constants::PERMISSION_CREATE ]); @@ -141,7 +143,7 @@ class PermissionsMaskTest extends \Test\Files\Storage\Storage { $this->assertEquals(Constants::PERMISSION_READ, $wrappedStorage->getCache()->get('foo')->getPermissions()); } - public function testScanUnchanged() { + public function testScanUnchanged(): void { $this->sourceStorage->mkdir('foo'); $this->sourceStorage->file_put_contents('foo/bar.txt', 'bar'); @@ -150,7 +152,7 @@ class PermissionsMaskTest extends \Test\Files\Storage\Storage { $storage = $this->getMaskedStorage(Constants::PERMISSION_READ); $scanner = $storage->getScanner(); $called = false; - $scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', function () use (&$called) { + $scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', function () use (&$called): void { $called = true; }); $scanner->scan('foo', IScanner::SCAN_RECURSIVE, IScanner::REUSE_ETAG | IScanner::REUSE_SIZE); @@ -158,7 +160,7 @@ class PermissionsMaskTest extends \Test\Files\Storage\Storage { $this->assertFalse($called); } - public function testScanUnchangedWrapped() { + public function testScanUnchangedWrapped(): void { $this->sourceStorage->mkdir('foo'); $this->sourceStorage->file_put_contents('foo/bar.txt', 'bar'); @@ -168,7 +170,7 @@ class PermissionsMaskTest extends \Test\Files\Storage\Storage { $wrappedStorage = new Wrapper(['storage' => $storage]); $scanner = $wrappedStorage->getScanner(); $called = false; - $scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', function () use (&$called) { + $scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', function () use (&$called): void { $called = true; }); $scanner->scan('foo', IScanner::SCAN_RECURSIVE, IScanner::REUSE_ETAG | IScanner::REUSE_SIZE); diff --git a/tests/lib/Files/Storage/Wrapper/QuotaTest.php b/tests/lib/Files/Storage/Wrapper/QuotaTest.php index 82b3e3556cb..2878fe6ca92 100644 --- a/tests/lib/Files/Storage/Wrapper/QuotaTest.php +++ b/tests/lib/Files/Storage/Wrapper/QuotaTest.php @@ -1,9 +1,9 @@ <?php + /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Storage\Wrapper; @@ -11,8 +11,10 @@ namespace Test\Files\Storage\Wrapper; //ensure the constants are loaded use OC\Files\Cache\CacheEntry; use OC\Files\Storage\Local; - -\OC::$loader->load('\OC\Files\Filesystem'); +use OC\Files\Storage\Wrapper\Quota; +use OCP\Files; +use OCP\ITempManager; +use OCP\Server; /** * Class QuotaTest @@ -30,13 +32,13 @@ class QuotaTest extends \Test\Files\Storage\Storage { protected function setUp(): void { parent::setUp(); - $this->tmpDir = \OC::$server->getTempManager()->getTemporaryFolder(); - $storage = new \OC\Files\Storage\Local(['datadir' => $this->tmpDir]); - $this->instance = new \OC\Files\Storage\Wrapper\Quota(['storage' => $storage, 'quota' => 10000000]); + $this->tmpDir = Server::get(ITempManager::class)->getTemporaryFolder(); + $storage = new Local(['datadir' => $this->tmpDir]); + $this->instance = new Quota(['storage' => $storage, 'quota' => 10000000]); } protected function tearDown(): void { - \OC_Helper::rmdirr($this->tmpDir); + Files::rmdirr($this->tmpDir); parent::tearDown(); } @@ -44,30 +46,30 @@ class QuotaTest extends \Test\Files\Storage\Storage { * @param integer $limit */ protected function getLimitedStorage($limit) { - $storage = new \OC\Files\Storage\Local(['datadir' => $this->tmpDir]); + $storage = new Local(['datadir' => $this->tmpDir]); $storage->mkdir('files'); $storage->getScanner()->scan(''); - return new \OC\Files\Storage\Wrapper\Quota(['storage' => $storage, 'quota' => $limit]); + return new Quota(['storage' => $storage, 'quota' => $limit]); } - public function testFilePutContentsNotEnoughSpace() { + public function testFilePutContentsNotEnoughSpace(): void { $instance = $this->getLimitedStorage(3); $this->assertFalse($instance->file_put_contents('files/foo', 'foobar')); } - public function testCopyNotEnoughSpace() { + public function testCopyNotEnoughSpace(): void { $instance = $this->getLimitedStorage(9); $this->assertEquals(6, $instance->file_put_contents('files/foo', 'foobar')); $instance->getScanner()->scan(''); $this->assertFalse($instance->copy('files/foo', 'files/bar')); } - public function testFreeSpace() { + public function testFreeSpace(): void { $instance = $this->getLimitedStorage(9); $this->assertEquals(9, $instance->free_space('')); } - public function testFreeSpaceWithUsedSpace() { + public function testFreeSpaceWithUsedSpace(): void { $instance = $this->getLimitedStorage(9); $instance->getCache()->put( '', ['size' => 3] @@ -75,9 +77,9 @@ class QuotaTest extends \Test\Files\Storage\Storage { $this->assertEquals(6, $instance->free_space('')); } - public function testFreeSpaceWithUnknownDiskSpace() { + public function testFreeSpaceWithUnknownDiskSpace(): void { $storage = $this->getMockBuilder(Local::class) - ->setMethods(['free_space']) + ->onlyMethods(['free_space']) ->setConstructorArgs([['datadir' => $this->tmpDir]]) ->getMock(); $storage->expects($this->any()) @@ -85,14 +87,14 @@ class QuotaTest extends \Test\Files\Storage\Storage { ->willReturn(-2); $storage->getScanner()->scan(''); - $instance = new \OC\Files\Storage\Wrapper\Quota(['storage' => $storage, 'quota' => 9]); + $instance = new Quota(['storage' => $storage, 'quota' => 9]); $instance->getCache()->put( '', ['size' => 3] ); $this->assertEquals(6, $instance->free_space('')); } - public function testFreeSpaceWithUsedSpaceAndEncryption() { + public function testFreeSpaceWithUsedSpaceAndEncryption(): void { $instance = $this->getLimitedStorage(9); $instance->getCache()->put( '', ['size' => 7] @@ -100,7 +102,7 @@ class QuotaTest extends \Test\Files\Storage\Storage { $this->assertEquals(2, $instance->free_space('')); } - public function testFWriteNotEnoughSpace() { + public function testFWriteNotEnoughSpace(): void { $instance = $this->getLimitedStorage(9); $stream = $instance->fopen('files/foo', 'w+'); $this->assertEquals(6, fwrite($stream, 'foobar')); @@ -109,7 +111,7 @@ class QuotaTest extends \Test\Files\Storage\Storage { $this->assertEquals('foobarqwe', $instance->file_get_contents('files/foo')); } - public function testStreamCopyWithEnoughSpace() { + public function testStreamCopyWithEnoughSpace(): void { $instance = $this->getLimitedStorage(16); $inputStream = fopen('data://text/plain,foobarqwerty', 'r'); $outputStream = $instance->fopen('files/foo', 'w+'); @@ -120,7 +122,7 @@ class QuotaTest extends \Test\Files\Storage\Storage { fclose($outputStream); } - public function testStreamCopyNotEnoughSpace() { + public function testStreamCopyNotEnoughSpace(): void { $instance = $this->getLimitedStorage(9); $inputStream = fopen('data://text/plain,foobarqwerty', 'r'); $outputStream = $instance->fopen('files/foo', 'w+'); @@ -131,21 +133,21 @@ class QuotaTest extends \Test\Files\Storage\Storage { fclose($outputStream); } - public function testReturnFalseWhenFopenFailed() { + public function testReturnFalseWhenFopenFailed(): void { $failStorage = $this->getMockBuilder(Local::class) - ->setMethods(['fopen']) + ->onlyMethods(['fopen']) ->setConstructorArgs([['datadir' => $this->tmpDir]]) ->getMock(); $failStorage->expects($this->any()) ->method('fopen') ->willReturn(false); - $instance = new \OC\Files\Storage\Wrapper\Quota(['storage' => $failStorage, 'quota' => 1000]); + $instance = new Quota(['storage' => $failStorage, 'quota' => 1000]); $this->assertFalse($instance->fopen('failedfopen', 'r')); } - public function testReturnRegularStreamOnRead() { + public function testReturnRegularStreamOnRead(): void { $instance = $this->getLimitedStorage(9); // create test file first @@ -164,7 +166,7 @@ class QuotaTest extends \Test\Files\Storage\Storage { fclose($stream); } - public function testReturnRegularStreamWhenOutsideFiles() { + public function testReturnRegularStreamWhenOutsideFiles(): void { $instance = $this->getLimitedStorage(9); $instance->mkdir('files_other'); @@ -175,7 +177,7 @@ class QuotaTest extends \Test\Files\Storage\Storage { fclose($stream); } - public function testReturnQuotaStreamOnWrite() { + public function testReturnQuotaStreamOnWrite(): void { $instance = $this->getLimitedStorage(9); $stream = $instance->fopen('files/foo', 'w+'); $meta = stream_get_meta_data($stream); @@ -184,7 +186,7 @@ class QuotaTest extends \Test\Files\Storage\Storage { fclose($stream); } - public function testSpaceRoot() { + public function testSpaceRoot(): void { $storage = $this->getMockBuilder(Local::class)->disableOriginalConstructor()->getMock(); $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')->disableOriginalConstructor()->getMock(); $storage->expects($this->once()) @@ -198,24 +200,24 @@ class QuotaTest extends \Test\Files\Storage\Storage { ->with('files') ->willReturn(new CacheEntry(['size' => 50])); - $instance = new \OC\Files\Storage\Wrapper\Quota(['storage' => $storage, 'quota' => 1024, 'root' => 'files']); + $instance = new Quota(['storage' => $storage, 'quota' => 1024, 'root' => 'files']); $this->assertEquals(1024 - 50, $instance->free_space('')); } - public function testInstanceOfStorageWrapper() { + public function testInstanceOfStorageWrapper(): void { $this->assertTrue($this->instance->instanceOfStorage('\OC\Files\Storage\Local')); $this->assertTrue($this->instance->instanceOfStorage('\OC\Files\Storage\Wrapper\Wrapper')); $this->assertTrue($this->instance->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')); } - public function testNoMkdirQuotaZero() { + public function testNoMkdirQuotaZero(): void { $instance = $this->getLimitedStorage(0.0); $this->assertFalse($instance->mkdir('files')); $this->assertFalse($instance->mkdir('files/foobar')); } - public function testMkdirQuotaZeroTrashbin() { + public function testMkdirQuotaZeroTrashbin(): void { $instance = $this->getLimitedStorage(0.0); $this->assertTrue($instance->mkdir('files_trashbin')); $this->assertTrue($instance->mkdir('files_trashbin/files')); @@ -223,7 +225,7 @@ class QuotaTest extends \Test\Files\Storage\Storage { $this->assertTrue($instance->mkdir('cache')); } - public function testNoTouchQuotaZero() { + public function testNoTouchQuotaZero(): void { $instance = $this->getLimitedStorage(0.0); $this->assertFalse($instance->touch('foobar')); } diff --git a/tests/lib/Files/Storage/Wrapper/WrapperTest.php b/tests/lib/Files/Storage/Wrapper/WrapperTest.php index b9bb54e09b2..60f139450c7 100644 --- a/tests/lib/Files/Storage/Wrapper/WrapperTest.php +++ b/tests/lib/Files/Storage/Wrapper/WrapperTest.php @@ -1,13 +1,19 @@ <?php + /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Storage\Wrapper; +use OC\Files\Storage\Local; +use OC\Files\Storage\Wrapper\Wrapper; +use OCP\Files; +use OCP\ITempManager; +use OCP\Server; + class WrapperTest extends \Test\Files\Storage\Storage { /** * @var string tmpDir @@ -17,17 +23,17 @@ class WrapperTest extends \Test\Files\Storage\Storage { protected function setUp(): void { parent::setUp(); - $this->tmpDir = \OC::$server->getTempManager()->getTemporaryFolder(); - $storage = new \OC\Files\Storage\Local(['datadir' => $this->tmpDir]); - $this->instance = new \OC\Files\Storage\Wrapper\Wrapper(['storage' => $storage]); + $this->tmpDir = Server::get(ITempManager::class)->getTemporaryFolder(); + $storage = new Local(['datadir' => $this->tmpDir]); + $this->instance = new Wrapper(['storage' => $storage]); } protected function tearDown(): void { - \OC_Helper::rmdirr($this->tmpDir); + Files::rmdirr($this->tmpDir); parent::tearDown(); } - public function testInstanceOfStorageWrapper() { + public function testInstanceOfStorageWrapper(): void { $this->assertTrue($this->instance->instanceOfStorage('\OC\Files\Storage\Local')); $this->assertTrue($this->instance->instanceOfStorage('\OC\Files\Storage\Wrapper\Wrapper')); } diff --git a/tests/lib/Files/Stream/DummyEncryptionWrapper.php b/tests/lib/Files/Stream/DummyEncryptionWrapper.php index 223e8256cd2..89904e6de73 100644 --- a/tests/lib/Files/Stream/DummyEncryptionWrapper.php +++ b/tests/lib/Files/Stream/DummyEncryptionWrapper.php @@ -1,27 +1,16 @@ <?php + /** - * @author Björn Schießle <schiessle@owncloud.com> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace Test\Files\Stream; -class DummyEncryptionWrapper extends \OC\Files\Stream\Encryption { +use OC\Files\Stream\Encryption; + +class DummyEncryptionWrapper extends Encryption { /** * simulate a non-seekable stream wrapper by always return false * diff --git a/tests/lib/Files/Stream/EncryptionTest.php b/tests/lib/Files/Stream/EncryptionTest.php index 67311e886e8..62eaab3cc7e 100644 --- a/tests/lib/Files/Stream/EncryptionTest.php +++ b/tests/lib/Files/Stream/EncryptionTest.php @@ -1,29 +1,40 @@ <?php +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ namespace Test\Files\Stream; +use OC\Encryption\File; +use OC\Encryption\Util; use OC\Files\Cache\CacheEntry; +use OC\Files\Storage\Storage; +use OC\Files\Storage\Wrapper\Wrapper; +use OC\Files\Stream\Encryption; use OC\Files\View; use OC\Memcache\ArrayCache; +use OCP\Encryption\IEncryptionModule; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Cache\ICache; use OCP\ICacheFactory; use OCP\IConfig; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; class EncryptionTest extends \Test\TestCase { public const DEFAULT_WRAPPER = '\OC\Files\Stream\Encryption'; - /** @var \OCP\Encryption\IEncryptionModule | \PHPUnit\Framework\MockObject\MockObject */ - private $encryptionModule; + private IEncryptionModule&MockObject $encryptionModule; /** - * @param string $fileName - * @param string $mode - * @param integer $unencryptedSize + * @param class-string<Wrapper> $wrapper * @return resource */ - protected function getStream($fileName, $mode, $unencryptedSize, $wrapper = self::DEFAULT_WRAPPER, $unencryptedSizeOnClose = 0) { + protected function getStream(string $fileName, string $mode, int $unencryptedSize, string $wrapper = self::DEFAULT_WRAPPER, int $unencryptedSizeOnClose = 0) { clearstatcache(); $size = filesize($fileName); $source = fopen($fileName, $mode); @@ -46,16 +57,16 @@ class EncryptionTest extends \Test\TestCase { ->getMock(); $file = $this->getMockBuilder('\OC\Encryption\File') ->disableOriginalConstructor() - ->setMethods(['getAccessList']) + ->onlyMethods(['getAccessList']) ->getMock(); $file->expects($this->any())->method('getAccessList')->willReturn([]); $util = $this->getMockBuilder('\OC\Encryption\Util') - ->setMethods(['getUidAndFilename']) + ->onlyMethods(['getUidAndFilename']) ->setConstructorArgs([new View(), new \OC\User\Manager( $config, - $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), - $this->createMock(IEventDispatcher::class) + $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), ), $groupManager, $config, $arrayCache]) ->getMock(); $util->expects($this->any()) @@ -70,38 +81,49 @@ class EncryptionTest extends \Test\TestCase { $cache->expects($this->any())->method('get')->willReturn($entry); $cache->expects($this->any())->method('update')->with(5, ['encrypted' => 3, 'encryptedVersion' => 3, 'unencrypted_size' => $unencryptedSizeOnClose]); - - return $wrapper::wrap($source, $internalPath, - $fullPath, $header, $uid, $this->encryptionModule, $storage, $encStorage, - $util, $file, $mode, $size, $unencryptedSize, 8192, $wrapper); + return $wrapper::wrap( + $source, + $internalPath, + $fullPath, + $header, + $uid, + $this->encryptionModule, + $storage, + $encStorage, + $util, + $file, + $mode, + $size, + $unencryptedSize, + 8192, + true, + $wrapper, + ); } - /** - * @dataProvider dataProviderStreamOpen() - */ - public function testStreamOpen($isMasterKeyUsed, - $mode, - $fullPath, - $fileExists, - $expectedSharePath, - $expectedSize, - $expectedUnencryptedSize, - $expectedReadOnly) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataProviderStreamOpen')] + public function testStreamOpen( + $isMasterKeyUsed, + $mode, + $fullPath, + $fileExists, + $expectedSharePath, + $expectedSize, + $expectedUnencryptedSize, + $expectedReadOnly, + ): void { // build mocks - $encryptionModuleMock = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule') - ->disableOriginalConstructor()->getMock(); + $encryptionModuleMock = $this->createMock(IEncryptionModule::class); $encryptionModuleMock->expects($this->any())->method('needDetailedAccessList')->willReturn(!$isMasterKeyUsed); $encryptionModuleMock->expects($this->once()) ->method('getUnencryptedBlockSize')->willReturn(99); $encryptionModuleMock->expects($this->once()) - ->method('begin')->willReturn(true); + ->method('begin')->willReturn([]); - $storageMock = $this->getMockBuilder('\OC\Files\Storage\Storage') - ->disableOriginalConstructor()->getMock(); + $storageMock = $this->createMock(Storage::class); $storageMock->expects($this->once())->method('file_exists')->willReturn($fileExists); - $fileMock = $this->getMockBuilder('\OC\Encryption\File') - ->disableOriginalConstructor()->getMock(); + $fileMock = $this->createMock(File::class); if ($isMasterKeyUsed) { $fileMock->expects($this->never())->method('getAccessList'); } else { @@ -111,18 +133,20 @@ class EncryptionTest extends \Test\TestCase { return []; }); } - $utilMock = $this->getMockBuilder('\OC\Encryption\Util') + $utilMock = $this->getMockBuilder(Util::class) ->disableOriginalConstructor()->getMock(); $utilMock->expects($this->any()) ->method('getHeaderSize') ->willReturn(8192); // get a instance of the stream wrapper - $streamWrapper = $this->getMockBuilder('\OC\Files\Stream\Encryption') - ->setMethods(['loadContext', 'writeHeader', 'skipHeader'])->disableOriginalConstructor()->getMock(); + $streamWrapper = $this->getMockBuilder(Encryption::class) + ->onlyMethods(['loadContext', 'writeHeader', 'skipHeader']) + ->disableOriginalConstructor() + ->getMock(); // set internal properties of the stream wrapper - $stream = new \ReflectionClass('\OC\Files\Stream\Encryption'); + $stream = new \ReflectionClass(Encryption::class); $encryptionModule = $stream->getProperty('encryptionModule'); $encryptionModule->setAccessible(true); $encryptionModule->setValue($streamWrapper, $encryptionModuleMock); @@ -148,6 +172,8 @@ class EncryptionTest extends \Test\TestCase { $header->setValue($streamWrapper, []); $header->setAccessible(false); $this->invokePrivate($streamWrapper, 'signed', [true]); + $this->invokePrivate($streamWrapper, 'internalPath', [$fullPath]); + $this->invokePrivate($streamWrapper, 'uid', ['test']); // call stream_open, that's the method we want to test $dummyVar = 'foo'; @@ -156,27 +182,21 @@ class EncryptionTest extends \Test\TestCase { // check internal properties $size = $stream->getProperty('size'); $size->setAccessible(true); - $this->assertSame($expectedSize, - $size->getValue($streamWrapper) - ); + $this->assertSame($expectedSize, $size->getValue($streamWrapper)); $size->setAccessible(false); $unencryptedSize = $stream->getProperty('unencryptedSize'); $unencryptedSize->setAccessible(true); - $this->assertSame($expectedUnencryptedSize, - $unencryptedSize->getValue($streamWrapper) - ); + $this->assertSame($expectedUnencryptedSize, $unencryptedSize->getValue($streamWrapper)); $unencryptedSize->setAccessible(false); $readOnly = $stream->getProperty('readOnly'); $readOnly->setAccessible(true); - $this->assertSame($expectedReadOnly, - $readOnly->getValue($streamWrapper) - ); + $this->assertSame($expectedReadOnly, $readOnly->getValue($streamWrapper)); $readOnly->setAccessible(false); } - public function dataProviderStreamOpen() { + public static function dataProviderStreamOpen(): array { return [ [false, 'r', '/foo/bar/test.txt', true, '/foo/bar/test.txt', null, null, true], [false, 'r', '/foo/bar/test.txt', false, '/foo/bar', null, null, true], @@ -187,8 +207,8 @@ class EncryptionTest extends \Test\TestCase { ]; } - public function testWriteRead() { - $fileName = tempnam("/tmp", "FOO"); + public function testWriteRead(): void { + $fileName = tempnam('/tmp', 'FOO'); $stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, 6); $this->assertEquals(6, fwrite($stream, 'foobar')); fclose($stream); @@ -208,8 +228,8 @@ class EncryptionTest extends \Test\TestCase { unlink($fileName); } - public function testRewind() { - $fileName = tempnam("/tmp", "FOO"); + public function testRewind(): void { + $fileName = tempnam('/tmp', 'FOO'); $stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, 6); $this->assertEquals(6, fwrite($stream, 'foobar')); $this->assertEquals(true, rewind($stream)); @@ -225,8 +245,8 @@ class EncryptionTest extends \Test\TestCase { unlink($fileName); } - public function testSeek() { - $fileName = tempnam("/tmp", "FOO"); + public function testSeek(): void { + $fileName = tempnam('/tmp', 'FOO'); $stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, 9); $this->assertEquals(6, fwrite($stream, 'foobar')); @@ -247,7 +267,7 @@ class EncryptionTest extends \Test\TestCase { unlink($fileName); } - public function dataFilesProvider() { + public static function dataFilesProvider(): array { return [ ['lorem-big.txt'], ['block-aligned.txt'], @@ -255,13 +275,11 @@ class EncryptionTest extends \Test\TestCase { ]; } - /** - * @dataProvider dataFilesProvider - */ - public function testWriteReadBigFile($testFile) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataFilesProvider')] + public function testWriteReadBigFile($testFile): void { $expectedData = file_get_contents(\OC::$SERVERROOT . '/tests/data/' . $testFile); // write it - $fileName = tempnam("/tmp", "FOO"); + $fileName = tempnam('/tmp', 'FOO'); $stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, strlen($expectedData)); // while writing the file from the beginning to the end we should never try // to read parts of the file. This should only happen for write operations @@ -292,17 +310,19 @@ class EncryptionTest extends \Test\TestCase { /** * simulate a non-seekable storage - * - * @dataProvider dataFilesProvider */ - public function testWriteToNonSeekableStorage($testFile) { - $wrapper = $this->getMockBuilder('\OC\Files\Stream\Encryption') - ->setMethods(['parentSeekStream'])->getMock(); - $wrapper->expects($this->any())->method('parentSeekStream')->willReturn(false); + #[\PHPUnit\Framework\Attributes\DataProvider('dataFilesProvider')] + public function testWriteToNonSeekableStorage($testFile): void { + $wrapper = $this->getMockBuilder(Encryption::class) + ->onlyMethods(['parentStreamSeek']) + ->getMock(); + $wrapper->expects($this->any()) + ->method('parentStreamSeek') + ->willReturn(false); $expectedData = file_get_contents(\OC::$SERVERROOT . '/tests/data/' . $testFile); // write it - $fileName = tempnam("/tmp", "FOO"); + $fileName = tempnam('/tmp', 'FOO'); $stream = $this->getStream($fileName, 'w+', 0, '\Test\Files\Stream\DummyEncryptionWrapper', strlen($expectedData)); // while writing the file from the beginning to the end we should never try // to read parts of the file. This should only happen for write operations @@ -331,13 +351,10 @@ class EncryptionTest extends \Test\TestCase { unlink($fileName); } - /** - * @return \PHPUnit\Framework\MockObject\MockObject - */ - protected function buildMockModule() { - $encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule') + protected function buildMockModule(): IEncryptionModule&MockObject { + $encryptionModule = $this->getMockBuilder(IEncryptionModule::class) ->disableOriginalConstructor() - ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList']) + ->onlyMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList']) ->getMock(); $encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE'); diff --git a/tests/lib/Files/Stream/HashWrapperTest.php b/tests/lib/Files/Stream/HashWrapperTest.php index e4bee18070d..459bc5c4318 100644 --- a/tests/lib/Files/Stream/HashWrapperTest.php +++ b/tests/lib/Files/Stream/HashWrapperTest.php @@ -2,23 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Stream; @@ -27,10 +12,8 @@ use OC\Files\Stream\HashWrapper; use Test\TestCase; class HashWrapperTest extends TestCase { - /** - * @dataProvider hashProvider - */ - public function testHashStream($data, string $algo, string $hash) { + #[\PHPUnit\Framework\Attributes\DataProvider('hashProvider')] + public function testHashStream($data, string $algo, string $hash): void { if (!is_resource($data)) { $tmpData = fopen('php://temp', 'r+'); if ($data !== null) { @@ -40,13 +23,13 @@ class HashWrapperTest extends TestCase { $data = $tmpData; } - $wrapper = HashWrapper::wrap($data, $algo, function ($result) use ($hash) { + $wrapper = HashWrapper::wrap($data, $algo, function ($result) use ($hash): void { $this->assertEquals($hash, $result); }); stream_get_contents($wrapper); } - public function hashProvider() { + public static function hashProvider(): array { return [ ['foo', 'md5', 'acbd18db4cc2f85cedef654fccc4a4d8'], ['foo', 'sha1', '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'], diff --git a/tests/lib/Files/Stream/QuotaTest.php b/tests/lib/Files/Stream/QuotaTest.php index 69cd322a2b3..4248d14f5a1 100644 --- a/tests/lib/Files/Stream/QuotaTest.php +++ b/tests/lib/Files/Stream/QuotaTest.php @@ -1,13 +1,15 @@ <?php + /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Files\Stream; +use OC\Files\Stream\Quota; + class QuotaTest extends \Test\TestCase { /** * @param string $mode @@ -16,24 +18,24 @@ class QuotaTest extends \Test\TestCase { */ protected function getStream($mode, $limit) { $source = fopen('php://temp', $mode); - return \OC\Files\Stream\Quota::wrap($source, $limit); + return Quota::wrap($source, $limit); } - public function testWriteEnoughSpace() { + public function testWriteEnoughSpace(): void { $stream = $this->getStream('w+', 100); $this->assertEquals(6, fwrite($stream, 'foobar')); rewind($stream); $this->assertEquals('foobar', fread($stream, 100)); } - public function testWriteNotEnoughSpace() { + public function testWriteNotEnoughSpace(): void { $stream = $this->getStream('w+', 3); $this->assertEquals(3, fwrite($stream, 'foobar')); rewind($stream); $this->assertEquals('foo', fread($stream, 100)); } - public function testWriteNotEnoughSpaceSecondTime() { + public function testWriteNotEnoughSpaceSecondTime(): void { $stream = $this->getStream('w+', 9); $this->assertEquals(6, fwrite($stream, 'foobar')); $this->assertEquals(3, fwrite($stream, 'qwerty')); @@ -41,7 +43,7 @@ class QuotaTest extends \Test\TestCase { $this->assertEquals('foobarqwe', fread($stream, 100)); } - public function testWriteEnoughSpaceRewind() { + public function testWriteEnoughSpaceRewind(): void { $stream = $this->getStream('w+', 6); $this->assertEquals(6, fwrite($stream, 'foobar')); rewind($stream); @@ -50,7 +52,7 @@ class QuotaTest extends \Test\TestCase { $this->assertEquals('qwebar', fread($stream, 100)); } - public function testWriteNotEnoughSpaceRead() { + public function testWriteNotEnoughSpaceRead(): void { $stream = $this->getStream('w+', 6); $this->assertEquals(6, fwrite($stream, 'foobar')); rewind($stream); @@ -58,26 +60,26 @@ class QuotaTest extends \Test\TestCase { $this->assertEquals(0, fwrite($stream, 'qwe')); } - public function testWriteNotEnoughSpaceExistingStream() { + public function testWriteNotEnoughSpaceExistingStream(): void { $source = fopen('php://temp', 'w+'); fwrite($source, 'foobar'); - $stream = \OC\Files\Stream\Quota::wrap($source, 3); + $stream = Quota::wrap($source, 3); $this->assertEquals(3, fwrite($stream, 'foobar')); rewind($stream); $this->assertEquals('foobarfoo', fread($stream, 100)); } - public function testWriteNotEnoughSpaceExistingStreamRewind() { + public function testWriteNotEnoughSpaceExistingStreamRewind(): void { $source = fopen('php://temp', 'w+'); fwrite($source, 'foobar'); - $stream = \OC\Files\Stream\Quota::wrap($source, 3); + $stream = Quota::wrap($source, 3); rewind($stream); $this->assertEquals(6, fwrite($stream, 'qwerty')); rewind($stream); $this->assertEquals('qwerty', fread($stream, 100)); } - public function testFseekReturnsSuccess() { + public function testFseekReturnsSuccess(): void { $stream = $this->getStream('w+', 100); fwrite($stream, '0123456789'); $this->assertEquals(0, fseek($stream, 3, SEEK_SET)); @@ -85,7 +87,7 @@ class QuotaTest extends \Test\TestCase { $this->assertEquals(0, fseek($stream, -4, SEEK_END)); } - public function testWriteAfterSeekEndWithEnoughSpace() { + public function testWriteAfterSeekEndWithEnoughSpace(): void { $stream = $this->getStream('w+', 100); fwrite($stream, '0123456789'); fseek($stream, -3, SEEK_END); @@ -94,7 +96,7 @@ class QuotaTest extends \Test\TestCase { $this->assertEquals('0123456abcdefghijk', fread($stream, 100)); } - public function testWriteAfterSeekEndWithNotEnoughSpace() { + public function testWriteAfterSeekEndWithNotEnoughSpace(): void { $stream = $this->getStream('w+', 13); fwrite($stream, '0123456789'); // seek forward first to potentially week out @@ -107,7 +109,7 @@ class QuotaTest extends \Test\TestCase { $this->assertEquals('0123456abcdef', fread($stream, 100)); } - public function testWriteAfterSeekSetWithEnoughSpace() { + public function testWriteAfterSeekSetWithEnoughSpace(): void { $stream = $this->getStream('w+', 100); fwrite($stream, '0123456789'); fseek($stream, 7, SEEK_SET); @@ -116,7 +118,7 @@ class QuotaTest extends \Test\TestCase { $this->assertEquals('0123456abcdefghijk', fread($stream, 100)); } - public function testWriteAfterSeekSetWithNotEnoughSpace() { + public function testWriteAfterSeekSetWithNotEnoughSpace(): void { $stream = $this->getStream('w+', 13); fwrite($stream, '0123456789'); fseek($stream, 7, SEEK_SET); @@ -125,7 +127,7 @@ class QuotaTest extends \Test\TestCase { $this->assertEquals('0123456abcdef', fread($stream, 100)); } - public function testWriteAfterSeekCurWithEnoughSpace() { + public function testWriteAfterSeekCurWithEnoughSpace(): void { $stream = $this->getStream('w+', 100); fwrite($stream, '0123456789'); rewind($stream); @@ -137,7 +139,7 @@ class QuotaTest extends \Test\TestCase { $this->assertEquals('0123456abcdefghijk', fread($stream, 100)); } - public function testWriteAfterSeekCurWithNotEnoughSpace() { + public function testWriteAfterSeekCurWithNotEnoughSpace(): void { $stream = $this->getStream('w+', 13); fwrite($stream, '0123456789'); rewind($stream); diff --git a/tests/lib/Files/Type/DetectionTest.php b/tests/lib/Files/Type/DetectionTest.php index 079117f5622..c30b21de401 100644 --- a/tests/lib/Files/Type/DetectionTest.php +++ b/tests/lib/Files/Type/DetectionTest.php @@ -1,28 +1,16 @@ <?php + /** - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace Test\Files\Type; use OC\Files\Type\Detection; use OCP\IURLGenerator; +use OCP\Server; use Psr\Log\LoggerInterface; class DetectionTest extends \Test\TestCase { @@ -32,14 +20,14 @@ class DetectionTest extends \Test\TestCase { protected function setUp(): void { parent::setUp(); $this->detection = new Detection( - \OC::$server->getURLGenerator(), - \OC::$server->get(LoggerInterface::class), + Server::get(IURLGenerator::class), + Server::get(LoggerInterface::class), \OC::$SERVERROOT . '/config/', \OC::$SERVERROOT . '/resources/config/' ); } - public function dataDetectPath(): array { + public static function dataDetectPath(): array { return [ ['foo.txt', 'text/plain'], ['foo.png', 'image/png'], @@ -59,16 +47,16 @@ class DetectionTest extends \Test\TestCase { } /** - * @dataProvider dataDetectPath * * @param string $path * @param string $expected */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataDetectPath')] public function testDetectPath(string $path, string $expected): void { $this->assertEquals($expected, $this->detection->detectPath($path)); } - public function dataDetectContent(): array { + public static function dataDetectContent(): array { return [ ['/', 'httpd/unix-directory'], ['/data.tar.gz', 'application/gzip'], @@ -79,16 +67,16 @@ class DetectionTest extends \Test\TestCase { } /** - * @dataProvider dataDetectContent * * @param string $path * @param string $expected */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataDetectContent')] public function testDetectContent(string $path, string $expected): void { $this->assertEquals($expected, $this->detection->detectContent(\OC::$SERVERROOT . '/tests/data' . $path)); } - public function dataDetect(): array { + public static function dataDetect(): array { return [ ['/', 'httpd/unix-directory'], ['/data.tar.gz', 'application/gzip'], @@ -99,11 +87,11 @@ class DetectionTest extends \Test\TestCase { } /** - * @dataProvider dataDetect * * @param string $path * @param string $expected */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataDetect')] public function testDetect(string $path, string $expected): void { $this->assertEquals($expected, $this->detection->detect(\OC::$SERVERROOT . '/tests/data' . $path)); } @@ -114,7 +102,42 @@ class DetectionTest extends \Test\TestCase { $this->assertEquals($expected, $result); } - public function dataGetSecureMimeType(): array { + public static function dataMimeTypeCustom(): array { + return [ + ['123', 'foobar/123'], + ['a123', 'foobar/123'], + ['bar', 'foobar/bar'], + ]; + } + + /** + * + * @param string $ext + * @param string $mime + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataMimeTypeCustom')] + public function testDetectMimeTypeCustom(string $ext, string $mime): void { + $confDir = sys_get_temp_dir(); + file_put_contents($confDir . '/mimetypemapping.dist.json', json_encode([])); + + /** @var IURLGenerator $urlGenerator */ + $urlGenerator = $this->getMockBuilder(IURLGenerator::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var LoggerInterface $logger */ + $logger = $this->createMock(LoggerInterface::class); + + // Create new mapping file + file_put_contents($confDir . '/mimetypemapping.dist.json', json_encode([$ext => [$mime]])); + + $detection = new Detection($urlGenerator, $logger, $confDir, $confDir); + $mappings = $detection->getAllMappings(); + $this->assertArrayHasKey($ext, $mappings); + $this->assertEquals($mime, $detection->detectPath('foo.' . $ext)); + } + + public static function dataGetSecureMimeType(): array { return [ ['image/svg+xml', 'text/plain'], ['image/png', 'image/png'], @@ -122,16 +145,16 @@ class DetectionTest extends \Test\TestCase { } /** - * @dataProvider dataGetSecureMimeType * * @param string $mimeType * @param string $expected */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetSecureMimeType')] public function testGetSecureMimeType(string $mimeType, string $expected): void { $this->assertEquals($expected, $this->detection->getSecureMimeType($mimeType)); } - public function testMimeTypeIcon() { + public function testMimeTypeIcon(): void { if (!class_exists('org\\bovigo\\vfs\\vfsStream')) { $this->markTestSkipped('Package vfsStream not installed'); } @@ -234,14 +257,16 @@ class DetectionTest extends \Test\TestCase { ->getMock(); //Only call the url generator once + $calls = [ + ['core', 'filetypes/my-type.png'], + ['core', 'filetypes/my.png'], + ]; $urlGenerator->expects($this->exactly(2)) ->method('imagePath') - ->withConsecutive( - [$this->equalTo('core'), $this->equalTo('filetypes/my-type.png')], - [$this->equalTo('core'), $this->equalTo('filetypes/my.png')] - ) ->willReturnCallback( - function ($appName, $file) { + function ($appName, $file) use (&$calls) { + $expected = array_shift($calls); + $this->assertEquals($expected, [$appName, $file]); if ($file === 'filetypes/my.png') { return 'my.svg'; } @@ -264,15 +289,17 @@ class DetectionTest extends \Test\TestCase { ->getMock(); //Only call the url generator once + $calls = [ + ['core', 'filetypes/foo-bar.png'], + ['core', 'filetypes/foo.png'], + ['core', 'filetypes/file.png'], + ]; $urlGenerator->expects($this->exactly(3)) ->method('imagePath') - ->withConsecutive( - [$this->equalTo('core'), $this->equalTo('filetypes/foo-bar.png')], - [$this->equalTo('core'), $this->equalTo('filetypes/foo.png')], - [$this->equalTo('core'), $this->equalTo('filetypes/file.png')] - ) ->willReturnCallback( - function ($appName, $file) { + function ($appName, $file) use (&$calls) { + $expected = array_shift($calls); + $this->assertEquals($expected, [$appName, $file]); if ($file === 'filetypes/file.png') { return 'file.svg'; } diff --git a/tests/lib/Files/Type/LoaderTest.php b/tests/lib/Files/Type/LoaderTest.php index fd3ec552dd2..44745a50dc0 100644 --- a/tests/lib/Files/Type/LoaderTest.php +++ b/tests/lib/Files/Type/LoaderTest.php @@ -1,37 +1,24 @@ <?php + /** - * @author Robin McCorkell <rmccorkell@owncloud.com> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace Test\Files\Type; use OC\Files\Type\Loader; use OCP\IDBConnection; +use OCP\Server; +use Test\TestCase; -class LoaderTest extends \Test\TestCase { - /** @var IDBConnection */ - protected $db; - /** @var Loader */ - protected $loader; +class LoaderTest extends TestCase { + protected IDBConnection $db; + protected Loader $loader; protected function setUp(): void { - $this->db = \OC::$server->getDatabaseConnection(); + $this->db = Server::get(IDBConnection::class); $this->loader = new Loader($this->db); } @@ -45,7 +32,7 @@ class LoaderTest extends \Test\TestCase { } - public function testGetMimetype() { + public function testGetMimetype(): void { $qb = $this->db->getQueryBuilder(); $qb->insert('mimetypes') ->values([ @@ -61,13 +48,13 @@ class LoaderTest extends \Test\TestCase { $this->assertEquals('testing/mymimetype', $mimetype); } - public function testGetNonexistentMimetype() { + public function testGetNonexistentMimetype(): void { $this->assertFalse($this->loader->exists('testing/nonexistent')); // hopefully this ID doesn't exist $this->assertNull($this->loader->getMimetypeById(12345)); } - public function testStore() { + public function testStore(): void { $this->assertFalse($this->loader->exists('testing/mymimetype')); $mimetypeId = $this->loader->getId('testing/mymimetype'); @@ -85,7 +72,7 @@ class LoaderTest extends \Test\TestCase { $this->assertEquals($mimetypeId, $this->loader->getId('testing/mymimetype')); } - public function testStoreExists() { + public function testStoreExists(): void { $mimetypeId = $this->loader->getId('testing/mymimetype'); $mimetypeId2 = $this->loader->getId('testing/mymimetype'); diff --git a/tests/lib/Files/Utils/ScannerTest.php b/tests/lib/Files/Utils/ScannerTest.php index eb0b9938886..49399ef70a6 100644 --- a/tests/lib/Files/Utils/ScannerTest.php +++ b/tests/lib/Files/Utils/ScannerTest.php @@ -1,9 +1,9 @@ <?php + /** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Utils; @@ -11,20 +11,25 @@ namespace Test\Files\Utils; use OC\Files\Filesystem; use OC\Files\Mount\MountPoint; use OC\Files\Storage\Temporary; +use OC\Files\Utils\Scanner; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Config\IMountProvider; +use OCP\Files\Config\IMountProviderCollection; use OCP\Files\Storage\IStorageFactory; +use OCP\IDBConnection; use OCP\IUser; +use OCP\IUserManager; +use OCP\Server; use Psr\Log\LoggerInterface; -class TestScanner extends \OC\Files\Utils\Scanner { +class TestScanner extends Scanner { /** - * @var \OC\Files\Mount\MountPoint[] $mounts + * @var MountPoint[] $mounts */ private $mounts = []; /** - * @param \OC\Files\Mount\MountPoint $mount + * @param MountPoint $mount */ public function addMount($mount) { $this->mounts[] = $mount; @@ -52,17 +57,17 @@ class ScannerTest extends \Test\TestCase { parent::setUp(); $this->userBackend = new \Test\Util\User\Dummy(); - \OC::$server->getUserManager()->registerBackend($this->userBackend); + Server::get(IUserManager::class)->registerBackend($this->userBackend); $this->loginAsUser(); } protected function tearDown(): void { $this->logout(); - \OC::$server->getUserManager()->removeBackend($this->userBackend); + Server::get(IUserManager::class)->removeBackend($this->userBackend); parent::tearDown(); } - public function testReuseExistingRoot() { + public function testReuseExistingRoot(): void { $storage = new Temporary([]); $mount = new MountPoint($storage, ''); Filesystem::getMountManager()->addMount($mount); @@ -72,7 +77,7 @@ class ScannerTest extends \Test\TestCase { $storage->file_put_contents('foo.txt', 'qwerty'); $storage->file_put_contents('folder/bar.txt', 'qwerty'); - $scanner = new TestScanner('', \OC::$server->getDatabaseConnection(), $this->createMock(IEventDispatcher::class), \OC::$server->get(LoggerInterface::class)); + $scanner = new TestScanner('', Server::get(IDBConnection::class), $this->createMock(IEventDispatcher::class), Server::get(LoggerInterface::class)); $scanner->addMount($mount); $scanner->scan(''); @@ -84,7 +89,7 @@ class ScannerTest extends \Test\TestCase { $this->assertEquals($oldRoot, $newRoot); } - public function testReuseExistingFile() { + public function testReuseExistingFile(): void { $storage = new Temporary([]); $mount = new MountPoint($storage, ''); Filesystem::getMountManager()->addMount($mount); @@ -94,7 +99,7 @@ class ScannerTest extends \Test\TestCase { $storage->file_put_contents('foo.txt', 'qwerty'); $storage->file_put_contents('folder/bar.txt', 'qwerty'); - $scanner = new TestScanner('', \OC::$server->getDatabaseConnection(), $this->createMock(IEventDispatcher::class), \OC::$server->get(LoggerInterface::class)); + $scanner = new TestScanner('', Server::get(IDBConnection::class), $this->createMock(IEventDispatcher::class), Server::get(LoggerInterface::class)); $scanner->addMount($mount); $scanner->scan(''); @@ -106,7 +111,7 @@ class ScannerTest extends \Test\TestCase { $this->assertEquals($old, $new); } - public function testScanSubMount() { + public function testScanSubMount(): void { $uid = $this->getUniqueID(); $this->userBackend->createUser($uid, 'test'); @@ -125,24 +130,21 @@ class ScannerTest extends \Test\TestCase { } }); - \OC::$server->getMountProviderCollection()->registerProvider($mountProvider); + Server::get(IMountProviderCollection::class)->registerProvider($mountProvider); $cache = $storage->getCache(); $storage->mkdir('folder'); $storage->file_put_contents('foo.txt', 'qwerty'); $storage->file_put_contents('folder/bar.txt', 'qwerty'); - $scanner = new \OC\Files\Utils\Scanner($uid, \OC::$server->getDatabaseConnection(), \OC::$server->query(IEventDispatcher::class), \OC::$server->get(LoggerInterface::class)); + $scanner = new Scanner($uid, Server::get(IDBConnection::class), Server::get(IEventDispatcher::class), Server::get(LoggerInterface::class)); $this->assertFalse($cache->inCache('folder/bar.txt')); $scanner->scan('/' . $uid . '/files/foo'); $this->assertTrue($cache->inCache('folder/bar.txt')); } - /** - * @return array - */ - public function invalidPathProvider() { + public static function invalidPathProvider(): array { return [ [ '../', @@ -157,18 +159,18 @@ class ScannerTest extends \Test\TestCase { } /** - * @dataProvider invalidPathProvider * @param string $invalidPath */ - public function testInvalidPathScanning($invalidPath) { + #[\PHPUnit\Framework\Attributes\DataProvider('invalidPathProvider')] + public function testInvalidPathScanning($invalidPath): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Invalid path to scan'); - $scanner = new TestScanner('', \OC::$server->getDatabaseConnection(), $this->createMock(IEventDispatcher::class), \OC::$server->get(LoggerInterface::class)); + $scanner = new TestScanner('', Server::get(IDBConnection::class), $this->createMock(IEventDispatcher::class), Server::get(LoggerInterface::class)); $scanner->scan($invalidPath); } - public function testPropagateEtag() { + public function testPropagateEtag(): void { $storage = new Temporary([]); $mount = new MountPoint($storage, ''); Filesystem::getMountManager()->addMount($mount); @@ -178,7 +180,7 @@ class ScannerTest extends \Test\TestCase { $storage->file_put_contents('folder/bar.txt', 'qwerty'); $storage->touch('folder/bar.txt', time() - 200); - $scanner = new TestScanner('', \OC::$server->getDatabaseConnection(), $this->createMock(IEventDispatcher::class), \OC::$server->get(LoggerInterface::class)); + $scanner = new TestScanner('', Server::get(IDBConnection::class), $this->createMock(IEventDispatcher::class), Server::get(LoggerInterface::class)); $scanner->addMount($mount); $scanner->scan(''); @@ -192,7 +194,7 @@ class ScannerTest extends \Test\TestCase { $this->assertNotEquals($oldRoot->getEtag(), $newRoot->getEtag()); } - public function testShallow() { + public function testShallow(): void { $storage = new Temporary([]); $mount = new MountPoint($storage, ''); Filesystem::getMountManager()->addMount($mount); @@ -204,7 +206,7 @@ class ScannerTest extends \Test\TestCase { $storage->file_put_contents('folder/bar.txt', 'qwerty'); $storage->file_put_contents('folder/subfolder/foobar.txt', 'qwerty'); - $scanner = new TestScanner('', \OC::$server->getDatabaseConnection(), $this->createMock(IEventDispatcher::class), \OC::$server->get(LoggerInterface::class)); + $scanner = new TestScanner('', Server::get(IDBConnection::class), $this->createMock(IEventDispatcher::class), Server::get(LoggerInterface::class)); $scanner->addMount($mount); $scanner->scan('', $recusive = false); diff --git a/tests/lib/Files/ViewTest.php b/tests/lib/Files/ViewTest.php index 2189e7c09f4..ad27c3f798c 100644 --- a/tests/lib/Files/ViewTest.php +++ b/tests/lib/Files/ViewTest.php @@ -1,13 +1,14 @@ <?php + /** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. */ + * 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; -use OCP\Cache\CappedMemoryCache; +use OC\Files\Cache\Scanner; use OC\Files\Cache\Watcher; use OC\Files\Filesystem; use OC\Files\Mount\MountPoint; @@ -16,46 +17,76 @@ use OC\Files\Storage\Common; use OC\Files\Storage\Storage; use OC\Files\Storage\Temporary; use OC\Files\View; +use OC\Share20\ShareDisableChecker; +use OCA\Files_Trashbin\Trash\ITrashManager; +use OCP\Cache\CappedMemoryCache; use OCP\Constants; use OCP\Files\Config\IMountProvider; +use OCP\Files\Config\IMountProviderCollection; +use OCP\Files\Config\IUserMountCache; use OCP\Files\FileInfo; +use OCP\Files\ForbiddenException; use OCP\Files\GenericFileException; +use OCP\Files\InvalidPathException; use OCP\Files\Mount\IMountManager; +use OCP\Files\NotFoundException; use OCP\Files\Storage\IStorage; +use OCP\Files\Storage\IStorageFactory; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\IGroup; +use OCP\IGroupManager; +use OCP\ITempManager; +use OCP\IUser; +use OCP\IUserManager; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; +use OCP\Server; +use OCP\Share\IManager as IShareManager; use OCP\Share\IShare; use OCP\Util; +use PHPUnit\Framework\MockObject\MockObject; use Test\HookHelper; use Test\TestMoveableMountPoint; use Test\Traits\UserTrait; class TemporaryNoTouch extends Temporary { - public function touch($path, $mtime = null) { + public function touch(string $path, ?int $mtime = null): bool { return false; } } class TemporaryNoCross extends Temporary { - public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = null) { + public function copyFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath, bool $preserveMtime = false): bool { return Common::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime); } - public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + public function moveFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool { return Common::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } } class TemporaryNoLocal extends Temporary { - public function instanceOfStorage($className) { - if ($className === '\OC\Files\Storage\Local') { + public function instanceOfStorage(string $class): bool { + if ($class === '\OC\Files\Storage\Local') { return false; } else { - return parent::instanceOfStorage($className); + return parent::instanceOfStorage($class); } } } +class TestEventHandler { + public function umount() { + } + public function post_umount() { + } + public function preCallback() { + } + public function postCallback() { + } +} + /** * Class ViewTest * @@ -67,7 +98,7 @@ class ViewTest extends \Test\TestCase { use UserTrait; /** - * @var \OC\Files\Storage\Storage[] $storages + * @var Storage[] $storages */ private $storages = []; @@ -77,28 +108,28 @@ class ViewTest extends \Test\TestCase { private $user; /** - * @var \OCP\IUser + * @var IUser */ private $userObject; /** - * @var \OCP\IGroup + * @var IGroup */ private $groupObject; - /** @var \OC\Files\Storage\Storage */ + /** @var Storage */ private $tempStorage; protected function setUp(): void { parent::setUp(); \OC_Hook::clear(); - \OC_User::clearBackends(); - \OC_User::useBackend(new \Test\Util\User\Dummy()); + Server::get(IUserManager::class)->clearBackends(); + Server::get(IUserManager::class)->registerBackend(new \Test\Util\User\Dummy()); //login - $userManager = \OC::$server->getUserManager(); - $groupManager = \OC::$server->getGroupManager(); + $userManager = Server::get(IUserManager::class); + $groupManager = Server::get(IGroupManager::class); $this->user = 'test'; $this->userObject = $userManager->createUser('test', 'test'); @@ -108,7 +139,7 @@ class ViewTest extends \Test\TestCase { self::loginAsUser($this->user); /** @var IMountManager $manager */ - $manager = \OC::$server->get(IMountManager::class); + $manager = Server::get(IMountManager::class); $manager->removeMount('/test'); $this->tempStorage = null; @@ -129,13 +160,13 @@ class ViewTest extends \Test\TestCase { self::logout(); /** @var SetupManager $setupManager */ - $setupManager = \OC::$server->get(SetupManager::class); + $setupManager = Server::get(SetupManager::class); $setupManager->setupRoot(); $this->userObject->delete(); $this->groupObject->delete(); - $mountProviderCollection = \OC::$server->getMountProviderCollection(); + $mountProviderCollection = Server::get(IMountProviderCollection::class); self::invokePrivate($mountProviderCollection, 'providers', [[]]); parent::tearDown(); @@ -144,7 +175,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testCacheAPI() { + public function testCacheAPI(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); @@ -227,35 +258,43 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testGetPath() { + public function testGetPath(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('test'); $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); - Filesystem::mount($storage1, [], '/'); - Filesystem::mount($storage2, [], '/substorage'); - Filesystem::mount($storage3, [], '/folder/anotherstorage'); + Filesystem::mount($storage1, [], '/test/files'); + Filesystem::mount($storage2, [], '/test/files/substorage'); + Filesystem::mount($storage3, [], '/test/files/folder/anotherstorage'); - $rootView = new View(''); + $userMountCache = Server::get(IUserMountCache::class); + $userMountCache->registerMounts($user, [ + new MountPoint($storage1, '/test/files'), + new MountPoint($storage2, '/test/files/substorage'), + new MountPoint($storage3, '/test/files/folder/anotherstorage'), + ]); + + $rootView = new View('/test/files'); $cachedData = $rootView->getFileInfo('/foo.txt'); - /** @var int $id1 */ - $id1 = $cachedData['fileid']; + $id1 = $cachedData->getId(); $this->assertEquals('/foo.txt', $rootView->getPath($id1)); $cachedData = $rootView->getFileInfo('/substorage/foo.txt'); - /** @var int $id2 */ - $id2 = $cachedData['fileid']; + $id2 = $cachedData->getId(); $this->assertEquals('/substorage/foo.txt', $rootView->getPath($id2)); - $folderView = new View('/substorage'); + $folderView = new View('/test/files/substorage'); $this->assertEquals('/foo.txt', $folderView->getPath($id2)); } - public function testGetPathNotExisting() { - $this->expectException(\OCP\Files\NotFoundException::class); + public function testGetPathNotExisting(): void { + $this->expectException(NotFoundException::class); $storage1 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); @@ -271,7 +310,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testMountPointOverwrite() { + public function testMountPointOverwrite(): void { $storage1 = $this->getTestStorage(false); $storage2 = $this->getTestStorage(); $storage1->mkdir('substorage'); @@ -283,21 +322,19 @@ class ViewTest extends \Test\TestCase { $this->assertCount(4, $folderContent); } - public function sharingDisabledPermissionProvider() { + public static function sharingDisabledPermissionProvider(): array { return [ ['no', '', true], ['yes', 'group1', false], ]; } - /** - * @dataProvider sharingDisabledPermissionProvider - */ - public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGroups, $excludeGroupsList, $expectedShareable) { + #[\PHPUnit\Framework\Attributes\DataProvider('sharingDisabledPermissionProvider')] + public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGroups, $excludeGroupsList, $expectedShareable): void { // Reset sharing disabled for users cache - self::invokePrivate(\OC::$server->getShareManager(), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); + self::invokePrivate(Server::get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); - $config = \OC::$server->getConfig(); + $config = Server::get(IConfig::class); $oldExcludeGroupsFlag = $config->getAppValue('core', 'shareapi_exclude_groups', 'no'); $oldExcludeGroupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', ''); $config->setAppValue('core', 'shareapi_exclude_groups', $excludeGroups); @@ -320,10 +357,10 @@ class ViewTest extends \Test\TestCase { $config->setAppValue('core', 'shareapi_exclude_groups_list', $oldExcludeGroupsList); // Reset sharing disabled for users cache - self::invokePrivate(\OC::$server->getShareManager(), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); + self::invokePrivate(Server::get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); } - public function testCacheIncompleteFolder() { + public function testCacheIncompleteFolder(): void { $storage1 = $this->getTestStorage(false); Filesystem::mount($storage1, [], '/incomplete'); $rootView = new View('/incomplete'); @@ -336,7 +373,7 @@ class ViewTest extends \Test\TestCase { $this->assertCount(1, $entries); } - public function testAutoScan() { + public function testAutoScan(): void { $storage1 = $this->getTestStorage(false); $storage2 = $this->getTestStorage(false); Filesystem::mount($storage1, [], '/'); @@ -357,7 +394,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testSearch() { + public function testSearch(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); @@ -407,7 +444,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testWatcher() { + public function testWatcher(): void { $storage1 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); $storage1->getWatcher()->setPolicy(Watcher::CHECK_ALWAYS); @@ -428,7 +465,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testCopyBetweenStorageNoCross() { + public function testCopyBetweenStorageNoCross(): void { $storage1 = $this->getTestStorage(true, TemporaryNoCross::class); $storage2 = $this->getTestStorage(true, TemporaryNoCross::class); $this->copyBetweenStorages($storage1, $storage2); @@ -437,7 +474,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testCopyBetweenStorageCross() { + public function testCopyBetweenStorageCross(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $this->copyBetweenStorages($storage1, $storage2); @@ -446,7 +483,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testCopyBetweenStorageCrossNonLocal() { + public function testCopyBetweenStorageCrossNonLocal(): void { $storage1 = $this->getTestStorage(true, TemporaryNoLocal::class); $storage2 = $this->getTestStorage(true, TemporaryNoLocal::class); $this->copyBetweenStorages($storage1, $storage2); @@ -474,7 +511,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testMoveBetweenStorageNoCross() { + public function testMoveBetweenStorageNoCross(): void { $storage1 = $this->getTestStorage(true, TemporaryNoCross::class); $storage2 = $this->getTestStorage(true, TemporaryNoCross::class); $this->moveBetweenStorages($storage1, $storage2); @@ -483,7 +520,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testMoveBetweenStorageCross() { + public function testMoveBetweenStorageCross(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $this->moveBetweenStorages($storage1, $storage2); @@ -492,17 +529,17 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testMoveBetweenStorageCrossNonLocal() { + public function testMoveBetweenStorageCrossNonLocal(): void { $storage1 = $this->getTestStorage(true, TemporaryNoLocal::class); $storage2 = $this->getTestStorage(true, TemporaryNoLocal::class); $this->moveBetweenStorages($storage1, $storage2); } public function moveBetweenStorages($storage1, $storage2) { - Filesystem::mount($storage1, [], '/'); - Filesystem::mount($storage2, [], '/substorage'); + Filesystem::mount($storage1, [], '/' . $this->user . '/'); + Filesystem::mount($storage2, [], '/' . $this->user . '/substorage'); - $rootView = new View(''); + $rootView = new View('/' . $this->user); $rootView->rename('foo.txt', 'substorage/folder/foo.txt'); $this->assertFalse($rootView->file_exists('foo.txt')); $this->assertTrue($rootView->file_exists('substorage/folder/foo.txt')); @@ -515,7 +552,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testUnlink() { + public function testUnlink(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); @@ -535,14 +572,12 @@ class ViewTest extends \Test\TestCase { $this->assertFalse($rootView->file_exists('substorage/bar.txt')); } - public function rmdirOrUnlinkDataProvider() { + public static function rmdirOrUnlinkDataProvider(): array { return [['rmdir'], ['unlink']]; } - /** - * @dataProvider rmdirOrUnlinkDataProvider - */ - public function testRmdir($method) { + #[\PHPUnit\Framework\Attributes\DataProvider('rmdirOrUnlinkDataProvider')] + public function testRmdir($method): void { $storage1 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); @@ -561,7 +596,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testUnlinkRootMustFail() { + public function testUnlinkRootMustFail(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); @@ -580,7 +615,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testTouch() { + public function testTouch(): void { $storage = $this->getTestStorage(true, TemporaryNoTouch::class); Filesystem::mount($storage, [], '/'); @@ -604,7 +639,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testTouchFloat() { + public function testTouchFloat(): void { $storage = $this->getTestStorage(true, TemporaryNoTouch::class); Filesystem::mount($storage, [], '/'); @@ -621,7 +656,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testViewHooks() { + public function testViewHooks(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $defaultRoot = Filesystem::getRoot(); @@ -646,7 +681,7 @@ class ViewTest extends \Test\TestCase { $this->hookPath = $params['path']; } - public function testSearchNotOutsideView() { + public function testSearchNotOutsideView(): void { $storage1 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); $storage1->rename('folder', 'foo'); @@ -662,11 +697,11 @@ class ViewTest extends \Test\TestCase { /** * @param bool $scan * @param string $class - * @return \OC\Files\Storage\Storage + * @return Storage */ private function getTestStorage($scan = true, $class = Temporary::class) { /** - * @var \OC\Files\Storage\Storage $storage + * @var Storage $storage */ $storage = new $class([]); $textData = "dummy file data\n"; @@ -687,7 +722,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testViewHooksIfRootStartsTheSame() { + public function testViewHooksIfRootStartsTheSame(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $defaultRoot = Filesystem::getRoot(); @@ -718,7 +753,7 @@ class ViewTest extends \Test\TestCase { $this->hookCreatePath = $params['path']; } - public function testEditNoCreateHook() { + public function testEditNoCreateHook(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $defaultRoot = Filesystem::getRoot(); @@ -748,10 +783,8 @@ class ViewTest extends \Test\TestCase { \OC_Hook::clear('OC_Filesystem', 'post_write'); } - /** - * @dataProvider resolvePathTestProvider - */ - public function testResolvePath($expected, $pathToTest) { + #[\PHPUnit\Framework\Attributes\DataProvider('resolvePathTestProvider')] + public function testResolvePath($expected, $pathToTest): void { $storage1 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); @@ -767,7 +800,7 @@ class ViewTest extends \Test\TestCase { $this->assertTrue($exists); } - public function resolvePathTestProvider() { + public static function resolvePathTestProvider(): array { return [ ['foo.txt', 'foo.txt'], ['foo.txt', '/foo.txt'], @@ -782,7 +815,7 @@ class ViewTest extends \Test\TestCase { ]; } - public function testUTF8Names() { + public function testUTF8Names(): void { $names = ['虚', '和知しゃ和で', 'regular ascii', 'sɨˈrɪlɪk', 'ѨѬ', 'أنا أحب القراءة كثيرا']; $storage = new Temporary([]); @@ -825,7 +858,7 @@ class ViewTest extends \Test\TestCase { * 1024 is the max path length in mac */ $folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789'; - $tmpdirLength = strlen(\OC::$server->getTempManager()->getTemporaryFolder()); + $tmpdirLength = strlen(Server::get(ITempManager::class)->getTemporaryFolder()); if (\OC_Util::runningOnMac()) { $depth = ((1024 - $tmpdirLength) / 57); } else { @@ -861,7 +894,7 @@ class ViewTest extends \Test\TestCase { } } - public function testTouchNotSupported() { + public function testTouchNotSupported(): void { $storage = new TemporaryNoTouch([]); $scanner = $storage->getScanner(); Filesystem::mount($storage, [], '/test/'); @@ -872,13 +905,13 @@ class ViewTest extends \Test\TestCase { $info = $view->getFileInfo('/test/test'); $view->touch('/test/test', $past); - $scanner->scanFile('test', \OC\Files\Cache\Scanner::REUSE_ETAG); + $scanner->scanFile('test', Scanner::REUSE_ETAG); $info2 = $view->getFileInfo('/test/test'); $this->assertSame($info['etag'], $info2['etag']); } - public function testWatcherEtagCrossStorage() { + public function testWatcherEtagCrossStorage(): void { $storage1 = new Temporary([]); $storage2 = new Temporary([]); $scanner1 = $storage1->getScanner(); @@ -910,29 +943,29 @@ class ViewTest extends \Test\TestCase { $this->assertNotEquals($newFolderInfo->getEtag(), $oldEtag); } - /** - * @dataProvider absolutePathProvider - */ - public function testGetAbsolutePath($expectedPath, $relativePath) { + #[\PHPUnit\Framework\Attributes\DataProvider('absolutePathProvider')] + public function testGetAbsolutePath($expectedPath, $relativePath): void { $view = new View('/files'); $this->assertEquals($expectedPath, $view->getAbsolutePath($relativePath)); } - public function testPartFileInfo() { + public function testPartFileInfo(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); Filesystem::mount($storage, [], '/test/'); - $storage->file_put_contents('test.part', 'foobar'); + $sizeWritten = $storage->file_put_contents('test.part', 'foobar'); $scanner->scan(''); $view = new View('/test'); $info = $view->getFileInfo('test.part'); $this->assertInstanceOf('\OCP\Files\FileInfo', $info); $this->assertNull($info->getId()); + $this->assertEquals(6, $sizeWritten); $this->assertEquals(6, $info->getSize()); + $this->assertEquals('foobar', $view->file_get_contents('test.part')); } - public function absolutePathProvider() { + public static function absolutePathProvider(): array { return [ ['/files/', ''], ['/files/0', '0'], @@ -944,32 +977,28 @@ class ViewTest extends \Test\TestCase { ]; } - /** - * @dataProvider chrootRelativePathProvider - */ - public function testChrootGetRelativePath($root, $absolutePath, $expectedPath) { + #[\PHPUnit\Framework\Attributes\DataProvider('chrootRelativePathProvider')] + public function testChrootGetRelativePath($root, $absolutePath, $expectedPath): void { $view = new View('/files'); $view->chroot($root); $this->assertEquals($expectedPath, $view->getRelativePath($absolutePath)); } - public function chrootRelativePathProvider() { - return $this->relativePathProvider('/'); + public static function chrootRelativePathProvider(): array { + return self::relativePathProvider('/'); } - /** - * @dataProvider initRelativePathProvider - */ - public function testInitGetRelativePath($root, $absolutePath, $expectedPath) { + #[\PHPUnit\Framework\Attributes\DataProvider('initRelativePathProvider')] + public function testInitGetRelativePath($root, $absolutePath, $expectedPath): void { $view = new View($root); $this->assertEquals($expectedPath, $view->getRelativePath($absolutePath)); } - public function initRelativePathProvider() { - return $this->relativePathProvider(null); + public static function initRelativePathProvider(): array { + return self::relativePathProvider(null); } - public function relativePathProvider($missingRootExpectedPath) { + public static function relativePathProvider($missingRootExpectedPath): array { return [ // No root - returns the path ['', '/files', '/files'], @@ -1040,7 +1069,7 @@ class ViewTest extends \Test\TestCase { ]; } - public function testFileView() { + public function testFileView(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->file_put_contents('foo.txt', 'bar'); @@ -1056,11 +1085,9 @@ class ViewTest extends \Test\TestCase { $this->assertEquals('foo', $view->file_get_contents('')); } - /** - * @dataProvider tooLongPathDataProvider - */ - public function testTooLongPath($operation, $param0 = null) { - $this->expectException(\OCP\Files\InvalidPathException::class); + #[\PHPUnit\Framework\Attributes\DataProvider('tooLongPathDataProvider')] + public function testTooLongPath($operation, $param0 = null): void { + $this->expectException(InvalidPathException::class); $longPath = ''; @@ -1089,14 +1116,13 @@ class ViewTest extends \Test\TestCase { call_user_func([$rootView, $operation], $longPath, $param0); } - public function tooLongPathDataProvider() { + public static function tooLongPathDataProvider(): array { return [ ['getAbsolutePath'], ['getRelativePath'], ['getMountPoint'], ['resolvePath'], ['getLocalFile'], - ['getLocalFolder'], ['mkdir'], ['rmdir'], ['opendir'], @@ -1135,7 +1161,7 @@ class ViewTest extends \Test\TestCase { ]; } - public function testRenameCrossStoragePreserveMtime() { + public function testRenameCrossStoragePreserveMtime(): void { $storage1 = new Temporary([]); $storage2 = new Temporary([]); $storage1->mkdir('sub'); @@ -1160,11 +1186,11 @@ class ViewTest extends \Test\TestCase { $this->assertEquals($time, $view->filemtime('/test/sub/storage/foo/bar.txt')); } - public function testRenameFailDeleteTargetKeepSource() { + public function testRenameFailDeleteTargetKeepSource(): void { $this->doTestCopyRenameFail('rename'); } - public function testCopyFailDeleteTargetKeepSource() { + public function testCopyFailDeleteTargetKeepSource(): void { $this->doTestCopyRenameFail('copy'); } @@ -1173,11 +1199,11 @@ class ViewTest extends \Test\TestCase { /** @var \PHPUnit\Framework\MockObject\MockObject|Temporary $storage2 */ $storage2 = $this->getMockBuilder(TemporaryNoCross::class) ->setConstructorArgs([[]]) - ->setMethods(['fopen', 'writeStream']) + ->onlyMethods(['fopen', 'writeStream']) ->getMock(); $storage2->method('writeStream') - ->willThrowException(new GenericFileException("Failed to copy stream")); + ->willThrowException(new GenericFileException('Failed to copy stream')); $storage1->mkdir('sub'); $storage1->file_put_contents('foo.txt', '0123456789ABCDEFGH'); @@ -1215,11 +1241,11 @@ class ViewTest extends \Test\TestCase { $this->assertFalse($storage2->getCache()->get('dirtomove/indir2.txt')); } - public function testDeleteFailKeepCache() { + public function testDeleteFailKeepCache(): void { /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) ->setConstructorArgs([[]]) - ->setMethods(['unlink']) + ->onlyMethods(['unlink']) ->getMock(); $storage->expects($this->once()) ->method('unlink') @@ -1236,7 +1262,7 @@ class ViewTest extends \Test\TestCase { $this->assertTrue($cache->inCache('foo.txt')); } - public function directoryTraversalProvider() { + public static function directoryTraversalProvider(): array { return [ ['../test/'], ['..\\test\\my/../folder'], @@ -1245,16 +1271,16 @@ class ViewTest extends \Test\TestCase { } /** - * @dataProvider directoryTraversalProvider * @param string $root */ - public function testConstructDirectoryTraversalException($root) { + #[\PHPUnit\Framework\Attributes\DataProvider('directoryTraversalProvider')] + public function testConstructDirectoryTraversalException($root): void { $this->expectException(\Exception::class); new View($root); } - public function testRenameOverWrite() { + public function testRenameOverWrite(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->mkdir('sub'); @@ -1267,36 +1293,36 @@ class ViewTest extends \Test\TestCase { $this->assertTrue($view->rename('/test/foo.txt', '/test/foo/bar.txt')); } - public function testSetMountOptionsInStorage() { + public function testSetMountOptionsInStorage(): void { $mount = new MountPoint(Temporary::class, '/asd/', [[]], Filesystem::getLoader(), ['foo' => 'bar']); Filesystem::getMountManager()->addMount($mount); - /** @var \OC\Files\Storage\Common $storage */ + /** @var Common $storage */ $storage = $mount->getStorage(); $this->assertEquals($storage->getMountOption('foo'), 'bar'); } - public function testSetMountOptionsWatcherPolicy() { + public function testSetMountOptionsWatcherPolicy(): void { $mount = new MountPoint(Temporary::class, '/asd/', [[]], Filesystem::getLoader(), ['filesystem_check_changes' => Watcher::CHECK_NEVER]); Filesystem::getMountManager()->addMount($mount); - /** @var \OC\Files\Storage\Common $storage */ + /** @var Common $storage */ $storage = $mount->getStorage(); $watcher = $storage->getWatcher(); $this->assertEquals(Watcher::CHECK_NEVER, $watcher->getPolicy()); } - public function testGetAbsolutePathOnNull() { + public function testGetAbsolutePathOnNull(): void { $view = new View(); $this->assertNull($view->getAbsolutePath(null)); } - public function testGetRelativePathOnNull() { + public function testGetRelativePathOnNull(): void { $view = new View(); $this->assertNull($view->getRelativePath(null)); } - public function testNullAsRoot() { - $this->expectException(\InvalidArgumentException::class); + public function testNullAsRoot(): void { + $this->expectException(\TypeError::class); new View(null); } @@ -1305,13 +1331,13 @@ class ViewTest extends \Test\TestCase { * e.g. reading from a folder that's being renamed * * - * @dataProvider dataLockPaths * * @param string $rootPath * @param string $pathPrefix */ - public function testReadFromWriteLockedPath($rootPath, $pathPrefix) { - $this->expectException(\OCP\Lock\LockedException::class); + #[\PHPUnit\Framework\Attributes\DataProvider('dataLockPaths')] + public function testReadFromWriteLockedPath($rootPath, $pathPrefix): void { + $this->expectException(LockedException::class); $rootPath = str_replace('{folder}', 'files', $rootPath); $pathPrefix = str_replace('{folder}', 'files', $pathPrefix); @@ -1326,12 +1352,12 @@ class ViewTest extends \Test\TestCase { /** * Reading from a files_encryption folder that's being renamed * - * @dataProvider dataLockPaths * * @param string $rootPath * @param string $pathPrefix */ - public function testReadFromWriteUnlockablePath($rootPath, $pathPrefix) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataLockPaths')] + public function testReadFromWriteUnlockablePath($rootPath, $pathPrefix): void { $rootPath = str_replace('{folder}', 'files_encryption', $rootPath); $pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix); @@ -1346,13 +1372,13 @@ class ViewTest extends \Test\TestCase { * e.g. writing a file that's being downloaded * * - * @dataProvider dataLockPaths * * @param string $rootPath * @param string $pathPrefix */ - public function testWriteToReadLockedFile($rootPath, $pathPrefix) { - $this->expectException(\OCP\Lock\LockedException::class); + #[\PHPUnit\Framework\Attributes\DataProvider('dataLockPaths')] + public function testWriteToReadLockedFile($rootPath, $pathPrefix): void { + $this->expectException(LockedException::class); $rootPath = str_replace('{folder}', 'files', $rootPath); $pathPrefix = str_replace('{folder}', 'files', $pathPrefix); @@ -1367,12 +1393,12 @@ class ViewTest extends \Test\TestCase { /** * Writing a file that's being downloaded * - * @dataProvider dataLockPaths * * @param string $rootPath * @param string $pathPrefix */ - public function testWriteToReadUnlockableFile($rootPath, $pathPrefix) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataLockPaths')] + public function testWriteToReadUnlockableFile($rootPath, $pathPrefix): void { $rootPath = str_replace('{folder}', 'files_encryption', $rootPath); $pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix); @@ -1386,8 +1412,8 @@ class ViewTest extends \Test\TestCase { /** * Test that locks are on mount point paths instead of mount root */ - public function testLockLocalMountPointPathInsteadOfStorageRoot() { - $lockingProvider = \OC::$server->getLockingProvider(); + public function testLockLocalMountPointPathInsteadOfStorageRoot(): void { + $lockingProvider = Server::get(ILockingProvider::class); $view = new View('/testuser/files/'); $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); @@ -1416,8 +1442,8 @@ class ViewTest extends \Test\TestCase { /** * Test that locks are on mount point paths and also mount root when requested */ - public function testLockStorageRootButNotLocalMountPoint() { - $lockingProvider = \OC::$server->getLockingProvider(); + public function testLockStorageRootButNotLocalMountPoint(): void { + $lockingProvider = Server::get(ILockingProvider::class); $view = new View('/testuser/files/'); $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); @@ -1446,8 +1472,8 @@ class ViewTest extends \Test\TestCase { /** * Test that locks are on mount point paths and also mount root when requested */ - public function testLockMountPointPathFailReleasesBoth() { - $lockingProvider = \OC::$server->getLockingProvider(); + public function testLockMountPointPathFailReleasesBoth(): void { + $lockingProvider = Server::get(ILockingProvider::class); $view = new View('/testuser/files/'); $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); @@ -1474,7 +1500,7 @@ class ViewTest extends \Test\TestCase { $lockingProvider->releaseAll(); } - public function dataLockPaths() { + public static function dataLockPaths(): array { return [ ['/testuser/{folder}', ''], ['/testuser', '/{folder}'], @@ -1482,7 +1508,7 @@ class ViewTest extends \Test\TestCase { ]; } - public function pathRelativeToFilesProvider() { + public static function pathRelativeToFilesProvider(): array { return [ ['admin/files', ''], ['admin/files/x', 'x'], @@ -1494,15 +1520,13 @@ class ViewTest extends \Test\TestCase { ]; } - /** - * @dataProvider pathRelativeToFilesProvider - */ - public function testGetPathRelativeToFiles($path, $expectedPath) { + #[\PHPUnit\Framework\Attributes\DataProvider('pathRelativeToFilesProvider')] + public function testGetPathRelativeToFiles($path, $expectedPath): void { $view = new View(); $this->assertEquals($expectedPath, $view->getPathRelativeToFiles($path)); } - public function pathRelativeToFilesProviderExceptionCases() { + public static function pathRelativeToFilesProviderExceptionCases(): array { return [ [''], ['x'], @@ -1513,10 +1537,10 @@ class ViewTest extends \Test\TestCase { } /** - * @dataProvider pathRelativeToFilesProviderExceptionCases * @param string $path */ - public function testGetPathRelativeToFilesWithInvalidArgument($path) { + #[\PHPUnit\Framework\Attributes\DataProvider('pathRelativeToFilesProviderExceptionCases')] + public function testGetPathRelativeToFilesWithInvalidArgument($path): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('$absolutePath must be relative to "files"'); @@ -1524,7 +1548,7 @@ class ViewTest extends \Test\TestCase { $view->getPathRelativeToFiles($path); } - public function testChangeLock() { + public function testChangeLock(): void { $view = new View('/testuser/files/'); $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); @@ -1545,7 +1569,7 @@ class ViewTest extends \Test\TestCase { $this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE)); } - public function hookPathProvider() { + public static function hookPathProvider(): array { return [ ['/foo/files', '/foo', true], ['/foo/files/bar', '/foo', true], @@ -1558,21 +1582,21 @@ class ViewTest extends \Test\TestCase { } /** - * @dataProvider hookPathProvider * @param $root * @param $path * @param $shouldEmit */ - public function testHookPaths($root, $path, $shouldEmit) { + #[\PHPUnit\Framework\Attributes\DataProvider('hookPathProvider')] + public function testHookPaths($root, $path, $shouldEmit): void { $filesystemReflection = new \ReflectionClass(Filesystem::class); $defaultRootValue = $filesystemReflection->getProperty('defaultInstance'); $defaultRootValue->setAccessible(true); $oldRoot = $defaultRootValue->getValue(); $defaultView = new View('/foo/files'); - $defaultRootValue->setValue($defaultView); + $defaultRootValue->setValue(null, $defaultView); $view = new View($root); $result = self::invokePrivate($view, 'shouldEmitHooks', [$path]); - $defaultRootValue->setValue($oldRoot); + $defaultRootValue->setValue(null, $oldRoot); $this->assertEquals($shouldEmit, $result); } @@ -1586,13 +1610,15 @@ class ViewTest extends \Test\TestCase { $mounts = []; foreach ($mountPoints as $mountPoint) { $storage = $this->getMockBuilder(Storage::class) - ->setMethods([]) - ->setConstructorArgs([[]]) + ->onlyMethods([]) ->getMock(); $storage->method('getId')->willReturn('non-null-id'); + $storage->method('getStorageCache')->willReturnCallback(function () use ($storage) { + return new \OC\Files\Cache\Storage($storage, true, Server::get(IDBConnection::class)); + }); $mounts[] = $this->getMockBuilder(TestMoveableMountPoint::class) - ->setMethods(['moveMount']) + ->onlyMethods(['moveMount']) ->setConstructorArgs([$storage, $mountPoint]) ->getMock(); } @@ -1603,7 +1629,7 @@ class ViewTest extends \Test\TestCase { ->method('getMountsForUser') ->willReturn($mounts); - $mountProviderCollection = \OC::$server->getMountProviderCollection(); + $mountProviderCollection = Server::get(IMountProviderCollection::class); $mountProviderCollection->registerProvider($mountProvider); return $mounts; @@ -1612,7 +1638,7 @@ class ViewTest extends \Test\TestCase { /** * Test mount point move */ - public function testMountPointMove() { + public function testMountPointMove(): void { self::loginAsUser($this->user); [$mount1, $mount2] = $this->createTestMovableMountPoints([ @@ -1634,10 +1660,27 @@ class ViewTest extends \Test\TestCase { $this->assertTrue($view->rename('mount2', 'sub/moved_mount'), 'Can move a mount point into a subdirectory'); } - /** - * Test that moving a mount point into another is forbidden - */ - public function testMoveMountPointIntoAnother() { + public function testMoveMountPointOverwrite(): void { + self::loginAsUser($this->user); + + [$mount1, $mount2] = $this->createTestMovableMountPoints([ + $this->user . '/files/mount1', + $this->user . '/files/mount2', + ]); + + $mount1->expects($this->never()) + ->method('moveMount'); + + $mount2->expects($this->never()) + ->method('moveMount'); + + $view = new View('/' . $this->user . '/files/'); + + $this->expectException(ForbiddenException::class); + $view->rename('mount1', 'mount2'); + } + + public function testMoveMountPointIntoMount(): void { self::loginAsUser($this->user); [$mount1, $mount2] = $this->createTestMovableMountPoints([ @@ -1653,53 +1696,74 @@ class ViewTest extends \Test\TestCase { $view = new View('/' . $this->user . '/files/'); - $this->assertFalse($view->rename('mount1', 'mount2'), 'Cannot overwrite another mount point'); - $this->assertFalse($view->rename('mount1', 'mount2/sub'), 'Cannot move a mount point into another'); + $this->expectException(ForbiddenException::class); + $view->rename('mount1', 'mount2/sub'); } /** * Test that moving a mount point into a shared folder is forbidden */ - public function testMoveMountPointIntoSharedFolder() { + public function testMoveMountPointIntoSharedFolder(): void { self::loginAsUser($this->user); - [$mount1] = $this->createTestMovableMountPoints([ + [$mount1, $mount2] = $this->createTestMovableMountPoints([ $this->user . '/files/mount1', + $this->user . '/files/mount2', ]); $mount1->expects($this->never()) ->method('moveMount'); + $mount2->expects($this->once()) + ->method('moveMount') + ->willReturn(true); + $view = new View('/' . $this->user . '/files/'); $view->mkdir('shareddir'); $view->mkdir('shareddir/sub'); $view->mkdir('shareddir/sub2'); + // Create a similar named but non-shared folder + $view->mkdir('shareddir notshared'); $fileId = $view->getFileInfo('shareddir')->getId(); - $userObject = \OC::$server->getUserManager()->createUser('test2', 'IHateNonMockableStaticClasses'); + $userObject = Server::get(IUserManager::class)->createUser('test2', 'IHateNonMockableStaticClasses'); $userFolder = \OC::$server->getUserFolder($this->user); $shareDir = $userFolder->get('shareddir'); - $shareManager = \OC::$server->getShareManager(); + $shareManager = Server::get(IShareManager::class); $share = $shareManager->newShare(); $share->setSharedWith('test2') ->setSharedBy($this->user) ->setShareType(IShare::TYPE_USER) - ->setPermissions(\OCP\Constants::PERMISSION_READ) - ->setId(42) - ->setProviderId('foo') + ->setPermissions(Constants::PERMISSION_READ) ->setNode($shareDir); $shareManager->createShare($share); - $this->assertFalse($view->rename('mount1', 'shareddir'), 'Cannot overwrite shared folder'); - $this->assertFalse($view->rename('mount1', 'shareddir/sub'), 'Cannot move mount point into shared folder'); - $this->assertFalse($view->rename('mount1', 'shareddir/sub/sub2'), 'Cannot move mount point into shared subfolder'); + try { + $view->rename('mount1', 'shareddir'); + $this->fail('Cannot overwrite shared folder'); + } catch (ForbiddenException $e) { + + } + try { + $view->rename('mount1', 'shareddir/sub'); + $this->fail('Cannot move mount point into shared folder'); + } catch (ForbiddenException $e) { + + } + try { + $view->rename('mount1', 'shareddir/sub/sub2'); + $this->fail('Cannot move mount point into shared subfolder'); + } catch (ForbiddenException $e) { + + } + $this->assertTrue($view->rename('mount2', 'shareddir notshared/sub'), 'Can move mount point into a similarly named but non-shared folder'); $shareManager->deleteShare($share); $userObject->delete(); } - public function basicOperationProviderForLocks() { + public static function basicOperationProviderForLocks(): array { return [ // --- write hook ---- [ @@ -1739,6 +1803,8 @@ class ViewTest extends \Test\TestCase { ILockingProvider::LOCK_SHARED, ILockingProvider::LOCK_EXCLUSIVE, ILockingProvider::LOCK_SHARED, + null, + 0, ], // ---- delete hook ---- @@ -1770,6 +1836,8 @@ class ViewTest extends \Test\TestCase { ILockingProvider::LOCK_SHARED, ILockingProvider::LOCK_SHARED, null, + null, + false, ], [ 'fopen', @@ -1796,23 +1864,63 @@ class ViewTest extends \Test\TestCase { // ---- no hooks, no locks --- ['is_dir', ['dir'], 'dir', null], ['is_file', ['dir'], 'dir', null], - ['stat', ['dir'], 'dir', null], - ['filetype', ['dir'], 'dir', null], - ['filesize', ['dir'], 'dir', null], + [ + 'stat', + ['dir'], + 'dir', + null, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + null, + false, + ], + [ + 'filetype', + ['dir'], + 'dir', + null, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + null, + false, + ], + [ + 'filesize', + ['dir'], + 'dir', + null, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + null, + /* Return an int */ + 100 + ], ['isCreatable', ['dir'], 'dir', null], ['isReadable', ['dir'], 'dir', null], ['isUpdatable', ['dir'], 'dir', null], ['isDeletable', ['dir'], 'dir', null], ['isSharable', ['dir'], 'dir', null], ['file_exists', ['dir'], 'dir', null], - ['filemtime', ['dir'], 'dir', null], + [ + 'filemtime', + ['dir'], + 'dir', + null, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + null, + false, + ], ]; } /** * Test whether locks are set before and after the operation * - * @dataProvider basicOperationProviderForLocks * * @param string $operation operation name on the view * @param array $operationArgs arguments for the operation @@ -1822,8 +1930,9 @@ class ViewTest extends \Test\TestCase { * @param int $expectedLockDuring expected lock during operation * @param int $expectedLockAfter expected lock during post hooks * @param int $expectedStrayLock expected lock after returning, should - * be null (unlock) for most operations + * be null (unlock) for most operations */ + #[\PHPUnit\Framework\Attributes\DataProvider('basicOperationProviderForLocks')] public function testLockBasicOperation( $operation, $operationArgs, @@ -1832,15 +1941,21 @@ class ViewTest extends \Test\TestCase { $expectedLockBefore = ILockingProvider::LOCK_SHARED, $expectedLockDuring = ILockingProvider::LOCK_SHARED, $expectedLockAfter = ILockingProvider::LOCK_SHARED, - $expectedStrayLock = null - ) { + $expectedStrayLock = null, + $returnValue = true, + ): void { $view = new View('/' . $this->user . '/files/'); - /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ + /** @var Temporary&MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods([$operation]) + ->onlyMethods([$operation]) ->getMock(); + /* Pause trash to avoid the trashbin intercepting rmdir and unlink calls */ + Server::get(ITrashManager::class)->pauseTrash(); + /* Same thing with encryption wrapper */ + Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption'); + Filesystem::mount($storage, [], $this->user . '/'); // work directly on disk because mkdir might be mocked @@ -1853,10 +1968,10 @@ class ViewTest extends \Test\TestCase { $storage->expects($this->once()) ->method($operation) ->willReturnCallback( - function () use ($view, $lockedPath, &$lockTypeDuring) { + function () use ($view, $lockedPath, &$lockTypeDuring, $returnValue) { $lockTypeDuring = $this->getFileLockType($view, $lockedPath); - return true; + return $returnValue; } ); @@ -1876,19 +1991,22 @@ class ViewTest extends \Test\TestCase { } $this->assertEquals($expectedStrayLock, $this->getFileLockType($view, $lockedPath)); + + /* Resume trash to avoid side effects */ + Server::get(ITrashManager::class)->resumeTrash(); } /** * Test locks for file_put_content with stream. * This code path uses $storage->fopen instead */ - public function testLockFilePutContentWithStream() { + public function testLockFilePutContentWithStream(): void { $view = new View('/' . $this->user . '/files/'); $path = 'test_file_put_contents.txt'; /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods(['fopen']) + ->onlyMethods(['fopen']) ->getMock(); Filesystem::mount($storage, [], $this->user . '/'); @@ -1921,13 +2039,13 @@ class ViewTest extends \Test\TestCase { /** * Test locks for fopen with fclose at the end */ - public function testLockFopen() { + public function testLockFopen(): void { $view = new View('/' . $this->user . '/files/'); $path = 'test_file_put_contents.txt'; /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods(['fopen']) + ->onlyMethods(['fopen']) ->getMock(); Filesystem::mount($storage, [], $this->user . '/'); @@ -1964,27 +2082,32 @@ class ViewTest extends \Test\TestCase { /** * Test locks for fopen with fclose at the end * - * @dataProvider basicOperationProviderForLocks * * @param string $operation operation name on the view * @param array $operationArgs arguments for the operation * @param string $path path of the locked item to check */ + #[\PHPUnit\Framework\Attributes\DataProvider('basicOperationProviderForLocks')] public function testLockBasicOperationUnlocksAfterException( $operation, $operationArgs, - $path - ) { + $path, + ): void { if ($operation === 'touch') { - $this->markTestSkipped("touch handles storage exceptions internally"); + $this->markTestSkipped('touch handles storage exceptions internally'); } $view = new View('/' . $this->user . '/files/'); /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods([$operation]) + ->onlyMethods([$operation]) ->getMock(); + /* Pause trash to avoid the trashbin intercepting rmdir and unlink calls */ + Server::get(ITrashManager::class)->pauseTrash(); + /* Same thing with encryption wrapper */ + Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption'); + Filesystem::mount($storage, [], $this->user . '/'); // work directly on disk because mkdir might be mocked @@ -1997,7 +2120,7 @@ class ViewTest extends \Test\TestCase { $storage->expects($this->once()) ->method($operation) ->willReturnCallback( - function () { + function (): void { throw new \Exception('Simulated exception'); } ); @@ -2011,9 +2134,12 @@ class ViewTest extends \Test\TestCase { } $this->assertTrue($thrown, 'Exception was rethrown'); $this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception'); + + /* Resume trash to avoid side effects */ + Server::get(ITrashManager::class)->resumeTrash(); } - public function testLockBasicOperationUnlocksAfterLockException() { + public function testLockBasicOperationUnlocksAfterLockException(): void { $view = new View('/' . $this->user . '/files/'); $storage = new Temporary([]); @@ -2047,24 +2173,24 @@ class ViewTest extends \Test\TestCase { /** * Test locks for fopen with fclose at the end * - * @dataProvider basicOperationProviderForLocks * * @param string $operation operation name on the view * @param array $operationArgs arguments for the operation * @param string $path path of the locked item to check * @param string $hookType hook type */ + #[\PHPUnit\Framework\Attributes\DataProvider('basicOperationProviderForLocks')] public function testLockBasicOperationUnlocksAfterCancelledHook( $operation, $operationArgs, $path, - $hookType - ) { + $hookType, + ): void { $view = new View('/' . $this->user . '/files/'); /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods([$operation]) + ->onlyMethods([$operation]) ->getMock(); Filesystem::mount($storage, [], $this->user . '/'); @@ -2082,7 +2208,7 @@ class ViewTest extends \Test\TestCase { $this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception'); } - public function lockFileRenameOrCopyDataProvider() { + public static function lockFileRenameOrCopyDataProvider(): array { return [ ['rename', ILockingProvider::LOCK_EXCLUSIVE], ['copy', ILockingProvider::LOCK_SHARED], @@ -2092,29 +2218,29 @@ class ViewTest extends \Test\TestCase { /** * Test locks for rename or copy operation * - * @dataProvider lockFileRenameOrCopyDataProvider * * @param string $operation operation to be done on the view * @param int $expectedLockTypeSourceDuring expected lock type on source file during - * the operation + * the operation */ - public function testLockFileRename($operation, $expectedLockTypeSourceDuring) { + #[\PHPUnit\Framework\Attributes\DataProvider('lockFileRenameOrCopyDataProvider')] + public function testLockFileRename($operation, $expectedLockTypeSourceDuring): void { $view = new View('/' . $this->user . '/files/'); /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods([$operation, 'getMetaData', 'filemtime']) + ->onlyMethods([$operation, 'getMetaData', 'filemtime']) ->getMock(); $storage->expects($this->any()) ->method('getMetaData') - ->will($this->returnValue([ + ->willReturn([ 'mtime' => 1885434487, 'etag' => '', 'mimetype' => 'text/plain', 'permissions' => Constants::PERMISSION_ALL, 'size' => 3 - ])); + ]); $storage->expects($this->any()) ->method('filemtime') ->willReturn(123456789); @@ -2122,6 +2248,9 @@ class ViewTest extends \Test\TestCase { $sourcePath = 'original.txt'; $targetPath = 'target.txt'; + /* Disable encryption wrapper to avoid it intercepting mocked call */ + Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption'); + Filesystem::mount($storage, [], $this->user . '/'); $storage->mkdir('files'); $view->file_put_contents($sourcePath, 'meh'); @@ -2162,19 +2291,22 @@ class ViewTest extends \Test\TestCase { * We expect that we catch the exception, free the lock and re-throw it. * */ - public function testLockFileCopyException() { + public function testLockFileCopyException(): void { $this->expectException(\Exception::class); $view = new View('/' . $this->user . '/files/'); /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods(['copy']) + ->onlyMethods(['copy']) ->getMock(); $sourcePath = 'original.txt'; $targetPath = 'target.txt'; + /* Disable encryption wrapper to avoid it intercepting mocked call */ + Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption'); + Filesystem::mount($storage, [], $this->user . '/'); $storage->mkdir('files'); $view->file_put_contents($sourcePath, 'meh'); @@ -2182,7 +2314,7 @@ class ViewTest extends \Test\TestCase { $storage->expects($this->once()) ->method('copy') ->willReturnCallback( - function () { + function (): void { throw new \Exception(); } ); @@ -2205,7 +2337,7 @@ class ViewTest extends \Test\TestCase { /** * Test rename operation: unlock first path when second path was locked */ - public function testLockFileRenameUnlockOnException() { + public function testLockFileRenameUnlockOnException(): void { self::loginAsUser('test'); $view = new View('/' . $this->user . '/files/'); @@ -2238,7 +2370,7 @@ class ViewTest extends \Test\TestCase { /** * Test rename operation: unlock first path when second path was locked */ - public function testGetOwner() { + public function testGetOwner(): void { self::loginAsUser('test'); $view = new View('/test/files/'); @@ -2266,7 +2398,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals('test', $folderInfo[0]->getOwner()->getUID()); } - public function lockFileRenameOrCopyCrossStorageDataProvider() { + public static function lockFileRenameOrCopyCrossStorageDataProvider(): array { return [ ['rename', 'moveFromStorage', ILockingProvider::LOCK_EXCLUSIVE], ['copy', 'copyFromStorage', ILockingProvider::LOCK_SHARED], @@ -2276,34 +2408,34 @@ class ViewTest extends \Test\TestCase { /** * Test locks for rename or copy operation cross-storage * - * @dataProvider lockFileRenameOrCopyCrossStorageDataProvider * * @param string $viewOperation operation to be done on the view * @param string $storageOperation operation to be mocked on the storage * @param int $expectedLockTypeSourceDuring expected lock type on source file during - * the operation + * the operation */ - public function testLockFileRenameCrossStorage($viewOperation, $storageOperation, $expectedLockTypeSourceDuring) { + #[\PHPUnit\Framework\Attributes\DataProvider('lockFileRenameOrCopyCrossStorageDataProvider')] + public function testLockFileRenameCrossStorage($viewOperation, $storageOperation, $expectedLockTypeSourceDuring): void { $view = new View('/' . $this->user . '/files/'); /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods([$storageOperation]) + ->onlyMethods([$storageOperation]) ->getMock(); /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage2 */ $storage2 = $this->getMockBuilder(Temporary::class) - ->setMethods([$storageOperation, 'getMetaData', 'filemtime']) + ->onlyMethods([$storageOperation, 'getMetaData', 'filemtime']) ->getMock(); $storage2->expects($this->any()) ->method('getMetaData') - ->will($this->returnValue([ + ->willReturn([ 'mtime' => 1885434487, 'etag' => '', 'mimetype' => 'text/plain', 'permissions' => Constants::PERMISSION_ALL, 'size' => 3 - ])); + ]); $storage2->expects($this->any()) ->method('filemtime') ->willReturn(123456789); @@ -2311,10 +2443,14 @@ class ViewTest extends \Test\TestCase { $sourcePath = 'original.txt'; $targetPath = 'substorage/target.txt'; + /* Disable encryption wrapper to avoid it intercepting mocked call */ + Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption'); + Filesystem::mount($storage, [], $this->user . '/'); Filesystem::mount($storage2, [], $this->user . '/files/substorage'); $storage->mkdir('files'); $view->file_put_contents($sourcePath, 'meh'); + $storage2->getUpdater()->update(''); $storage->expects($this->never()) ->method($storageOperation); @@ -2352,7 +2488,7 @@ class ViewTest extends \Test\TestCase { /** * Test locks when moving a mount point */ - public function testLockMoveMountPoint() { + public function testLockMoveMountPoint(): void { self::loginAsUser('test'); [$mount] = $this->createTestMovableMountPoints([ @@ -2419,28 +2555,28 @@ class ViewTest extends \Test\TestCase { * @param int $lockTypePre variable to receive lock type that was active in the pre-hook * @param int $lockTypePost variable to receive lock type that was active in the post-hook * @param bool $onMountPoint true to check the mount point instead of the - * mounted storage + * mounted storage */ private function connectMockHooks($hookType, $view, $path, &$lockTypePre, &$lockTypePost, $onMountPoint = false) { if ($hookType === null) { return; } - $eventHandler = $this->getMockBuilder(\stdclass::class) - ->setMethods(['preCallback', 'postCallback']) + $eventHandler = $this->getMockBuilder(TestEventHandler::class) + ->onlyMethods(['preCallback', 'postCallback']) ->getMock(); $eventHandler->expects($this->any()) ->method('preCallback') ->willReturnCallback( - function () use ($view, $path, $onMountPoint, &$lockTypePre) { + function () use ($view, $path, $onMountPoint, &$lockTypePre): void { $lockTypePre = $this->getFileLockType($view, $path, $onMountPoint); } ); $eventHandler->expects($this->any()) ->method('postCallback') ->willReturnCallback( - function () use ($view, $path, $onMountPoint, &$lockTypePost) { + function () use ($view, $path, $onMountPoint, &$lockTypePost): void { $lockTypePost = $this->getFileLockType($view, $path, $onMountPoint); } ); @@ -2467,7 +2603,7 @@ class ViewTest extends \Test\TestCase { * @param View $view view * @param string $path path * @param bool $onMountPoint true to check the mount point instead of the - * mounted storage + * mounted storage * * @return int lock type or null if file was not locked */ @@ -2481,7 +2617,7 @@ class ViewTest extends \Test\TestCase { } - public function testRemoveMoveableMountPoint() { + public function testRemoveMoveableMountPoint(): void { $mountPoint = '/' . $this->user . '/files/mount/'; // Mock the mount point @@ -2501,8 +2637,8 @@ class ViewTest extends \Test\TestCase { Filesystem::getMountManager()->addMount($mount); // Listen for events - $eventHandler = $this->getMockBuilder(\stdclass::class) - ->setMethods(['umount', 'post_umount']) + $eventHandler = $this->getMockBuilder(TestEventHandler::class) + ->onlyMethods(['umount', 'post_umount']) ->getMock(); $eventHandler->expects($this->once()) ->method('umount') @@ -2528,7 +2664,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals('foo', $view->rmdir('mount')); } - public function mimeFilterProvider() { + public static function mimeFilterProvider(): array { return [ [null, ['test1.txt', 'test2.txt', 'test3.md', 'test4.png']], ['text/plain', ['test1.txt', 'test2.txt']], @@ -2540,9 +2676,9 @@ class ViewTest extends \Test\TestCase { /** * @param string $filter * @param string[] $expected - * @dataProvider mimeFilterProvider */ - public function testGetDirectoryContentMimeFilter($filter, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('mimeFilterProvider')] + public function testGetDirectoryContentMimeFilter($filter, $expected): void { $storage1 = new Temporary(); $root = self::getUniqueID('/'); Filesystem::mount($storage1, [], $root . '/'); @@ -2563,7 +2699,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals($expected, $files); } - public function testFilePutContentsClearsChecksum() { + public function testFilePutContentsClearsChecksum(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->file_put_contents('foo.txt', 'bar'); @@ -2584,7 +2720,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals('', $data->getChecksum()); } - public function testDeleteGhostFile() { + public function testDeleteGhostFile(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $cache = $storage->getCache(); @@ -2607,7 +2743,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals(0, $newInfo->getSize()); } - public function testDeleteGhostFolder() { + public function testDeleteGhostFolder(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $cache = $storage->getCache(); @@ -2633,10 +2769,10 @@ class ViewTest extends \Test\TestCase { $this->assertEquals(0, $newInfo->getSize()); } - public function testCreateParentDirectories() { + public function testCreateParentDirectories(): void { $view = $this->getMockBuilder(View::class) ->disableOriginalConstructor() - ->setMethods([ + ->onlyMethods([ 'is_file', 'file_exists', 'mkdir', @@ -2645,37 +2781,34 @@ class ViewTest extends \Test\TestCase { $view->expects($this->exactly(3)) ->method('is_file') - ->withConsecutive( - ['/new'], - ['/new/folder'], - ['/new/folder/structure'], - ) - ->willReturn(false); + ->willReturnMap([ + ['/new', false], + ['/new/folder', false], + ['/new/folder/structure', false], + ]); $view->expects($this->exactly(3)) ->method('file_exists') - ->withConsecutive( - ['/new'], - ['/new/folder'], - ['/new/folder/structure'], - )->willReturnOnConsecutiveCalls( - true, - false, - false, - ); + ->willReturnMap([ + ['/new', true], + ['/new/folder', false], + ['/new/folder/structure', false], + ]); + + $calls = ['/new/folder', '/new/folder/structure']; $view->expects($this->exactly(2)) ->method('mkdir') - ->withConsecutive( - ['/new/folder'], - ['/new/folder/structure'], - ); + ->willReturnCallback(function ($dir) use (&$calls): void { + $expected = array_shift($calls); + $this->assertEquals($expected, $dir); + }); $this->assertTrue(self::invokePrivate($view, 'createParentDirectories', ['/new/folder/structure'])); } - public function testCreateParentDirectoriesWithExistingFile() { + public function testCreateParentDirectoriesWithExistingFile(): void { $view = $this->getMockBuilder(View::class) ->disableOriginalConstructor() - ->setMethods([ + ->onlyMethods([ 'is_file', 'file_exists', 'mkdir', @@ -2690,7 +2823,7 @@ class ViewTest extends \Test\TestCase { $this->assertFalse(self::invokePrivate($view, 'createParentDirectories', ['/file.txt/folder/structure'])); } - public function testCacheExtension() { + public function testCacheExtension(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->file_put_contents('foo.txt', 'bar'); @@ -2710,7 +2843,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals(0, $info->getCreationTime()); } - public function testFopenGone() { + public function testFopenGone(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->file_put_contents('foo.txt', 'bar'); @@ -2728,4 +2861,43 @@ class ViewTest extends \Test\TestCase { $this->assertFalse($cache->inCache('foo.txt')); } + + public function testMountpointParentsCreated(): void { + $storage1 = $this->getTestStorage(); + Filesystem::mount($storage1, [], '/'); + + $storage2 = $this->getTestStorage(); + Filesystem::mount($storage2, [], '/A/B/C'); + + $rootView = new View(''); + + $folderData = $rootView->getDirectoryContent('/'); + $this->assertCount(4, $folderData); + $this->assertEquals('folder', $folderData[0]['name']); + $this->assertEquals('foo.png', $folderData[1]['name']); + $this->assertEquals('foo.txt', $folderData[2]['name']); + $this->assertEquals('A', $folderData[3]['name']); + + $folderData = $rootView->getDirectoryContent('/A'); + $this->assertCount(1, $folderData); + $this->assertEquals('B', $folderData[0]['name']); + + $folderData = $rootView->getDirectoryContent('/A/B'); + $this->assertCount(1, $folderData); + $this->assertEquals('C', $folderData[0]['name']); + + $folderData = $rootView->getDirectoryContent('/A/B/C'); + $this->assertCount(3, $folderData); + $this->assertEquals('folder', $folderData[0]['name']); + $this->assertEquals('foo.png', $folderData[1]['name']); + $this->assertEquals('foo.txt', $folderData[2]['name']); + } + + public function testCopyPreservesContent() { + $viewUser1 = new View('/' . 'userId' . '/files'); + $viewUser1->mkdir(''); + $viewUser1->file_put_contents('foo.txt', 'foo'); + $viewUser1->copy('foo.txt', 'bar.txt'); + $this->assertEquals('foo', $viewUser1->file_get_contents('bar.txt')); + } } |