From 02222457a164e94c70eb8394dcc0ca0059b4c77e Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Thu, 22 May 2025 14:32:50 +0200 Subject: [PATCH 01/12] feat(IUserFolder): add a user folder class * Implements https://github.com/nextcloud/server/issues/52896 Similar to the root folder this represents the users home folder, it allows to group user based methods on the folder like the user quota. Signed-off-by: Ferdinand Thiessen --- lib/composer/composer/autoload_classmap.php | 2 + lib/composer/composer/autoload_static.php | 2 + lib/private/Files/Node/LazyUserFolder.php | 52 +++++++----- lib/private/Files/Node/Root.php | 24 +++++- lib/private/Files/Node/UserFolder.php | 93 +++++++++++++++++++++ lib/public/Files/IRootFolder.php | 8 +- lib/public/Files/IUserFolder.php | 27 ++++++ 7 files changed, 183 insertions(+), 25 deletions(-) create mode 100644 lib/private/Files/Node/UserFolder.php create mode 100644 lib/public/Files/IUserFolder.php diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 795fb8eb63e92..fcfdd6ce50345 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -555,6 +555,7 @@ 'OCP\\Files\\IMimeTypeLoader' => $baseDir . '/lib/public/Files/IMimeTypeLoader.php', 'OCP\\Files\\IRootFolder' => $baseDir . '/lib/public/Files/IRootFolder.php', 'OCP\\Files\\ISetupManager' => $baseDir . '/lib/public/Files/ISetupManager.php', + 'OCP\\Files\\IUserFolder' => $baseDir . '/lib/public/Files/IUserFolder.php', 'OCP\\Files\\InvalidCharacterInPathException' => $baseDir . '/lib/public/Files/InvalidCharacterInPathException.php', 'OCP\\Files\\InvalidContentException' => $baseDir . '/lib/public/Files/InvalidContentException.php', 'OCP\\Files\\InvalidDirectoryException' => $baseDir . '/lib/public/Files/InvalidDirectoryException.php', @@ -1932,6 +1933,7 @@ 'OC\\Files\\Node\\NonExistingFile' => $baseDir . '/lib/private/Files/Node/NonExistingFile.php', 'OC\\Files\\Node\\NonExistingFolder' => $baseDir . '/lib/private/Files/Node/NonExistingFolder.php', 'OC\\Files\\Node\\Root' => $baseDir . '/lib/private/Files/Node/Root.php', + 'OC\\Files\\Node\\UserFolder' => $baseDir . '/lib/private/Files/Node/UserFolder.php', 'OC\\Files\\Notify\\Change' => $baseDir . '/lib/private/Files/Notify/Change.php', 'OC\\Files\\Notify\\RenameChange' => $baseDir . '/lib/private/Files/Notify/RenameChange.php', 'OC\\Files\\ObjectStore\\AppdataPreviewObjectStoreStorage' => $baseDir . '/lib/private/Files/ObjectStore/AppdataPreviewObjectStoreStorage.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index b9114de204f71..fa6bc202aa4e8 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -596,6 +596,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Files\\IMimeTypeLoader' => __DIR__ . '/../../..' . '/lib/public/Files/IMimeTypeLoader.php', 'OCP\\Files\\IRootFolder' => __DIR__ . '/../../..' . '/lib/public/Files/IRootFolder.php', 'OCP\\Files\\ISetupManager' => __DIR__ . '/../../..' . '/lib/public/Files/ISetupManager.php', + 'OCP\\Files\\IUserFolder' => __DIR__ . '/../../..' . '/lib/public/Files/IUserFolder.php', 'OCP\\Files\\InvalidCharacterInPathException' => __DIR__ . '/../../..' . '/lib/public/Files/InvalidCharacterInPathException.php', 'OCP\\Files\\InvalidContentException' => __DIR__ . '/../../..' . '/lib/public/Files/InvalidContentException.php', 'OCP\\Files\\InvalidDirectoryException' => __DIR__ . '/../../..' . '/lib/public/Files/InvalidDirectoryException.php', @@ -1973,6 +1974,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Files\\Node\\NonExistingFile' => __DIR__ . '/../../..' . '/lib/private/Files/Node/NonExistingFile.php', 'OC\\Files\\Node\\NonExistingFolder' => __DIR__ . '/../../..' . '/lib/private/Files/Node/NonExistingFolder.php', 'OC\\Files\\Node\\Root' => __DIR__ . '/../../..' . '/lib/private/Files/Node/Root.php', + 'OC\\Files\\Node\\UserFolder' => __DIR__ . '/../../..' . '/lib/private/Files/Node/UserFolder.php', 'OC\\Files\\Notify\\Change' => __DIR__ . '/../../..' . '/lib/private/Files/Notify/Change.php', 'OC\\Files\\Notify\\RenameChange' => __DIR__ . '/../../..' . '/lib/private/Files/Notify/RenameChange.php', 'OC\\Files\\ObjectStore\\AppdataPreviewObjectStoreStorage' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/AppdataPreviewObjectStoreStorage.php', diff --git a/lib/private/Files/Node/LazyUserFolder.php b/lib/private/Files/Node/LazyUserFolder.php index 1989372c67c0c..581b4b54850b1 100644 --- a/lib/private/Files/Node/LazyUserFolder.php +++ b/lib/private/Files/Node/LazyUserFolder.php @@ -9,18 +9,20 @@ namespace OC\Files\Node; +use OC\Files\View; use OCP\Constants; -use OCP\Files\File; use OCP\Files\FileInfo; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Mount\IMountManager; -use OCP\Files\NotFoundException; +use OCP\ICacheFactory; +use OCP\IConfig; use OCP\IUser; use OCP\Server; use Psr\Log\LoggerInterface; -class LazyUserFolder extends LazyFolder { +class LazyUserFolder extends LazyFolder implements IUserFolder { private string $path; public function __construct( @@ -45,23 +47,30 @@ public function __construct( parent::__construct( $rootFolder, - function () use ($user): Folder { - try { - $node = $this->getRootFolder()->get($this->path); - if ($node instanceof File) { - $e = new \RuntimeException(); - Server::get(LoggerInterface::class)->error('User root storage is not a folder: ' . $this->path, [ - 'exception' => $e, - ]); - throw $e; - } - return $node; - } catch (NotFoundException $e) { - if (!$this->getRootFolder()->nodeExists('/' . $user->getUID())) { - $this->getRootFolder()->newFolder('/' . $user->getUID()); - } - return $this->getRootFolder()->newFolder($this->path); + function () use ($user): UserFolder { + $root = $this->getRootFolder(); + if (!$root->nodeExists('/' . $user->getUID())) { + $parent = $root->newFolder('/' . $user->getUID()); + } else { + $parent = $root->get('/' . $user->getUID()); } + if (!($parent instanceof Folder)) { + $e = new \RuntimeException(); + \OCP\Server::get(LoggerInterface::class)->error('User root storage is not a folder: ' . $this->path, [ + 'exception' => $e, + ]); + throw $e; + } + $realFolder = $root->newFolder('/' . $user->getUID() . '/files'); + return new UserFolder( + $root, + new View(), + $realFolder->getPath(), + $parent, + Server::get(IConfig::class), + $user, + Server::get(ICacheFactory::class), + ); }, $data, ); @@ -78,4 +87,9 @@ public function getMountPoint() { } return $mountPoint; } + + #[\Override] + public function getUserQuota(bool $useCache = true): array { + return $this->__call(__FUNCTION__, func_get_args()); + } } diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php index 3428768ad6299..8e1f36ca1e1f8 100644 --- a/lib/private/Files/Node/Root.php +++ b/lib/private/Files/Node/Root.php @@ -28,6 +28,7 @@ use OCP\Files\Config\IUserMountCache; use OCP\Files\Events\Node\FilesystemTornDownEvent; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Mount\IMountPoint; use OCP\Files\Node as INode; use OCP\Files\NotFoundException; @@ -35,6 +36,7 @@ use OCP\IAppConfig; use OCP\ICache; use OCP\ICacheFactory; +use OCP\IConfig; use OCP\IUser; use OCP\IUserManager; use OCP\Server; @@ -73,7 +75,7 @@ public function __construct( private LoggerInterface $logger, private IUserManager $userManager, IEventDispatcher $eventDispatcher, - ICacheFactory $cacheFactory, + private ICacheFactory $cacheFactory, IAppConfig $appConfig, ) { parent::__construct($this, $view, ''); @@ -316,7 +318,7 @@ public function getName() { } #[\Override] - public function getUserFolder($userId) { + public function getUserFolder(string $userId): IUserFolder { $userObject = $this->userManager->get($userId); if (is_null($userObject)) { @@ -347,9 +349,23 @@ public function getUserFolder($userId) { } } catch (NotFoundException $e) { if (!$this->nodeExists('/' . $userId)) { - $this->newFolder('/' . $userId); + $parent = $this->newFolder('/' . $userId); + } else { + $parent = $this->get('/' . $userId); + if (!$parent instanceof \OCP\Files\Folder) { + throw new \Exception("Account folder for \"$userId\" exists as a file"); + } } - $folder = $this->newFolder('/' . $userId . '/files'); + $realFolder = $this->newFolder('/' . $userId . '/files'); + $folder = new UserFolder( + $this->root, + $this->view, + $realFolder->getPath(), + $parent, + Server::get(IConfig::class), + $userObject, + $this->cacheFactory, + ); } } else { $folder = new LazyUserFolder($this, $userObject, $this->mountManager, $this->useDefaultHomeFoldersPermissions); diff --git a/lib/private/Files/Node/UserFolder.php b/lib/private/Files/Node/UserFolder.php new file mode 100644 index 0000000000000..4a37507dda2a8 --- /dev/null +++ b/lib/private/Files/Node/UserFolder.php @@ -0,0 +1,93 @@ +cacheFactory->createLocal('storage_info'); + if ($useCache) { + $cached = $memcache->get($this->getPath()); + if ($cached) { + return $cached; + } + } + + $quotaIncludeExternalStorage = $this->config->getSystemValueBool('quota_include_external_storage'); + $rootInfo = $this->getFileInfo($quotaIncludeExternalStorage); + + /** @var int|float $used */ + $used = max($rootInfo->getSize(), 0.0); + /** @var int|float $quota */ + $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED; + $mount = $rootInfo->getMountPoint(); + $storage = $mount->getStorage(); + if ($storage === null) { + throw new \RuntimeException('Storage returned from mount point is null.'); + } + + if ($storage->instanceOfStorage(Quota::class)) { + $quota = $storage->getQuota(); + } elseif ($quotaIncludeExternalStorage) { + $quota = $this->user->getQuotaBytes(); + } + + $free = $storage->free_space($rootInfo->getInternalPath()); + if (is_bool($free)) { + $free = 0.0; + } + + if ($free >= 0) { + $total = $free + $used; + } else { + $total = $free; //either unknown or unlimited + } + + $relative = $total > 0 + ? $used / $total + : 0; + $this->config->setUserValue($this->user->getUID(), 'files', 'lastSeenQuotaUsage', (string)$relative); + + $info = [ + 'free' => $free, + 'used' => $used, + 'quota' => $quota, + 'total' => $total, + ]; + $memcache->set($this->getPath(), $info, 5 * 60); + + return $info; + } + +} diff --git a/lib/public/Files/IRootFolder.php b/lib/public/Files/IRootFolder.php index 8a31df38bcaab..cbee6d0989571 100644 --- a/lib/public/Files/IRootFolder.php +++ b/lib/public/Files/IRootFolder.php @@ -1,5 +1,7 @@ Date: Thu, 22 May 2025 14:34:39 +0200 Subject: [PATCH 02/12] chore: migrate away from deprecated OC_Helper to IUserFolder Signed-off-by: Ferdinand Thiessen --- core/Command/User/Info.php | 19 +++++++------------ lib/private/User/User.php | 7 ++++++- lib/private/legacy/OC_Helper.php | 4 ++++ 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/core/Command/User/Info.php b/core/Command/User/Info.php index ce9bad0a2d38f..661b5cf13a65d 100644 --- a/core/Command/User/Info.php +++ b/core/Command/User/Info.php @@ -8,11 +8,13 @@ namespace OC\Core\Command\User; use OC\Core\Command\Base; +use OC\User\NoUserException; +use OCP\Files\IRootFolder; use OCP\Files\ISetupManager; -use OCP\Files\NotFoundException; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserManager; +use OCP\Server; use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -87,20 +89,13 @@ private function formatLoginDate(int $timestamp): string { * @return array */ protected function getStorageInfo(IUser $user): array { - $this->setupManager->tearDown(); - $this->setupManager->setupForUser($user); + $root = Server::get(IRootFolder::class); try { - $storage = \OC_Helper::getStorageInfo('/'); - } catch (NotFoundException $e) { + $userFolder = $root->getUserFolder($user->getUID()); + return $userFolder->getUserQuota(); + } catch (NoUserException) { return []; } - return [ - 'free' => $storage['free'], - 'used' => $storage['used'], - 'total' => $storage['total'], - 'relative' => $storage['relative'], - 'quota' => $storage['quota'], - ]; } /** diff --git a/lib/private/User/User.php b/lib/private/User/User.php index e3424211e0ced..a09a0ff558a71 100644 --- a/lib/private/User/User.php +++ b/lib/private/User/User.php @@ -17,6 +17,7 @@ use OCP\Config\IUserConfig; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\FileInfo; +use OCP\Files\IRootFolder; use OCP\Group\Events\BeforeUserRemovedEvent; use OCP\Group\Events\UserRemovedEvent; use OCP\IAvatarManager; @@ -606,7 +607,11 @@ public function setQuota($quota): void { $this->userConfig->setValueString($this->uid, 'files', 'quota', $quota); $this->triggerChange('quota', $quota, $oldQuota); } - \OC_Helper::clearStorageInfo('/' . $this->uid . '/files'); + + // Refresh the quota cache + $root = Server::get(IRootFolder::class); + $userFolder = $root->getUserFolder($this->uid); + $userFolder->getUserQuota(false); } #[\Override] diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php index 8e9135b185707..fb58090a134c8 100644 --- a/lib/private/legacy/OC_Helper.php +++ b/lib/private/legacy/OC_Helper.php @@ -155,6 +155,7 @@ public static function findBinaryPath(string $program): ?string { * @psalm-suppress LessSpecificReturnStatement Legacy code outputs weird types - manually validated that they are correct * @return StorageInfo * @throws NotFoundException + * @deprecated 33.0.0 use \OCP\Files\IUserFolder::getUserQuota */ public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true, $useCache = true) { if (!self::$cacheFactory) { @@ -346,6 +347,9 @@ private static function getGlobalStorageInfo(int|float $quota, IUser $user, IMou ]; } + /** + * @deprecated 33.0.0 + */ public static function clearStorageInfo(string $absolutePath): void { /** @var ICacheFactory $cacheFactory */ $cacheFactory = Server::get(ICacheFactory::class); From 286642a6117085fa9287ac56e341d06b4f41a365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 16 Dec 2025 11:57:34 +0100 Subject: [PATCH 03/12] fix: Use new getOrCreate method and fix code issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/Files/Node/LazyUserFolder.php | 16 ++-------- lib/private/Files/Node/Root.php | 36 +++++++---------------- lib/private/Files/Node/UserFolder.php | 3 +- 3 files changed, 15 insertions(+), 40 deletions(-) diff --git a/lib/private/Files/Node/LazyUserFolder.php b/lib/private/Files/Node/LazyUserFolder.php index 581b4b54850b1..d2b85a9457190 100644 --- a/lib/private/Files/Node/LazyUserFolder.php +++ b/lib/private/Files/Node/LazyUserFolder.php @@ -20,7 +20,6 @@ use OCP\IConfig; use OCP\IUser; use OCP\Server; -use Psr\Log\LoggerInterface; class LazyUserFolder extends LazyFolder implements IUserFolder { private string $path; @@ -49,19 +48,8 @@ public function __construct( $rootFolder, function () use ($user): UserFolder { $root = $this->getRootFolder(); - if (!$root->nodeExists('/' . $user->getUID())) { - $parent = $root->newFolder('/' . $user->getUID()); - } else { - $parent = $root->get('/' . $user->getUID()); - } - if (!($parent instanceof Folder)) { - $e = new \RuntimeException(); - \OCP\Server::get(LoggerInterface::class)->error('User root storage is not a folder: ' . $this->path, [ - 'exception' => $e, - ]); - throw $e; - } - $realFolder = $root->newFolder('/' . $user->getUID() . '/files'); + $parent = $root->getOrCreateFolder('/' . $user->getUID(), maxRetries: 1); + $realFolder = $root->getOrCreateFolder('/' . $user->getUID() . '/files', maxRetries: 1); return new UserFolder( $root, new View(), diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php index 8e1f36ca1e1f8..d64135b378802 100644 --- a/lib/private/Files/Node/Root.php +++ b/lib/private/Files/Node/Root.php @@ -342,31 +342,17 @@ public function getUserFolder(string $userId): IUserFolder { if (!$this->userFolderCache->hasKey($userId)) { if ($this->mountManager->getSetupManager()->isSetupComplete($userObject)) { - try { - $folder = $this->get('/' . $userId . '/files'); - if (!$folder instanceof \OCP\Files\Folder) { - throw new \Exception("Account folder for \"$userId\" exists as a file"); - } - } catch (NotFoundException $e) { - if (!$this->nodeExists('/' . $userId)) { - $parent = $this->newFolder('/' . $userId); - } else { - $parent = $this->get('/' . $userId); - if (!$parent instanceof \OCP\Files\Folder) { - throw new \Exception("Account folder for \"$userId\" exists as a file"); - } - } - $realFolder = $this->newFolder('/' . $userId . '/files'); - $folder = new UserFolder( - $this->root, - $this->view, - $realFolder->getPath(), - $parent, - Server::get(IConfig::class), - $userObject, - $this->cacheFactory, - ); - } + $parent = $this->getOrCreateFolder('/' . $userId, maxRetries: 1); + $realFolder = $this->getOrCreateFolder('/' . $userId . '/files', maxRetries: 1); + $folder = new UserFolder( + $this, + $this->view, + $realFolder->getPath(), + $parent, + Server::get(IConfig::class), + $userObject, + $this->cacheFactory, + ); } else { $folder = new LazyUserFolder($this, $userObject, $this->mountManager, $this->useDefaultHomeFoldersPermissions); } diff --git a/lib/private/Files/Node/UserFolder.php b/lib/private/Files/Node/UserFolder.php index 4a37507dda2a8..646c299f086d4 100644 --- a/lib/private/Files/Node/UserFolder.php +++ b/lib/private/Files/Node/UserFolder.php @@ -10,6 +10,7 @@ use OC\Files\Storage\Wrapper\Quota; use OC\Files\View; +use OCP\Files\Folder as IFolder; use OCP\Files\IRootFolder; use OCP\Files\IUserFolder; use OCP\ICacheFactory; @@ -25,7 +26,7 @@ public function __construct( IRootFolder $root, View $view, string $path, - Folder $parent, + IFolder $parent, protected IConfig $config, protected IUser $user, protected ICacheFactory $cacheFactory, From bda8cf3e4be160fa78c144f495662d1aa807f610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 16 Dec 2025 12:45:55 +0100 Subject: [PATCH 04/12] fix: Adapt LazyRoot::getUserFolder signature and remove LazyFolder::getUserFolder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/Files/Node/LazyFolder.php | 7 ------- lib/private/Files/Node/LazyRoot.php | 3 ++- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/private/Files/Node/LazyFolder.php b/lib/private/Files/Node/LazyFolder.php index 2d01e7bf5c8ce..afa875e6e8388 100644 --- a/lib/private/Files/Node/LazyFolder.php +++ b/lib/private/Files/Node/LazyFolder.php @@ -313,13 +313,6 @@ public function getName() { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ - public function getUserFolder($userId) { - return $this->__call(__FUNCTION__, func_get_args()); - } - #[\Override] public function getMimetype(): string { if (isset($this->data['mimetype'])) { diff --git a/lib/private/Files/Node/LazyRoot.php b/lib/private/Files/Node/LazyRoot.php index 203a31ccba69e..1c69ac72127d5 100644 --- a/lib/private/Files/Node/LazyRoot.php +++ b/lib/private/Files/Node/LazyRoot.php @@ -11,6 +11,7 @@ use OC\Hooks\Emitter; use OCP\Files\Cache\ICacheEntry; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Mount\IMountPoint; use OCP\Files\Node as INode; @@ -37,7 +38,7 @@ protected function getRootFolder(): IRootFolder { } #[\Override] - public function getUserFolder($userId) { + public function getUserFolder(string $userId): IUserFolder { return $this->__call(__FUNCTION__, func_get_args()); } From 3f9f865f034226c8533c4e777f76d14d3cf9a523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 16 Dec 2025 13:46:50 +0100 Subject: [PATCH 05/12] chore: Adapt tests to getUserFolder return type change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- .../tests/Unit/Activity/ListenerTest.php | 4 +- .../Unit/Controller/NotificationsTest.php | 6 +- .../tests/Unit/Notification/NotifierTest.php | 8 +- .../unit/Controller/DirectControllerTest.php | 7 +- apps/dav/tests/unit/Direct/DirectFileTest.php | 6 +- .../ConversionApiControllerTest.php | 6 +- .../tests/Controller/ViewControllerTest.php | 7 +- apps/files_sharing/tests/ApplicationTest.php | 5 +- .../ShareRecipientSorterTest.php | 8 +- .../Controller/ShareAPIControllerTest.php | 129 ++++++++-------- .../tests/External/ManagerTest.php | 6 +- .../Controller/PreviewControllerTest.php | 7 +- apps/files_trashbin/tests/StorageTest.php | 3 +- .../tests/Command/CleanupTest.php | 4 +- .../Controller/PreviewControllerTest.php | 5 +- lib/private/Files/Node/Folder.php | 2 +- .../Core/Controller/AvatarControllerTest.php | 9 +- .../Core/Controller/PreviewControllerTest.php | 19 +-- tests/lib/DirectEditing/ManagerTest.php | 8 +- tests/lib/Files/Node/RootTest.php | 4 +- .../Files/Template/TemplateManagerTest.php | 3 +- .../CleanPreviewsBackgroundJobTest.php | 9 +- .../lib/Share20/DefaultShareProviderTest.php | 143 ++++++++++-------- tests/lib/Share20/ManagerTest.php | 46 +++--- tests/lib/TagsTest.php | 4 +- tests/lib/User/UserTest.php | 11 ++ 26 files changed, 251 insertions(+), 218 deletions(-) diff --git a/apps/comments/tests/Unit/Activity/ListenerTest.php b/apps/comments/tests/Unit/Activity/ListenerTest.php index 5280dc77a6572..1ae8a38c24509 100644 --- a/apps/comments/tests/Unit/Activity/ListenerTest.php +++ b/apps/comments/tests/Unit/Activity/ListenerTest.php @@ -18,8 +18,8 @@ use OCP\Files\Config\ICachedMountFileInfo; use OCP\Files\Config\IMountProviderCollection; use OCP\Files\Config\IUserMountCache; -use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Node; use OCP\IUser; use OCP\IUserSession; @@ -95,7 +95,7 @@ public function testCommentEvent(): void { $node = $this->createMock(Node::class); - $ownerFolder = $this->createMock(Folder::class); + $ownerFolder = $this->createMock(IUserFolder::class); $ownerFolder->expects($this->any()) ->method('getFirstNodeById') ->willReturn($node); diff --git a/apps/comments/tests/Unit/Controller/NotificationsTest.php b/apps/comments/tests/Unit/Controller/NotificationsTest.php index 46746091c7aca..58c9ad664b321 100644 --- a/apps/comments/tests/Unit/Controller/NotificationsTest.php +++ b/apps/comments/tests/Unit/Controller/NotificationsTest.php @@ -16,8 +16,8 @@ use OCP\Comments\IComment; use OCP\Comments\ICommentsManager; use OCP\Comments\NotFoundException; -use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Node; use OCP\IRequest; use OCP\IURLGenerator; @@ -101,7 +101,7 @@ public function testViewSuccess(): void { ->willReturn($comment); $file = $this->createMock(Node::class); - $folder = $this->createMock(Folder::class); + $folder = $this->createMock(IUserFolder::class); $user = $this->createMock(IUser::class); $this->rootFolder->expects($this->once()) @@ -178,7 +178,7 @@ public function testViewNoFile(): void { ->with('42') ->willReturn($comment); - $folder = $this->createMock(Folder::class); + $folder = $this->createMock(IUserFolder::class); $this->rootFolder->expects($this->once()) ->method('getUserFolder') diff --git a/apps/comments/tests/Unit/Notification/NotifierTest.php b/apps/comments/tests/Unit/Notification/NotifierTest.php index b26e183e53cf0..7bec485149dde 100644 --- a/apps/comments/tests/Unit/Notification/NotifierTest.php +++ b/apps/comments/tests/Unit/Notification/NotifierTest.php @@ -13,8 +13,8 @@ use OCP\Comments\IComment; use OCP\Comments\ICommentsManager; use OCP\Comments\NotFoundException; -use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Node; use OCP\IL10N; use OCP\IURLGenerator; @@ -82,7 +82,7 @@ public function testPrepareSuccess(): void { ->method('getPath') ->willReturn('/you/files/' . $fileName); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->folder->expects($this->once()) ->method('getUserFolder') ->with('you') @@ -198,7 +198,7 @@ public function testPrepareSuccessDeletedUser(): void { ->method('getPath') ->willReturn('/you/files/' . $fileName); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->folder->expects($this->once()) ->method('getUserFolder') ->with('you') @@ -488,7 +488,7 @@ public function testPrepareUnresolvableFileID(): void { $displayName = 'Huraga'; - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->folder->expects($this->once()) ->method('getUserFolder') ->with('you') diff --git a/apps/dav/tests/unit/Controller/DirectControllerTest.php b/apps/dav/tests/unit/Controller/DirectControllerTest.php index c32e648ba7f93..417b888ccbb79 100644 --- a/apps/dav/tests/unit/Controller/DirectControllerTest.php +++ b/apps/dav/tests/unit/Controller/DirectControllerTest.php @@ -21,6 +21,7 @@ use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\IRequest; use OCP\IURLGenerator; use OCP\Security\ISecureRandom; @@ -68,7 +69,7 @@ public function testGetUrlNonExistingFileId(): void { $this->shareManager->method('shareApiAllowLinks') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with('awesomeUser') ->willReturn($userFolder); @@ -85,7 +86,7 @@ public function testGetUrlForFolder(): void { $this->shareManager->method('shareApiAllowLinks') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with('awesomeUser') ->willReturn($userFolder); @@ -104,7 +105,7 @@ public function testGetUrlValid(): void { $this->shareManager->method('shareApiAllowLinks') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with('awesomeUser') ->willReturn($userFolder); diff --git a/apps/dav/tests/unit/Direct/DirectFileTest.php b/apps/dav/tests/unit/Direct/DirectFileTest.php index a40b04bfc5fce..e64911e420a38 100644 --- a/apps/dav/tests/unit/Direct/DirectFileTest.php +++ b/apps/dav/tests/unit/Direct/DirectFileTest.php @@ -13,8 +13,8 @@ use OCA\DAV\Direct\DirectFile; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\File; -use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use PHPUnit\Framework\MockObject\MockObject; use Sabre\DAV\Exception\Forbidden; use Test\TestCase; @@ -22,7 +22,7 @@ class DirectFileTest extends TestCase { private Direct $direct; private IRootFolder&MockObject $rootFolder; - private Folder&MockObject $userFolder; + private IUserFolder&MockObject $userFolder; private File&MockObject $file; private IEventDispatcher&MockObject $eventDispatcher; private DirectFile $directFile; @@ -38,7 +38,7 @@ protected function setUp(): void { $this->rootFolder = $this->createMock(IRootFolder::class); - $this->userFolder = $this->createMock(Folder::class); + $this->userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with('directUser') ->willReturn($this->userFolder); diff --git a/apps/files/tests/Controller/ConversionApiControllerTest.php b/apps/files/tests/Controller/ConversionApiControllerTest.php index 659fbe1a9560d..a994d3374d725 100644 --- a/apps/files/tests/Controller/ConversionApiControllerTest.php +++ b/apps/files/tests/Controller/ConversionApiControllerTest.php @@ -14,8 +14,8 @@ use OCP\AppFramework\OCS\OCSNotFoundException; use OCP\Files\Conversion\IConversionManager; use OCP\Files\File; -use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\IL10N; use OCP\IRequest; use PHPUnit\Framework\MockObject\MockObject; @@ -33,7 +33,7 @@ class ConversionApiControllerTest extends TestCase { private IConversionManager&MockObject $fileConversionManager; private IRootFolder&MockObject $rootFolder; private File&MockObject $file; - private Folder&MockObject $userFolder; + private IUserFolder&MockObject $userFolder; private IL10N&MockObject $l10n; private string $user; @@ -46,7 +46,7 @@ protected function setUp(): void { $this->l10n = $this->createMock(IL10N::class); $this->user = 'userid'; - $this->userFolder = $this->createMock(Folder::class); + $this->userFolder = $this->createMock(IUserFolder::class); $this->rootFolder = $this->createMock(IRootFolder::class); $this->rootFolder->method('getUserFolder')->with($this->user)->willReturn($this->userFolder); diff --git a/apps/files/tests/Controller/ViewControllerTest.php b/apps/files/tests/Controller/ViewControllerTest.php index ee2753da58b6f..1738cfa847c1e 100644 --- a/apps/files/tests/Controller/ViewControllerTest.php +++ b/apps/files/tests/Controller/ViewControllerTest.php @@ -27,6 +27,7 @@ use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Template\ITemplateManager; use OCP\ICacheFactory; use OCP\IConfig; @@ -182,7 +183,7 @@ public function testIndexWithRegularBrowser(): void { [$this->user->getUID(), 'files', 'show_grid', true], ]); - $baseFolderFiles = $this->getMockBuilder(Folder::class)->getMock(); + $baseFolderFiles = $this->getMockBuilder(IUserFolder::class)->getMock(); $this->rootFolder->expects($this->any()) ->method('getUserFolder') @@ -230,7 +231,7 @@ public function testShortRedirect(?string $openfile, ?string $opendetails, strin ->with('files') ->willReturn(true); - $baseFolderFiles = $this->getMockBuilder(Folder::class)->getMock(); + $baseFolderFiles = $this->getMockBuilder(IUserFolder::class)->getMock(); $this->rootFolder->expects($this->any()) ->method('getUserFolder') ->with('testuser1') @@ -265,7 +266,7 @@ public function testShowFileRouteWithTrashedFile(): void { ->method('getPath') ->willReturn('testuser1/files_trashbin/files/test.d1462861890/sub'); - $baseFolderFiles = $this->createMock(Folder::class); + $baseFolderFiles = $this->createMock(IUserFolder::class); $baseFolderTrash = $this->createMock(Folder::class); $this->rootFolder->expects($this->any()) diff --git a/apps/files_sharing/tests/ApplicationTest.php b/apps/files_sharing/tests/ApplicationTest.php index a6ff620722a5d..1379aa7428901 100644 --- a/apps/files_sharing/tests/ApplicationTest.php +++ b/apps/files_sharing/tests/ApplicationTest.php @@ -18,6 +18,7 @@ use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Storage\IStorage; use OCP\IUser; use OCP\IUserSession; @@ -78,7 +79,7 @@ public function testCheckDirectCanBeDownloaded( $file = $this->createMock(File::class); $file->method('getStorage')->willReturn($fileStorage); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder->method('get')->willReturn($file); $user = $this->createMock(IUser::class); @@ -166,7 +167,7 @@ function (string $fileStorage) use ($nonSharedStorage, $secureSharedStorage) { $rootFolder->method('getStorage')->willReturn($nonSharedStorage); $rootFolder->method('getDirectoryListing')->willReturn([$folder]); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder->method('get')->willReturn($rootFolder); $user = $this->createMock(IUser::class); diff --git a/apps/files_sharing/tests/Collaboration/ShareRecipientSorterTest.php b/apps/files_sharing/tests/Collaboration/ShareRecipientSorterTest.php index 296132919c7c7..8c633ae2a7624 100644 --- a/apps/files_sharing/tests/Collaboration/ShareRecipientSorterTest.php +++ b/apps/files_sharing/tests/Collaboration/ShareRecipientSorterTest.php @@ -8,8 +8,8 @@ namespace OCA\Files_Sharing\Tests\Collaboration; use OCA\Files_Sharing\Collaboration\ShareRecipientSorter; -use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Node; use OCP\IUser; use OCP\IUserSession; @@ -43,8 +43,7 @@ protected function setUp(): void { public function testSort($data): void { $node = $this->createMock(Node::class); - /** @var Folder|\PHPUnit\Framework\MockObject\MockObject $folder */ - $folder = $this->createMock(Folder::class); + $folder = $this->createMock(IUserFolder::class); $this->rootFolder->expects($this->any()) ->method('getUserFolder') ->willReturn($folder); @@ -82,8 +81,7 @@ public function testSort($data): void { } public function testSortNoNodes(): void { - /** @var Folder|\PHPUnit\Framework\MockObject\MockObject $folder */ - $folder = $this->createMock(Folder::class); + $folder = $this->createMock(IUserFolder::class); $this->rootFolder->expects($this->any()) ->method('getUserFolder') ->willReturn($folder); diff --git a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php index 104fc53ec8b44..454c575618d97 100644 --- a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php @@ -24,6 +24,7 @@ use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Mount\IMountPoint; use OCP\Files\Mount\IShareOwnerlessMount; use OCP\Files\Node; @@ -278,7 +279,7 @@ public function testDeleteShareLocked(): void { $share = $this->newShare(); $share->setNode($node); - $userFolder = $this->getMockBuilder(Folder::class)->getMock(); + $userFolder = $this->getMockBuilder(IUserFolder::class)->getMock(); $this->rootFolder->method('getUserFolder') ->with($this->currentUser) ->willReturn($userFolder); @@ -443,7 +444,7 @@ public function testDeleteSharedWithMyGroup(): void { ->method('lock') ->with(ILockingProvider::LOCK_SHARED); - $userFolder = $this->getMockBuilder(Folder::class)->getMock(); + $userFolder = $this->getMockBuilder(IUserFolder::class)->getMock(); $this->rootFolder->method('getUserFolder') ->with($this->currentUser) ->willReturn($userFolder); @@ -506,7 +507,7 @@ public function testDeleteSharedWithGroupIDontBelongTo(): void { ->method('lock') ->with(ILockingProvider::LOCK_SHARED); - $userFolder = $this->getMockBuilder(Folder::class)->getMock(); + $userFolder = $this->getMockBuilder(IUserFolder::class)->getMock(); $this->rootFolder->method('getUserFolder') ->with($this->currentUser) ->willReturn($userFolder); @@ -542,7 +543,7 @@ public function testDeleteShareOwnerless(): void { ->method('getMountPoint') ->willReturn($mount); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder->method('getById') ->with(2) ->willReturn([$file]); @@ -922,7 +923,7 @@ public function testGetShare(array $shareParams, array $result, bool $attributes ->with($share->getFullId(), 'currentUser') ->willReturn($share); - $userFolder = $this->getMockBuilder(Folder::class)->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getRelativePath') ->willReturnArgument(0); @@ -987,7 +988,7 @@ public function testGetShareInvalidNode(): void { ->with('ocinternal:42', 'currentUser') ->willReturn($share); - $userFolder = $this->getMockBuilder(Folder::class)->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with($this->currentUser) ->willReturn($userFolder); @@ -1636,7 +1637,7 @@ function ($share) { } ); - $userFolder = $this->getMockBuilder(Folder::class)->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $userFolder->method('get') ->with('path') ->willReturn($this->mockSimpleNode(...$getSharesParameters['node'])); @@ -1706,7 +1707,7 @@ public function testCannotAccessLinkShare(): void { $share->method('getShareType')->willReturn(IShare::TYPE_LINK); $share->method('getNodeId')->willReturn(42); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with($this->currentUser) ->willReturn($userFolder); @@ -1723,7 +1724,7 @@ public function testCanAccessShareWithPermissions(int $permissions, bool $expect $file = $this->createMock(File::class); - $userFolder = $this->getMockBuilder(Folder::class)->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $userFolder->method('getFirstNodeById') ->with($share->getNodeId()) ->willReturn($file); @@ -1761,7 +1762,7 @@ public function testCanAccessShareAsGroupMember(string $group, bool $expected): $file = $this->createMock(File::class); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder->method('getFirstNodeById') ->with($share->getNodeId()) ->willReturn($file); @@ -1822,7 +1823,7 @@ public function testCanAccessRoomShare( $share->method('getShareType')->willReturn(IShare::TYPE_ROOM); $share->method('getSharedWith')->willReturn('recipientRoom'); - $userFolder = $this->getMockBuilder(Folder::class)->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with($this->currentUser) ->willReturn($userFolder); @@ -1860,7 +1861,7 @@ public function testCreateShareInvalidPath(): void { $this->expectException(OCSNotFoundException::class); $this->expectExceptionMessage('Wrong path, file/folder does not exist'); - $userFolder = $this->getMockBuilder(Folder::class)->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->expects($this->once()) ->method('getUserFolder') ->with('currentUser') @@ -2066,10 +2067,11 @@ public function testCreateShareLinkPassword(): void { ['OCA\Files_Sharing\SharedStorage', false], ]); $path->method('getStorage')->willReturn($storage); - $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); - $this->rootFolder->method('get')->with('valid-path')->willReturn($path); - $this->rootFolder->method('getById') + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('get')->with('valid-path')->willReturn($path); + $userFolder->method('getById') ->willReturn([]); + $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturn($userFolder); $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare()); @@ -2103,10 +2105,11 @@ public function testCreateShareLinkSendPasswordByTalk(): void { ['OCA\Files_Sharing\SharedStorage', false], ]); $path->method('getStorage')->willReturn($storage); - $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); - $this->rootFolder->method('get')->with('valid-path')->willReturn($path); - $this->rootFolder->method('getById') + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('get')->with('valid-path')->willReturn($path); + $userFolder->method('getById') ->willReturn([]); + $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturn($userFolder); $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare()); @@ -2147,10 +2150,11 @@ public function testCreateShareLinkSendPasswordByTalkWithTalkDisabled(): void { ]); $path->method('getStorage')->willReturn($storage); $path->method('getPath')->willReturn('valid-path'); - $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); - $this->rootFolder->method('get')->with('valid-path')->willReturn($path); - $this->rootFolder->method('getById') + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('get')->with('valid-path')->willReturn($path); + $userFolder->method('getById') ->willReturn([]); + $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturn($userFolder); $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare()); @@ -2183,10 +2187,11 @@ public function testCreateShareValidExpireDate(): void { ['OCA\Files_Sharing\SharedStorage', false], ]); $path->method('getStorage')->willReturn($storage); - $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); - $this->rootFolder->method('get')->with('valid-path')->willReturn($path); - $this->rootFolder->method('getById') + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('get')->with('valid-path')->willReturn($path); + $userFolder->method('getById') ->willReturn([]); + $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturn($userFolder); $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare()); @@ -2226,10 +2231,11 @@ public function testCreateShareInvalidExpireDate(): void { ['OCA\Files_Sharing\SharedStorage', false], ]); $path->method('getStorage')->willReturn($storage); - $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); - $this->rootFolder->method('get')->with('valid-path')->willReturn($path); - $this->rootFolder->method('getById') + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('get')->with('valid-path')->willReturn($path); + $userFolder->method('getById') ->willReturn([]); + $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturn($userFolder); $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare()); @@ -2559,7 +2565,7 @@ public function testCreateReshareOfFederatedMountNoDeletePermissions(): void { ])->onlyMethods(['formatShare']) ->getMock(); - $userFolder = $this->getMockBuilder(Folder::class)->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->expects($this->exactly(2)) ->method('getUserFolder') ->with('currentUser') @@ -3638,7 +3644,7 @@ public function testUpdateShareOwnerless(): void { ->method('getMountPoint') ->willReturn($mount); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder->method('getById') ->with(2) ->willReturn([$file]); @@ -4617,20 +4623,18 @@ function ($user) { ->with('files_sharing.sharecontroller.showShare', ['token' => 'myToken']) ->willReturn('myLink'); - $this->rootFolder->method('getUserFolder') - ->with($this->currentUser) - ->willReturnSelf(); - $this->dateTimeZone->method('getTimezone')->willReturn(new \DateTimeZone('UTC')); - + $userFolder = $this->createMock(IUserFolder::class); if (!$exception) { - $this->rootFolder->method('getFirstNodeById') + $userFolder->method('getFirstNodeById') ->with($share->getNodeId()) ->willReturn($share->getNode()); - $this->rootFolder->method('getRelativePath') + $userFolder->method('getRelativePath') ->with($share->getNode()->getPath()) ->willReturnArgument(0); } + $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturn($userFolder); + $this->dateTimeZone->method('getTimezone')->willReturn(new \DateTimeZone('UTC')); $cm = $this->createMock(\OCP\Contacts\IManager::class); $this->overwriteService(\OCP\Contacts\IManager::class, $cm); @@ -4810,17 +4814,14 @@ public function testFormatRoomShare(array $expects, bool $helperAvailable, array ->setNote('personal note') ->setId(42); - $this->rootFolder->method('getUserFolder') - ->with($this->currentUser) - ->willReturnSelf(); - - $this->rootFolder->method('getFirstNodeById') + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById') ->with($share->getNodeId()) ->willReturn($share->getNode()); - - $this->rootFolder->method('getRelativePath') + $userFolder->method('getRelativePath') ->with($share->getNode()->getPath()) ->willReturnArgument(0); + $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturn($userFolder); if (!$helperAvailable) { $this->appManager->method('isEnabledForUser') @@ -4849,7 +4850,7 @@ public function testFormatRoomShare(array $expects, bool $helperAvailable, array */ private function getNonSharedUserFolder(): array { $node = $this->getMockBuilder(Folder::class)->getMock(); - $userFolder = $this->getMockBuilder(Folder::class)->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $storage = $this->createMock(IStorage::class); $storage->method('instanceOfStorage') ->willReturnMap([ @@ -4866,11 +4867,11 @@ private function getNonSharedUserFolder(): array { } /** - * @return list{Folder, File} + * @return list{IUserFolder, File} */ private function getNonSharedUserFile(): array { $node = $this->getMockBuilder(File::class)->getMock(); - $userFolder = $this->getMockBuilder(Folder::class)->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $storage = $this->createMock(IStorage::class); $storage->method('instanceOfStorage') ->willReturnMap([ @@ -4958,17 +4959,14 @@ public function testFormatShareWithFederatedShare(bool $isKnownServer, bool $isT $this->previewManager->method('isAvailable')->with($node)->willReturn(false); - $this->rootFolder->method('getUserFolder') - ->with($this->currentUser) - ->willReturnSelf(); - - $this->rootFolder->method('getFirstNodeById') + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById') ->with($share->getNodeId()) ->willReturn($node); - - $this->rootFolder->method('getRelativePath') + $userFolder->method('getRelativePath') ->with($node->getPath()) ->willReturnArgument(0); + $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturn($userFolder); $serverName = 'remoteserver.com'; $this->trustedServers->method('isTrustedServer') @@ -5021,17 +5019,14 @@ public function testFormatShareWithFederatedShareWithAtInUsername(): void { $this->previewManager->method('isAvailable')->with($node)->willReturn(false); - $this->rootFolder->method('getUserFolder') - ->with($this->currentUser) - ->willReturnSelf(); - - $this->rootFolder->method('getFirstNodeById') + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById') ->with($share->getNodeId()) ->willReturn($node); - - $this->rootFolder->method('getRelativePath') + $userFolder->method('getRelativePath') ->with($node->getPath()) ->willReturnArgument(0); + $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturn($userFolder); $serverName = 'remoteserver.com'; $this->trustedServers->method('isTrustedServer') @@ -5048,7 +5043,7 @@ public function testOwnerCanAlwaysDownload(): void { $share = $this->createMock(IShare::class); $node = $this->createMock(File::class); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $owner = $this->createMock(IUser::class); $share->method('getSharedBy')->willReturn('sharedByUser'); @@ -5070,7 +5065,7 @@ public function testParentHideDownloadEnforcedOnChild(): void { $share = $this->createMock(IShare::class); $node = $this->createMock(File::class); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $owner = $this->createMock(IUser::class); $storage = $this->createMock(SharedStorage::class); $originalShare = $this->createMock(IShare::class); @@ -5101,7 +5096,7 @@ public function testUserCanHideWhenParentAllows(): void { $share = $this->createMock(IShare::class); $node = $this->createMock(File::class); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $owner = $this->createMock(IUser::class); $storage = $this->createMock(SharedStorage::class); $originalShare = $this->createMock(IShare::class); @@ -5132,7 +5127,7 @@ public function testParentDownloadAttributeInherited(): void { $share = $this->createMock(IShare::class); $node = $this->createMock(File::class); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $owner = $this->createMock(IUser::class); $storage = $this->createMock(SharedStorage::class); $originalShare = $this->createMock(IShare::class); @@ -5170,7 +5165,7 @@ public function testFederatedStorageRespectsUserChoice(): void { $share = $this->createMock(IShare::class); $node = $this->createMock(File::class); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $owner = $this->createMock(IUser::class); $storage = $this->createMock(Storage::class); @@ -5199,7 +5194,7 @@ public function testUserAllowsDownloadWhenParentPermits(): void { $share = $this->createMock(IShare::class); $node = $this->createMock(File::class); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $owner = $this->createMock(IUser::class); $storage = $this->createMock(SharedStorage::class); $originalShare = $this->createMock(IShare::class); @@ -5230,7 +5225,7 @@ public function testWrapperStorageUnwrapped(): void { $share = $this->createMock(IShare::class); $node = $this->createMock(File::class); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $owner = $this->createMock(IUser::class); $wrapperStorage = $this->createMock(Wrapper::class); $innerStorage = $this->createMock(SharedStorage::class); diff --git a/apps/files_sharing/tests/External/ManagerTest.php b/apps/files_sharing/tests/External/ManagerTest.php index bfe6567872a4d..68fb4839cbf36 100644 --- a/apps/files_sharing/tests/External/ManagerTest.php +++ b/apps/files_sharing/tests/External/ManagerTest.php @@ -22,9 +22,9 @@ use OCP\EventDispatcher\IEventDispatcher; use OCP\Federation\ICloudFederationFactory; use OCP\Federation\ICloudFederationProviderManager; -use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\ISetupManager; +use OCP\Files\IUserFolder; use OCP\Files\NotFoundException; use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; @@ -99,8 +99,8 @@ protected function setUp(): void { $this->setupManagerEncTrait = $this->createMock(ISetupManager::class); $this->rootFolder = $this->createMock(IRootFolder::class); $this->rootFolder->method('getUserFolder') - ->willReturnCallback(function (string $userId): Folder { - $folder = $this->createMock(Folder::class); + ->willReturnCallback(function (string $userId): IUserFolder { + $folder = $this->createMock(IUserFolder::class); $folder->method('get') ->willReturn($folder); $folder->method('getNonExistingName') diff --git a/apps/files_trashbin/tests/Controller/PreviewControllerTest.php b/apps/files_trashbin/tests/Controller/PreviewControllerTest.php index fd13f34032249..6e1bec1f9bf3f 100644 --- a/apps/files_trashbin/tests/Controller/PreviewControllerTest.php +++ b/apps/files_trashbin/tests/Controller/PreviewControllerTest.php @@ -18,6 +18,7 @@ use OCP\Files\Folder; use OCP\Files\IMimeTypeDetector; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\SimpleFS\ISimpleFile; use OCP\IPreview; use OCP\IRequest; @@ -82,7 +83,7 @@ public function testInvalidHeight(): void { } public function testValidPreview(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userRoot = $this->createMock(Folder::class); $trash = $this->createMock(Folder::class); @@ -135,7 +136,7 @@ public function testValidPreview(): void { } public function testTrashFileNotFound(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userRoot = $this->createMock(Folder::class); $trash = $this->createMock(Folder::class); @@ -159,7 +160,7 @@ public function testTrashFileNotFound(): void { } public function testTrashFolder(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userRoot = $this->createMock(Folder::class); $trash = $this->createMock(Folder::class); diff --git a/apps/files_trashbin/tests/StorageTest.php b/apps/files_trashbin/tests/StorageTest.php index 016f9f3efce4a..2890336f16257 100644 --- a/apps/files_trashbin/tests/StorageTest.php +++ b/apps/files_trashbin/tests/StorageTest.php @@ -28,6 +28,7 @@ use OCP\Files\Cache\ICache; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Node; use OCP\Files\Storage\IStorage; use OCP\IUserManager; @@ -656,7 +657,7 @@ public function testShouldMoveToTrash(string $mountPoint, string $path, bool $us $logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); $eventDispatcher = $this->createMock(IEventDispatcher::class); $rootFolder = $this->createMock(IRootFolder::class); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $node = $this->getMockBuilder(Node::class)->disableOriginalConstructor()->getMock(); $trashManager = $this->createMock(ITrashManager::class); $event = $this->getMockBuilder(MoveToTrashEvent::class)->disableOriginalConstructor()->getMock(); diff --git a/apps/files_versions/tests/Command/CleanupTest.php b/apps/files_versions/tests/Command/CleanupTest.php index d671b49cc6bc3..80dbfe13bb001 100644 --- a/apps/files_versions/tests/Command/CleanupTest.php +++ b/apps/files_versions/tests/Command/CleanupTest.php @@ -13,8 +13,8 @@ use OCA\Files_Versions\Command\CleanUp; use OCA\Files_Versions\Db\VersionsMapper; use OCP\Files\Cache\ICache; -use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Storage\IStorage; use OCP\UserInterface; use PHPUnit\Framework\MockObject\MockObject; @@ -53,7 +53,7 @@ public function testDeleteVersions(bool $nodeExists): void { ->with('/testUser/files_versions') ->willReturn($nodeExists); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userHomeStorage = $this->createMock(IStorage::class); $userHomeStorageCache = $this->createMock(ICache::class); $this->rootFolder->expects($this->once()) diff --git a/apps/files_versions/tests/Controller/PreviewControllerTest.php b/apps/files_versions/tests/Controller/PreviewControllerTest.php index 542ea2b6b3485..7932f6399967c 100644 --- a/apps/files_versions/tests/Controller/PreviewControllerTest.php +++ b/apps/files_versions/tests/Controller/PreviewControllerTest.php @@ -15,6 +15,7 @@ use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\NotFoundException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\IPreview; @@ -92,7 +93,7 @@ public function testInvalidVersion(): void { } public function testValidPreview(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userRoot = $this->createMock(Folder::class); $this->rootFolder->method('getUserFolder') @@ -130,7 +131,7 @@ public function testValidPreview(): void { } public function testVersionNotFound(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userRoot = $this->createMock(Folder::class); $this->rootFolder->method('getUserFolder') diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index b6774987193c8..9d5b9e807e66c 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -550,7 +550,7 @@ public function getOrCreateFolder(string $path, int $maxRetries = 5): IFolder { return $folder; } } catch (NotFoundException) { - $folder = dirname($path) === '.' ? $this : $this->get(dirname($path)); + $folder = in_array(dirname($path), ['.','/']) ? $this : $this->get(dirname($path)); if (!($folder instanceof Folder)) { throw new NotPermittedException("Unable to create folder $path. Parent is not a directory."); } diff --git a/tests/Core/Controller/AvatarControllerTest.php b/tests/Core/Controller/AvatarControllerTest.php index 9a93d90245c97..125c015a30335 100644 --- a/tests/Core/Controller/AvatarControllerTest.php +++ b/tests/Core/Controller/AvatarControllerTest.php @@ -23,6 +23,7 @@ function is_uploaded_file($filename) { use OC\Core\Controller\GuestAvatarController; use OCP\AppFramework\Http; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; @@ -371,7 +372,7 @@ public function testPostAvatarFromFile(): void { $file->expects($this->once()) ->method('getMimeType') ->willReturn('image/jpeg'); - $userFolder = $this->getMockBuilder('OCP\Files\Folder')->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder')->with('userid')->willReturn($userFolder); $userFolder->method('get')->willReturn($file); @@ -387,7 +388,7 @@ public function testPostAvatarFromFile(): void { */ public function testPostAvatarFromNoFile(): void { $file = $this->getMockBuilder('OCP\Files\Node')->getMock(); - $userFolder = $this->getMockBuilder('OCP\Files\Folder')->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder')->with('userid')->willReturn($userFolder); $userFolder ->method('get') @@ -409,7 +410,7 @@ public function testPostAvatarInvalidType(): void { $file->expects($this->exactly(2)) ->method('getMimeType') ->willReturn('text/plain'); - $userFolder = $this->getMockBuilder('OCP\Files\Folder')->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder')->with('userid')->willReturn($userFolder); $userFolder->method('get')->willReturn($file); @@ -426,7 +427,7 @@ public function testPostAvatarNotPermittedException(): void { $file->expects($this->once()) ->method('getMimeType') ->willReturn('image/jpeg'); - $userFolder = $this->getMockBuilder('OCP\Files\Folder')->getMock(); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder')->with('userid')->willReturn($userFolder); $userFolder->method('get')->willReturn($file); diff --git a/tests/Core/Controller/PreviewControllerTest.php b/tests/Core/Controller/PreviewControllerTest.php index 91fed380d2c54..19676af3256ad 100644 --- a/tests/Core/Controller/PreviewControllerTest.php +++ b/tests/Core/Controller/PreviewControllerTest.php @@ -13,6 +13,7 @@ use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\NotFoundException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\Storage\ISharedStorage; @@ -73,7 +74,7 @@ public function testInvalidHeight(): void { } public function testFileNotFound(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with($this->equalTo($this->userId)) ->willReturn($userFolder); @@ -89,7 +90,7 @@ public function testFileNotFound(): void { } public function testNotAFile(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with($this->equalTo($this->userId)) ->willReturn($userFolder); @@ -106,7 +107,7 @@ public function testNotAFile(): void { } public function testNoPreviewAndNoIcon(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with($this->equalTo($this->userId)) ->willReturn($userFolder); @@ -127,7 +128,7 @@ public function testNoPreviewAndNoIcon(): void { } public function testNoPreview() { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with($this->equalTo($this->userId)) ->willReturn($userFolder); @@ -158,7 +159,7 @@ public function testNoPreview() { $this->assertEquals($expected, $res); } public function testFileWithoutReadPermission() { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with($this->equalTo($this->userId)) ->willReturn($userFolder); @@ -182,7 +183,7 @@ public function testFileWithoutReadPermission() { } public function testFileWithoutDownloadPermission() { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with($this->equalTo($this->userId)) ->willReturn($userFolder); @@ -222,7 +223,7 @@ public function testFileWithoutDownloadPermission() { } public function testFileWithoutDownloadPermissionButHeader() { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with($this->equalTo($this->userId)) ->willReturn($userFolder); @@ -275,7 +276,7 @@ public function testFileWithoutDownloadPermissionButHeader() { } public function testValidPreview(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with($this->equalTo($this->userId)) ->willReturn($userFolder); @@ -314,7 +315,7 @@ public function testValidPreview(): void { } public function testValidPreviewOfShare() { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with($this->equalTo($this->userId)) ->willReturn($userFolder); diff --git a/tests/lib/DirectEditing/ManagerTest.php b/tests/lib/DirectEditing/ManagerTest.php index 1853e7d254a9d..dcbf4273ad90a 100644 --- a/tests/lib/DirectEditing/ManagerTest.php +++ b/tests/lib/DirectEditing/ManagerTest.php @@ -18,6 +18,7 @@ use OCP\Encryption\IManager; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\IDBConnection; use OCP\IL10N; use OCP\IUser; @@ -117,10 +118,7 @@ class ManagerTest extends TestCase { * @var MockObject|IRootFolder */ private $rootFolder; - /** - * @var MockObject|Folder - */ - private $userFolder; + private IUserFolder&MockObject $userFolder; /** * @var MockObject|IL10N */ @@ -140,7 +138,7 @@ protected function setUp(): void { $this->connection = Server::get(IDBConnection::class); $this->userSession = $this->createMock(IUserSession::class); $this->rootFolder = $this->createMock(IRootFolder::class); - $this->userFolder = $this->createMock(Folder::class); + $this->userFolder = $this->createMock(IUserFolder::class); $this->l10n = $this->createMock(IL10N::class); $this->encryptionManager = $this->createMock(IManager::class); diff --git a/tests/lib/Files/Node/RootTest.php b/tests/lib/Files/Node/RootTest.php index e8841690ac4d5..b2a2afc1d54fb 100644 --- a/tests/lib/Files/Node/RootTest.php +++ b/tests/lib/Files/Node/RootTest.php @@ -10,7 +10,6 @@ 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; @@ -18,6 +17,7 @@ use OCP\Cache\CappedMemoryCache; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Config\IUserMountCache; +use OCP\Files\IUserFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\IAppConfig; @@ -208,7 +208,7 @@ public function testGetUserFolder(): void { ->expects($this->once()) ->method('hasKey') ->willReturn(true); - $folder = $this->createMock(Folder::class); + $folder = $this->createMock(IUserFolder::class); $cappedMemoryCache ->expects($this->once()) ->method('get') diff --git a/tests/lib/Files/Template/TemplateManagerTest.php b/tests/lib/Files/Template/TemplateManagerTest.php index efc0a85d9bfa4..8c82e1d739c06 100644 --- a/tests/lib/Files/Template/TemplateManagerTest.php +++ b/tests/lib/Files/Template/TemplateManagerTest.php @@ -17,6 +17,7 @@ use OCP\Files\Folder; use OCP\Files\GenericFileException; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\NotFoundException; use OCP\IConfig; use OCP\IDBConnection; @@ -94,7 +95,7 @@ public function testCreateFromTemplateShoudValidateFilename(): void { $fileDirectory = '/'; $filePath = $fileDirectory . str_repeat('a', 251); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder->method('get') ->willReturnCallback(function ($path) use ($filePath, $fileDirectory) { if ($path === $filePath) { diff --git a/tests/lib/Repair/Owncloud/CleanPreviewsBackgroundJobTest.php b/tests/lib/Repair/Owncloud/CleanPreviewsBackgroundJobTest.php index 9b51da4de01be..b5a4781a1e313 100644 --- a/tests/lib/Repair/Owncloud/CleanPreviewsBackgroundJobTest.php +++ b/tests/lib/Repair/Owncloud/CleanPreviewsBackgroundJobTest.php @@ -12,6 +12,7 @@ use OCP\BackgroundJob\IJobList; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\IUserManager; @@ -50,7 +51,7 @@ public function setUp(): void { } public function testCleanupPreviewsUnfinished(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userRoot = $this->createMock(Folder::class); $thumbnailFolder = $this->createMock(Folder::class); @@ -98,7 +99,7 @@ public function testCleanupPreviewsUnfinished(): void { } public function testCleanupPreviewsFinished(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userRoot = $this->createMock(Folder::class); $thumbnailFolder = $this->createMock(Folder::class); @@ -162,7 +163,7 @@ public function testNoUserFolder(): void { } public function testNoThumbnailFolder(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userRoot = $this->createMock(Folder::class); $this->rootFolder->method('getUserFolder') @@ -190,7 +191,7 @@ public function testNoThumbnailFolder(): void { } public function testNotPermittedToDelete(): void { - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userRoot = $this->createMock(Folder::class); $thumbnailFolder = $this->createMock(Folder::class); diff --git a/tests/lib/Share20/DefaultShareProviderTest.php b/tests/lib/Share20/DefaultShareProviderTest.php index 8917f0abaa290..c10542a1433ed 100644 --- a/tests/lib/Share20/DefaultShareProviderTest.php +++ b/tests/lib/Share20/DefaultShareProviderTest.php @@ -20,6 +20,7 @@ use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\IConfig; use OCP\IDBConnection; use OCP\IGroup; @@ -219,7 +220,7 @@ public function testGetShareByIdUserShare(): void { $shareOwner->method('getUID')->willReturn('shareOwner'); $ownerPath = $this->createMock(File::class); - $shareOwnerFolder = $this->createMock(Folder::class); + $shareOwnerFolder = $this->createMock(IUserFolder::class); $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($ownerPath); $this->rootFolder @@ -297,7 +298,7 @@ public function testGetShareByIdLazy2(): void { $ownerPath = $this->createMock(File::class); - $shareOwnerFolder = $this->createMock(Folder::class); + $shareOwnerFolder = $this->createMock(IUserFolder::class); $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($ownerPath); $this->rootFolder @@ -341,7 +342,7 @@ public function testGetShareByIdGroupShare(): void { $id = $qb->getLastInsertId(); $ownerPath = $this->createMock(Folder::class); - $shareOwnerFolder = $this->createMock(Folder::class); + $shareOwnerFolder = $this->createMock(IUserFolder::class); $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($ownerPath); $this->rootFolder @@ -381,8 +382,9 @@ public function testGetShareByIdUserGroupShare(): void { $node->method('getId')->willReturn(42); $node->method('getName')->willReturn('myTarget'); - $this->rootFolder->method('getUserFolder')->with('user0')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->willReturn($node); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->willReturn($node); + $this->rootFolder->method('getUserFolder')->with('user0')->willReturn($userFolder); $this->userManager->method('get')->willReturnMap([ ['user0', $user0], @@ -426,7 +428,7 @@ public function testGetShareByIdLinkShare(): void { $id = $qb->getLastInsertId(); $ownerPath = $this->createMock(Folder::class); - $shareOwnerFolder = $this->createMock(Folder::class); + $shareOwnerFolder = $this->createMock(IUserFolder::class); $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($ownerPath); $this->rootFolder @@ -649,7 +651,7 @@ public function testGetChildren(): void { $qb->executeStatement(); $ownerPath = $this->createMock(Folder::class); - $ownerFolder = $this->createMock(Folder::class); + $ownerFolder = $this->createMock(IUserFolder::class); $ownerFolder->method('getFirstNodeById')->willReturn($ownerPath); $this->rootFolder @@ -699,8 +701,8 @@ public function testCreateUserShare(): void { $path->method('getId')->willReturn(100); $path->method('getOwner')->willReturn($shareOwner); - $ownerFolder = $this->createMock(Folder::class); - $userFolder = $this->createMock(Folder::class); + $ownerFolder = $this->createMock(IUserFolder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder ->method('getUserFolder') ->willReturnMap([ @@ -771,8 +773,8 @@ public function testCreateGroupShare(): void { $path->method('getId')->willReturn(100); $path->method('getOwner')->willReturn($shareOwner); - $ownerFolder = $this->createMock(Folder::class); - $userFolder = $this->createMock(Folder::class); + $ownerFolder = $this->createMock(IUserFolder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder ->method('getUserFolder') ->willReturnMap([ @@ -841,8 +843,8 @@ public function testCreateLinkShare(): void { $path->method('getId')->willReturn(100); $path->method('getOwner')->willReturn($shareOwner); - $ownerFolder = $this->createMock(Folder::class); - $userFolder = $this->createMock(Folder::class); + $ownerFolder = $this->createMock(IUserFolder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder ->method('getUserFolder') ->willReturnMap([ @@ -908,8 +910,9 @@ public function testGetShareByToken(): void { $file = $this->createMock(File::class); - $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file); + $shareOwnerFolder = $this->createMock(IUserFolder::class); + $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturn($shareOwnerFolder); $share = $this->provider->getShareByToken('secrettoken'); $this->assertEquals($id, $share->getId()); @@ -947,8 +950,9 @@ public function testGetShareByTokenNullLabel(): void { $file = $this->createMock(File::class); - $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file); + $shareOwnerFolder = $this->createMock(IUserFolder::class); + $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturn($shareOwnerFolder); $share = $this->provider->getShareByToken('secrettoken'); $this->assertEquals($id, $share->getId()); @@ -1030,8 +1034,9 @@ public function testGetSharedWithUser($storageStringId, $fileName1, $fileName2): $this->assertEquals(1, $qb->executeStatement()); $file = $this->createMock(File::class); - $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with($fileId)->willReturn($file); + $shareOwnerFolder = $this->createMock(IUserFolder::class); + $shareOwnerFolder->method('getFirstNodeById')->with($fileId)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturn($shareOwnerFolder); $share = $this->provider->getSharedWith('sharedWith', IShare::TYPE_USER, null, 1, 0); $this->assertCount(1, $share); @@ -1231,8 +1236,9 @@ public function testGetSharedWithUserWithNode($storageStringId, $fileName1, $fil $file = $this->createMock(File::class); $file->method('getId')->willReturn($fileId2); - $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with($fileId2)->willReturn($file); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with($fileId2)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('user1')->willReturn($userFolder); $share = $this->provider->getSharedWith('user0', IShare::TYPE_USER, $file, -1, 0); $this->assertCount(1, $share); @@ -1272,8 +1278,9 @@ public function testGetSharedWithGroupWithNode($storageStringId, $fileName1, $fi $node = $this->createMock(Folder::class); $node->method('getId')->willReturn($fileId2); - $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with($fileId2)->willReturn($node); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with($fileId2)->willReturn($node); + $this->rootFolder->method('getUserFolder')->with('user1')->willReturn($userFolder); $share = $this->provider->getSharedWith('user0', IShare::TYPE_GROUP, $node, -1, 0); $this->assertCount(1, $share); @@ -1321,8 +1328,9 @@ public function testGetSharedWithWithDeletedFile($shareType, $trashed): void { $this->assertEquals(1, $qb->executeStatement()); $file = $this->createMock(File::class); - $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with($deletedFileId)->willReturn($file); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with($deletedFileId)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturn($userFolder); $groups = []; foreach (range(0, 100) as $i) { @@ -1433,8 +1441,9 @@ public function testGetSharesNode(): void { $file = $this->createMock(File::class); $file->method('getId')->willReturn(42); - $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with(42)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturn($userFolder); $share = $this->provider->getSharesBy('sharedBy', IShare::TYPE_USER, $file, false, 1, 0); $this->assertCount(1, $share); @@ -1483,8 +1492,9 @@ public function testGetSharesReshare(): void { $file = $this->createMock(File::class); $file->method('getId')->willReturn(42); - $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with(42)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturn($userFolder); $shares = $this->provider->getSharesBy('shareOwner', IShare::TYPE_USER, null, true, -1, 0); $this->assertCount(2, $shares); @@ -1543,8 +1553,9 @@ public function testDeleteFromSelfGroupNoCustomShare(): void { $file = $this->createMock(File::class); $file->method('getId')->willReturn(1); - $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('user1')->willReturn($userFolder); $share = $this->provider->getShareById($id); @@ -1615,8 +1626,9 @@ public function testDeleteFromSelfGroupAlreadyCustomShare(): void { $file = $this->createMock(File::class); $file->method('getId')->willReturn(1); - $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('user1')->willReturn($userFolder); $share = $this->provider->getShareById($id); @@ -1672,8 +1684,9 @@ public function testDeleteFromSelfGroupUserNotInGroup(): void { $file = $this->createMock(File::class); $file->method('getId')->willReturn(1); - $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('user1')->willReturn($userFolder); $share = $this->provider->getShareById($id); @@ -1713,8 +1726,9 @@ public function testDeleteFromSelfGroupDoesNotExist(): void { $file = $this->createMock(File::class); $file->method('getId')->willReturn(1); - $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('user1')->willReturn($userFolder); $share = $this->provider->getShareById($id); @@ -1751,8 +1765,9 @@ public function testDeleteFromSelfUser(): void { $file = $this->createMock(File::class); $file->method('getId')->willReturn(1); - $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('user1')->willReturn($userFolder); $share = $this->provider->getShareById($id); @@ -1804,8 +1819,9 @@ public function testDeleteFromSelfUserNotRecipient(): void { $file = $this->createMock(File::class); $file->method('getId')->willReturn(1); - $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('user1')->willReturn($userFolder); $share = $this->provider->getShareById($id); @@ -1840,8 +1856,9 @@ public function testDeleteFromSelfLink(): void { $file = $this->createMock(File::class); $file->method('getId')->willReturn(1); - $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with(1)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('user1')->willReturn($userFolder); $share = $this->provider->getShareById($id); @@ -1871,9 +1888,9 @@ function ($userId) use ($users) { $file2 = $this->createMock(File::class); $file2->method('getId')->willReturn(43); - $folder1 = $this->createMock(Folder::class); + $folder1 = $this->createMock(IUserFolder::class); $folder1->method('getFirstNodeById')->with(42)->willReturn($file1); - $folder2 = $this->createMock(Folder::class); + $folder2 = $this->createMock(IUserFolder::class); $folder2->method('getFirstNodeById')->with(43)->willReturn($file2); $this->rootFolder->method('getUserFolder')->willReturnMap([ @@ -1928,9 +1945,9 @@ function ($userId) use ($users) { $file2 = $this->createMock(File::class); $file2->method('getId')->willReturn(43); - $folder1 = $this->createMock(Folder::class); + $folder1 = $this->createMock(IUserFolder::class); $folder1->method('getFirstNodeById')->with(42)->willReturn($file1); - $folder2 = $this->createMock(Folder::class); + $folder2 = $this->createMock(IUserFolder::class); $folder2->method('getFirstNodeById')->with(43)->willReturn($file2); $this->rootFolder->method('getUserFolder')->willReturnMap([ @@ -1994,9 +2011,9 @@ function ($userId) use ($users) { $file2 = $this->createMock(File::class); $file2->method('getId')->willReturn(43); - $folder1 = $this->createMock(Folder::class); + $folder1 = $this->createMock(IUserFolder::class); $folder1->method('getFirstNodeById')->with(42)->willReturn($file1); - $folder2 = $this->createMock(Folder::class); + $folder2 = $this->createMock(IUserFolder::class); $folder2->method('getFirstNodeById')->with(43)->willReturn($file2); $this->rootFolder->method('getUserFolder')->willReturnMap([ @@ -2065,9 +2082,9 @@ function ($groupId) use ($groups) { $file2 = $this->createMock(File::class); $file2->method('getId')->willReturn(43); - $folder1 = $this->createMock(Folder::class); + $folder1 = $this->createMock(IUserFolder::class); $folder1->method('getFirstNodeById')->with(42)->willReturn($file1); - $folder2 = $this->createMock(Folder::class); + $folder2 = $this->createMock(IUserFolder::class); $folder2->method('getFirstNodeById')->with(43)->willReturn($file2); $this->rootFolder->method('getUserFolder')->willReturnMap([ @@ -2144,9 +2161,9 @@ function ($groupId) use ($groups) { $file2 = $this->createMock(File::class); $file2->method('getId')->willReturn(43); - $folder1 = $this->createMock(Folder::class); + $folder1 = $this->createMock(IUserFolder::class); $folder1->method('getFirstNodeById')->with(42)->willReturn($file1); - $folder2 = $this->createMock(Folder::class); + $folder2 = $this->createMock(IUserFolder::class); $folder2->method('getFirstNodeById')->with(43)->willReturn($file2); $this->rootFolder->method('getUserFolder')->willReturnMap([ @@ -2221,8 +2238,9 @@ public function testMoveUserShare(): void { $file = $this->createMock(File::class); $file->method('getId')->willReturn(42); - $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->willReturn($file); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with(42)->willReturn($file); + $this->rootFolder->method('getUserFolder')->with('user1')->willReturn($userFolder); $share = $this->provider->getShareById($id, null); @@ -2257,8 +2275,9 @@ public function testMoveGroupShare(): void { $folder = $this->createMock(Folder::class); $folder->method('getId')->willReturn(42); - $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getFirstNodeById')->willReturn($folder); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getFirstNodeById')->with(42)->willReturn($folder); + $this->rootFolder->method('getUserFolder')->with('user1')->willReturn($userFolder); $share = $this->provider->getShareById($id, 'user0'); @@ -2948,23 +2967,23 @@ public function testGetAllShares(): void { $id5 = $qb->getLastInsertId(); $ownerPath1 = $this->createMock(File::class); - $shareOwner1Folder = $this->createMock(Folder::class); + $shareOwner1Folder = $this->createMock(IUserFolder::class); $shareOwner1Folder->method('getFirstNodeById')->willReturn($ownerPath1); $ownerPath2 = $this->createMock(File::class); - $shareOwner2Folder = $this->createMock(Folder::class); + $shareOwner2Folder = $this->createMock(IUserFolder::class); $shareOwner2Folder->method('getFirstNodeById')->willReturn($ownerPath2); $ownerPath3 = $this->createMock(File::class); - $shareOwner3Folder = $this->createMock(Folder::class); + $shareOwner3Folder = $this->createMock(IUserFolder::class); $shareOwner3Folder->method('getFirstNodeById')->willReturn($ownerPath3); $ownerPath4 = $this->createMock(File::class); - $shareOwner4Folder = $this->createMock(Folder::class); + $shareOwner4Folder = $this->createMock(IUserFolder::class); $shareOwner4Folder->method('getFirstNodeById')->willReturn($ownerPath4); $ownerPath5 = $this->createMock(File::class); - $shareOwner5Folder = $this->createMock(Folder::class); + $shareOwner5Folder = $this->createMock(IUserFolder::class); $shareOwner5Folder->method('getFirstNodeById')->willReturn($ownerPath5); $this->rootFolder diff --git a/tests/lib/Share20/ManagerTest.php b/tests/lib/Share20/ManagerTest.php index 8dd1af514cf42..e287faf12459c 100644 --- a/tests/lib/Share20/ManagerTest.php +++ b/tests/lib/Share20/ManagerTest.php @@ -26,6 +26,7 @@ use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Mount\IMountManager; use OCP\Files\Mount\IMountPoint; use OCP\Files\Mount\IMovableMount; @@ -524,7 +525,7 @@ public function testPromoteReshareFile(): void { $manager->expects($this->exactly(1))->method('updateShare')->with($reShare)->willReturn($reShare); $this->userManager->method('userExists')->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder')->with('userA')->willReturn($userFolder); $userFolder->method('getFirstNodeById') ->with(42) @@ -615,7 +616,7 @@ public function testPromoteReshare(): void { }); $this->userManager->method('userExists')->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder')->with('userA')->willReturn($userFolder); $userFolder->method('getFirstNodeById') ->willReturnCallback(function ($id) use ($subFolder, $otherFolder, $folder) { @@ -677,7 +678,7 @@ public function testPromoteReshareWhenUserHasAnotherShare(): void { $manager->expects($this->never())->method('updateShare'); $this->userManager->method('userExists')->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder')->with('userA')->willReturn($userFolder); $userFolder->method('getFirstNodeById') ->with(42) @@ -759,7 +760,7 @@ public function testPromoteReshareOfUsersInGroupShare(): void { $manager->method('getSharedWith')->willReturn([]); $this->userManager->method('userExists')->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder')->with('userA')->willReturn($userFolder); $userFolder->method('getFirstNodeById') ->with(42) @@ -1206,7 +1207,7 @@ public function testGeneralChecks(array $shareParams, ?string $exceptionMessage, ['group0', true], ]); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getId') @@ -1283,7 +1284,7 @@ public function testGeneralCheckShareRoot(): void { ['user1', $this->createMock(IUser::class)], ]); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('isSubNode') ->with($userFolder) @@ -2091,7 +2092,7 @@ public function testUserCreateChecksShareWithGroupMembersOnlyDifferentGroups(): ->method('isShareable') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getById') ->with(108) @@ -2158,7 +2159,7 @@ public function testUserCreateChecksShareWithGroupMembersOnlySharedGroup(): void ->method('isShareable') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getById') ->with(108) @@ -2343,7 +2344,7 @@ public function testUserCreateChecksIdenticalPathNotSharedWithUser(): void { ->method('isShareable') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getById') ->with(108) @@ -2432,7 +2433,7 @@ public function testGroupCreateChecksShareWithGroupMembersGroupSharingNotAllowed ->method('isShareable') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getById') ->with(108) @@ -2497,7 +2498,7 @@ public function testGroupCreateChecksShareWithGroupMembersOnlyNotInGroup(): void ->method('isShareable') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getById') ->with(108) @@ -2566,7 +2567,7 @@ public function testGroupCreateChecksShareWithGroupMembersOnlyInGroup(): void { ->method('isShareable') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getById') ->with(108) @@ -2700,7 +2701,7 @@ public function testLinkCreateChecksNoLinkSharesAllowed(): void { ->method('isShareable') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getById') ->with(108) @@ -2755,7 +2756,7 @@ public function testFileLinkCreateChecksNoPublicUpload(): void { ->method('isShareable') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getById') ->with(108) @@ -2820,7 +2821,7 @@ public function testFolderLinkCreateChecksNoPublicUpload(): void { ->method('isShareable') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getById') ->with(108) @@ -2882,7 +2883,7 @@ public function testLinkCreateChecksPublicUpload(): void { ->method('isShareable') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getById') ->with(108) @@ -2944,7 +2945,7 @@ public function testLinkCreateChecksReadOnly(): void { ->method('isShareable') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getById') ->with(108) @@ -3155,7 +3156,7 @@ public function testCanShare($expected, $sharingEnabled, $disabledForUser): void ->method('isShareable') ->willReturn(true); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder ->method('getById') ->with(108) @@ -4291,8 +4292,9 @@ public function testUpdateShareUser(): void { Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post'); $hookListener->expects($this->never())->method('post'); - $this->rootFolder->method('getUserFolder')->with('newUser')->willReturnSelf(); - $this->rootFolder->method('getRelativePath')->with('/newUser/files/myPath')->willReturn('/myPath'); + $userFolder = $this->createMock(IUserFolder::class); + $userFolder->method('getRelativePath')->with('/newUser/files/myPath')->willReturn('/myPath'); + $this->rootFolder->method('getUserFolder')->with('newUser')->willReturn($userFolder); $hookListener2 = $this->createMock(DummyShareManagerListener::class); Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener2, 'post'); @@ -5365,7 +5367,7 @@ public function testGetAccessList(): void { $node->method('getId') ->willReturn(42); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $file = $this->createMock(File::class); $folder = $this->createMock(Folder::class); @@ -5464,7 +5466,7 @@ public function testGetAccessListWithCurrentAccess(): void { $node->method('getId') ->willReturn(42); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $file = $this->createMock(File::class); $owner = $this->createMock(IUser::class); diff --git a/tests/lib/TagsTest.php b/tests/lib/TagsTest.php index f03bdc5d38561..728e301dcf3c1 100644 --- a/tests/lib/TagsTest.php +++ b/tests/lib/TagsTest.php @@ -12,8 +12,8 @@ use OC\TagManager; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\EventDispatcher\IEventDispatcher; -use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Node; use OCP\IDBConnection; use OCP\IUser; @@ -59,7 +59,7 @@ protected function setUp(): void { ->expects($this->any()) ->method('getUser') ->willReturn($this->user); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $node = $this->createMock(Node::class); $this->rootFolder = $this->createMock(IRootFolder::class); $this->rootFolder diff --git a/tests/lib/User/UserTest.php b/tests/lib/User/UserTest.php index 895dd8e39e333..f8c3937be9db8 100644 --- a/tests/lib/User/UserTest.php +++ b/tests/lib/User/UserTest.php @@ -17,6 +17,7 @@ use OCP\Config\IUserConfig; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\FileInfo; +use OCP\Files\IRootFolder; use OCP\Files\Storage\IStorageFactory; use OCP\IConfig; use OCP\IURLGenerator; @@ -660,8 +661,13 @@ public function testSetQuota(): void { '23 TB' ); + /* Overwrite IRootFolder to avoid crash about unknown user */ + $this->overwriteService(IRootFolder::class, $this->createMock(IRootFolder::class)); + $user = new User('foo', $backend, $this->dispatcher, $emitter, null, $userConfig); $user->setQuota('23 TB'); + + $this->restoreService(IRootFolder::class); } public function testGetDefaultUnlimitedQuota(): void { @@ -741,8 +747,13 @@ public function testSetQuotaAddressNoChange(): void { $userConfig->expects($this->never()) ->method('setValueString'); + /* Overwrite IRootFolder to avoid crash about unknown user */ + $this->overwriteService(IRootFolder::class, $this->createMock(IRootFolder::class)); + $user = new User('foo', $backend, $this->dispatcher, $emitter, null, $userConfig); $user->setQuota('23 TB'); + + $this->restoreService(IRootFolder::class); } public function testGetLastLogin(): void { From 4502eb05c3ea18d8be3cc25e83f1c887a74fd558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 16 Dec 2025 23:54:33 +0100 Subject: [PATCH 06/12] chore: Fix psalm issues and update baseline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- .../lib/Search/CommentsSearchProvider.php | 4 ---- apps/files_sharing/tests/ApiTest.php | 2 +- apps/files_versions/lib/Storage.php | 12 ++++-------- build/psalm-baseline.xml | 15 +++++++-------- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/apps/comments/lib/Search/CommentsSearchProvider.php b/apps/comments/lib/Search/CommentsSearchProvider.php index 210be879865d2..45cfd59872342 100644 --- a/apps/comments/lib/Search/CommentsSearchProvider.php +++ b/apps/comments/lib/Search/CommentsSearchProvider.php @@ -58,10 +58,6 @@ public function getOrder(string $route, array $routeParameters): int { public function search(IUser $user, ISearchQuery $query): SearchResult { $userFolder = $this->rootFolder->getUserFolder($user->getUID()); - if ($userFolder === null) { - return SearchResult::complete($this->l10n->t('Comments'), []); - } - $result = []; $numComments = 50; $offset = 0; diff --git a/apps/files_sharing/tests/ApiTest.php b/apps/files_sharing/tests/ApiTest.php index 04c56e7b6552a..27b79bee7dfa9 100644 --- a/apps/files_sharing/tests/ApiTest.php +++ b/apps/files_sharing/tests/ApiTest.php @@ -86,7 +86,7 @@ protected function setUp(): void { $mount = $this->view->getMount($this->filename); $mount->getStorage()->getScanner()->scan('', Scanner::SCAN_RECURSIVE); - $this->userFolder = \OC::$server->getUserFolder(self::TEST_FILES_SHARING_API_USER1); + $this->userFolder = Server::get(IRootFolder::class)->getUserFolder(self::TEST_FILES_SHARING_API_USER1); $this->appConfig = $this->createMock(IAppConfig::class); } diff --git a/apps/files_versions/lib/Storage.php b/apps/files_versions/lib/Storage.php index 63831db04fbd0..0035750596f58 100644 --- a/apps/files_versions/lib/Storage.php +++ b/apps/files_versions/lib/Storage.php @@ -917,15 +917,11 @@ public static function expire($filename, $uid) { if ($softQuota) { $root = Server::get(IRootFolder::class); $userFolder = $root->getUserFolder($uid); - if (is_null($userFolder)) { - $availableSpace = 0; + $free = $quota - $userFolder->getSize(false); // remaining free space for user + if ($free > 0) { + $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions } else { - $free = $quota - $userFolder->getSize(false); // remaining free space for user - if ($free > 0) { - $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions - } else { - $availableSpace = $free - $versionsSize; - } + $availableSpace = $free - $versionsSize; } } else { $availableSpace = $quota; diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index c06c6df854fc4..b9990096345cd 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -681,6 +681,9 @@ + + info, false)]]> + @@ -1418,6 +1421,7 @@ + @@ -1431,6 +1435,7 @@ + @@ -2179,6 +2184,7 @@ + @@ -2319,6 +2325,7 @@ + @@ -3651,14 +3658,6 @@ __call(__FUNCTION__, func_get_args())]]> - - - - - - - - From 3406d45ef1f255be99a943fbe42c5d32518316dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Fri, 6 Mar 2026 15:59:37 +0100 Subject: [PATCH 07/12] fix: Do not set parent for user folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/Files/Node/LazyUserFolder.php | 3 +-- lib/private/Files/Node/Root.php | 3 +-- lib/private/Files/Node/UserFolder.php | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/private/Files/Node/LazyUserFolder.php b/lib/private/Files/Node/LazyUserFolder.php index d2b85a9457190..facf8bfd712f6 100644 --- a/lib/private/Files/Node/LazyUserFolder.php +++ b/lib/private/Files/Node/LazyUserFolder.php @@ -48,13 +48,12 @@ public function __construct( $rootFolder, function () use ($user): UserFolder { $root = $this->getRootFolder(); - $parent = $root->getOrCreateFolder('/' . $user->getUID(), maxRetries: 1); $realFolder = $root->getOrCreateFolder('/' . $user->getUID() . '/files', maxRetries: 1); return new UserFolder( $root, new View(), $realFolder->getPath(), - $parent, + null, Server::get(IConfig::class), $user, Server::get(ICacheFactory::class), diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php index d64135b378802..3932c3b3d7cc0 100644 --- a/lib/private/Files/Node/Root.php +++ b/lib/private/Files/Node/Root.php @@ -342,13 +342,12 @@ public function getUserFolder(string $userId): IUserFolder { if (!$this->userFolderCache->hasKey($userId)) { if ($this->mountManager->getSetupManager()->isSetupComplete($userObject)) { - $parent = $this->getOrCreateFolder('/' . $userId, maxRetries: 1); $realFolder = $this->getOrCreateFolder('/' . $userId . '/files', maxRetries: 1); $folder = new UserFolder( $this, $this->view, $realFolder->getPath(), - $parent, + null, Server::get(IConfig::class), $userObject, $this->cacheFactory, diff --git a/lib/private/Files/Node/UserFolder.php b/lib/private/Files/Node/UserFolder.php index 646c299f086d4..ad69a7ef6dbd1 100644 --- a/lib/private/Files/Node/UserFolder.php +++ b/lib/private/Files/Node/UserFolder.php @@ -26,7 +26,7 @@ public function __construct( IRootFolder $root, View $view, string $path, - IFolder $parent, + ?IFolder $parent, protected IConfig $config, protected IUser $user, protected ICacheFactory $cacheFactory, From 3b2a5fff24a0019d1bdeafe4560770d0377c1a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Wed, 11 Mar 2026 15:41:18 +0100 Subject: [PATCH 08/12] chore: Fix failing test on s3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- .../tests/Command/DeleteOrphanedFilesTest.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/files/tests/Command/DeleteOrphanedFilesTest.php b/apps/files/tests/Command/DeleteOrphanedFilesTest.php index 4745669cebeec..ae63c39f78812 100644 --- a/apps/files/tests/Command/DeleteOrphanedFilesTest.php +++ b/apps/files/tests/Command/DeleteOrphanedFilesTest.php @@ -13,6 +13,7 @@ use OCA\Files\Command\DeleteOrphanedFiles; use OCP\Console\IOutput; use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; use OCP\Files\StorageNotAvailableException; use OCP\IDBConnection; use OCP\IUserManager; @@ -46,6 +47,13 @@ protected function setUp(): void { } protected function tearDown(): void { + // since we deleted the storage it might throw a (valid) StorageNotAvailableException + try { + $view = new View('/' . $this->user1 . '/files'); + $view->unlink('test'); + } catch (StorageNotAvailableException|NotFoundException $e) { + } + $userManager = Server::get(IUserManager::class); $user1 = $userManager->get($this->user1); if ($user1) { @@ -86,10 +94,10 @@ public function testClearFiles(): void { $this->loginAsUser($this->user1); - $view = new View('/' . $this->user1 . '/'); - $view->mkdir('files/test'); + $view = new View('/' . $this->user1 . '/files'); + $view->mkdir('test'); - $fileInfo = $view->getFileInfo('files/test'); + $fileInfo = $view->getFileInfo('test'); $storageId = $fileInfo->getStorage()->getId(); $numericStorageId = $fileInfo->getStorage()->getCache()->getNumericStorageId(); @@ -127,13 +135,5 @@ public function testClearFiles(): void { $this->assertCount(0, $this->getFile($fileInfo->getId()), 'Asserts that file gets cleaned up'); $this->assertEquals(0, $this->getMountsCount($numericStorageId), 'Asserts that mount gets cleaned up'); - - // Rescan folder to add back to cache before deleting - $rootFolder->getUserFolder($this->user1)->getStorage()->getScanner()->scan(''); - // since we deleted the storage it might throw a (valid) StorageNotAvailableException - try { - $view->unlink('files/test'); - } catch (StorageNotAvailableException $e) { - } } } From 8e439db54c039a2beaad36e76c48e39215d55158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Wed, 11 Mar 2026 16:16:42 +0100 Subject: [PATCH 09/12] chore: Remove useless check in dav app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- apps/dav/lib/Files/RootCollection.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/dav/lib/Files/RootCollection.php b/apps/dav/lib/Files/RootCollection.php index 358d9eaa57e03..f4779b6b73b2a 100644 --- a/apps/dav/lib/Files/RootCollection.php +++ b/apps/dav/lib/Files/RootCollection.php @@ -8,7 +8,6 @@ namespace OCA\DAV\Files; -use OCP\Files\FileInfo; use OCP\Files\IRootFolder; use OCP\IUserSession; use OCP\Server; @@ -39,9 +38,6 @@ public function getChildForPrincipal(array $principalInfo) { return new SimpleCollection($name); } $userFolder = Server::get(IRootFolder::class)->getUserFolder($user->getUID()); - if (!($userFolder instanceof FileInfo)) { - throw new \Exception('Home does not exist'); - } return new FilesHome($principalInfo, $userFolder); } From 80401b191d5c2cc524d338b6ba9efc77ee455699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Thu, 27 Aug 2026 16:49:36 +0200 Subject: [PATCH 10/12] chore: Fix tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- apps/dav/tests/unit/Controller/DirectControllerTest.php | 2 +- apps/files/tests/Controller/ApiControllerTest.php | 6 +++--- apps/files_trashbin/tests/Sabre/TrashbinPluginTest.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/dav/tests/unit/Controller/DirectControllerTest.php b/apps/dav/tests/unit/Controller/DirectControllerTest.php index 417b888ccbb79..26b7edb1170a2 100644 --- a/apps/dav/tests/unit/Controller/DirectControllerTest.php +++ b/apps/dav/tests/unit/Controller/DirectControllerTest.php @@ -156,7 +156,7 @@ public function testGetUrlNoLinkShares(): void { $this->shareManager->method('shareApiAllowLinks') ->willReturn(false); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $this->rootFolder->method('getUserFolder') ->with('awesomeUser') ->willReturn($userFolder); diff --git a/apps/files/tests/Controller/ApiControllerTest.php b/apps/files/tests/Controller/ApiControllerTest.php index a3681ddf0e6e9..2e929c2ccbc11 100644 --- a/apps/files/tests/Controller/ApiControllerTest.php +++ b/apps/files/tests/Controller/ApiControllerTest.php @@ -17,8 +17,8 @@ use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Http\Response; use OCP\Files\File; -use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\NotFoundException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\Storage\ISharedStorage; @@ -50,7 +50,7 @@ class ApiControllerTest extends TestCase { private ApiController $apiController; private IManager $shareManager; private IConfig&MockObject $config; - private Folder&MockObject $userFolder; + private IUserFolder&MockObject $userFolder; private UserConfig&MockObject $userConfig; private ViewConfig&MockObject $viewConfig; private IL10N&MockObject $l10n; @@ -73,7 +73,7 @@ protected function setUp(): void { $this->shareManager = $this->createMock(IManager::class); $this->preview = $this->createMock(IPreview::class); $this->config = $this->createMock(IConfig::class); - $this->userFolder = $this->createMock(Folder::class); + $this->userFolder = $this->createMock(IUserFolder::class); $this->userConfig = $this->createMock(UserConfig::class); $this->viewConfig = $this->createMock(ViewConfig::class); $this->l10n = $this->createMock(IL10N::class); diff --git a/apps/files_trashbin/tests/Sabre/TrashbinPluginTest.php b/apps/files_trashbin/tests/Sabre/TrashbinPluginTest.php index 001e3bcb5ba1d..cd1da7f3bfd75 100644 --- a/apps/files_trashbin/tests/Sabre/TrashbinPluginTest.php +++ b/apps/files_trashbin/tests/Sabre/TrashbinPluginTest.php @@ -13,8 +13,8 @@ use OCA\Files_Trashbin\Sabre\RestoreFolder; use OCA\Files_Trashbin\Sabre\TrashbinPlugin; use OCA\Files_Trashbin\Trash\ITrashItem; -use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\IUserFolder; use OCP\Files\Mount\IMountManager; use OCP\Files\Mount\IMountPoint; use OCP\Files\Storage\IStorage; @@ -58,7 +58,7 @@ public function testQuota(int $quota, int $fileSize, bool $expectedResult): void $previewManager = $this->createMock(IPreview::class); - $userFolder = $this->createMock(Folder::class); + $userFolder = $this->createMock(IUserFolder::class); $userFolder->method('getFullPath') ->with('relative/original') // the parent path ->willReturn('/full/path/to/original'); From d80b2f94da2d22c30194be50266d923f057a4fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Thu, 10 Sep 2026 10:04:22 +0200 Subject: [PATCH 11/12] chore: Bump since and deprecated annotations version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/legacy/OC_Helper.php | 4 ++-- lib/public/Files/IRootFolder.php | 2 +- lib/public/Files/IUserFolder.php | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php index fb58090a134c8..9807acdadb1e3 100644 --- a/lib/private/legacy/OC_Helper.php +++ b/lib/private/legacy/OC_Helper.php @@ -155,7 +155,7 @@ public static function findBinaryPath(string $program): ?string { * @psalm-suppress LessSpecificReturnStatement Legacy code outputs weird types - manually validated that they are correct * @return StorageInfo * @throws NotFoundException - * @deprecated 33.0.0 use \OCP\Files\IUserFolder::getUserQuota + * @deprecated 36.0.0 use \OCP\Files\IUserFolder::getUserQuota */ public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true, $useCache = true) { if (!self::$cacheFactory) { @@ -348,7 +348,7 @@ private static function getGlobalStorageInfo(int|float $quota, IUser $user, IMou } /** - * @deprecated 33.0.0 + * @deprecated 36.0.0 */ public static function clearStorageInfo(string $absolutePath): void { /** @var ICacheFactory $cacheFactory */ diff --git a/lib/public/Files/IRootFolder.php b/lib/public/Files/IRootFolder.php index cbee6d0989571..6bc3bfe938110 100644 --- a/lib/public/Files/IRootFolder.php +++ b/lib/public/Files/IRootFolder.php @@ -31,7 +31,7 @@ interface IRootFolder extends Folder { * @throws NotPermittedException * * @since 8.2.0 - * @since 33.0.0 returns OCP\Files\IUserFolder instead of OCP\Files\Folder + * @since 36.0.0 returns OCP\Files\IUserFolder instead of OCP\Files\Folder */ public function getUserFolder(string $userId): IUserFolder; diff --git a/lib/public/Files/IUserFolder.php b/lib/public/Files/IUserFolder.php index 007d354c5677f..d49e87c0b7142 100644 --- a/lib/public/Files/IUserFolder.php +++ b/lib/public/Files/IUserFolder.php @@ -12,15 +12,15 @@ use OCP\AppFramework\Attribute\Consumable; /** - * @since 33.0.0 + * @since 36.0.0 */ -#[Consumable(since: '33.0.0')] +#[Consumable(since: '36.0.0')] interface IUserFolder extends Folder { /** * @param bool $useCache - Use the cached value if available instead of recalculate. * @return array{used: int|float, free: int|float, total: int|float, quota: int|float} - * @since 33.0.0 + * @since 36.0.0 */ public function getUserQuota(bool $useCache = true): array; From b6e35e00dc9a6d4cff1ffd955a84eb4e57bbb7fc Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Wed, 2 Sep 2026 16:39:14 +0200 Subject: [PATCH 12/12] fix(trashbin): properly unpause trashbin on error Signed-off-by: Ferdinand Thiessen --- .../files_trashbin/lib/Trash/TrashManager.php | 16 ++-- .../tests/Trash/TrashManagerTest.php | 86 +++++++++++++++++++ 2 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 apps/files_trashbin/tests/Trash/TrashManagerTest.php diff --git a/apps/files_trashbin/lib/Trash/TrashManager.php b/apps/files_trashbin/lib/Trash/TrashManager.php index cf0f34cff6d69..1a5831c68fd93 100644 --- a/apps/files_trashbin/lib/Trash/TrashManager.php +++ b/apps/files_trashbin/lib/Trash/TrashManager.php @@ -109,13 +109,19 @@ public function moveToTrash(IStorage $storage, string $internalPath): bool { } try { $backend = $this->getBackendForStorage($storage); - $this->trashPaused = true; - $result = $backend->moveToTrash($storage, $internalPath); - $this->trashPaused = false; - return $result; - } catch (BackendNotFoundException $e) { + } catch (BackendNotFoundException) { return false; } + + // pausing prevents the backend from recursing into the trash logic again, + // it has to be released even when the move fails or the trash bin would + // stay disabled for the rest of the request + $this->trashPaused = true; + try { + return $backend->moveToTrash($storage, $internalPath); + } finally { + $this->trashPaused = false; + } } #[\Override] diff --git a/apps/files_trashbin/tests/Trash/TrashManagerTest.php b/apps/files_trashbin/tests/Trash/TrashManagerTest.php new file mode 100644 index 0000000000000..2d9c0141e13e9 --- /dev/null +++ b/apps/files_trashbin/tests/Trash/TrashManagerTest.php @@ -0,0 +1,86 @@ +manager = new TrashManager(); + $this->storage = $this->createMock(IStorage::class); + $this->storage->method('instanceOfStorage') + ->with(IStorage::class) + ->willReturn(true); + } + + private function registerBackend(ITrashBackend $backend): void { + $this->manager->registerBackend(IStorage::class, $backend); + } + + public function testMoveToTrashWithoutBackend(): void { + $this->assertFalse($this->manager->moveToTrash($this->storage, 'files/test.txt')); + } + + public function testMoveToTrashUsesBackend(): void { + $backend = $this->createMock(ITrashBackend::class); + $backend->expects($this->once()) + ->method('moveToTrash') + ->with($this->storage, 'files/test.txt') + ->willReturn(true); + $this->registerBackend($backend); + + $this->assertTrue($this->manager->moveToTrash($this->storage, 'files/test.txt')); + } + + /** + * The backend must not see the delete it performs itself, but the pause has + * to be released again afterwards so following deletes still get trashed. + */ + public function testMoveToTrashPausesOnlyDuringTheMove(): void { + $backend = $this->createMock(ITrashBackend::class); + $backend->expects($this->exactly(2)) + ->method('moveToTrash') + ->willReturnCallback(function (): bool { + $this->assertFalse( + $this->manager->moveToTrash($this->storage, 'files/nested.txt'), + 'Trash has to be paused while the backend moves a file', + ); + return true; + }); + $this->registerBackend($backend); + + $this->assertTrue($this->manager->moveToTrash($this->storage, 'files/first.txt')); + $this->assertTrue($this->manager->moveToTrash($this->storage, 'files/second.txt')); + } + + public function testMoveToTrashReleasesPauseOnException(): void { + $backend = $this->createMock(ITrashBackend::class); + $backend->method('moveToTrash') + ->willThrowException(new NotFoundException('test not found while trying to get owner')); + $this->registerBackend($backend); + + $this->expectException(NotFoundException::class); + try { + $this->manager->moveToTrash($this->storage, 'files/test.txt'); + } finally { + $this->assertFalse($this->invokePrivate($this->manager, 'trashPaused')); + } + } +}