diff --git a/apps/comments/appinfo/app.php b/apps/comments/appinfo/app.php index b491b1d4d3f0..bbc292dc4a1f 100644 --- a/apps/comments/appinfo/app.php +++ b/apps/comments/appinfo/app.php @@ -54,7 +54,7 @@ function () { $eventDispatcher->addListener(\OCP\Comments\CommentsEntityEvent::EVENT_ENTITY, function (\OCP\Comments\CommentsEntityEvent $event) { $event->addEntityCollection('files', function ($name) { - $nodes = \OC::$server->getUserFolder()->getById(\intval($name)); + $nodes = \OC::$server->getUserFolder()->getById((int)$name, true); return !empty($nodes); }); }); diff --git a/apps/comments/lib/Activity/Listener.php b/apps/comments/lib/Activity/Listener.php index a4ca3b4aa463..1781286713d9 100644 --- a/apps/comments/lib/Activity/Listener.php +++ b/apps/comments/lib/Activity/Listener.php @@ -86,10 +86,9 @@ public function commentEvent(CommentsEvent $event) { foreach ($mounts as $mount) { $owner = $mount->getUser()->getUID(); $ownerFolder = $this->rootFolder->getUserFolder($owner); - $nodes = $ownerFolder->getById($event->getComment()->getObjectId()); - if (!empty($nodes)) { - /** @var Node $node */ - $node = \array_shift($nodes); + $nodes = $ownerFolder->getById($event->getComment()->getObjectId(), true); + $node = $nodes[0] ?? null; + if ($node) { $path = $node->getPath(); if (\strpos($path, '/' . $owner . '/files/') === 0) { $path = \substr($path, \strlen('/' . $owner . '/files')); diff --git a/apps/comments/tests/unit/ActivityListenerTest.php b/apps/comments/tests/unit/ActivityListenerTest.php new file mode 100644 index 000000000000..6f81cc1da7d6 --- /dev/null +++ b/apps/comments/tests/unit/ActivityListenerTest.php @@ -0,0 +1,151 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Comments\Tests\unit; + +use OCA\Comments\Activity\Listener; +use OCP\Comments\CommentsEntityEvent; +use Test\Traits\UserTrait; +use OCP\IUserSession; +use OCP\Activity\IManager; +use OCP\App\IAppManager; +use OCP\Files\Config\IMountProviderCollection; +use OCP\Files\IRootFolder; +use OCP\Files\Config\IUserMountCache; +use OCP\Files\Config\ICachedMountInfo; +use OCP\IUser; +use OCP\Files\Folder; +use OCP\Activity\IEvent; +use OCA\Comments\Activity\Extension; +use OCP\Comments\IComment; +use OCP\Comments\CommentsEvent; + +/** + * Tests for the activity listener + * + * @group DB + */ +class ActivityListenerTest extends \Test\TestCase { + use UserTrait; + + /** + * @var Listener + */ + private $listener; + + /** + * @var IUserMountCache + */ + private $userMountCache; + + /** + * @var IRootFolder + */ + private $rootFolder; + + /** + * @var IUserSession + */ + private $userSession; + + /** + * @var IManager + */ + private $activityManager; + + protected function setUp() { + parent::setUp(); + + $this->activityManager = $this->createMock(IManager::class); + $this->userSession = $this->createMock(IUserSession::class); + $appManager = $this->createMock(IAppManager::class); + $appManager->method('isInstalled')->with('activity')->willReturn(true); + + $this->userMountCache = $this->createMock(IUserMountCache::class); + + $mountProviderCollection = $this->createMock(IMountProviderCollection::class); + $mountProviderCollection->method('getMountCache')->willReturn($this->userMountCache); + + $this->rootFolder = $this->createMock(IRootFolder::class); + + // needed for the one unmockable static method "Share::getUsersSharingFile"... + $this->createUser('user1'); + $this->createUser('actor1'); + + $this->listener = new Listener( + $this->activityManager, + $this->userSession, + $appManager, + $mountProviderCollection, + $this->rootFolder + ); + } + + public function testActivityOnFilesComment() { + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('user1'); + + $actor = $this->createMock(IUser::class); + $actor->method('getUID')->willReturn('actor1'); + + $this->userSession->method('getUser')->willReturn($actor); + + $cachedMountInfo = $this->createMock(ICachedMountInfo::class); + $cachedMountInfo->method('getUser')->willReturn($user); + + $node = $this->createMock(Folder::class); + $node->method('getPath')->willReturn('/user1/files/folder'); + + $ownerFolder = $this->createMock(Folder::class); + $ownerFolder->method('getById') + ->with(123, true) + ->willReturn([$node]); + + $this->rootFolder->method('getUserFolder') + ->with('user1') + ->willReturn($ownerFolder); + + $this->userMountCache->method('getMountsForFileId') + ->with(123) + ->willReturn([$cachedMountInfo]); + + $activityEvent = $this->createMock(IEvent::class); + $activityEvent->expects($this->once())->method('setApp')->with('comments')->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setType')->with('comments')->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setAuthor')->with('actor1')->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setObject')->with('files', 123)->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setMessage')->with(Extension::ADD_COMMENT_MESSAGE, [111])->willReturn($activityEvent); + + $this->activityManager->method('generateEvent') + ->willReturn($activityEvent); + $this->activityManager->expects($this->once()) + ->method('publish') + ->with($activityEvent); + + $comment = $this->createMock(IComment::class); + $comment->method('getObjectType')->willReturn('files'); + $comment->method('getObjectId')->willReturn(123); + $comment->method('getId')->willReturn(111); + + $commentsEvent = new CommentsEvent(CommentsEvent::EVENT_ADD, $comment); + $this->listener->commentEvent($commentsEvent); + } +} diff --git a/apps/dav/lib/Connector/Sabre/FilesReportPlugin.php b/apps/dav/lib/Connector/Sabre/FilesReportPlugin.php index 3ea18fabfb0d..e6512b376aaf 100644 --- a/apps/dav/lib/Connector/Sabre/FilesReportPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesReportPlugin.php @@ -360,9 +360,9 @@ public function findNodesByFileIds($rootNode, $fileIds) { $results = []; foreach ($fileIds as $fileId) { - $entry = $folder->getById($fileId); + $entries = $folder->getById($fileId, true); + $entry = $entries[0] ?? null; if ($entry) { - $entry = \current($entry); $node = $this->makeSabreNode($entry); if ($node) { $results[] = $node; diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php index faa8e3fba63d..43eb9b178c03 100644 --- a/apps/dav/lib/RootCollection.php +++ b/apps/dav/lib/RootCollection.php @@ -74,7 +74,7 @@ public function __construct() { \OC::$server->getSystemTagObjectMapper(), \OC::$server->getUserSession(), \OC::$server->getGroupManager(), - \OC::$server->getRootFolder() + \OC::$server->getLazyRootFolder() ); $usersCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, $groupPrincipalBackend, $dispatcher); @@ -109,7 +109,7 @@ public function __construct() { $systemTagRelationsCollection, $uploadCollection, $avatarCollection, - new \OCA\DAV\Meta\RootCollection(\OC::$server->getRootFolder()), + new \OCA\DAV\Meta\RootCollection(\OC::$server->getLazyRootFolder()), $queueCollection ]; diff --git a/apps/dav/lib/SystemTag/SystemTagsObjectTypeCollection.php b/apps/dav/lib/SystemTag/SystemTagsObjectTypeCollection.php index fa44c0b2f47b..4ffd4aa20d14 100644 --- a/apps/dav/lib/SystemTag/SystemTagsObjectTypeCollection.php +++ b/apps/dav/lib/SystemTag/SystemTagsObjectTypeCollection.php @@ -139,7 +139,7 @@ public function childExists($name) { if ($this->objectType === 'files') { // make sure the object is reachable for the current user $userId = $this->userSession->getUser()->getUID(); - $nodes = $this->fileRoot->getUserFolder($userId)->getById(\intval($name)); + $nodes = $this->fileRoot->getUserFolder($userId)->getById((int)$name, true); return !empty($nodes); } return true; diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index c6bea791e61a..01606ce5097a 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -886,7 +886,7 @@ private function getNode($userId, $id) { throw new InvalidShare(); } - $nodes = $userFolder->getById($id); + $nodes = $userFolder->getById($id, true); if (empty($nodes)) { throw new InvalidShare(); diff --git a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php index 8b0189c4be0c..ffb0fbd6574c 100644 --- a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php +++ b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php @@ -37,6 +37,8 @@ use OCP\IUserManager; use OCP\Share\IManager; use OCP\Share\IShare; +use OCP\Files\Folder; +use OCP\IUser; /** * Class FederatedShareProviderTest @@ -188,6 +190,90 @@ public function testCreate() { $this->assertEquals('token', $share->getToken()); } + public function testCreateLegacy() { + $share = $this->shareManager->newShare(); + + $node = $this->createMock('\OCP\Files\File'); + $node->method('getId')->willReturn(42); + $node->method('getName')->willReturn('myFile'); + + $share->setSharedWith('user@server.com') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + + $this->tokenHandler->method('generateToken')->willReturn('token'); + + $shareWithAddress = new Address('user@server.com'); + $ownerAddress = new Address('shareOwner@http://localhost/'); + $sharedByAddress = new Address('sharedBy@http://localhost/'); + $this->addressHandler->expects($this->any())->method('getLocalUserFederatedAddress') + ->will($this->onConsecutiveCalls($ownerAddress, $sharedByAddress, $ownerAddress)); + + $this->addressHandler->expects($this->any())->method('splitUserRemote') + ->willReturn(['user', 'server.com']); + + $this->notifications->expects($this->once()) + ->method('sendRemoteShare') + ->with( + $this->equalTo($shareWithAddress), + $this->equalTo($ownerAddress), + $this->equalTo($sharedByAddress), + $this->equalTo('token'), + $this->equalTo('myFile'), + $this->anything() + )->willReturn(true); + + $folderOwner = $this->createMock(IUser::class); + $folderOwner->method('getUID')->willReturn('folderOwner'); + $node = $this->createMock(Folder::class); + $node->method('getOwner')->willReturn($folderOwner); + + $userFolder = $this->createMock(Folder::class); + $userFolder->method('getById') + ->with(42, true) + ->willReturn([$node]); + $this->rootFolder->expects($this->once()) + ->method('getUserFolder') + ->with('shareOwner') + ->willReturn($userFolder); + + $share = $this->provider->create($share); + + $qb = $this->connection->getQueryBuilder(); + $stmt = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->execute(); + + $data = $stmt->fetch(); + $stmt->closeCursor(); + + $expected = [ + 'share_type' => \OCP\Share::SHARE_TYPE_REMOTE, + 'share_with' => 'user@server.com', + 'uid_owner' => 'shareOwner', + 'uid_initiator' => null, + 'item_type' => 'file', + 'item_source' => 42, + 'file_source' => 42, + 'permissions' => 19, + 'accepted' => 0, + 'token' => 'token', + ]; + $this->assertArraySubset($expected, $data); + + $this->assertEquals($data['id'], $share->getId()); + $this->assertEquals(\OCP\Share::SHARE_TYPE_REMOTE, $share->getShareType()); + $this->assertEquals('user@server.com', $share->getSharedWith()); + $this->assertEquals('shareOwner', $share->getSharedBy()); + $this->assertEquals('folderOwner', $share->getShareOwner()); + $this->assertEquals('file', $share->getNodeType()); + $this->assertEquals(42, $share->getNodeId()); + $this->assertEquals(19, $share->getPermissions()); + $this->assertEquals('token', $share->getToken()); + } + public function testCreateCouldNotFindServer() { $share = $this->shareManager->newShare(); diff --git a/apps/files/lib/ActivityHelper.php b/apps/files/lib/ActivityHelper.php index 20ac77cbc75a..c198c10353cb 100644 --- a/apps/files/lib/ActivityHelper.php +++ b/apps/files/lib/ActivityHelper.php @@ -59,10 +59,9 @@ public function getFavoriteFilePaths($user) { $rootFolder = \OC::$server->getUserFolder($user); $folders = $items = []; foreach ($favorites as $favorite) { - $nodes = $rootFolder->getById($favorite); - if (!empty($nodes)) { - /** @var \OCP\Files\Node $node */ - $node = \array_shift($nodes); + $nodes = $rootFolder->getById($favorite, true); + $node = $nodes[0] ?? null; + if ($node) { $path = \substr($node->getPath(), \strlen($user . '/files/')); $items[] = $path; diff --git a/apps/files/tests/ActivityHelperTest.php b/apps/files/tests/ActivityHelperTest.php new file mode 100644 index 000000000000..e2c04ea276e8 --- /dev/null +++ b/apps/files/tests/ActivityHelperTest.php @@ -0,0 +1,137 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +use OCA\Files\ActivityHelper; +use OCP\ITagManager; +use OCP\ITags; +use Test\Traits\UserTrait; + +/** + * Class ActivityHelperTest + * + * @group DB + */ +class ActivityHelperTest extends \Test\TestCase { + use UserTrait; + + /** + * @var ITagManager + */ + private $tagManager; + + /** + * @var ITags + */ + private $tags; + + /** + * @var ActivityHelper + */ + private $helper; + + /** + * @var string + */ + private $user; + + protected function setUp() { + parent::setUp(); + + $this->tags = $this->createMock(ITags::class); + + $this->user = $this->getUniqueID('files_activityhelpertest_user_'); + + // because \OC::$server->getUserFolder() + $this->createUser($this->user); + $this->loginAsUser($this->user); + + $this->tagManager = $this->createMock(ITagManager::class); + $this->tagManager->expects($this->once()) + ->method('load') + ->with('files', [], false, $this->user) + ->willReturn($this->tags); + + $this->helper = new ActivityHelper($this->tagManager); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage No favorites + */ + public function testGetFavoriteFilePathsNoFavorites() { + $this->tags->method('getFavorites')->willReturn([]); + $this->helper->getFavoriteFilePaths($this->user); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Too many favorites + */ + public function testGetFavoriteFilePathsTooManyFavorites() { + $tooManyFavorites = []; + for ($i = 0; $i < ActivityHelper::FAVORITE_LIMIT + 1; $i++) { + $tooManyFavorites[] = []; + } + + $this->tags->method('getFavorites')->willReturn($tooManyFavorites); + + $this->helper->getFavoriteFilePaths($this->user); + } + + public function testGetFavoriteFilePaths() { + $userFolder = \OC::$server->getUserFolder(); + $fav1 = $userFolder->newFolder('fav1'); + $fav2 = $userFolder->newFile('fav2.txt'); + $userFolder->newFolder('nonfav1'); + $userFolder->newFolder('nonfav2'); + + $favorites = [ + $fav1->getId(), + $fav2->getId(), + $fav2->getId() + 999, // non-existing + ]; + + $this->tags->method('getFavorites')->willReturn($favorites); + + $result = $this->helper->getFavoriteFilePaths($this->user); + + $this->assertEquals(['/fav1', '/fav2.txt'], $result['items']); + $this->assertEquals(['/fav1'], $result['folders']); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage No favorites + */ + public function testGetFavoriteFilePathsMissingFolder() { + $userFolder = \OC::$server->getUserFolder(); + $aFolder = $userFolder->newFolder('x'); + + $favorites = [ + // non-existing + $aFolder->getId() + 999, + ]; + + $this->tags->method('getFavorites')->willReturn($favorites); + + $result = $this->helper->getFavoriteFilePaths($this->user); + } +} diff --git a/apps/files_sharing/lib/Controller/Share20OcsController.php b/apps/files_sharing/lib/Controller/Share20OcsController.php index bde1320991ef..8d1ee1c7645a 100644 --- a/apps/files_sharing/lib/Controller/Share20OcsController.php +++ b/apps/files_sharing/lib/Controller/Share20OcsController.php @@ -166,14 +166,12 @@ protected function formatShare(IShare $share, $received = false) { $userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); } - $nodes = $userFolder->getById($share->getNodeId()); - - if (empty($nodes)) { + $nodes = $userFolder->getById($share->getNodeId(), true); + $node = $nodes[0] ?? null; + if ($node === null) { throw new NotFoundException(); } - $node = $nodes[0]; - $result['path'] = $userFolder->getRelativePath($node->getPath()); if ($node instanceof \OCP\Files\Folder) { $result['item_type'] = 'folder'; @@ -912,8 +910,8 @@ public function notifyRecipients($itemSource, $shareType, $recipient) { ); $userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); - $nodes = $userFolder->getById($itemSource); - $node = $nodes[0]; + $nodes = $userFolder->getById($itemSource, true); + $node = $nodes[0] ?? null; $result = $mailNotification->sendInternalShareMail($node, $shareType, $recipientList); // if we were able to send to at least one recipient, mark as sent @@ -950,8 +948,8 @@ public function notifyRecipients($itemSource, $shareType, $recipient) { */ public function notifyRecipientsDisabled($itemSource, $shareType, $recipient) { $userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); - $nodes = $userFolder->getById($itemSource); - $node = $nodes[0]; + $nodes = $userFolder->getById($itemSource, true); + $node = $nodes[0] ?? null; $items = $this->shareManager->getSharedWith($recipient, $shareType, $node); if (\count($items) > 0) { diff --git a/apps/files_sharing/lib/SharedMount.php b/apps/files_sharing/lib/SharedMount.php index 3923c58da4e1..059687095d97 100644 --- a/apps/files_sharing/lib/SharedMount.php +++ b/apps/files_sharing/lib/SharedMount.php @@ -193,13 +193,13 @@ public function isTargetAllowed($target) { $fileId = (int)$targetStorage->getCache()->getId(\dirname($targetInternalPath)); } - $targetNodes = \OC::$server->getRootFolder()->getById($fileId); - if (empty($targetNodes)) { + $targetNodes = \OC::$server->getRootFolder()->getById($fileId, true); + $targetNode = $targetNodes[0] ?? null; + if ($targetNode === null) { return false; } $shareManager = \OC::$server->getShareManager(); - $targetNode = $targetNodes[0]; // FIXME: make it stop earlier in '/$userId/files' while ($targetNode !== null && $targetNode->getPath() !== '/') { $shares = $shareManager->getSharesByPath($targetNode); diff --git a/apps/systemtags/lib/Activity/Listener.php b/apps/systemtags/lib/Activity/Listener.php index 112ab4a8fcf9..3cbd9223235a 100644 --- a/apps/systemtags/lib/Activity/Listener.php +++ b/apps/systemtags/lib/Activity/Listener.php @@ -159,16 +159,15 @@ public function mapperEvent(MapperEvent $event) { foreach ($mounts as $mount) { $owner = $mount->getUser()->getUID(); $ownerFolder = $this->rootFolder->getUserFolder($owner); - $nodes = $ownerFolder->getById($event->getObjectId()); - if (!empty($nodes)) { - /** @var Node $node */ - $node = \array_shift($nodes); + $nodes = $ownerFolder->getById($event->getObjectId(), true); + $node = $nodes[0] ?? null; + if ($node) { $path = $node->getPath(); if (\strpos($path, '/' . $owner . '/files/') === 0) { $path = \substr($path, \strlen('/' . $owner . '/files')); } // Get all users that have access to the mount point - $users = \array_merge($users, Share::getUsersSharingFile($path, $owner, true, true)); + $users = \array_merge($users, $this->getUsersSharingFile($path, $owner)); } } @@ -227,4 +226,8 @@ protected function prepareTagAsParameter(ISystemTag $tag) { return '{{{' . $tag->getName() . '|||assignable}}}'; } } + + public function getUsersSharingFile($path, $owner) { + return Share::getUsersSharingFile($path, $owner, true, true); + } } diff --git a/apps/systemtags/tests/ListenerTest.php b/apps/systemtags/tests/ListenerTest.php deleted file mode 100644 index aad3e3c4c2b7..000000000000 --- a/apps/systemtags/tests/ListenerTest.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * @copyright Copyright (c) 2018, ownCloud GmbH - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -namespace OCA\SystemTags\Tests; - -use OC\SystemTag\SystemTag; -use OCA\SystemTags\Activity\Listener; -use OCP\Activity\IManager; -use OCP\App\IAppManager; -use OCP\Files\Config\IMountProviderCollection; -use OCP\Files\IRootFolder; -use OCP\IGroupManager; -use OCP\IUserSession; -use OCP\SystemTag\ISystemTagManager; -use Test\TestCase; - -class ListenerTest extends TestCase { - /** @var IGroupManager | \PHPUnit_Framework_MockObject_MockObject */ - private $groupManager; - /** @var IManager | \PHPUnit_Framework_MockObject_MockObject */ - private $activityManager; - /** @var IUserSession | \PHPUnit_Framework_MockObject_MockObject */ - private $userSession; - /** @var ISystemTagManager | \PHPUnit_Framework_MockObject_MockObject */ - private $tagManager; - /** @var IAppManager | \PHPUnit_Framework_MockObject_MockObject */ - private $appManager; - /** @var IMountProviderCollection | \PHPUnit_Framework_MockObject_MockObject */ - private $mountCollection; - /** @var IRootFolder | \PHPUnit_Framework_MockObject_MockObject */ - private $rootFolder; - /** @var Listener */ - private $listener; - - public function setUp() { - parent::setUp(); - - $this->groupManager = $this->createMock(IGroupManager::class); - $this->activityManager = $this->createMock(IManager::class); - $this->userSession = $this->createMock(IUserSession::class); - $this->tagManager = $this->createMock(ISystemTagManager::class); - $this->appManager = $this->createMock(IAppManager::class); - $this->mountCollection = $this->createMock(IMountProviderCollection::class); - $this->rootFolder = $this->createMock(IRootFolder::class); - $this->listener = new Listener($this->groupManager, $this->activityManager, - $this->userSession, $this->tagManager, $this->appManager, - $this->mountCollection, $this->rootFolder); - } - - public function prepareTagAsParameterProvider() { - return [ - [new SystemTag('1', 'visibleTag', true, true, true), "{{{visibleTag|||assignable}}}"], - [new SystemTag('2', 'invisibleTag', false, false, false), "{{{invisibleTag|||invisible}}}"], - [new SystemTag('3', 'restrictTag', true, false, false), "{{{restrictTag|||not-assignable}}}"], - [new SystemTag('3', 'staticTag', true, true, false), "{{{staticTag|||not-editable}}}"], - ]; - } - - /** - * @dataProvider prepareTagAsParameterProvider - */ - public function testPrepareTagAsParameter(SystemTag $tag, $expectedResult) { - $result = $this->invokePrivate($this->listener, 'prepareTagAsParameter', [$tag]); - $this->assertEquals($expectedResult, $result); - } -} diff --git a/apps/systemtags/tests/ExtensionTest.php b/apps/systemtags/tests/unit/activity/ExtensionTest.php similarity index 100% rename from apps/systemtags/tests/ExtensionTest.php rename to apps/systemtags/tests/unit/activity/ExtensionTest.php diff --git a/apps/systemtags/tests/unit/activity/ListenerTest.php b/apps/systemtags/tests/unit/activity/ListenerTest.php new file mode 100644 index 000000000000..1ecc2df62897 --- /dev/null +++ b/apps/systemtags/tests/unit/activity/ListenerTest.php @@ -0,0 +1,218 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\SystemTags\Tests\unit; + +use Test\Traits\UserTrait; +use OCA\SystemTags\Activity\Listener; +use OCA\SystemTags\Activity\Extension; +use OCP\IUserSession; +use OCP\Activity\IManager; +use OCP\App\IAppManager; +use OCP\Files\Config\IMountProviderCollection; +use OCP\Files\IRootFolder; +use OCP\Files\Config\IUserMountCache; +use OCP\Files\Config\ICachedMountInfo; +use OCP\IUser; +use OCP\Files\Folder; +use OCP\Activity\IEvent; +use OCP\SystemTag\MapperEvent; +use OCP\IGroupManager; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\ISystemTag; +use OC\SystemTag\SystemTag; + +/** + * Tests for the activity listener + * + * @group DB + */ +class ActivityListenerTest extends \Test\TestCase { + use UserTrait; + + /** + * @var IGroupManager + */ + private $groupManager; + + /** + * @var ISystemTagManager + */ + private $tagManager; + + /** + * @var Listener + */ + private $listener; + + /** + * @var IUserMountCache + */ + private $userMountCache; + + /** + * @var IRootFolder + */ + private $rootFolder; + + /** + * @var IUserSession + */ + private $userSession; + + /** + * @var IManager + */ + private $activityManager; + + protected function setUp() { + parent::setUp(); + + $this->groupManager = $this->createMock(IGroupManager::class); + + $this->tagManager = $this->createMock(ISystemTagManager::class); + $this->activityManager = $this->createMock(IManager::class); + $this->userSession = $this->createMock(IUserSession::class); + + $appManager = $this->createMock(IAppManager::class); + $appManager->method('isInstalled')->with('activity')->willReturn(true); + + $this->userMountCache = $this->createMock(IUserMountCache::class); + + $mountProviderCollection = $this->createMock(IMountProviderCollection::class); + $mountProviderCollection->method('getMountCache')->willReturn($this->userMountCache); + + $this->rootFolder = $this->createMock(IRootFolder::class); + + $this->listener = $this->getMockBuilder(Listener::class) + ->setConstructorArgs([ + $this->groupManager, + $this->activityManager, + $this->userSession, + $this->tagManager, + $appManager, + $mountProviderCollection, + $this->rootFolder + ]) + ->setMethods(['getUsersSharingFile']) + ->getMock(); + } + + public function providesEventTypes() { + return [ + [MapperEvent::EVENT_ASSIGN, Extension::ASSIGN_TAG], + [MapperEvent::EVENT_UNASSIGN, Extension::UNASSIGN_TAG], + ]; + } + + /** + * @dataProvider providesEventTypes + */ + public function testActivityOnMapperEvent($eventType, $extensionEvent) { + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('user1'); + + $actor = $this->createMock(IUser::class); + $actor->method('getUID')->willReturn('actor1'); + + $this->userSession->method('getUser')->willReturn($actor); + + $tag1 = $this->createMock(ISystemTag::class); + $tag1->method('isUserVisible')->willReturn(true); + $tag1->method('isUserAssignable')->willReturn(true); + $tag1->method('isUserEditable')->willReturn(true); + $tag1->method('getName')->willReturn('tag1'); + + $tag2 = $this->createMock(ISystemTag::class); + $tag2->method('isUserVisible')->willReturn(true); + $tag2->method('isUserAssignable')->willReturn(false); + $tag2->method('isUserEditable')->willReturn(false); + $tag2->method('getName')->willReturn('tag2'); + + $invisibleTag = $this->createMock(ISystemTag::class); + $invisibleTag->method('isUserVisible')->willReturn(false); + + $this->groupManager->method('isAdmin')->willReturn(false); + + $this->tagManager->method('getTagsByIds') + ->with([111, 222, 333]) + ->willReturn([$tag1, $tag2, $invisibleTag]); + + $cachedMountInfo = $this->createMock(ICachedMountInfo::class); + $cachedMountInfo->method('getUser')->willReturn($user); + + $node = $this->createMock(Folder::class); + $node->method('getPath')->willReturn('/user1/files/folder'); + + $ownerFolder = $this->createMock(Folder::class); + $ownerFolder->method('getById') + ->with(123, true) + ->willReturn([$node]); + + $this->rootFolder->method('getUserFolder') + ->with('user1') + ->willReturn($ownerFolder); + + $this->userMountCache->method('getMountsForFileId') + ->with(123) + ->willReturn([$cachedMountInfo]); + + $this->listener->method('getUsersSharingFile')->willReturn(['user1' => '/folder']); + + $activityEvent = $this->createMock(IEvent::class); + $activityEvent->expects($this->once())->method('setApp')->with('systemtags')->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setType')->with('systemtags')->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setAuthor')->with('actor1')->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setObject')->with('files', 123)->willReturn($activityEvent); + $activityEvent->expects($this->exactly(2))->method('setSubject') + ->withConsecutive( + [$extensionEvent, ['actor1', '/folder', '{{{tag1|||assignable}}}']], + [$extensionEvent, ['actor1', '/folder', '{{{tag2|||not-assignable}}}']] + ) + ->willReturn($activityEvent); + + $this->activityManager->method('generateEvent') + ->willReturn($activityEvent); + $this->activityManager->expects($this->exactly(2)) + ->method('publish') + ->with($activityEvent); + + $event = new MapperEvent($eventType, 'files', 123, [111, 222, 333]); + $this->listener->mapperEvent($event); + } + + public function prepareTagAsParameterProvider() { + return [ + [new SystemTag('1', 'visibleTag', true, true, true), "{{{visibleTag|||assignable}}}"], + [new SystemTag('2', 'invisibleTag', false, false, false), "{{{invisibleTag|||invisible}}}"], + [new SystemTag('3', 'restrictTag', true, false, false), "{{{restrictTag|||not-assignable}}}"], + [new SystemTag('3', 'staticTag', true, true, false), "{{{staticTag|||not-editable}}}"], + ]; + } + + /** + * @dataProvider prepareTagAsParameterProvider + */ + public function testPrepareTagAsParameter(SystemTag $tag, $expectedResult) { + $result = $this->invokePrivate($this->listener, 'prepareTagAsParameter', [$tag]); + $this->assertEquals($expectedResult, $result); + } +} diff --git a/lib/private/Files/Config/CachedMountInfo.php b/lib/private/Files/Config/CachedMountInfo.php index d2b3c78f393a..cc3074150142 100644 --- a/lib/private/Files/Config/CachedMountInfo.php +++ b/lib/private/Files/Config/CachedMountInfo.php @@ -91,12 +91,8 @@ public function getMountPointNode() { // TODO injection etc Filesystem::initMountPoints($this->getUser()->getUID()); $userNode = \OC::$server->getUserFolder($this->getUser()->getUID()); - $nodes = $userNode->getParent()->getById($this->getRootId()); - if (\count($nodes) > 0) { - return $nodes[0]; - } else { - return null; - } + $nodes = $userNode->getParent()->getById($this->getRootId(), true); + return $nodes[0] ?? null; } /** diff --git a/lib/private/Files/Meta/MetaFileIdNode.php b/lib/private/Files/Meta/MetaFileIdNode.php index b04d344b247e..2f8407ea9fb5 100644 --- a/lib/private/Files/Meta/MetaFileIdNode.php +++ b/lib/private/Files/Meta/MetaFileIdNode.php @@ -34,8 +34,8 @@ */ class MetaFileIdNode extends AbstractFolder { - /** @var int */ - private $fileId; + /** @var \OCP\Files\Node */ + private $node; /** @var MetaRootNode */ private $parentNode; /** @var IRootFolder */ @@ -45,12 +45,13 @@ class MetaFileIdNode extends AbstractFolder { * MetaFileIdNode constructor. * * @param MetaRootNode $parentNode - * @param int $fileId + * @param IRootFolder $root + * @param \OCP\Files\Node $node */ - public function __construct(MetaRootNode $parentNode, IRootFolder $root, $fileId) { + public function __construct(MetaRootNode $parentNode, IRootFolder $root, \OCP\Files\Node $node) { $this->parentNode = $parentNode; - $this->fileId = $fileId; $this->root = $root; + $this->node = $node; } /** @@ -79,7 +80,7 @@ public function isMounted() { */ public function getDirectoryListing() { return [ - new MetaVersionCollection($this->fileId, $this->root) + new MetaVersionCollection($this->root, $this->node) ]; } @@ -90,7 +91,7 @@ public function get($path) { $pieces = \explode('/', $path); if ($pieces[0] === 'v') { \array_shift($pieces); - $node = new MetaVersionCollection($this->fileId, $this->root); + $node = new MetaVersionCollection($this->root, $this->node); if (empty($pieces)) { return $node; } @@ -117,7 +118,7 @@ public function getPath() { * @inheritdoc */ public function getInternalPath() { - return "/meta/{$this->fileId}"; + return "/meta/{$this->node->getId()}"; } /** @@ -166,6 +167,6 @@ public function getParent() { * @inheritdoc */ public function getName() { - return "{$this->fileId}"; + return "{$this->node->getId()}"; } } diff --git a/lib/private/Files/Meta/MetaRootNode.php b/lib/private/Files/Meta/MetaRootNode.php index 6a893652c115..930180f5d714 100644 --- a/lib/private/Files/Meta/MetaRootNode.php +++ b/lib/private/Files/Meta/MetaRootNode.php @@ -76,13 +76,13 @@ public function get($path) { $pieces = \explode('/', $path); $fileId = (int)$pieces[0]; - // check if file exists - if (empty($this->rootFolder->getById($fileId))) { + $nodes = $this->rootFolder->getById($fileId, true); + if (empty($nodes)) { throw new NotFoundException(); } \array_shift($pieces); - $node = new MetaFileIdNode($this, $this->rootFolder, $fileId); + $node = new MetaFileIdNode($this, $this->rootFolder, $nodes[0]); if (empty($pieces)) { return $node; } @@ -92,7 +92,7 @@ public function get($path) { /** * @inheritdoc */ - public function getById($id) { + public function getById($id, $first = false) { return [ $this->get("$id") ]; diff --git a/lib/private/Files/Meta/MetaVersionCollection.php b/lib/private/Files/Meta/MetaVersionCollection.php index 5e63c7d0f43e..fd3a238efa64 100644 --- a/lib/private/Files/Meta/MetaVersionCollection.php +++ b/lib/private/Files/Meta/MetaVersionCollection.php @@ -1,5 +1,6 @@ * @author Thomas Müller * * @copyright Copyright (c) 2018, ownCloud GmbH @@ -24,7 +25,6 @@ use OC\Files\Node\AbstractFolder; use OCP\Files\IRootFolder; use OCP\Files\Storage\IVersionedStorage; -use OC\Files\View; use OCP\Files\NotFoundException; use OCP\Files\Storage; @@ -36,20 +36,20 @@ */ class MetaVersionCollection extends AbstractFolder { - /** @var int */ - private $fileId; /** @var IRootFolder */ private $root; + /** @var \OCP\Files\Node */ + private $node; /** * MetaVersionCollection constructor. * - * @param int $fileId * @param IRootFolder $root + * @param \OCP\Files\Node $node */ - public function __construct($fileId, IRootFolder $root) { - $this->fileId = $fileId; + public function __construct(IRootFolder $root, \OCP\Files\Node $node) { $this->root = $root; + $this->node = $node; } /** @@ -66,22 +66,19 @@ public function isShared() { return false; } - /** - * @inheritdoc - */ public function getDirectoryListing() { - $view = new View(); - $path = $view->getPath($this->fileId, false); - /** @var Storage $storage */ - list($storage, $internalPath) = $view->resolvePath($path); + $node = $this->node; + $storage = $node->getStorage(); + $internalPath = $node->getInternalPath(); + if (!$storage->instanceOfStorage(IVersionedStorage::class)) { return []; } /** @var IVersionedStorage | Storage $storage */ $versions = $storage->getVersions($internalPath); - return \array_values(\array_map(function ($version) use ($storage, $internalPath, $view, $path) { + return \array_values(\array_map(function ($version) use ($storage, $node, $internalPath) { if (!isset($version['mimetype'])) { - $version['mimetype'] = $view->getMimeType($path); + $version['mimetype'] = $node->getMimetype(); } return new MetaFileVersionNode($this, $this->root, $version, $storage, $internalPath); @@ -97,10 +94,10 @@ public function get($path) { throw new NotFoundException(); } $versionId = $pieces[0]; - $view = new View(); - $path = $view->getPath($this->fileId); - /** @var Storage $storage */ - list($storage, $internalPath) = $view->resolvePath($path); + + $storage = $this->node->getStorage(); + $internalPath = $this->node->getInternalPath(); + if (!$storage->instanceOfStorage(IVersionedStorage::class)) { throw new NotFoundException(); } @@ -110,7 +107,7 @@ public function get($path) { throw new NotFoundException(); } if (!isset($version['mimetype'])) { - $version['mimetype'] = $view->getMimeType($path); + $version['mimetype'] = $this->node->getMimetype(); } return new MetaFileVersionNode($this, $this->root, $version, $storage, $internalPath); } @@ -119,7 +116,7 @@ public function get($path) { * @inheritdoc */ public function getId() { - return $this->fileId; + return $this->node->getId(); } public function getName() { @@ -127,6 +124,6 @@ public function getName() { } public function getPath() { - return "/meta/{$this->fileId}/v"; + return "/meta/{$this->getId()}/v"; } } diff --git a/lib/private/Files/Node/AbstractFolder.php b/lib/private/Files/Node/AbstractFolder.php index fb6c3feea3a4..d652c026a2f6 100644 --- a/lib/private/Files/Node/AbstractFolder.php +++ b/lib/private/Files/Node/AbstractFolder.php @@ -112,7 +112,7 @@ public function searchByTag($tag, $userId) { /** * @inheritdoc */ - public function getById($id) { + public function getById($id, $first = false) { throw new NotPermittedException(); } diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index adb9e56a1b4c..ca559a1f7e86 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -269,9 +269,11 @@ private function searchCommon($method, $args) { /** * @param int $id + * @param boolean $first only return the first node that is found * @return \OC\Files\Node\Node[] + * @throws NotFoundException */ - public function getById($id) { + public function getById($id, $first = false) { $mounts = $this->root->getMountsIn($this->path); $mounts[] = $this->root->getMount($this->path); // reverse the array so we start with the storage this view is in @@ -290,6 +292,9 @@ public function getById($id) { $fullPath = $mount->getMountPoint() . $internalPath; if (($path = $this->getRelativePath($fullPath)) !== null) { $nodes[] = $this->get($path); + if ($first) { + break; + } } } } diff --git a/lib/private/Files/Node/LazyRoot.php b/lib/private/Files/Node/LazyRoot.php index 9e43f7e09b47..c9bf01f71525 100644 --- a/lib/private/Files/Node/LazyRoot.php +++ b/lib/private/Files/Node/LazyRoot.php @@ -417,7 +417,7 @@ public function searchByTag($tag, $userId) { /** * @inheritDoc */ - public function getById($id) { + public function getById($id, $first = false) { return $this->__call(__FUNCTION__, \func_get_args()); } diff --git a/lib/private/Files/Node/NonExistingFolder.php b/lib/private/Files/Node/NonExistingFolder.php index c3634403fea6..032e5764312c 100644 --- a/lib/private/Files/Node/NonExistingFolder.php +++ b/lib/private/Files/Node/NonExistingFolder.php @@ -154,7 +154,7 @@ public function searchByTag($tag, $userId) { throw new NotFoundException(); } - public function getById($id) { + public function getById($id, $first = false) { throw new NotFoundException(); } diff --git a/lib/private/Files/Storage/Wrapper/Jail.php b/lib/private/Files/Storage/Wrapper/Jail.php index f533cd8330f1..24d12774d8a0 100644 --- a/lib/private/Files/Storage/Wrapper/Jail.php +++ b/lib/private/Files/Storage/Wrapper/Jail.php @@ -26,6 +26,7 @@ use OC\Files\Cache\Wrapper\CacheJail; use OCP\Files\Storage\IStorage; +use OCP\Files\Storage\IVersionedStorage; use OCP\Lock\ILockingProvider; /** @@ -33,7 +34,8 @@ * * This restricts access to a subfolder of the wrapped storage with the subfolder becoming the root folder new storage */ -class Jail extends Wrapper { +class Jail extends Wrapper /* implements IVersionedStorage */ +{ /** * @var string */ @@ -497,4 +499,62 @@ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceIntern } return $this->getWrapperStorage()->moveFromStorage($sourceStorage, $sourceInternalPath, $this->getSourcePath($targetInternalPath)); } + + /** + * Get the content of a given version of a given file as stream resource + * + * @param string $internalPath + * @param string $versionId + * @return resource + * @since 10.0.9 + */ + public function getContentOfVersion($internalPath, $versionId) { + return $this->getWrapperStorage()->getContentOfVersion($this->getSourcePath($internalPath), $versionId); + } + + /** + * Restore the given version of a given file + * + * @param string $internalPath + * @param string $versionId + * @return boolean + * @since 10.0.9 + */ + public function restoreVersion($internalPath, $versionId) { + return $this->getWrapperStorage()->restoreVersion($this->getSourcePath($internalPath), $versionId); + } + + /** + * Tells the storage to explicitly create a version of a given file + * + * @param string $internalPath + * @return bool + * @since 10.0.9 + */ + public function saveVersion($internalPath) { + return $this->getWrapperStorage()->saveVersion($this->getSourcePath($internalPath)); + } + + /** + * List all versions for the given file + * + * @param string $internalPath + * @return array + * @since 10.0.9 + */ + public function getVersions($internalPath) { + return $this->getWrapperStorage()->getVersions($this->getSourcePath($internalPath)); + } + + /** + * Get one explicit version for the given file + * + * @param string $internalPath + * @param string $versionId + * @return array + * @since 10.0.9 + */ + public function getVersion($internalPath, $versionId) { + return $this->getWrapperStorage()->getVersion($this->getSourcePath($internalPath), $versionId); + } } diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php index 6f774f08f653..4fa7e4ccfe93 100644 --- a/lib/private/Share20/Share.php +++ b/lib/private/Share20/Share.php @@ -164,7 +164,7 @@ public function getNode() { $userFolder = $this->rootFolder->getUserFolder($this->sharedBy); } - $nodes = $userFolder->getById($this->fileId); + $nodes = $userFolder->getById($this->fileId, true); if (empty($nodes)) { throw new NotFoundException(); } diff --git a/lib/public/Files/Folder.php b/lib/public/Files/Folder.php index 2dfa83a409ed..55f5211bc22d 100644 --- a/lib/public/Files/Folder.php +++ b/lib/public/Files/Folder.php @@ -145,10 +145,11 @@ public function searchByTag($tag, $userId); * get a file or folder inside the folder by it's internal id * * @param int $id + * @param boolean $first only return the first node that is found * @return \OCP\Files\Node[] * @since 6.0.0 */ - public function getById($id); + public function getById($id, $first = false); /** * Get the amount of free space inside the folder diff --git a/tests/lib/Files/MetaFilesTest.php b/tests/lib/Files/MetaFilesTest.php index 28bac72710ff..9db1638234dd 100644 --- a/tests/lib/Files/MetaFilesTest.php +++ b/tests/lib/Files/MetaFilesTest.php @@ -43,11 +43,36 @@ class MetaFilesTest extends TestCase { use UserTrait; + /** + * @var string + */ + private $userId; + + protected function setUp() { + parent::setUp(); + + // workaround: re-setup versions hooks + Hooks::connectHooks(); + + $this->userId = $this->getUniqueID('meta-data-user-'); + $this->createUser($this->userId); + $this->loginAsUser($this->userId); + } + protected function tearDown() { self::logout(); parent::tearDown(); } + private function createFile() { + // create file + $file = $this->getUniqueID('file') . '.txt'; + $fileName = "{$this->userId}/files/$file"; + $view = new View(); + $view->file_put_contents($fileName, '1234'); + return $view->getFileInfo($fileName); + } + /** * @throws \Exception * @throws \OCP\Files\ForbiddenException @@ -55,17 +80,11 @@ protected function tearDown() { * @throws \OCP\Files\NotPermittedException */ public function testMetaInNodeAPI() { - // workaround: re-setup versions hooks - Hooks::connectHooks(); - - // create user - $userId = 'meta-data-user'; - $this->createUser($userId); - $this->loginAsUser($userId); + $userId = $this->userId; // create file $file = $this->getUniqueID('file') . '.txt'; - $fileName = "$userId/files/$file"; + $fileName = "{$this->userId}/files/$file"; $view = new View(); $view->file_put_contents($fileName, '1234'); $info = $view->getFileInfo($fileName); @@ -124,4 +143,25 @@ public function testMetaInNodeAPI() { $metaNodeOfFile->copy($fileName); $this->assertEquals('1234', $target->getContent()); } + + public function testMetaRootGetById() { + $info = $this->createFile(); + + $metaRoot = \OC::$server->getRootFolder(); + $info2 = $metaRoot->getById($info->getId()); + + $this->assertEquals($info->getId(), $info2[0]->getId()); + $this->assertEquals($info->getPath(), $info2[0]->getPath()); + } + + /** + * @expectedException OCP\Files\NotFoundException + */ + public function testMetaRootGetNotFound() { + $info = $this->createFile(); + + $metaRoot = \OC::$server->getRootFolder(); + // get non-existing + $metaRoot->get($info->getId() + 100); + } } diff --git a/tests/lib/Files/MetaVersionCollectionTest.php b/tests/lib/Files/MetaVersionCollectionTest.php new file mode 100644 index 000000000000..0659d6057361 --- /dev/null +++ b/tests/lib/Files/MetaVersionCollectionTest.php @@ -0,0 +1,166 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace Test\Files; + +use Test\TestCase; +use OCP\Files\Node; +use OC\Files\Meta\MetaVersionCollection; +use OCP\Files\IRootFolder; +use OCP\Files\Storage\IStorage; +use OCA\Files_Sharing\External\Storage; +use OCP\Files\Storage\IVersionedStorage; +use OC\Files\Meta\MetaFileVersionNode; + +/** + * Class MetaFilesTest + * + * @package Test\Files + */ +class MetaVersionCollectionTest extends TestCase { + + /** + * @var Node + */ + private $node; + + /** + * @var IRootFolder + */ + private $rootFolder; + + /** + * @var MetaVersionCollection + */ + private $collection; + + /** + * @var IStorage|IVersionedStorage + */ + private $storage; + + protected function setUp() { + parent::setUp(); + + $this->rootFolder = $this->createMock(IRootFolder::class); + $this->node = $this->createMock(Node::class); + $this->storage = $this->createMock([IStorage::class, IVersionedStorage::class]); + $this->node->method('getStorage')->willReturn($this->storage); + $this->collection = new MetaVersionCollection($this->rootFolder, $this->node); + } + + protected function tearDown() { + parent::tearDown(); + } + + public function testGetDirectoryListing() { + $this->node->method('getInternalPath')->willReturn('/abc'); + $this->node->method('getMimetype')->willReturn('application/json'); + + $this->storage->method('instanceOfStorage') + ->with(IVersionedStorage::class) + ->willReturn(true); + + $this->storage->expects($this->once()) + ->method('getVersions') + ->with('/abc') + ->willReturn([ + ['version' => '1014', 'timestamp' => 12345678], + ['version' => '1015', 'mimetype' => 'text/plain', 'timestamp' => 12345679], + ]); + + $children = $this->collection->getDirectoryListing(); + + $this->assertCount(2, $children); + $this->assertInstanceOf(MetaFileVersionNode::class, $children[0]); + $this->assertEquals('1014', $children[0]->getName()); + $this->assertEquals('application/json', $children[0]->getMimetype()); + $this->assertEquals(12345678, $children[0]->getMtime()); + $this->assertInstanceOf(MetaFileVersionNode::class, $children[1]); + $this->assertEquals('1015', $children[1]->getName()); + $this->assertEquals('text/plain', $children[1]->getMimetype()); + $this->assertEquals(12345679, $children[1]->getMtime()); + } + + public function testGetDirectoryListingNonVersionedStorage() { + $this->node->method('getInternalPath')->willReturn('/abc'); + $this->node->method('getMimetype')->willReturn('application/json'); + + $this->storage->method('instanceOfStorage') + ->with(IVersionedStorage::class) + ->willReturn(false); + + $this->storage->expects($this->never()) + ->method('getVersions'); + + $children = $this->collection->getDirectoryListing(); + + $this->assertCount(0, $children); + } + + public function testGet() { + $this->node->method('getInternalPath')->willReturn('/abc'); + $this->node->method('getMimetype')->willReturn('application/json'); + + $this->storage->method('instanceOfStorage') + ->with(IVersionedStorage::class) + ->willReturn(true); + + $this->storage->expects($this->once()) + ->method('getVersion') + ->with('/abc', '1014') + ->willReturn(['version' => '1014', 'timestamp' => 12345678]); + + $result = $this->collection->get('1014'); + + $this->assertInstanceOf(MetaFileVersionNode::class, $result); + $this->assertEquals('1014', $result->getName()); + $this->assertEquals('application/json', $result->getMimetype()); + $this->assertEquals(12345678, $result->getMtime()); + } + + /** + * @expectedException OCP\Files\NotFoundException + */ + public function testGetNonVersionedStorageFails() { + $this->node->method('getInternalPath')->willReturn('/abc'); + $this->node->method('getMimetype')->willReturn('application/json'); + + $this->storage->method('instanceOfStorage') + ->with(IVersionedStorage::class) + ->willReturn(false); + + $this->storage->expects($this->never()) + ->method('getVersion'); + + $this->collection->get('1014'); + } + + /** + * @expectedException OCP\Files\NotFoundException + */ + public function testGetSubEntryFails() { + $this->storage->expects($this->never()) + ->method('instanceOfStorage'); + + $this->collection->get('1014/1'); + } +} diff --git a/tests/lib/Files/Storage/Wrapper/JailTest.php b/tests/lib/Files/Storage/Wrapper/JailTest.php index 122953e9e54d..1b8f708155a5 100644 --- a/tests/lib/Files/Storage/Wrapper/JailTest.php +++ b/tests/lib/Files/Storage/Wrapper/JailTest.php @@ -8,6 +8,9 @@ namespace Test\Files\Storage\Wrapper; +use OCP\Files\Storage\IStorage; +use OCP\Files\Storage\IVersionedStorage; + class JailTest extends \Test\Files\Storage\Storage { /** @@ -48,4 +51,37 @@ public function testFilePutContentsRooted() { $this->instance->file_put_contents('bar', 'asd'); $this->assertEquals('asd', $this->sourceStorage->file_get_contents('foo/bar')); } + + public function providesVersionMethods() { + return [ + ['getContentOfVersion', 1014], + ['restoreVersion', 1014], + ['saveVersion'], + ['getVersions'], + ['getVersion', 1014], + ]; + } + + /** + * @dataProvider providesVersionMethods + */ + public function testCallsSourceVersionMethods($method, $extraArg = null) { + $sourceStorage = $this->createMock([IStorage::class, IVersionedStorage::class]); + $instance = new \OC\Files\Storage\Wrapper\Jail([ + 'storage' => $sourceStorage, + 'root' => 'foo' + ]); + + $matcher = $sourceStorage->expects($this->once()) + ->method($method); + // FIXME: normalize path... + if ($extraArg !== null) { + $matcher = $matcher->with('foo//bar', $extraArg); + } else { + $matcher = $matcher->with('foo//bar'); + } + $matcher->willReturn('returnValue'); + + $this->assertEquals('returnValue', $instance->$method('/bar', $extraArg)); + } }