diff options
Diffstat (limited to 'apps/files_versions/tests')
-rw-r--r-- | apps/files_versions/tests/BackgroundJob/ExpireVersionsTest.php | 55 | ||||
-rw-r--r-- | apps/files_versions/tests/Command/CleanupTest.php | 162 | ||||
-rw-r--r-- | apps/files_versions/tests/Command/ExpireTest.php | 28 | ||||
-rw-r--r-- | apps/files_versions/tests/Controller/PreviewControllerTest.php | 155 | ||||
-rw-r--r-- | apps/files_versions/tests/ExpirationTest.php | 114 | ||||
-rw-r--r-- | apps/files_versions/tests/StorageTest.php | 101 | ||||
-rw-r--r-- | apps/files_versions/tests/VersioningTest.php | 998 | ||||
-rw-r--r-- | apps/files_versions/tests/Versions/VersionManagerTest.php | 140 | ||||
-rw-r--r-- | apps/files_versions/tests/command/cleanuptest.php | 170 | ||||
-rw-r--r-- | apps/files_versions/tests/command/expiretest.php | 43 | ||||
-rw-r--r-- | apps/files_versions/tests/expirationtest.php | 206 | ||||
-rw-r--r-- | apps/files_versions/tests/js/versioncollectionSpec.js | 161 | ||||
-rw-r--r-- | apps/files_versions/tests/js/versionmodelSpec.js | 96 | ||||
-rw-r--r-- | apps/files_versions/tests/js/versionstabviewSpec.js | 208 | ||||
-rw-r--r-- | apps/files_versions/tests/versions.php | 848 |
15 files changed, 1753 insertions, 1732 deletions
diff --git a/apps/files_versions/tests/BackgroundJob/ExpireVersionsTest.php b/apps/files_versions/tests/BackgroundJob/ExpireVersionsTest.php new file mode 100644 index 00000000000..21e88e86f90 --- /dev/null +++ b/apps/files_versions/tests/BackgroundJob/ExpireVersionsTest.php @@ -0,0 +1,55 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\Files_Versions\Tests\BackgroundJob; + +use OCA\Files_Versions\BackgroundJob\ExpireVersions; +use OCA\Files_Versions\Expiration; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJobList; +use OCP\IConfig; +use OCP\IUserManager; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class ExpireVersionsTest extends TestCase { + private IConfig&MockObject $config; + private IUserManager&MockObject $userManager; + private Expiration&MockObject $expiration; + private IJobList&MockObject $jobList; + + protected function setUp(): void { + parent::setUp(); + + $this->config = $this->createMock(IConfig::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->expiration = $this->createMock(Expiration::class); + $this->jobList = $this->createMock(IJobList::class); + + $this->jobList->expects($this->once()) + ->method('setLastRun'); + $this->jobList->expects($this->once()) + ->method('setExecutionTime'); + } + + public function testBackgroundJobDeactivated(): void { + $this->config->method('getAppValue') + ->with('files_versions', 'background_job_expire_versions', 'yes') + ->willReturn('no'); + $this->expiration->expects($this->never()) + ->method('getMaxAgeAsTimestamp'); + + $timeFactory = $this->createMock(ITimeFactory::class); + $timeFactory->method('getTime') + ->with() + ->willReturn(999999999); + + $job = new ExpireVersions($this->config, $this->userManager, $this->expiration, $timeFactory); + $job->start($this->jobList); + } +} diff --git a/apps/files_versions/tests/Command/CleanupTest.php b/apps/files_versions/tests/Command/CleanupTest.php new file mode 100644 index 00000000000..dd6665f5aef --- /dev/null +++ b/apps/files_versions/tests/Command/CleanupTest.php @@ -0,0 +1,162 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\Files_Versions\Tests\Command; + +use OC\User\Manager; +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\Storage\IStorage; +use OCP\UserInterface; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +/** + * Class CleanupTest + * + * @group DB + * + * @package OCA\Files_Versions\Tests\Command + */ +class CleanupTest extends TestCase { + protected Manager&MockObject $userManager; + protected IRootFolder&MockObject $rootFolder; + protected VersionsMapper&MockObject $versionMapper; + protected CleanUp $cleanup; + + protected function setUp(): void { + parent::setUp(); + + $this->rootFolder = $this->createMock(IRootFolder::class); + $this->userManager = $this->createMock(Manager::class); + $this->versionMapper = $this->createMock(VersionsMapper::class); + + $this->cleanup = new CleanUp($this->rootFolder, $this->userManager, $this->versionMapper); + } + + /** + * @param boolean $nodeExists + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestDeleteVersions')] + public function testDeleteVersions(bool $nodeExists): void { + $this->rootFolder->expects($this->once()) + ->method('nodeExists') + ->with('/testUser/files_versions') + ->willReturn($nodeExists); + + $userFolder = $this->createMock(Folder::class); + $userHomeStorage = $this->createMock(IStorage::class); + $userHomeStorageCache = $this->createMock(ICache::class); + $this->rootFolder->expects($this->once()) + ->method('getUserFolder') + ->willReturn($userFolder); + $userFolder->expects($this->once()) + ->method('getStorage') + ->willReturn($userHomeStorage); + $userHomeStorage->expects($this->once()) + ->method('getCache') + ->willReturn($userHomeStorageCache); + $userHomeStorageCache->expects($this->once()) + ->method('getNumericStorageId') + ->willReturn(1); + + if ($nodeExists) { + $this->rootFolder->expects($this->once()) + ->method('get') + ->with('/testUser/files_versions') + ->willReturn($this->rootFolder); + $this->rootFolder->expects($this->once()) + ->method('delete'); + } else { + $this->rootFolder->expects($this->never()) + ->method('get'); + $this->rootFolder->expects($this->never()) + ->method('delete'); + } + + $this->invokePrivate($this->cleanup, 'deleteVersions', ['testUser']); + } + + public static function dataTestDeleteVersions(): array { + return [ + [true], + [false] + ]; + } + + + /** + * test delete versions from users given as parameter + */ + public function testExecuteDeleteListOfUsers(): void { + $userIds = ['user1', 'user2', 'user3']; + + $instance = $this->getMockBuilder(CleanUp::class) + ->onlyMethods(['deleteVersions']) + ->setConstructorArgs([$this->rootFolder, $this->userManager, $this->versionMapper]) + ->getMock(); + $instance->expects($this->exactly(count($userIds))) + ->method('deleteVersions') + ->willReturnCallback(function ($user) use ($userIds): void { + $this->assertTrue(in_array($user, $userIds)); + }); + + $this->userManager->expects($this->exactly(count($userIds))) + ->method('userExists')->willReturn(true); + + $inputInterface = $this->createMock(\Symfony\Component\Console\Input\InputInterface::class); + $inputInterface->expects($this->once())->method('getArgument') + ->with('user_id') + ->willReturn($userIds); + + $outputInterface = $this->createMock(\Symfony\Component\Console\Output\OutputInterface::class); + + $this->invokePrivate($instance, 'execute', [$inputInterface, $outputInterface]); + } + + /** + * test delete versions of all users + */ + public function testExecuteAllUsers(): void { + $userIds = []; + $backendUsers = ['user1', 'user2']; + + $instance = $this->getMockBuilder(CleanUp::class) + ->onlyMethods(['deleteVersions']) + ->setConstructorArgs([$this->rootFolder, $this->userManager, $this->versionMapper]) + ->getMock(); + + $backend = $this->getMockBuilder(UserInterface::class) + ->disableOriginalConstructor()->getMock(); + $backend->expects($this->once())->method('getUsers') + ->with('', 500, 0) + ->willReturn($backendUsers); + + $instance->expects($this->exactly(count($backendUsers))) + ->method('deleteVersions') + ->willReturnCallback(function ($user) use ($backendUsers): void { + $this->assertTrue(in_array($user, $backendUsers)); + }); + + $inputInterface = $this->createMock(\Symfony\Component\Console\Input\InputInterface::class); + $inputInterface->expects($this->once())->method('getArgument') + ->with('user_id') + ->willReturn($userIds); + + $outputInterface = $this->createMock(\Symfony\Component\Console\Output\OutputInterface::class); + + $this->userManager->expects($this->once()) + ->method('getBackends') + ->willReturn([$backend]); + + $this->invokePrivate($instance, 'execute', [$inputInterface, $outputInterface]); + } +} diff --git a/apps/files_versions/tests/Command/ExpireTest.php b/apps/files_versions/tests/Command/ExpireTest.php new file mode 100644 index 00000000000..b74457a7fd6 --- /dev/null +++ b/apps/files_versions/tests/Command/ExpireTest.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\Files_Versions\Tests\Command; + +use OCA\Files_Versions\Command\Expire; +use Test\TestCase; + +/** + * Class ExpireTest + * + * @group DB + * + * @package OCA\Files_Versions\Tests\Command + */ +class ExpireTest extends TestCase { + public function testExpireNonExistingUser(): void { + $command = new Expire($this->getUniqueID('test'), ''); + $command->handle(); + + $this->addToAssertionCount(1); + } +} diff --git a/apps/files_versions/tests/Controller/PreviewControllerTest.php b/apps/files_versions/tests/Controller/PreviewControllerTest.php new file mode 100644 index 00000000000..542ea2b6b34 --- /dev/null +++ b/apps/files_versions/tests/Controller/PreviewControllerTest.php @@ -0,0 +1,155 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\Files_Versions\Tests\Controller; + +use OCA\Files_Versions\Controller\PreviewController; +use OCA\Files_Versions\Versions\IVersionManager; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\Files\SimpleFS\ISimpleFile; +use OCP\IPreview; +use OCP\IRequest; +use OCP\IUser; +use OCP\IUserSession; +use OCP\Preview\IMimeIconProvider; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class PreviewControllerTest extends TestCase { + private IRootFolder&MockObject $rootFolder; + private string $userId; + private IPreview&MockObject $previewManager; + private IUserSession&MockObject $userSession; + private IVersionManager&MockObject $versionManager; + + private IMimeIconProvider&MockObject $mimeIconProvider; + private PreviewController $controller; + + protected function setUp(): void { + parent::setUp(); + + $this->rootFolder = $this->createMock(IRootFolder::class); + $this->userId = 'user'; + $user = $this->createMock(IUser::class); + $user->expects($this->any()) + ->method('getUID') + ->willReturn($this->userId); + $this->previewManager = $this->createMock(IPreview::class); + $this->userSession = $this->createMock(IUserSession::class); + $this->userSession->expects($this->any()) + ->method('getUser') + ->willReturn($user); + $this->versionManager = $this->createMock(IVersionManager::class); + $this->mimeIconProvider = $this->createMock(IMimeIconProvider::class); + + $this->controller = new PreviewController( + 'files_versions', + $this->createMock(IRequest::class), + $this->rootFolder, + $this->userSession, + $this->versionManager, + $this->previewManager, + $this->mimeIconProvider, + ); + } + + public function testInvalidFile(): void { + $res = $this->controller->getPreview(''); + $expected = new DataResponse([], Http::STATUS_BAD_REQUEST); + + $this->assertEquals($expected, $res); + } + + public function testInvalidWidth(): void { + $res = $this->controller->getPreview('file', 0); + $expected = new DataResponse([], Http::STATUS_BAD_REQUEST); + + $this->assertEquals($expected, $res); + } + + public function testInvalidHeight(): void { + $res = $this->controller->getPreview('file', 10, 0); + $expected = new DataResponse([], Http::STATUS_BAD_REQUEST); + + $this->assertEquals($expected, $res); + } + + public function testInvalidVersion(): void { + $res = $this->controller->getPreview('file', 10, 0); + $expected = new DataResponse([], Http::STATUS_BAD_REQUEST); + + $this->assertEquals($expected, $res); + } + + public function testValidPreview(): void { + $userFolder = $this->createMock(Folder::class); + $userRoot = $this->createMock(Folder::class); + + $this->rootFolder->method('getUserFolder') + ->with($this->userId) + ->willReturn($userFolder); + $userFolder->method('getParent') + ->willReturn($userRoot); + + $sourceFile = $this->createMock(File::class); + $userFolder->method('get') + ->with('file') + ->willReturn($sourceFile); + + $file = $this->createMock(File::class); + $file->method('getMimetype') + ->willReturn('myMime'); + + $this->versionManager->method('getVersionFile') + ->willReturn($file); + + $preview = $this->createMock(ISimpleFile::class); + $preview->method('getName')->willReturn('name'); + $preview->method('getMTime')->willReturn(42); + $this->previewManager->method('getPreview') + ->with($this->equalTo($file), 10, 10, true, IPreview::MODE_FILL, 'myMime') + ->willReturn($preview); + $preview->method('getMimeType') + ->willReturn('previewMime'); + + $res = $this->controller->getPreview('file', 10, 10, '42'); + + $this->assertEquals('previewMime', $res->getHeaders()['Content-Type']); + $this->assertEquals(Http::STATUS_OK, $res->getStatus()); + $this->assertEquals($preview, $this->invokePrivate($res, 'file')); + } + + public function testVersionNotFound(): void { + $userFolder = $this->createMock(Folder::class); + $userRoot = $this->createMock(Folder::class); + + $this->rootFolder->method('getUserFolder') + ->with($this->userId) + ->willReturn($userFolder); + $userFolder->method('getParent') + ->willReturn($userRoot); + + $sourceFile = $this->createMock(File::class); + $userFolder->method('get') + ->with('file') + ->willReturn($sourceFile); + + $this->versionManager->method('getVersionFile') + ->willThrowException(new NotFoundException()); + + $res = $this->controller->getPreview('file', 10, 10, '42'); + $expected = new DataResponse([], Http::STATUS_NOT_FOUND); + + $this->assertEquals($expected, $res); + } +} diff --git a/apps/files_versions/tests/ExpirationTest.php b/apps/files_versions/tests/ExpirationTest.php new file mode 100644 index 00000000000..8cf412c3fe0 --- /dev/null +++ b/apps/files_versions/tests/ExpirationTest.php @@ -0,0 +1,114 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\Files_Versions\Tests; + +use OCA\Files_Versions\Expiration; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IConfig; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; + +class ExpirationTest extends \Test\TestCase { + public const SECONDS_PER_DAY = 86400; //60*60*24 + + public static function expirationData(): array { + $today = 100 * self::SECONDS_PER_DAY; + $back10Days = (100 - 10) * self::SECONDS_PER_DAY; + $back20Days = (100 - 20) * self::SECONDS_PER_DAY; + $back30Days = (100 - 30) * self::SECONDS_PER_DAY; + $back35Days = (100 - 35) * self::SECONDS_PER_DAY; + + // it should never happen, but who knows :/ + $ahead100Days = (100 + 100) * self::SECONDS_PER_DAY; + + return [ + // Expiration is disabled - always should return false + [ 'disabled', $today, $back10Days, false, false], + [ 'disabled', $today, $back10Days, true, false], + [ 'disabled', $today, $ahead100Days, true, false], + + // Default: expire in 30 days or earlier when quota requirements are met + [ 'auto', $today, $back10Days, false, false], + [ 'auto', $today, $back35Days, false, false], + [ 'auto', $today, $back10Days, true, true], + [ 'auto', $today, $back35Days, true, true], + [ 'auto', $today, $ahead100Days, true, true], + + // The same with 'auto' + [ 'auto, auto', $today, $back10Days, false, false], + [ 'auto, auto', $today, $back35Days, false, false], + [ 'auto, auto', $today, $back10Days, true, true], + [ 'auto, auto', $today, $back35Days, true, true], + + // Keep for 15 days but expire anytime if space needed + [ '15, auto', $today, $back10Days, false, false], + [ '15, auto', $today, $back20Days, false, false], + [ '15, auto', $today, $back10Days, true, true], + [ '15, auto', $today, $back20Days, true, true], + [ '15, auto', $today, $ahead100Days, true, true], + + // Expire anytime if space needed, Expire all older than max + [ 'auto, 15', $today, $back10Days, false, false], + [ 'auto, 15', $today, $back20Days, false, true], + [ 'auto, 15', $today, $back10Days, true, true], + [ 'auto, 15', $today, $back20Days, true, true], + [ 'auto, 15', $today, $ahead100Days, true, true], + + // Expire all older than max OR older than min if space needed + [ '15, 25', $today, $back10Days, false, false], + [ '15, 25', $today, $back20Days, false, false], + [ '15, 25', $today, $back30Days, false, true], + [ '15, 25', $today, $back10Days, false, false], + [ '15, 25', $today, $back20Days, true, true], + [ '15, 25', $today, $back30Days, true, true], + [ '15, 25', $today, $ahead100Days, true, false], + + // Expire all older than max OR older than min if space needed + // Max<Min case + [ '25, 15', $today, $back10Days, false, false], + [ '25, 15', $today, $back20Days, false, false], + [ '25, 15', $today, $back30Days, false, true], + [ '25, 15', $today, $back10Days, false, false], + [ '25, 15', $today, $back20Days, true, false], + [ '25, 15', $today, $back30Days, true, true], + [ '25, 15', $today, $ahead100Days, true, false], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('expirationData')] + public function testExpiration(string $retentionObligation, int $timeNow, int $timestamp, bool $quotaExceeded, bool $expectedResult): void { + $mockedConfig = $this->getMockedConfig($retentionObligation); + $mockedTimeFactory = $this->getMockedTimeFactory($timeNow); + $mockedLogger = $this->createMock(LoggerInterface::class); + + $expiration = new Expiration($mockedConfig, $mockedTimeFactory, $mockedLogger); + $actualResult = $expiration->isExpired($timestamp, $quotaExceeded); + + $this->assertEquals($expectedResult, $actualResult); + } + + + private function getMockedTimeFactory(int $time): ITimeFactory&MockObject { + $mockedTimeFactory = $this->createMock(ITimeFactory::class); + $mockedTimeFactory->expects($this->any()) + ->method('getTime') + ->willReturn($time); + + return $mockedTimeFactory; + } + + private function getMockedConfig(string $returnValue): IConfig&MockObject { + $mockedConfig = $this->createMock(IConfig::class); + $mockedConfig->expects($this->any()) + ->method('getSystemValue') + ->willReturn($returnValue); + + return $mockedConfig; + } +} diff --git a/apps/files_versions/tests/StorageTest.php b/apps/files_versions/tests/StorageTest.php new file mode 100644 index 00000000000..443cff3ee06 --- /dev/null +++ b/apps/files_versions/tests/StorageTest.php @@ -0,0 +1,101 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\files_versions\tests; + +use OCA\Files_Versions\Expiration; +use OCA\Files_Versions\Storage; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\Server; +use Test\TestCase; +use Test\Traits\UserTrait; + +/** + * @group DB + */ +class StorageTest extends TestCase { + use UserTrait; + + private $versionsRoot; + private $userFolder; + private int $expireTimestamp = 10; + + protected function setUp(): void { + parent::setUp(); + + $expiration = $this->createMock(Expiration::class); + $expiration->method('getMaxAgeAsTimestamp') + ->willReturnCallback(function () { + return $this->expireTimestamp; + }); + $this->overwriteService(Expiration::class, $expiration); + + \OC::$server->boot(); + + $this->createUser('version_test', ''); + $this->loginAsUser('version_test'); + /** @var IRootFolder $root */ + $root = Server::get(IRootFolder::class); + $this->userFolder = $root->getUserFolder('version_test'); + } + + + protected function createPastFile(string $path, int $mtime): void { + try { + $file = $this->userFolder->get($path); + $file->putContent((string)$mtime); + } catch (NotFoundException $e) { + $file = $this->userFolder->newFile($path, (string)$mtime); + } + $file->touch($mtime); + } + + public function testExpireMaxAge(): void { + $this->userFolder->newFolder('folder1'); + $this->userFolder->newFolder('folder1/sub1'); + $this->userFolder->newFolder('folder2'); + + $this->createPastFile('file1', 100); + $this->createPastFile('file1', 500); + $this->createPastFile('file1', 900); + + $this->createPastFile('folder1/file2', 100); + $this->createPastFile('folder1/file2', 200); + $this->createPastFile('folder1/file2', 300); + + $this->createPastFile('folder1/sub1/file3', 400); + $this->createPastFile('folder1/sub1/file3', 500); + $this->createPastFile('folder1/sub1/file3', 600); + + $this->createPastFile('folder2/file4', 100); + $this->createPastFile('folder2/file4', 600); + $this->createPastFile('folder2/file4', 800); + + $this->assertCount(2, Storage::getVersions('version_test', 'file1')); + $this->assertCount(2, Storage::getVersions('version_test', 'folder1/file2')); + $this->assertCount(2, Storage::getVersions('version_test', 'folder1/sub1/file3')); + $this->assertCount(2, Storage::getVersions('version_test', 'folder2/file4')); + + $this->expireTimestamp = 150; + Storage::expireOlderThanMaxForUser('version_test'); + + $this->assertCount(1, Storage::getVersions('version_test', 'file1')); + $this->assertCount(1, Storage::getVersions('version_test', 'folder1/file2')); + $this->assertCount(2, Storage::getVersions('version_test', 'folder1/sub1/file3')); + $this->assertCount(1, Storage::getVersions('version_test', 'folder2/file4')); + + $this->expireTimestamp = 550; + Storage::expireOlderThanMaxForUser('version_test'); + + $this->assertCount(0, Storage::getVersions('version_test', 'file1')); + $this->assertCount(0, Storage::getVersions('version_test', 'folder1/file2')); + $this->assertCount(0, Storage::getVersions('version_test', 'folder1/sub1/file3')); + $this->assertCount(1, Storage::getVersions('version_test', 'folder2/file4')); + } +} diff --git a/apps/files_versions/tests/VersioningTest.php b/apps/files_versions/tests/VersioningTest.php new file mode 100644 index 00000000000..c9f7d27d7ab --- /dev/null +++ b/apps/files_versions/tests/VersioningTest.php @@ -0,0 +1,998 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\Files_Versions\Tests; + +use OC\AllConfig; +use OC\Files\Cache\Watcher; +use OC\Files\Filesystem; +use OC\Files\Storage\Temporary; +use OC\Files\View; +use OC\SystemConfig; +use OC\User\NoUserException; +use OCA\Files_Sharing\AppInfo\Application; +use OCA\Files_Versions\Db\VersionEntity; +use OCA\Files_Versions\Db\VersionsMapper; +use OCA\Files_Versions\Events\VersionRestoredEvent; +use OCA\Files_Versions\Storage; +use OCA\Files_Versions\Versions\IVersionManager; +use OCP\Constants; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\IMimeTypeLoader; +use OCP\IConfig; +use OCP\IUser; +use OCP\IUserManager; +use OCP\Server; +use OCP\Share\IShare; +use OCP\Util; + +/** + * Class Test_Files_versions + * this class provide basic files versions test + * + * @group DB + */ +class VersioningTest extends \Test\TestCase { + public const TEST_VERSIONS_USER = 'test-versions-user'; + public const TEST_VERSIONS_USER2 = 'test-versions-user2'; + public const USERS_VERSIONS_ROOT = '/test-versions-user/files_versions'; + + /** + * @var View + */ + private $rootView; + /** + * @var VersionsMapper + */ + private $versionsMapper; + /** + * @var IMimeTypeLoader + */ + private $mimeTypeLoader; + private $user1; + private $user2; + + public static function setUpBeforeClass(): void { + parent::setUpBeforeClass(); + + $application = new Application(); + + // create test user + self::loginHelper(self::TEST_VERSIONS_USER2, true); + self::loginHelper(self::TEST_VERSIONS_USER, true); + } + + public static function tearDownAfterClass(): void { + // cleanup test user + $user = Server::get(IUserManager::class)->get(self::TEST_VERSIONS_USER); + if ($user !== null) { + $user->delete(); + } + $user = Server::get(IUserManager::class)->get(self::TEST_VERSIONS_USER2); + if ($user !== null) { + $user->delete(); + } + + parent::tearDownAfterClass(); + } + + protected function setUp(): void { + parent::setUp(); + + $config = Server::get(IConfig::class); + $mockConfig = $this->getMockBuilder(AllConfig::class) + ->onlyMethods(['getSystemValue']) + ->setConstructorArgs([Server::get(SystemConfig::class)]) + ->getMock(); + $mockConfig->expects($this->any()) + ->method('getSystemValue') + ->willReturnCallback(function ($key, $default) use ($config) { + if ($key === 'filesystem_check_changes') { + return Watcher::CHECK_ONCE; + } else { + return $config->getSystemValue($key, $default); + } + }); + $this->overwriteService(AllConfig::class, $mockConfig); + + // clear hooks + \OC_Hook::clear(); + \OC::registerShareHooks(Server::get(SystemConfig::class)); + \OC::$server->boot(); + + self::loginHelper(self::TEST_VERSIONS_USER); + $this->rootView = new View(); + if (!$this->rootView->file_exists(self::USERS_VERSIONS_ROOT)) { + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT); + } + + $this->versionsMapper = Server::get(VersionsMapper::class); + $this->mimeTypeLoader = Server::get(IMimeTypeLoader::class); + + $this->user1 = $this->createMock(IUser::class); + $this->user1->method('getUID') + ->willReturn(self::TEST_VERSIONS_USER); + $this->user2 = $this->createMock(IUser::class); + $this->user2->method('getUID') + ->willReturn(self::TEST_VERSIONS_USER2); + } + + protected function tearDown(): void { + $this->restoreService(AllConfig::class); + + if ($this->rootView) { + $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files/'); + $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files/'); + $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files_versions/'); + $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files_versions/'); + } + + \OC_Hook::clear(); + + parent::tearDown(); + } + + /** + * @medium + * test expire logic + */ + #[\PHPUnit\Framework\Attributes\DataProvider('versionsProvider')] + public function testGetExpireList($versions, $sizeOfAllDeletedFiles): void { + + // last interval end at 2592000 + $startTime = 5000000; + + $testClass = new VersionStorageToTest(); + [$deleted, $size] = $testClass->callProtectedGetExpireList($startTime, $versions); + + // we should have deleted 16 files each of the size 1 + $this->assertEquals($sizeOfAllDeletedFiles, $size); + + // the deleted array should only contain versions which should be deleted + foreach ($deleted as $key => $path) { + unset($versions[$key]); + $this->assertEquals('delete', substr($path, 0, strlen('delete'))); + } + + // the versions array should only contain versions which should be kept + foreach ($versions as $version) { + $this->assertEquals('keep', $version['path']); + } + } + + public static function versionsProvider(): array { + return [ + // first set of versions uniformly distributed versions + [ + [ + // first slice (10sec) keep one version every 2 seconds + ['version' => 4999999, 'path' => 'keep', 'size' => 1], + ['version' => 4999998, 'path' => 'delete', 'size' => 1], + ['version' => 4999997, 'path' => 'keep', 'size' => 1], + ['version' => 4999995, 'path' => 'keep', 'size' => 1], + ['version' => 4999994, 'path' => 'delete', 'size' => 1], + //next slice (60sec) starts at 4999990 keep one version every 10 secons + ['version' => 4999988, 'path' => 'keep', 'size' => 1], + ['version' => 4999978, 'path' => 'keep', 'size' => 1], + ['version' => 4999975, 'path' => 'delete', 'size' => 1], + ['version' => 4999972, 'path' => 'delete', 'size' => 1], + ['version' => 4999967, 'path' => 'keep', 'size' => 1], + ['version' => 4999958, 'path' => 'delete', 'size' => 1], + ['version' => 4999957, 'path' => 'keep', 'size' => 1], + //next slice (3600sec) start at 4999940 keep one version every 60 seconds + ['version' => 4999900, 'path' => 'keep', 'size' => 1], + ['version' => 4999841, 'path' => 'delete', 'size' => 1], + ['version' => 4999840, 'path' => 'keep', 'size' => 1], + ['version' => 4999780, 'path' => 'keep', 'size' => 1], + ['version' => 4996401, 'path' => 'keep', 'size' => 1], + // next slice (86400sec) start at 4996400 keep one version every 3600 seconds + ['version' => 4996350, 'path' => 'delete', 'size' => 1], + ['version' => 4992800, 'path' => 'keep', 'size' => 1], + ['version' => 4989800, 'path' => 'delete', 'size' => 1], + ['version' => 4989700, 'path' => 'delete', 'size' => 1], + ['version' => 4989200, 'path' => 'keep', 'size' => 1], + // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds + ['version' => 4913600, 'path' => 'keep', 'size' => 1], + ['version' => 4852800, 'path' => 'delete', 'size' => 1], + ['version' => 4827201, 'path' => 'delete', 'size' => 1], + ['version' => 4827200, 'path' => 'keep', 'size' => 1], + ['version' => 4777201, 'path' => 'delete', 'size' => 1], + ['version' => 4777501, 'path' => 'delete', 'size' => 1], + ['version' => 4740000, 'path' => 'keep', 'size' => 1], + // final slice starts at 2408000 keep one version every 604800 secons + ['version' => 2408000, 'path' => 'keep', 'size' => 1], + ['version' => 1803201, 'path' => 'delete', 'size' => 1], + ['version' => 1803200, 'path' => 'keep', 'size' => 1], + ['version' => 1800199, 'path' => 'delete', 'size' => 1], + ['version' => 1800100, 'path' => 'delete', 'size' => 1], + ['version' => 1198300, 'path' => 'keep', 'size' => 1], + ], + 16 // size of all deleted files (every file has the size 1) + ], + // second set of versions, here we have only really old versions + [ + [ + // first slice (10sec) keep one version every 2 seconds + // next slice (60sec) starts at 4999990 keep one version every 10 secons + // next slice (3600sec) start at 4999940 keep one version every 60 seconds + // next slice (86400sec) start at 4996400 keep one version every 3600 seconds + ['version' => 4996400, 'path' => 'keep', 'size' => 1], + ['version' => 4996350, 'path' => 'delete', 'size' => 1], + ['version' => 4996350, 'path' => 'delete', 'size' => 1], + ['version' => 4992800, 'path' => 'keep', 'size' => 1], + ['version' => 4989800, 'path' => 'delete', 'size' => 1], + ['version' => 4989700, 'path' => 'delete', 'size' => 1], + ['version' => 4989200, 'path' => 'keep', 'size' => 1], + // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds + ['version' => 4913600, 'path' => 'keep', 'size' => 1], + ['version' => 4852800, 'path' => 'delete', 'size' => 1], + ['version' => 4827201, 'path' => 'delete', 'size' => 1], + ['version' => 4827200, 'path' => 'keep', 'size' => 1], + ['version' => 4777201, 'path' => 'delete', 'size' => 1], + ['version' => 4777501, 'path' => 'delete', 'size' => 1], + ['version' => 4740000, 'path' => 'keep', 'size' => 1], + // final slice starts at 2408000 keep one version every 604800 secons + ['version' => 2408000, 'path' => 'keep', 'size' => 1], + ['version' => 1803201, 'path' => 'delete', 'size' => 1], + ['version' => 1803200, 'path' => 'keep', 'size' => 1], + ['version' => 1800199, 'path' => 'delete', 'size' => 1], + ['version' => 1800100, 'path' => 'delete', 'size' => 1], + ['version' => 1198300, 'path' => 'keep', 'size' => 1], + ], + 11 // size of all deleted files (every file has the size 1) + ], + // third set of versions, with some gaps between + [ + [ + // first slice (10sec) keep one version every 2 seconds + ['version' => 4999999, 'path' => 'keep', 'size' => 1], + ['version' => 4999998, 'path' => 'delete', 'size' => 1], + ['version' => 4999997, 'path' => 'keep', 'size' => 1], + ['version' => 4999995, 'path' => 'keep', 'size' => 1], + ['version' => 4999994, 'path' => 'delete', 'size' => 1], + //next slice (60sec) starts at 4999990 keep one version every 10 secons + ['version' => 4999988, 'path' => 'keep', 'size' => 1], + ['version' => 4999978, 'path' => 'keep', 'size' => 1], + //next slice (3600sec) start at 4999940 keep one version every 60 seconds + // next slice (86400sec) start at 4996400 keep one version every 3600 seconds + ['version' => 4989200, 'path' => 'keep', 'size' => 1], + // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds + ['version' => 4913600, 'path' => 'keep', 'size' => 1], + ['version' => 4852800, 'path' => 'delete', 'size' => 1], + ['version' => 4827201, 'path' => 'delete', 'size' => 1], + ['version' => 4827200, 'path' => 'keep', 'size' => 1], + ['version' => 4777201, 'path' => 'delete', 'size' => 1], + ['version' => 4777501, 'path' => 'delete', 'size' => 1], + ['version' => 4740000, 'path' => 'keep', 'size' => 1], + // final slice starts at 2408000 keep one version every 604800 secons + ['version' => 2408000, 'path' => 'keep', 'size' => 1], + ['version' => 1803201, 'path' => 'delete', 'size' => 1], + ['version' => 1803200, 'path' => 'keep', 'size' => 1], + ['version' => 1800199, 'path' => 'delete', 'size' => 1], + ['version' => 1800100, 'path' => 'delete', 'size' => 1], + ['version' => 1198300, 'path' => 'keep', 'size' => 1], + ], + 9 // size of all deleted files (every file has the size 1) + ], + // fourth set of versions: empty (see issue #19066) + [ + [], + 0 + ] + + ]; + } + + public function testRename(): void { + Filesystem::file_put_contents('test.txt', 'test file'); + + $t1 = time(); + // second version is two weeks older, this way we make sure that no + // version will be expired + $t2 = $t1 - 60 * 60 * 24 * 14; + + // create some versions + $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; + $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; + $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; + $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; + + $this->rootView->file_put_contents($v1, 'version1'); + $this->rootView->file_put_contents($v2, 'version2'); + + // execute rename hook of versions app + Filesystem::rename('test.txt', 'test2.txt'); + + $this->runCommands(); + + $this->assertFalse($this->rootView->file_exists($v1), 'version 1 of old file does not exist'); + $this->assertFalse($this->rootView->file_exists($v2), 'version 2 of old file does not exist'); + + $this->assertTrue($this->rootView->file_exists($v1Renamed), 'version 1 of renamed file exists'); + $this->assertTrue($this->rootView->file_exists($v2Renamed), 'version 2 of renamed file exists'); + } + + public function testRenameInSharedFolder(): void { + Filesystem::mkdir('folder1'); + Filesystem::mkdir('folder1/folder2'); + Filesystem::file_put_contents('folder1/test.txt', 'test file'); + + $t1 = time(); + // second version is two weeks older, this way we make sure that no + // version will be expired + $t2 = $t1 - 60 * 60 * 24 * 14; + + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1'); + // create some versions + $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1; + $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2; + $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t1; + $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t2; + + $this->rootView->file_put_contents($v1, 'version1'); + $this->rootView->file_put_contents($v2, 'version2'); + + $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1'); + $share = Server::get(\OCP\Share\IManager::class)->newShare(); + $share->setNode($node) + ->setShareType(IShare::TYPE_USER) + ->setSharedBy(self::TEST_VERSIONS_USER) + ->setSharedWith(self::TEST_VERSIONS_USER2) + ->setPermissions(Constants::PERMISSION_ALL); + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); + + self::loginHelper(self::TEST_VERSIONS_USER2); + + $this->assertTrue(Filesystem::file_exists('folder1/test.txt')); + + // execute rename hook of versions app + Filesystem::rename('/folder1/test.txt', '/folder1/folder2/test.txt'); + + $this->runCommands(); + + self::loginHelper(self::TEST_VERSIONS_USER); + + $this->assertFalse($this->rootView->file_exists($v1), 'version 1 of old file does not exist'); + $this->assertFalse($this->rootView->file_exists($v2), 'version 2 of old file does not exist'); + + $this->assertTrue($this->rootView->file_exists($v1Renamed), 'version 1 of renamed file exists'); + $this->assertTrue($this->rootView->file_exists($v2Renamed), 'version 2 of renamed file exists'); + + Server::get(\OCP\Share\IManager::class)->deleteShare($share); + } + + public function testMoveFolder(): void { + Filesystem::mkdir('folder1'); + Filesystem::mkdir('folder2'); + Filesystem::file_put_contents('folder1/test.txt', 'test file'); + + $t1 = time(); + // second version is two weeks older, this way we make sure that no + // version will be expired + $t2 = $t1 - 60 * 60 * 24 * 14; + + // create some versions + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1'); + $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1; + $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2; + $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t1; + $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t2; + + $this->rootView->file_put_contents($v1, 'version1'); + $this->rootView->file_put_contents($v2, 'version2'); + + // execute rename hook of versions app + Filesystem::rename('folder1', 'folder2/folder1'); + + $this->runCommands(); + + $this->assertFalse($this->rootView->file_exists($v1)); + $this->assertFalse($this->rootView->file_exists($v2)); + + $this->assertTrue($this->rootView->file_exists($v1Renamed)); + $this->assertTrue($this->rootView->file_exists($v2Renamed)); + } + + + public function testMoveFileIntoSharedFolderAsRecipient(): void { + Filesystem::mkdir('folder1'); + $fileInfo = Filesystem::getFileInfo('folder1'); + + $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1'); + $share = Server::get(\OCP\Share\IManager::class)->newShare(); + $share->setNode($node) + ->setShareType(IShare::TYPE_USER) + ->setSharedBy(self::TEST_VERSIONS_USER) + ->setSharedWith(self::TEST_VERSIONS_USER2) + ->setPermissions(Constants::PERMISSION_ALL); + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); + + self::loginHelper(self::TEST_VERSIONS_USER2); + $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions'; + Filesystem::file_put_contents('test.txt', 'test file'); + + $t1 = time(); + // second version is two weeks older, this way we make sure that no + // version will be expired + $t2 = $t1 - 60 * 60 * 24 * 14; + + $this->rootView->mkdir($versionsFolder2); + // create some versions + $v1 = $versionsFolder2 . '/test.txt.v' . $t1; + $v2 = $versionsFolder2 . '/test.txt.v' . $t2; + + $this->rootView->file_put_contents($v1, 'version1'); + $this->rootView->file_put_contents($v2, 'version2'); + + // move file into the shared folder as recipient + $success = Filesystem::rename('/test.txt', '/folder1/test.txt'); + + $this->assertTrue($success); + $this->assertFalse($this->rootView->file_exists($v1)); + $this->assertFalse($this->rootView->file_exists($v2)); + + self::loginHelper(self::TEST_VERSIONS_USER); + + $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions'; + + $v1Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t1; + $v2Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t2; + + $this->assertTrue($this->rootView->file_exists($v1Renamed)); + $this->assertTrue($this->rootView->file_exists($v2Renamed)); + + Server::get(\OCP\Share\IManager::class)->deleteShare($share); + } + + public function testMoveFolderIntoSharedFolderAsRecipient(): void { + Filesystem::mkdir('folder1'); + + $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1'); + $share = Server::get(\OCP\Share\IManager::class)->newShare(); + $share->setNode($node) + ->setShareType(IShare::TYPE_USER) + ->setSharedBy(self::TEST_VERSIONS_USER) + ->setSharedWith(self::TEST_VERSIONS_USER2) + ->setPermissions(Constants::PERMISSION_ALL); + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); + + self::loginHelper(self::TEST_VERSIONS_USER2); + $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions'; + Filesystem::mkdir('folder2'); + Filesystem::file_put_contents('folder2/test.txt', 'test file'); + + $t1 = time(); + // second version is two weeks older, this way we make sure that no + // version will be expired + $t2 = $t1 - 60 * 60 * 24 * 14; + + $this->rootView->mkdir($versionsFolder2); + $this->rootView->mkdir($versionsFolder2 . '/folder2'); + // create some versions + $v1 = $versionsFolder2 . '/folder2/test.txt.v' . $t1; + $v2 = $versionsFolder2 . '/folder2/test.txt.v' . $t2; + + $this->rootView->file_put_contents($v1, 'version1'); + $this->rootView->file_put_contents($v2, 'version2'); + + // move file into the shared folder as recipient + Filesystem::rename('/folder2', '/folder1/folder2'); + + $this->assertFalse($this->rootView->file_exists($v1)); + $this->assertFalse($this->rootView->file_exists($v2)); + + self::loginHelper(self::TEST_VERSIONS_USER); + + $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions'; + + $v1Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t1; + $v2Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t2; + + $this->assertTrue($this->rootView->file_exists($v1Renamed)); + $this->assertTrue($this->rootView->file_exists($v2Renamed)); + + Server::get(\OCP\Share\IManager::class)->deleteShare($share); + } + + public function testRenameSharedFile(): void { + Filesystem::file_put_contents('test.txt', 'test file'); + + $t1 = time(); + // second version is two weeks older, this way we make sure that no + // version will be expired + $t2 = $t1 - 60 * 60 * 24 * 14; + + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT); + // create some versions + $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; + $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; + // the renamed versions should not exist! Because we only moved the mount point! + $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; + $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; + + $this->rootView->file_put_contents($v1, 'version1'); + $this->rootView->file_put_contents($v2, 'version2'); + + $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('test.txt'); + $share = Server::get(\OCP\Share\IManager::class)->newShare(); + $share->setNode($node) + ->setShareType(IShare::TYPE_USER) + ->setSharedBy(self::TEST_VERSIONS_USER) + ->setSharedWith(self::TEST_VERSIONS_USER2) + ->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE | Constants::PERMISSION_SHARE); + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); + + self::loginHelper(self::TEST_VERSIONS_USER2); + + $this->assertTrue(Filesystem::file_exists('test.txt')); + + // execute rename hook of versions app + Filesystem::rename('test.txt', 'test2.txt'); + + self::loginHelper(self::TEST_VERSIONS_USER); + + $this->runCommands(); + + $this->assertTrue($this->rootView->file_exists($v1)); + $this->assertTrue($this->rootView->file_exists($v2)); + + $this->assertFalse($this->rootView->file_exists($v1Renamed)); + $this->assertFalse($this->rootView->file_exists($v2Renamed)); + + Server::get(\OCP\Share\IManager::class)->deleteShare($share); + } + + public function testCopy(): void { + Filesystem::file_put_contents('test.txt', 'test file'); + + $t1 = time(); + // second version is two weeks older, this way we make sure that no + // version will be expired + $t2 = $t1 - 60 * 60 * 24 * 14; + + // create some versions + $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; + $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; + $v1Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; + $v2Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; + + $this->rootView->file_put_contents($v1, 'version1'); + $this->rootView->file_put_contents($v2, 'version2'); + + // execute copy hook of versions app + Filesystem::copy('test.txt', 'test2.txt'); + + $this->runCommands(); + + $this->assertTrue($this->rootView->file_exists($v1), 'version 1 of original file exists'); + $this->assertTrue($this->rootView->file_exists($v2), 'version 2 of original file exists'); + + $this->assertTrue($this->rootView->file_exists($v1Copied), 'version 1 of copied file exists'); + $this->assertTrue($this->rootView->file_exists($v2Copied), 'version 2 of copied file exists'); + } + + /** + * test if we find all versions and if the versions array contain + * the correct 'path' and 'name' + */ + public function testGetVersions(): void { + $t1 = time(); + // second version is two weeks older, this way we make sure that no + // version will be expired + $t2 = $t1 - 60 * 60 * 24 * 14; + + // create some versions + $v1 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t1; + $v2 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t2; + + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/subfolder/'); + + $this->rootView->file_put_contents($v1, 'version1'); + $this->rootView->file_put_contents($v2, 'version2'); + + // execute copy hook of versions app + $versions = Storage::getVersions(self::TEST_VERSIONS_USER, '/subfolder/test.txt'); + + $this->assertCount(2, $versions); + + foreach ($versions as $version) { + $this->assertSame('/subfolder/test.txt', $version['path']); + $this->assertSame('test.txt', $version['name']); + } + + //cleanup + $this->rootView->deleteAll(self::USERS_VERSIONS_ROOT . '/subfolder'); + } + + /** + * test if we find all versions and if the versions array contain + * the correct 'path' and 'name' + */ + public function testGetVersionsEmptyFile(): void { + // execute copy hook of versions app + $versions = Storage::getVersions(self::TEST_VERSIONS_USER, ''); + $this->assertCount(0, $versions); + + $versions = Storage::getVersions(self::TEST_VERSIONS_USER, null); + $this->assertCount(0, $versions); + } + + public function testExpireNonexistingFile(): void { + $this->logout(); + // needed to have a FS setup (the background job does this) + \OC_Util::setupFS(self::TEST_VERSIONS_USER); + + $this->assertFalse(Storage::expire('/void/unexist.txt', self::TEST_VERSIONS_USER)); + } + + + public function testExpireNonexistingUser(): void { + $this->expectException(NoUserException::class); + + $this->logout(); + // needed to have a FS setup (the background job does this) + \OC_Util::setupFS(self::TEST_VERSIONS_USER); + Filesystem::file_put_contents('test.txt', 'test file'); + + $this->assertFalse(Storage::expire('test.txt', 'unexist')); + } + + public function testRestoreSameStorage(): void { + Filesystem::mkdir('sub'); + $this->doTestRestore(); + } + + public function testRestoreCrossStorage(): void { + $storage2 = new Temporary([]); + Filesystem::mount($storage2, [], self::TEST_VERSIONS_USER . '/files/sub'); + + $this->doTestRestore(); + } + + public function testRestoreNoPermission(): void { + $this->loginAsUser(self::TEST_VERSIONS_USER); + + $userHome = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER); + $node = $userHome->newFolder('folder'); + $file = $node->newFile('test.txt'); + + $share = Server::get(\OCP\Share\IManager::class)->newShare(); + $share->setNode($node) + ->setShareType(IShare::TYPE_USER) + ->setSharedBy(self::TEST_VERSIONS_USER) + ->setSharedWith(self::TEST_VERSIONS_USER2) + ->setPermissions(Constants::PERMISSION_READ); + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); + + $versions = $this->createAndCheckVersions( + Filesystem::getView(), + 'folder/test.txt' + ); + + $file->putContent('test file'); + + $this->loginAsUser(self::TEST_VERSIONS_USER2); + + $firstVersion = current($versions); + + $this->assertFalse(Storage::rollback('folder/test.txt', (int)$firstVersion['version'], $this->user2), 'Revert did not happen'); + + $this->loginAsUser(self::TEST_VERSIONS_USER); + + Server::get(\OCP\Share\IManager::class)->deleteShare($share); + $this->assertEquals('test file', $file->getContent(), 'File content has not changed'); + } + + public function testRestoreMovedShare(): void { + $this->markTestSkipped('Unreliable test'); + $this->loginAsUser(self::TEST_VERSIONS_USER); + + $userHome = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER); + $node = $userHome->newFolder('folder'); + $file = $node->newFile('test.txt'); + + $userHome2 = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER2); + $userHome2->newFolder('subfolder'); + + $share = Server::get(\OCP\Share\IManager::class)->newShare(); + $share->setNode($node) + ->setShareType(IShare::TYPE_USER) + ->setSharedBy(self::TEST_VERSIONS_USER) + ->setSharedWith(self::TEST_VERSIONS_USER2) + ->setPermissions(Constants::PERMISSION_ALL); + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); + $shareManager = Server::get(\OCP\Share\IManager::class); + $shareManager->acceptShare($share, self::TEST_VERSIONS_USER2); + + $share->setTarget('subfolder/folder'); + $shareManager->moveShare($share, self::TEST_VERSIONS_USER2); + + $versions = $this->createAndCheckVersions( + Filesystem::getView(), + 'folder/test.txt' + ); + + $file->putContent('test file'); + + $this->loginAsUser(self::TEST_VERSIONS_USER2); + + $firstVersion = current($versions); + + $this->assertTrue(Storage::rollback('folder/test.txt', $firstVersion['version'], $this->user1)); + + $this->loginAsUser(self::TEST_VERSIONS_USER); + + Server::get(\OCP\Share\IManager::class)->deleteShare($share); + $this->assertEquals('version 2', $file->getContent(), 'File content has not changed'); + } + + /** + * @param string $hookName name of hook called + * @param string $params variable to receive parameters provided by hook + */ + private function connectMockHooks($hookName, &$params) { + if ($hookName === null) { + return; + } + + $eventHandler = $this->getMockBuilder(DummyHookListener::class) + ->onlyMethods(['callback']) + ->getMock(); + + $eventHandler->expects($this->any()) + ->method('callback') + ->willReturnCallback( + function ($p) use (&$params): void { + $params = $p; + } + ); + + Util::connectHook( + '\OCP\Versions', + $hookName, + $eventHandler, + 'callback' + ); + } + + private function doTestRestore(): void { + $filePath = self::TEST_VERSIONS_USER . '/files/sub/test.txt'; + $this->rootView->file_put_contents($filePath, 'test file'); + + $fileInfo = $this->rootView->getFileInfo($filePath); + $t0 = $this->rootView->filemtime($filePath); + + // not exactly the same timestamp as the file + $t1 = time() - 60; + // second version is two weeks older + $t2 = $t1 - 60 * 60 * 24 * 14; + + // create some versions + $v1 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t1; + $v2 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t2; + + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/sub'); + + $this->rootView->file_put_contents($v1, 'version1'); + $fileInfoV1 = $this->rootView->getFileInfo($v1); + $versionEntity = new VersionEntity(); + $versionEntity->setFileId($fileInfo->getId()); + $versionEntity->setTimestamp($t1); + $versionEntity->setSize($fileInfoV1->getSize()); + $versionEntity->setMimetype($this->mimeTypeLoader->getId($fileInfoV1->getMimetype())); + $versionEntity->setMetadata([]); + $this->versionsMapper->insert($versionEntity); + + $this->rootView->file_put_contents($v2, 'version2'); + $fileInfoV2 = $this->rootView->getFileInfo($v2); + $versionEntity = new VersionEntity(); + $versionEntity->setFileId($fileInfo->getId()); + $versionEntity->setTimestamp($t2); + $versionEntity->setSize($fileInfoV2->getSize()); + $versionEntity->setMimetype($this->mimeTypeLoader->getId($fileInfoV2->getMimetype())); + $versionEntity->setMetadata([]); + $this->versionsMapper->insert($versionEntity); + + $oldVersions = Storage::getVersions( + self::TEST_VERSIONS_USER, '/sub/test.txt' + ); + + $this->assertCount(2, $oldVersions); + + $this->assertEquals('test file', $this->rootView->file_get_contents($filePath)); + $info1 = $this->rootView->getFileInfo($filePath); + + $eventDispatcher = Server::get(IEventDispatcher::class); + $eventFired = false; + $eventDispatcher->addListener(VersionRestoredEvent::class, function ($event) use (&$eventFired, $t2): void { + $eventFired = true; + $this->assertEquals('/sub/test.txt', $event->getVersion()->getVersionPath()); + $this->assertTrue($event->getVersion()->getRevisionId() > 0); + }); + + $versionManager = Server::get(IVersionManager::class); + $versions = $versionManager->getVersionsForFile($this->user1, $info1); + $version = array_filter($versions, function ($version) use ($t2) { + return $version->getRevisionId() === $t2; + }); + $this->assertTrue($versionManager->rollback(current($version))); + + $this->assertTrue($eventFired, 'VersionRestoredEvent was not fired'); + + $this->assertEquals('version2', $this->rootView->file_get_contents($filePath)); + $info2 = $this->rootView->getFileInfo($filePath); + + $this->assertNotEquals( + $info2['etag'], + $info1['etag'], + 'Etag must change after rolling back version' + ); + $this->assertEquals( + $info2['fileid'], + $info1['fileid'], + 'File id must not change after rolling back version' + ); + $this->assertEquals( + $info2['mtime'], + $t2, + 'Restored file has mtime from version' + ); + + $newVersions = Storage::getVersions( + self::TEST_VERSIONS_USER, '/sub/test.txt' + ); + + $this->assertTrue( + $this->rootView->file_exists(self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t0), + 'A version file was created for the file before restoration' + ); + $this->assertTrue( + $this->rootView->file_exists($v1), + 'Untouched version file is still there' + ); + $this->assertFalse( + $this->rootView->file_exists($v2), + 'Restored version file gone from files_version folder' + ); + + $this->assertCount(2, $newVersions, 'Additional version created'); + + $this->assertTrue( + isset($newVersions[$t0 . '#' . 'test.txt']), + 'A version was created for the file before restoration' + ); + $this->assertTrue( + isset($newVersions[$t1 . '#' . 'test.txt']), + 'Untouched version is still there' + ); + $this->assertFalse( + isset($newVersions[$t2 . '#' . 'test.txt']), + 'Restored version is not in the list any more' + ); + } + + /** + * Test whether versions are created when overwriting as owner + */ + public function testStoreVersionAsOwner(): void { + $this->loginAsUser(self::TEST_VERSIONS_USER); + + $this->createAndCheckVersions( + Filesystem::getView(), + 'test.txt' + ); + } + + /** + * Test whether versions are created when overwriting as share recipient + */ + public function testStoreVersionAsRecipient(): void { + $this->loginAsUser(self::TEST_VERSIONS_USER); + + Filesystem::mkdir('folder'); + Filesystem::file_put_contents('folder/test.txt', 'test file'); + + $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder'); + $share = Server::get(\OCP\Share\IManager::class)->newShare(); + $share->setNode($node) + ->setShareType(IShare::TYPE_USER) + ->setSharedBy(self::TEST_VERSIONS_USER) + ->setSharedWith(self::TEST_VERSIONS_USER2) + ->setPermissions(Constants::PERMISSION_ALL); + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); + + $this->loginAsUser(self::TEST_VERSIONS_USER2); + + $this->createAndCheckVersions( + Filesystem::getView(), + 'folder/test.txt' + ); + + Server::get(\OCP\Share\IManager::class)->deleteShare($share); + } + + /** + * Test whether versions are created when overwriting anonymously. + * + * When uploading through a public link or publicwebdav, no user + * is logged in. File modification must still be able to find + * the owner and create versions. + */ + public function testStoreVersionAsAnonymous(): void { + $this->logout(); + + // note: public link upload does this, + // needed to make the hooks fire + \OC_Util::setupFS(self::TEST_VERSIONS_USER); + + $userView = new View('/' . self::TEST_VERSIONS_USER . '/files'); + $this->createAndCheckVersions( + $userView, + 'test.txt' + ); + } + + private function createAndCheckVersions(View $view, string $path): array { + $view->file_put_contents($path, 'test file'); + $view->file_put_contents($path, 'version 1'); + $view->file_put_contents($path, 'version 2'); + + $this->loginAsUser(self::TEST_VERSIONS_USER); + + // need to scan for the versions + [$rootStorage,] = $this->rootView->resolvePath(self::TEST_VERSIONS_USER . '/files_versions'); + $rootStorage->getScanner()->scan('files_versions'); + + $versions = Storage::getVersions( + self::TEST_VERSIONS_USER, '/' . $path + ); + + // note: we cannot predict how many versions are created due to + // test run timing + $this->assertGreaterThan(0, count($versions)); + + return $versions; + } + + public static function loginHelper(string $user, bool $create = false) { + if ($create) { + $backend = new \Test\Util\User\Dummy(); + $backend->createUser($user, $user); + Server::get(IUserManager::class)->registerBackend($backend); + } + + \OC_Util::tearDownFS(); + \OC_User::setUserId(''); + Filesystem::tearDown(); + \OC_User::setUserId($user); + \OC_Util::setupFS($user); + \OC::$server->getUserFolder($user); + } +} + +class DummyHookListener { + public function callback() { + } +} + +// extend the original class to make it possible to test protected methods +class VersionStorageToTest extends Storage { + + /** + * @param integer $time + */ + public function callProtectedGetExpireList($time, $versions) { + return self::getExpireList($time, $versions); + } +} diff --git a/apps/files_versions/tests/Versions/VersionManagerTest.php b/apps/files_versions/tests/Versions/VersionManagerTest.php new file mode 100644 index 00000000000..79caa65d5f1 --- /dev/null +++ b/apps/files_versions/tests/Versions/VersionManagerTest.php @@ -0,0 +1,140 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Files_Versions\Tests\Versions; + +use OC\Files\Storage\Local; +use OCA\Files_Versions\Events\VersionRestoredEvent; +use OCA\Files_Versions\Versions\IVersion; +use OCA\Files_Versions\Versions\IVersionBackend; +use OCA\Files_Versions\Versions\VersionManager; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Storage\IStorage; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class VersionManagerTest extends TestCase { + private function getBackend(bool $shouldUse = true): IVersionBackend { + $backend = $this->createMock(IVersionBackend::class); + $backend->method('useBackendForStorage') + ->willReturn($shouldUse); + return $backend; + } + + private function getStorage(string $class): IStorage&MockObject { + return $this->getMockBuilder($class) + ->disableOriginalConstructor() + ->onlyMethods(array_diff(get_class_methods($class), ['instanceOfStorage'])) + ->getMock(); + } + + public function testGetBackendSingle(): void { + $dispatcher = $this->createMock(IEventDispatcher::class); + $manager = new VersionManager($dispatcher); + $backend = $this->getBackend(); + $manager->registerBackend(IStorage::class, $backend); + + $this->assertEquals($backend, $manager->getBackendForStorage($this->getStorage(Local::class))); + } + + public function testGetBackendMoreSpecific(): void { + $dispatcher = $this->createMock(IEventDispatcher::class); + $manager = new VersionManager($dispatcher); + $backend1 = $this->getBackend(); + $backend2 = $this->getBackend(); + $manager->registerBackend(IStorage::class, $backend1); + $manager->registerBackend(Local::class, $backend2); + + $this->assertEquals($backend2, $manager->getBackendForStorage($this->getStorage(Local::class))); + } + + public function testGetBackendNoUse(): void { + $dispatcher = $this->createMock(IEventDispatcher::class); + $manager = new VersionManager($dispatcher); + $backend1 = $this->getBackend(); + $backend2 = $this->getBackend(false); + $manager->registerBackend(IStorage::class, $backend1); + $manager->registerBackend(Local::class, $backend2); + + $this->assertEquals($backend1, $manager->getBackendForStorage($this->getStorage(Local::class))); + } + + public function testGetBackendMultiple(): void { + $dispatcher = $this->createMock(IEventDispatcher::class); + $manager = new VersionManager($dispatcher); + $backend1 = $this->getBackend(); + $backend2 = $this->getBackend(false); + $backend3 = $this->getBackend(); + $manager->registerBackend(IStorage::class, $backend1); + $manager->registerBackend(Local::class, $backend2); + $manager->registerBackend(Local::class, $backend3); + + $this->assertEquals($backend3, $manager->getBackendForStorage($this->getStorage(Local::class))); + } + + public function testRollbackSuccess(): void { + $versionMock = $this->createMock(IVersion::class); + $backendMock = $this->createMock(IVersionBackend::class); + + $backendMock->expects($this->once()) + ->method('rollback') + ->with($versionMock) + ->willReturn(true); + + $versionMock->method('getBackend')->willReturn($backendMock); + + $dispatcherMock = $this->createMock(IEventDispatcher::class); + $dispatcherMock->expects($this->once()) + ->method('dispatchTyped') + ->with($this->isInstanceOf(VersionRestoredEvent::class)); + + $manager = new VersionManager($dispatcherMock); + + $this->assertTrue($manager->rollback($versionMock)); + } + + public function testRollbackNull(): void { + $versionMock = $this->createMock(IVersion::class); + $backendMock = $this->createMock(IVersionBackend::class); + + $backendMock->expects($this->once()) + ->method('rollback') + ->with($versionMock) + ->willReturn(null); + + $versionMock->method('getBackend')->willReturn($backendMock); + + $dispatcherMock = $this->createMock(IEventDispatcher::class); + $dispatcherMock->expects($this->once()) + ->method('dispatchTyped') + ->with($this->isInstanceOf(VersionRestoredEvent::class)); + + $manager = new VersionManager($dispatcherMock); + + $this->assertNull($manager->rollback($versionMock)); + } + + public function testRollbackFailure(): void { + $versionMock = $this->createMock(IVersion::class); + $backendMock = $this->createMock(IVersionBackend::class); + + $backendMock->expects($this->once()) + ->method('rollback') + ->with($versionMock) + ->willReturn(false); + + $versionMock->method('getBackend')->willReturn($backendMock); + + $dispatcherMock = $this->createMock(IEventDispatcher::class); + $dispatcherMock->expects($this->never())->method('dispatchTyped'); + + $manager = new VersionManager($dispatcherMock); + + $this->assertFalse($manager->rollback($versionMock)); + } +} diff --git a/apps/files_versions/tests/command/cleanuptest.php b/apps/files_versions/tests/command/cleanuptest.php deleted file mode 100644 index af217b18893..00000000000 --- a/apps/files_versions/tests/command/cleanuptest.php +++ /dev/null @@ -1,170 +0,0 @@ -<?php -/** - * @author Björn Schießle <schiessle@owncloud.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - - -namespace OCA\Files_Versions\Tests\Command; - - -use OCA\Files_Versions\Command\CleanUp; -use Test\TestCase; -use OC\User\Manager; -use OCP\Files\IRootFolder; - -/** - * Class CleanupTest - * - * @group DB - * - * @package OCA\Files_Versions\Tests\Command - */ -class CleanupTest extends TestCase { - - /** @var CleanUp */ - protected $cleanup; - - /** @var \PHPUnit_Framework_MockObject_MockObject | Manager */ - protected $userManager; - - /** @var \PHPUnit_Framework_MockObject_MockObject | IRootFolder */ - protected $rootFolder; - - public function setUp() { - parent::setUp(); - - $this->rootFolder = $this->getMockBuilder('OCP\Files\IRootFolder') - ->disableOriginalConstructor()->getMock(); - $this->userManager = $this->getMockBuilder('OC\User\Manager') - ->disableOriginalConstructor()->getMock(); - - - $this->cleanup = new CleanUp($this->rootFolder, $this->userManager); - } - - /** - * @dataProvider dataTestDeleteVersions - * @param boolean $nodeExists - */ - public function testDeleteVersions($nodeExists) { - - $this->rootFolder->expects($this->once()) - ->method('nodeExists') - ->with('/testUser/files_versions') - ->willReturn($nodeExists); - - - if($nodeExists) { - $this->rootFolder->expects($this->once()) - ->method('get') - ->with('/testUser/files_versions') - ->willReturn($this->rootFolder); - $this->rootFolder->expects($this->once()) - ->method('delete'); - } else { - $this->rootFolder->expects($this->never()) - ->method('get'); - $this->rootFolder->expects($this->never()) - ->method('delete'); - } - - $this->invokePrivate($this->cleanup, 'deleteVersions', ['testUser']); - } - - public function dataTestDeleteVersions() { - return array( - array(true), - array(false) - ); - } - - - /** - * test delete versions from users given as parameter - */ - public function testExecuteDeleteListOfUsers() { - $userIds = ['user1', 'user2', 'user3']; - - $instance = $this->getMockBuilder('OCA\Files_Versions\Command\CleanUp') - ->setMethods(['deleteVersions']) - ->setConstructorArgs([$this->rootFolder, $this->userManager]) - ->getMock(); - $instance->expects($this->exactly(count($userIds))) - ->method('deleteVersions') - ->willReturnCallback(function ($user) use ($userIds) { - $this->assertTrue(in_array($user, $userIds)); - }); - - $this->userManager->expects($this->exactly(count($userIds))) - ->method('userExists')->willReturn(true); - - $inputInterface = $this->getMockBuilder('\Symfony\Component\Console\Input\InputInterface') - ->disableOriginalConstructor()->getMock(); - $inputInterface->expects($this->once())->method('getArgument') - ->with('user_id') - ->willReturn($userIds); - - $outputInterface = $this->getMockBuilder('\Symfony\Component\Console\Output\OutputInterface') - ->disableOriginalConstructor()->getMock(); - - $this->invokePrivate($instance, 'execute', [$inputInterface, $outputInterface]); - } - - /** - * test delete versions of all users - */ - public function testExecuteAllUsers() { - $userIds = []; - $backendUsers = ['user1', 'user2']; - - $instance = $this->getMockBuilder('OCA\Files_Versions\Command\CleanUp') - ->setMethods(['deleteVersions']) - ->setConstructorArgs([$this->rootFolder, $this->userManager]) - ->getMock(); - - $backend = $this->getMockBuilder('OC_User_Interface') - ->disableOriginalConstructor()->getMock(); - $backend->expects($this->once())->method('getUsers') - ->with('', 500, 0) - ->willReturn($backendUsers); - - $instance->expects($this->exactly(count($backendUsers))) - ->method('deleteVersions') - ->willReturnCallback(function ($user) use ($backendUsers) { - $this->assertTrue(in_array($user, $backendUsers)); - }); - - $inputInterface = $this->getMockBuilder('\Symfony\Component\Console\Input\InputInterface') - ->disableOriginalConstructor()->getMock(); - $inputInterface->expects($this->once())->method('getArgument') - ->with('user_id') - ->willReturn($userIds); - - $outputInterface = $this->getMockBuilder('\Symfony\Component\Console\Output\OutputInterface') - ->disableOriginalConstructor()->getMock(); - - $this->userManager->expects($this->once()) - ->method('getBackends') - ->willReturn([$backend]); - - $this->invokePrivate($instance, 'execute', [$inputInterface, $outputInterface]); - } - -} diff --git a/apps/files_versions/tests/command/expiretest.php b/apps/files_versions/tests/command/expiretest.php deleted file mode 100644 index f89ece82515..00000000000 --- a/apps/files_versions/tests/command/expiretest.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php -/** - * @author Joas Schilling <nickvergessen@owncloud.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OCA\Files_Versions\Tests\Command; - -use OCA\Files_Versions\Command\Expire; -use Test\TestCase; - -/** - * Class ExpireTest - * - * @group DB - * - * @package OCA\Files_Versions\Tests\Command - */ -class ExpireTest extends TestCase { - public function testExpireNonExistingUser() { - $command = new Expire($this->getUniqueID('test'), ''); - $command->handle(); - - $this->assertTrue(true); - } -} diff --git a/apps/files_versions/tests/expirationtest.php b/apps/files_versions/tests/expirationtest.php deleted file mode 100644 index 2dfff19f230..00000000000 --- a/apps/files_versions/tests/expirationtest.php +++ /dev/null @@ -1,206 +0,0 @@ -<?php -/** - * @author Joas Schilling <nickvergessen@owncloud.com> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OCA\Files_Versions\Tests; - -use \OCA\Files_Versions\Expiration; - -class Expiration_Test extends \Test\TestCase { - const SECONDS_PER_DAY = 86400; //60*60*24 - - public function expirationData(){ - $today = 100*self::SECONDS_PER_DAY; - $back10Days = (100-10)*self::SECONDS_PER_DAY; - $back20Days = (100-20)*self::SECONDS_PER_DAY; - $back30Days = (100-30)*self::SECONDS_PER_DAY; - $back35Days = (100-35)*self::SECONDS_PER_DAY; - - // it should never happen, but who knows :/ - $ahead100Days = (100+100)*self::SECONDS_PER_DAY; - - return [ - // Expiration is disabled - always should return false - [ 'disabled', $today, $back10Days, false, false], - [ 'disabled', $today, $back10Days, true, false], - [ 'disabled', $today, $ahead100Days, true, false], - - // Default: expire in 30 days or earlier when quota requirements are met - [ 'auto', $today, $back10Days, false, false], - [ 'auto', $today, $back35Days, false, false], - [ 'auto', $today, $back10Days, true, true], - [ 'auto', $today, $back35Days, true, true], - [ 'auto', $today, $ahead100Days, true, true], - - // The same with 'auto' - [ 'auto, auto', $today, $back10Days, false, false], - [ 'auto, auto', $today, $back35Days, false, false], - [ 'auto, auto', $today, $back10Days, true, true], - [ 'auto, auto', $today, $back35Days, true, true], - - // Keep for 15 days but expire anytime if space needed - [ '15, auto', $today, $back10Days, false, false], - [ '15, auto', $today, $back20Days, false, false], - [ '15, auto', $today, $back10Days, true, true], - [ '15, auto', $today, $back20Days, true, true], - [ '15, auto', $today, $ahead100Days, true, true], - - // Expire anytime if space needed, Expire all older than max - [ 'auto, 15', $today, $back10Days, false, false], - [ 'auto, 15', $today, $back20Days, false, true], - [ 'auto, 15', $today, $back10Days, true, true], - [ 'auto, 15', $today, $back20Days, true, true], - [ 'auto, 15', $today, $ahead100Days, true, true], - - // Expire all older than max OR older than min if space needed - [ '15, 25', $today, $back10Days, false, false], - [ '15, 25', $today, $back20Days, false, false], - [ '15, 25', $today, $back30Days, false, true], - [ '15, 25', $today, $back10Days, false, false], - [ '15, 25', $today, $back20Days, true, true], - [ '15, 25', $today, $back30Days, true, true], - [ '15, 25', $today, $ahead100Days, true, false], - - // Expire all older than max OR older than min if space needed - // Max<Min case - [ '25, 15', $today, $back10Days, false, false], - [ '25, 15', $today, $back20Days, false, false], - [ '25, 15', $today, $back30Days, false, true], - [ '25, 15', $today, $back10Days, false, false], - [ '25, 15', $today, $back20Days, true, false], - [ '25, 15', $today, $back30Days, true, true], - [ '25, 15', $today, $ahead100Days, true, false], - ]; - } - - /** - * @dataProvider expirationData - * - * @param string $retentionObligation - * @param int $timeNow - * @param int $timestamp - * @param bool $quotaExceeded - * @param string $expectedResult - */ - public function testExpiration($retentionObligation, $timeNow, $timestamp, $quotaExceeded, $expectedResult){ - $mockedConfig = $this->getMockedConfig($retentionObligation); - $mockedTimeFactory = $this->getMockedTimeFactory($timeNow); - - $expiration = new Expiration($mockedConfig, $mockedTimeFactory); - $actualResult = $expiration->isExpired($timestamp, $quotaExceeded); - - $this->assertEquals($expectedResult, $actualResult); - } - - - public function configData(){ - return [ - [ 'disabled', null, null, null], - [ 'auto', Expiration::NO_OBLIGATION, Expiration::NO_OBLIGATION, true ], - [ 'auto,auto', Expiration::NO_OBLIGATION, Expiration::NO_OBLIGATION, true ], - [ 'auto, auto', Expiration::NO_OBLIGATION, Expiration::NO_OBLIGATION, true ], - [ 'auto, 3', Expiration::NO_OBLIGATION, 3, true ], - [ '5, auto', 5, Expiration::NO_OBLIGATION, true ], - [ '3, 5', 3, 5, false ], - [ '10, 3', 10, 10, false ], - [ 'g,a,r,b,a,g,e', Expiration::NO_OBLIGATION, Expiration::NO_OBLIGATION, true ], - [ '-3,8', Expiration::NO_OBLIGATION, Expiration::NO_OBLIGATION, true ] - ]; - } - - - /** - * @dataProvider configData - * - * @param string $configValue - * @param int $expectedMinAge - * @param int $expectedMaxAge - * @param bool $expectedCanPurgeToSaveSpace - */ - public function testParseRetentionObligation($configValue, $expectedMinAge, $expectedMaxAge, $expectedCanPurgeToSaveSpace){ - $mockedConfig = $this->getMockedConfig($configValue); - $mockedTimeFactory = $this->getMockedTimeFactory( - time() - ); - - $expiration = new Expiration($mockedConfig, $mockedTimeFactory); - $this->assertAttributeEquals($expectedMinAge, 'minAge', $expiration); - $this->assertAttributeEquals($expectedMaxAge, 'maxAge', $expiration); - $this->assertAttributeEquals($expectedCanPurgeToSaveSpace, 'canPurgeToSaveSpace', $expiration); - } - - /** - * - * @param int $time - * @return \OCP\AppFramework\Utility\ITimeFactory - */ - private function getMockedTimeFactory($time){ - $mockedTimeFactory = $this->getMockBuilder('\OCP\AppFramework\Utility\ITimeFactory') - ->disableOriginalConstructor() - ->setMethods(['getTime']) - ->getMock() - ; - $mockedTimeFactory->expects($this->any())->method('getTime')->will( - $this->returnValue($time) - ); - - return $mockedTimeFactory; - } - - /** - * - * @param string $returnValue - * @return \OCP\IConfig - */ - private function getMockedConfig($returnValue){ - $mockedConfig = $this->getMockBuilder('\OCP\IConfig') - ->disableOriginalConstructor() - ->setMethods( - [ - 'setSystemValues', - 'setSystemValue', - 'getSystemValue', - 'getFilteredSystemValue', - 'deleteSystemValue', - 'getAppKeys', - 'setAppValue', - 'getAppValue', - 'deleteAppValue', - 'deleteAppValues', - 'setUserValue', - 'getUserValue', - 'getUserValueForUsers', - 'getUserKeys', - 'deleteUserValue', - 'deleteAllUserValues', - 'deleteAppFromAllUsers', - 'getUsersForUserValue' - ] - ) - ->getMock() - ; - $mockedConfig->expects($this->any())->method('getSystemValue')->will( - $this->returnValue($returnValue) - ); - - return $mockedConfig; - } -} diff --git a/apps/files_versions/tests/js/versioncollectionSpec.js b/apps/files_versions/tests/js/versioncollectionSpec.js deleted file mode 100644 index 87065fa1d36..00000000000 --- a/apps/files_versions/tests/js/versioncollectionSpec.js +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2015 - * - * This file is licensed under the Affero General Public License version 3 - * or later. - * - * See the COPYING-README file. - * - */ -describe('OCA.Versions.VersionCollection', function() { - var VersionCollection = OCA.Versions.VersionCollection; - var collection, fileInfoModel; - - beforeEach(function() { - fileInfoModel = new OCA.Files.FileInfoModel({ - path: '/subdir', - name: 'some file.txt' - }); - collection = new VersionCollection(); - collection.setFileInfo(fileInfoModel); - }); - it('fetches the next page', function() { - collection.fetchNext(); - - expect(fakeServer.requests.length).toEqual(1); - expect(fakeServer.requests[0].url).toEqual( - OC.generateUrl('apps/files_versions/ajax/getVersions.php') + - '?source=%2Fsubdir%2Fsome%20file.txt&start=0' - ); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({ - status: 'success', - data: { - endReached: false, - versions: [{ - version: 10000000, - size: 123, - name: 'some file.txt', - fullPath: '/subdir/some file.txt' - },{ - version: 15000000, - size: 150, - name: 'some file.txt', - path: '/subdir/some file.txt' - }] - } - }) - ); - - expect(collection.length).toEqual(2); - expect(collection.hasMoreResults()).toEqual(true); - - collection.fetchNext(); - - expect(fakeServer.requests.length).toEqual(2); - expect(fakeServer.requests[1].url).toEqual( - OC.generateUrl('apps/files_versions/ajax/getVersions.php') + - '?source=%2Fsubdir%2Fsome%20file.txt&start=2' - ); - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({ - status: 'success', - data: { - endReached: true, - versions: [{ - version: 18000000, - size: 123, - name: 'some file.txt', - path: '/subdir/some file.txt' - }] - } - }) - ); - - expect(collection.length).toEqual(3); - expect(collection.hasMoreResults()).toEqual(false); - - collection.fetchNext(); - - // no further requests - expect(fakeServer.requests.length).toEqual(2); - }); - it('properly parses the results', function() { - collection.fetchNext(); - - expect(fakeServer.requests.length).toEqual(1); - expect(fakeServer.requests[0].url).toEqual( - OC.generateUrl('apps/files_versions/ajax/getVersions.php') + - '?source=%2Fsubdir%2Fsome%20file.txt&start=0' - ); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({ - status: 'success', - data: { - endReached: false, - versions: [{ - version: 10000000, - size: 123, - name: 'some file.txt', - path: '/subdir/some file.txt' - },{ - version: 15000000, - size: 150, - name: 'some file.txt', - path: '/subdir/some file.txt' - }] - } - }) - ); - - expect(collection.length).toEqual(2); - - var model = collection.at(0); - expect(model.get('id')).toEqual(10000000); - expect(model.get('timestamp')).toEqual(10000000); - expect(model.get('name')).toEqual('some file.txt'); - expect(model.get('fullPath')).toEqual('/subdir/some file.txt'); - expect(model.get('size')).toEqual(123); - - model = collection.at(1); - expect(model.get('id')).toEqual(15000000); - expect(model.get('timestamp')).toEqual(15000000); - expect(model.get('name')).toEqual('some file.txt'); - expect(model.get('fullPath')).toEqual('/subdir/some file.txt'); - expect(model.get('size')).toEqual(150); - }); - it('resets page counted when setting a new file info model', function() { - collection.fetchNext(); - - expect(fakeServer.requests.length).toEqual(1); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({ - status: 'success', - data: { - endReached: true, - versions: [{ - version: 18000000, - size: 123, - name: 'some file.txt', - path: '/subdir/some file.txt' - }] - } - }) - ); - - expect(collection.hasMoreResults()).toEqual(false); - - collection.setFileInfo(fileInfoModel); - - expect(collection.hasMoreResults()).toEqual(true); - }); -}); - diff --git a/apps/files_versions/tests/js/versionmodelSpec.js b/apps/files_versions/tests/js/versionmodelSpec.js deleted file mode 100644 index 0f1c06581d5..00000000000 --- a/apps/files_versions/tests/js/versionmodelSpec.js +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2015 - * - * This file is licensed under the Affero General Public License version 3 - * or later. - * - * See the COPYING-README file. - * - */ -describe('OCA.Versions.VersionModel', function() { - var VersionModel = OCA.Versions.VersionModel; - var model; - - beforeEach(function() { - model = new VersionModel({ - id: 10000000, - timestamp: 10000000, - fullPath: '/subdir/some file.txt', - name: 'some file.txt', - size: 150 - }); - }); - - it('returns the full path', function() { - expect(model.getFullPath()).toEqual('/subdir/some file.txt'); - }); - it('returns the preview url', function() { - expect(model.getPreviewUrl()) - .toEqual(OC.generateUrl('/apps/files_versions/preview') + - '?file=%2Fsubdir%2Fsome%20file.txt&version=10000000' - ); - }); - it('returns the download url', function() { - expect(model.getDownloadUrl()) - .toEqual(OC.generateUrl('/apps/files_versions/download.php') + - '?file=%2Fsubdir%2Fsome%20file.txt&revision=10000000' - ); - }); - describe('reverting', function() { - var revertEventStub; - var successStub; - var errorStub; - - beforeEach(function() { - revertEventStub = sinon.stub(); - errorStub = sinon.stub(); - successStub = sinon.stub(); - - model.on('revert', revertEventStub); - model.on('error', errorStub); - }); - it('tells the server to revert when calling the revert method', function() { - model.revert({ - success: successStub - }); - - expect(fakeServer.requests.length).toEqual(1); - expect(fakeServer.requests[0].url) - .toEqual( - OC.generateUrl('/apps/files_versions/ajax/rollbackVersion.php') + - '?file=%2Fsubdir%2Fsome+file.txt&revision=10000000' - ); - - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({ - status: 'success', - }) - ); - - expect(revertEventStub.calledOnce).toEqual(true); - expect(successStub.calledOnce).toEqual(true); - expect(errorStub.notCalled).toEqual(true); - }); - it('triggers error event when server returns a failure', function() { - model.revert({ - success: successStub - }); - - expect(fakeServer.requests.length).toEqual(1); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({ - status: 'error', - }) - ); - - expect(revertEventStub.notCalled).toEqual(true); - expect(successStub.notCalled).toEqual(true); - expect(errorStub.calledOnce).toEqual(true); - }); - }); -}); - diff --git a/apps/files_versions/tests/js/versionstabviewSpec.js b/apps/files_versions/tests/js/versionstabviewSpec.js deleted file mode 100644 index 306dd66be2a..00000000000 --- a/apps/files_versions/tests/js/versionstabviewSpec.js +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2015 - * - * This file is licensed under the Affero General Public License version 3 - * or later. - * - * See the COPYING-README file. - * - */ -describe('OCA.Versions.VersionsTabView', function() { - var VersionCollection = OCA.Versions.VersionCollection; - var VersionModel = OCA.Versions.VersionModel; - var VersionsTabView = OCA.Versions.VersionsTabView; - - var fetchStub, fileInfoModel, tabView, testVersions, clock; - - beforeEach(function() { - clock = sinon.useFakeTimers(Date.UTC(2015, 6, 17, 1, 2, 0, 3)); - var time1 = Date.UTC(2015, 6, 17, 1, 2, 0, 3) / 1000; - var time2 = Date.UTC(2015, 6, 15, 1, 2, 0, 3) / 1000; - - var version1 = new VersionModel({ - id: time1, - timestamp: time1, - name: 'some file.txt', - size: 140, - fullPath: '/subdir/some file.txt' - }); - var version2 = new VersionModel({ - id: time2, - timestamp: time2, - name: 'some file.txt', - size: 150, - fullPath: '/subdir/some file.txt' - }); - - testVersions = [version1, version2]; - - fetchStub = sinon.stub(VersionCollection.prototype, 'fetch'); - fileInfoModel = new OCA.Files.FileInfoModel({ - id: 123, - name: 'test.txt' - }); - tabView = new VersionsTabView(); - tabView.render(); - }); - - afterEach(function() { - fetchStub.restore(); - tabView.remove(); - clock.restore(); - }); - - describe('rendering', function() { - it('reloads matching versions when setting file info model', function() { - tabView.setFileInfo(fileInfoModel); - expect(fetchStub.calledOnce).toEqual(true); - }); - - it('renders loading icon while fetching versions', function() { - tabView.setFileInfo(fileInfoModel); - tabView.collection.trigger('request'); - - expect(tabView.$el.find('.loading').length).toEqual(1); - expect(tabView.$el.find('.versions li').length).toEqual(0); - }); - - it('renders versions', function() { - - tabView.setFileInfo(fileInfoModel); - tabView.collection.set(testVersions); - - var version1 = testVersions[0]; - var version2 = testVersions[1]; - var $versions = tabView.$el.find('.versions>li'); - expect($versions.length).toEqual(2); - var $item = $versions.eq(0); - expect($item.find('.downloadVersion').attr('href')).toEqual(version1.getDownloadUrl()); - expect($item.find('.versiondate').text()).toEqual('seconds ago'); - expect($item.find('.revertVersion').length).toEqual(1); - expect($item.find('.preview').attr('src')).toEqual(version1.getPreviewUrl()); - - $item = $versions.eq(1); - expect($item.find('.downloadVersion').attr('href')).toEqual(version2.getDownloadUrl()); - expect($item.find('.versiondate').text()).toEqual('2 days ago'); - expect($item.find('.revertVersion').length).toEqual(1); - expect($item.find('.preview').attr('src')).toEqual(version2.getPreviewUrl()); - }); - }); - - describe('More versions', function() { - var hasMoreResultsStub; - - beforeEach(function() { - tabView.collection.set(testVersions); - hasMoreResultsStub = sinon.stub(VersionCollection.prototype, 'hasMoreResults'); - }); - afterEach(function() { - hasMoreResultsStub.restore(); - }); - - it('shows "More versions" button when more versions are available', function() { - hasMoreResultsStub.returns(true); - tabView.collection.trigger('sync'); - - expect(tabView.$el.find('.showMoreVersions').hasClass('hidden')).toEqual(false); - }); - it('does not show "More versions" button when more versions are available', function() { - hasMoreResultsStub.returns(false); - tabView.collection.trigger('sync'); - - expect(tabView.$el.find('.showMoreVersions').hasClass('hidden')).toEqual(true); - }); - it('fetches and appends the next page when clicking the "More" button', function() { - hasMoreResultsStub.returns(true); - - expect(fetchStub.notCalled).toEqual(true); - - tabView.$el.find('.showMoreVersions').click(); - - expect(fetchStub.calledOnce).toEqual(true); - }); - it('appends version to the list when added to collection', function() { - var time3 = Date.UTC(2015, 6, 10, 1, 0, 0, 0) / 1000; - - var version3 = new VersionModel({ - id: time3, - timestamp: time3, - name: 'some file.txt', - size: 54, - fullPath: '/subdir/some file.txt' - }); - - tabView.collection.add(version3); - - expect(tabView.$el.find('.versions>li').length).toEqual(3); - - var $item = tabView.$el.find('.versions>li').eq(2); - expect($item.find('.downloadVersion').attr('href')).toEqual(version3.getDownloadUrl()); - expect($item.find('.versiondate').text()).toEqual('7 days ago'); - expect($item.find('.revertVersion').length).toEqual(1); - expect($item.find('.preview').attr('src')).toEqual(version3.getPreviewUrl()); - }); - }); - - describe('Reverting', function() { - var revertStub; - - beforeEach(function() { - revertStub = sinon.stub(VersionModel.prototype, 'revert'); - tabView.setFileInfo(fileInfoModel); - tabView.collection.set(testVersions); - }); - - afterEach(function() { - revertStub.restore(); - }); - - it('tells the model to revert when clicking "Revert"', function() { - tabView.$el.find('.revertVersion').eq(1).click(); - - expect(revertStub.calledOnce).toEqual(true); - }); - it('triggers busy state during revert', function() { - var busyStub = sinon.stub(); - fileInfoModel.on('busy', busyStub); - - tabView.$el.find('.revertVersion').eq(1).click(); - - expect(busyStub.calledOnce).toEqual(true); - expect(busyStub.calledWith(fileInfoModel, true)).toEqual(true); - - busyStub.reset(); - revertStub.getCall(0).args[0].success(); - - expect(busyStub.calledOnce).toEqual(true); - expect(busyStub.calledWith(fileInfoModel, false)).toEqual(true); - }); - it('updates the file info model with the information from the reverted revision', function() { - var changeStub = sinon.stub(); - fileInfoModel.on('change', changeStub); - - tabView.$el.find('.revertVersion').eq(1).click(); - - expect(changeStub.notCalled).toEqual(true); - - revertStub.getCall(0).args[0].success(); - - expect(changeStub.calledOnce).toEqual(true); - var changes = changeStub.getCall(0).args[0].changed; - expect(changes.size).toEqual(150); - expect(changes.mtime).toEqual(testVersions[1].get('timestamp') * 1000); - expect(changes.etag).toBeDefined(); - }); - it('shows notification on revert error', function() { - var notificationStub = sinon.stub(OC.Notification, 'showTemporary'); - - tabView.$el.find('.revertVersion').eq(1).click(); - - revertStub.getCall(0).args[0].error(); - - expect(notificationStub.calledOnce).toEqual(true); - - notificationStub.restore(); - }); - }); -}); - diff --git a/apps/files_versions/tests/versions.php b/apps/files_versions/tests/versions.php deleted file mode 100644 index f6658e092bd..00000000000 --- a/apps/files_versions/tests/versions.php +++ /dev/null @@ -1,848 +0,0 @@ -<?php -/** - * @author Arthur Schiwon <blizzz@owncloud.com> - * @author Björn Schießle <schiessle@owncloud.com> - * @author Georg Ehrke <georg@owncloud.com> - * @author Joas Schilling <nickvergessen@owncloud.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@owncloud.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <icewind@owncloud.com> - * @author Roeland Jago Douma <rullzer@owncloud.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <pvince81@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -require_once __DIR__ . '/../appinfo/app.php'; - -use OC\Files\Storage\Temporary; - -/** - * Class Test_Files_versions - * this class provide basic files versions test - * - * @group DB - */ -class Test_Files_Versioning extends \Test\TestCase { - - const TEST_VERSIONS_USER = 'test-versions-user'; - const TEST_VERSIONS_USER2 = 'test-versions-user2'; - const USERS_VERSIONS_ROOT = '/test-versions-user/files_versions'; - - /** - * @var \OC\Files\View - */ - private $rootView; - - public static function setUpBeforeClass() { - parent::setUpBeforeClass(); - - $application = new \OCA\Files_Sharing\AppInfo\Application(); - $application->registerMountProviders(); - - // create test user - self::loginHelper(self::TEST_VERSIONS_USER2, true); - self::loginHelper(self::TEST_VERSIONS_USER, true); - } - - public static function tearDownAfterClass() { - // cleanup test user - $user = \OC::$server->getUserManager()->get(self::TEST_VERSIONS_USER); - if ($user !== null) { $user->delete(); } - $user = \OC::$server->getUserManager()->get(self::TEST_VERSIONS_USER2); - if ($user !== null) { $user->delete(); } - - parent::tearDownAfterClass(); - } - - protected function setUp() { - parent::setUp(); - - $config = \OC::$server->getConfig(); - $mockConfig = $this->getMock('\OCP\IConfig'); - $mockConfig->expects($this->any()) - ->method('getSystemValue') - ->will($this->returnCallback(function ($key, $default) use ($config) { - if ($key === 'filesystem_check_changes') { - return \OC\Files\Cache\Watcher::CHECK_ONCE; - } else { - return $config->getSystemValue($key, $default); - } - })); - $this->overwriteService('AllConfig', $mockConfig); - - // clear hooks - \OC_Hook::clear(); - \OC::registerShareHooks(); - \OCA\Files_Versions\Hooks::connectHooks(); - - self::loginHelper(self::TEST_VERSIONS_USER); - $this->rootView = new \OC\Files\View(); - if (!$this->rootView->file_exists(self::USERS_VERSIONS_ROOT)) { - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT); - } - } - - protected function tearDown() { - $this->restoreService('AllConfig'); - - if ($this->rootView) { - $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files/'); - $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files/'); - $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files_versions/'); - $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files_versions/'); - } - - \OC_Hook::clear(); - - parent::tearDown(); - } - - /** - * @medium - * test expire logic - * @dataProvider versionsProvider - */ - public function testGetExpireList($versions, $sizeOfAllDeletedFiles) { - - // last interval end at 2592000 - $startTime = 5000000; - - $testClass = new VersionStorageToTest(); - list($deleted, $size) = $testClass->callProtectedGetExpireList($startTime, $versions); - - // we should have deleted 16 files each of the size 1 - $this->assertEquals($sizeOfAllDeletedFiles, $size); - - // the deleted array should only contain versions which should be deleted - foreach($deleted as $key => $path) { - unset($versions[$key]); - $this->assertEquals("delete", substr($path, 0, strlen("delete"))); - } - - // the versions array should only contain versions which should be kept - foreach ($versions as $version) { - $this->assertEquals("keep", $version['path']); - } - - } - - public function versionsProvider() { - return array( - // first set of versions uniformly distributed versions - array( - array( - // first slice (10sec) keep one version every 2 seconds - array("version" => 4999999, "path" => "keep", "size" => 1), - array("version" => 4999998, "path" => "delete", "size" => 1), - array("version" => 4999997, "path" => "keep", "size" => 1), - array("version" => 4999995, "path" => "keep", "size" => 1), - array("version" => 4999994, "path" => "delete", "size" => 1), - //next slice (60sec) starts at 4999990 keep one version every 10 secons - array("version" => 4999988, "path" => "keep", "size" => 1), - array("version" => 4999978, "path" => "keep", "size" => 1), - array("version" => 4999975, "path" => "delete", "size" => 1), - array("version" => 4999972, "path" => "delete", "size" => 1), - array("version" => 4999967, "path" => "keep", "size" => 1), - array("version" => 4999958, "path" => "delete", "size" => 1), - array("version" => 4999957, "path" => "keep", "size" => 1), - //next slice (3600sec) start at 4999940 keep one version every 60 seconds - array("version" => 4999900, "path" => "keep", "size" => 1), - array("version" => 4999841, "path" => "delete", "size" => 1), - array("version" => 4999840, "path" => "keep", "size" => 1), - array("version" => 4999780, "path" => "keep", "size" => 1), - array("version" => 4996401, "path" => "keep", "size" => 1), - // next slice (86400sec) start at 4996400 keep one version every 3600 seconds - array("version" => 4996350, "path" => "delete", "size" => 1), - array("version" => 4992800, "path" => "keep", "size" => 1), - array("version" => 4989800, "path" => "delete", "size" => 1), - array("version" => 4989700, "path" => "delete", "size" => 1), - array("version" => 4989200, "path" => "keep", "size" => 1), - // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds - array("version" => 4913600, "path" => "keep", "size" => 1), - array("version" => 4852800, "path" => "delete", "size" => 1), - array("version" => 4827201, "path" => "delete", "size" => 1), - array("version" => 4827200, "path" => "keep", "size" => 1), - array("version" => 4777201, "path" => "delete", "size" => 1), - array("version" => 4777501, "path" => "delete", "size" => 1), - array("version" => 4740000, "path" => "keep", "size" => 1), - // final slice starts at 2408000 keep one version every 604800 secons - array("version" => 2408000, "path" => "keep", "size" => 1), - array("version" => 1803201, "path" => "delete", "size" => 1), - array("version" => 1803200, "path" => "keep", "size" => 1), - array("version" => 1800199, "path" => "delete", "size" => 1), - array("version" => 1800100, "path" => "delete", "size" => 1), - array("version" => 1198300, "path" => "keep", "size" => 1), - ), - 16 // size of all deleted files (every file has the size 1) - ), - // second set of versions, here we have only really old versions - array( - array( - // first slice (10sec) keep one version every 2 seconds - // next slice (60sec) starts at 4999990 keep one version every 10 secons - // next slice (3600sec) start at 4999940 keep one version every 60 seconds - // next slice (86400sec) start at 4996400 keep one version every 3600 seconds - array("version" => 4996400, "path" => "keep", "size" => 1), - array("version" => 4996350, "path" => "delete", "size" => 1), - array("version" => 4996350, "path" => "delete", "size" => 1), - array("version" => 4992800, "path" => "keep", "size" => 1), - array("version" => 4989800, "path" => "delete", "size" => 1), - array("version" => 4989700, "path" => "delete", "size" => 1), - array("version" => 4989200, "path" => "keep", "size" => 1), - // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds - array("version" => 4913600, "path" => "keep", "size" => 1), - array("version" => 4852800, "path" => "delete", "size" => 1), - array("version" => 4827201, "path" => "delete", "size" => 1), - array("version" => 4827200, "path" => "keep", "size" => 1), - array("version" => 4777201, "path" => "delete", "size" => 1), - array("version" => 4777501, "path" => "delete", "size" => 1), - array("version" => 4740000, "path" => "keep", "size" => 1), - // final slice starts at 2408000 keep one version every 604800 secons - array("version" => 2408000, "path" => "keep", "size" => 1), - array("version" => 1803201, "path" => "delete", "size" => 1), - array("version" => 1803200, "path" => "keep", "size" => 1), - array("version" => 1800199, "path" => "delete", "size" => 1), - array("version" => 1800100, "path" => "delete", "size" => 1), - array("version" => 1198300, "path" => "keep", "size" => 1), - ), - 11 // size of all deleted files (every file has the size 1) - ), - // third set of versions, with some gaps inbetween - array( - array( - // first slice (10sec) keep one version every 2 seconds - array("version" => 4999999, "path" => "keep", "size" => 1), - array("version" => 4999998, "path" => "delete", "size" => 1), - array("version" => 4999997, "path" => "keep", "size" => 1), - array("version" => 4999995, "path" => "keep", "size" => 1), - array("version" => 4999994, "path" => "delete", "size" => 1), - //next slice (60sec) starts at 4999990 keep one version every 10 secons - array("version" => 4999988, "path" => "keep", "size" => 1), - array("version" => 4999978, "path" => "keep", "size" => 1), - //next slice (3600sec) start at 4999940 keep one version every 60 seconds - // next slice (86400sec) start at 4996400 keep one version every 3600 seconds - array("version" => 4989200, "path" => "keep", "size" => 1), - // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds - array("version" => 4913600, "path" => "keep", "size" => 1), - array("version" => 4852800, "path" => "delete", "size" => 1), - array("version" => 4827201, "path" => "delete", "size" => 1), - array("version" => 4827200, "path" => "keep", "size" => 1), - array("version" => 4777201, "path" => "delete", "size" => 1), - array("version" => 4777501, "path" => "delete", "size" => 1), - array("version" => 4740000, "path" => "keep", "size" => 1), - // final slice starts at 2408000 keep one version every 604800 secons - array("version" => 2408000, "path" => "keep", "size" => 1), - array("version" => 1803201, "path" => "delete", "size" => 1), - array("version" => 1803200, "path" => "keep", "size" => 1), - array("version" => 1800199, "path" => "delete", "size" => 1), - array("version" => 1800100, "path" => "delete", "size" => 1), - array("version" => 1198300, "path" => "keep", "size" => 1), - ), - 9 // size of all deleted files (every file has the size 1) - ), - - ); - } - - public function testRename() { - - \OC\Files\Filesystem::file_put_contents("test.txt", "test file"); - - $t1 = time(); - // second version is two weeks older, this way we make sure that no - // version will be expired - $t2 = $t1 - 60 * 60 * 24 * 14; - - // create some versions - $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; - $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; - $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; - $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; - - $this->rootView->file_put_contents($v1, 'version1'); - $this->rootView->file_put_contents($v2, 'version2'); - - // execute rename hook of versions app - \OC\Files\Filesystem::rename("test.txt", "test2.txt"); - - $this->runCommands(); - - $this->assertFalse($this->rootView->file_exists($v1)); - $this->assertFalse($this->rootView->file_exists($v2)); - - $this->assertTrue($this->rootView->file_exists($v1Renamed)); - $this->assertTrue($this->rootView->file_exists($v2Renamed)); - } - - public function testRenameInSharedFolder() { - - \OC\Files\Filesystem::mkdir('folder1'); - \OC\Files\Filesystem::mkdir('folder1/folder2'); - \OC\Files\Filesystem::file_put_contents("folder1/test.txt", "test file"); - - $fileInfo = \OC\Files\Filesystem::getFileInfo('folder1'); - - $t1 = time(); - // second version is two weeks older, this way we make sure that no - // version will be expired - $t2 = $t1 - 60 * 60 * 24 * 14; - - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1'); - // create some versions - $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1; - $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2; - $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t1; - $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t2; - - $this->rootView->file_put_contents($v1, 'version1'); - $this->rootView->file_put_contents($v2, 'version2'); - - \OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, self::TEST_VERSIONS_USER2, \OCP\Constants::PERMISSION_ALL); - - self::loginHelper(self::TEST_VERSIONS_USER2); - - $this->assertTrue(\OC\Files\Filesystem::file_exists('folder1/test.txt')); - - // execute rename hook of versions app - \OC\Files\Filesystem::rename('/folder1/test.txt', '/folder1/folder2/test.txt'); - - $this->runCommands(); - - self::loginHelper(self::TEST_VERSIONS_USER); - - $this->assertFalse($this->rootView->file_exists($v1)); - $this->assertFalse($this->rootView->file_exists($v2)); - - $this->assertTrue($this->rootView->file_exists($v1Renamed)); - $this->assertTrue($this->rootView->file_exists($v2Renamed)); - } - - public function testMoveFolder() { - - \OC\Files\Filesystem::mkdir('folder1'); - \OC\Files\Filesystem::mkdir('folder2'); - \OC\Files\Filesystem::file_put_contents('folder1/test.txt', 'test file'); - - $t1 = time(); - // second version is two weeks older, this way we make sure that no - // version will be expired - $t2 = $t1 - 60 * 60 * 24 * 14; - - // create some versions - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1'); - $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1; - $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2; - $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t1; - $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t2; - - $this->rootView->file_put_contents($v1, 'version1'); - $this->rootView->file_put_contents($v2, 'version2'); - - // execute rename hook of versions app - \OC\Files\Filesystem::rename('folder1', 'folder2/folder1'); - - $this->runCommands(); - - $this->assertFalse($this->rootView->file_exists($v1)); - $this->assertFalse($this->rootView->file_exists($v2)); - - $this->assertTrue($this->rootView->file_exists($v1Renamed)); - $this->assertTrue($this->rootView->file_exists($v2Renamed)); - } - - - public function testMoveFileIntoSharedFolderAsRecipient() { - - \OC\Files\Filesystem::mkdir('folder1'); - $fileInfo = \OC\Files\Filesystem::getFileInfo('folder1'); - - \OCP\Share::shareItem( - 'folder', - $fileInfo['fileid'], - \OCP\Share::SHARE_TYPE_USER, - self::TEST_VERSIONS_USER2, - \OCP\Constants::PERMISSION_ALL - ); - - self::loginHelper(self::TEST_VERSIONS_USER2); - $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions'; - \OC\Files\Filesystem::file_put_contents('test.txt', 'test file'); - - $t1 = time(); - // second version is two weeks older, this way we make sure that no - // version will be expired - $t2 = $t1 - 60 * 60 * 24 * 14; - - $this->rootView->mkdir($versionsFolder2); - // create some versions - $v1 = $versionsFolder2 . '/test.txt.v' . $t1; - $v2 = $versionsFolder2 . '/test.txt.v' . $t2; - - $this->rootView->file_put_contents($v1, 'version1'); - $this->rootView->file_put_contents($v2, 'version2'); - - // move file into the shared folder as recipient - \OC\Files\Filesystem::rename('/test.txt', '/folder1/test.txt'); - - $this->assertFalse($this->rootView->file_exists($v1)); - $this->assertFalse($this->rootView->file_exists($v2)); - - self::loginHelper(self::TEST_VERSIONS_USER); - - $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions'; - - $v1Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t1; - $v2Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t2; - - $this->assertTrue($this->rootView->file_exists($v1Renamed)); - $this->assertTrue($this->rootView->file_exists($v2Renamed)); - } - - public function testMoveFolderIntoSharedFolderAsRecipient() { - - \OC\Files\Filesystem::mkdir('folder1'); - $fileInfo = \OC\Files\Filesystem::getFileInfo('folder1'); - - \OCP\Share::shareItem( - 'folder', - $fileInfo['fileid'], - \OCP\Share::SHARE_TYPE_USER, - self::TEST_VERSIONS_USER2, - \OCP\Constants::PERMISSION_ALL - ); - - self::loginHelper(self::TEST_VERSIONS_USER2); - $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions'; - \OC\Files\Filesystem::mkdir('folder2'); - \OC\Files\Filesystem::file_put_contents('folder2/test.txt', 'test file'); - - $t1 = time(); - // second version is two weeks older, this way we make sure that no - // version will be expired - $t2 = $t1 - 60 * 60 * 24 * 14; - - $this->rootView->mkdir($versionsFolder2); - $this->rootView->mkdir($versionsFolder2 . '/folder2'); - // create some versions - $v1 = $versionsFolder2 . '/folder2/test.txt.v' . $t1; - $v2 = $versionsFolder2 . '/folder2/test.txt.v' . $t2; - - $this->rootView->file_put_contents($v1, 'version1'); - $this->rootView->file_put_contents($v2, 'version2'); - - // move file into the shared folder as recipient - \OC\Files\Filesystem::rename('/folder2', '/folder1/folder2'); - - $this->assertFalse($this->rootView->file_exists($v1)); - $this->assertFalse($this->rootView->file_exists($v2)); - - self::loginHelper(self::TEST_VERSIONS_USER); - - $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions'; - - $v1Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t1; - $v2Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t2; - - $this->assertTrue($this->rootView->file_exists($v1Renamed)); - $this->assertTrue($this->rootView->file_exists($v2Renamed)); - } - - public function testRenameSharedFile() { - - \OC\Files\Filesystem::file_put_contents("test.txt", "test file"); - - $fileInfo = \OC\Files\Filesystem::getFileInfo('test.txt'); - - $t1 = time(); - // second version is two weeks older, this way we make sure that no - // version will be expired - $t2 = $t1 - 60 * 60 * 24 * 14; - - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT); - // create some versions - $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; - $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; - // the renamed versions should not exist! Because we only moved the mount point! - $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; - $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; - - $this->rootView->file_put_contents($v1, 'version1'); - $this->rootView->file_put_contents($v2, 'version2'); - - \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, self::TEST_VERSIONS_USER2, \OCP\Constants::PERMISSION_ALL); - - self::loginHelper(self::TEST_VERSIONS_USER2); - - $this->assertTrue(\OC\Files\Filesystem::file_exists('test.txt')); - - // execute rename hook of versions app - \OC\Files\Filesystem::rename('test.txt', 'test2.txt'); - - self::loginHelper(self::TEST_VERSIONS_USER); - - $this->runCommands(); - - $this->assertTrue($this->rootView->file_exists($v1)); - $this->assertTrue($this->rootView->file_exists($v2)); - - $this->assertFalse($this->rootView->file_exists($v1Renamed)); - $this->assertFalse($this->rootView->file_exists($v2Renamed)); - } - - public function testCopy() { - - \OC\Files\Filesystem::file_put_contents("test.txt", "test file"); - - $t1 = time(); - // second version is two weeks older, this way we make sure that no - // version will be expired - $t2 = $t1 - 60 * 60 * 24 * 14; - - // create some versions - $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; - $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; - $v1Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; - $v2Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; - - $this->rootView->file_put_contents($v1, 'version1'); - $this->rootView->file_put_contents($v2, 'version2'); - - // execute copy hook of versions app - \OC\Files\Filesystem::copy("test.txt", "test2.txt"); - - $this->runCommands(); - - $this->assertTrue($this->rootView->file_exists($v1)); - $this->assertTrue($this->rootView->file_exists($v2)); - - $this->assertTrue($this->rootView->file_exists($v1Copied)); - $this->assertTrue($this->rootView->file_exists($v2Copied)); - } - - /** - * test if we find all versions and if the versions array contain - * the correct 'path' and 'name' - */ - public function testGetVersions() { - - $t1 = time(); - // second version is two weeks older, this way we make sure that no - // version will be expired - $t2 = $t1 - 60 * 60 * 24 * 14; - - // create some versions - $v1 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t1; - $v2 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t2; - - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/subfolder/'); - - $this->rootView->file_put_contents($v1, 'version1'); - $this->rootView->file_put_contents($v2, 'version2'); - - // execute copy hook of versions app - $versions = \OCA\Files_Versions\Storage::getVersions(self::TEST_VERSIONS_USER, '/subfolder/test.txt'); - - $this->assertCount(2, $versions); - - foreach ($versions as $version) { - $this->assertSame('/subfolder/test.txt', $version['path']); - $this->assertSame('test.txt', $version['name']); - } - - //cleanup - $this->rootView->deleteAll(self::USERS_VERSIONS_ROOT . '/subfolder'); - } - - /** - * test if we find all versions and if the versions array contain - * the correct 'path' and 'name' - */ - public function testGetVersionsEmptyFile() { - // execute copy hook of versions app - $versions = \OCA\Files_Versions\Storage::getVersions(self::TEST_VERSIONS_USER, ''); - $this->assertCount(0, $versions); - - $versions = \OCA\Files_Versions\Storage::getVersions(self::TEST_VERSIONS_USER, null); - $this->assertCount(0, $versions); - } - - public function testExpireNonexistingFile() { - $this->logout(); - // needed to have a FS setup (the background job does this) - \OC_Util::setupFS(self::TEST_VERSIONS_USER); - - $this->assertFalse(\OCA\Files_Versions\Storage::expire('/void/unexist.txt')); - } - - public function testRestoreSameStorage() { - \OC\Files\Filesystem::mkdir('sub'); - $this->doTestRestore(); - } - - public function testRestoreCrossStorage() { - $storage2 = new Temporary(array()); - \OC\Files\Filesystem::mount($storage2, array(), self::TEST_VERSIONS_USER . '/files/sub'); - - $this->doTestRestore(); - } - - /** - * @param string $hookName name of hook called - * @param string $params variable to recieve parameters provided by hook - */ - private function connectMockHooks($hookName, &$params) { - if ($hookName === null) { - return; - } - - $eventHandler = $this->getMockBuilder('\stdclass') - ->setMethods(['callback']) - ->getMock(); - - $eventHandler->expects($this->any()) - ->method('callback') - ->will($this->returnCallback( - function($p) use (&$params) { - $params = $p; - } - )); - - \OCP\Util::connectHook( - '\OCP\Versions', - $hookName, - $eventHandler, - 'callback' - ); - } - - private function doTestRestore() { - $filePath = self::TEST_VERSIONS_USER . '/files/sub/test.txt'; - $this->rootView->file_put_contents($filePath, 'test file'); - - $t0 = $this->rootView->filemtime($filePath); - - // not exactly the same timestamp as the file - $t1 = time() - 60; - // second version is two weeks older - $t2 = $t1 - 60 * 60 * 24 * 14; - - // create some versions - $v1 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t1; - $v2 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t2; - - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/sub'); - $this->rootView->file_put_contents($v1, 'version1'); - $this->rootView->file_put_contents($v2, 'version2'); - - $oldVersions = \OCA\Files_Versions\Storage::getVersions( - self::TEST_VERSIONS_USER, '/sub/test.txt' - ); - - $this->assertCount(2, $oldVersions); - - $this->assertEquals('test file', $this->rootView->file_get_contents($filePath)); - $info1 = $this->rootView->getFileInfo($filePath); - - $params = array(); - $this->connectMockHooks('rollback', $params); - - \OCA\Files_Versions\Storage::rollback('sub/test.txt', $t2); - $expectedParams = array( - 'path' => '/sub/test.txt', - ); - - $this->assertEquals($expectedParams['path'], $params['path']); - $this->assertTrue(array_key_exists('revision', $params)); - $this->assertTrue($params['revision'] > 0); - - $this->assertEquals('version2', $this->rootView->file_get_contents($filePath)); - $info2 = $this->rootView->getFileInfo($filePath); - - $this->assertNotEquals( - $info2['etag'], - $info1['etag'], - 'Etag must change after rolling back version' - ); - $this->assertEquals( - $info2['fileid'], - $info1['fileid'], - 'File id must not change after rolling back version' - ); - $this->assertEquals( - $info2['mtime'], - $t2, - 'Restored file has mtime from version' - ); - - $newVersions = \OCA\Files_Versions\Storage::getVersions( - self::TEST_VERSIONS_USER, '/sub/test.txt' - ); - - $this->assertTrue( - $this->rootView->file_exists(self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t0), - 'A version file was created for the file before restoration' - ); - $this->assertTrue( - $this->rootView->file_exists($v1), - 'Untouched version file is still there' - ); - $this->assertFalse( - $this->rootView->file_exists($v2), - 'Restored version file gone from files_version folder' - ); - - $this->assertCount(2, $newVersions, 'Additional version created'); - - $this->assertTrue( - isset($newVersions[$t0 . '#' . 'test.txt']), - 'A version was created for the file before restoration' - ); - $this->assertTrue( - isset($newVersions[$t1 . '#' . 'test.txt']), - 'Untouched version is still there' - ); - $this->assertFalse( - isset($newVersions[$t2 . '#' . 'test.txt']), - 'Restored version is not in the list any more' - ); - } - - /** - * Test whether versions are created when overwriting as owner - */ - public function testStoreVersionAsOwner() { - $this->loginAsUser(self::TEST_VERSIONS_USER); - - $this->createAndCheckVersions( - \OC\Files\Filesystem::getView(), - 'test.txt' - ); - } - - /** - * Test whether versions are created when overwriting as share recipient - */ - public function testStoreVersionAsRecipient() { - $this->loginAsUser(self::TEST_VERSIONS_USER); - - \OC\Files\Filesystem::mkdir('folder'); - \OC\Files\Filesystem::file_put_contents('folder/test.txt', 'test file'); - $fileInfo = \OC\Files\Filesystem::getFileInfo('folder'); - - \OCP\Share::shareItem( - 'folder', - $fileInfo['fileid'], - \OCP\Share::SHARE_TYPE_USER, - self::TEST_VERSIONS_USER2, - \OCP\Constants::PERMISSION_ALL - ); - - $this->loginAsUser(self::TEST_VERSIONS_USER2); - - $this->createAndCheckVersions( - \OC\Files\Filesystem::getView(), - 'folder/test.txt' - ); - } - - /** - * Test whether versions are created when overwriting anonymously. - * - * When uploading through a public link or publicwebdav, no user - * is logged in. File modification must still be able to find - * the owner and create versions. - */ - public function testStoreVersionAsAnonymous() { - $this->logout(); - - // note: public link upload does this, - // needed to make the hooks fire - \OC_Util::setupFS(self::TEST_VERSIONS_USER); - - $userView = new \OC\Files\View('/' . self::TEST_VERSIONS_USER . '/files'); - $this->createAndCheckVersions( - $userView, - 'test.txt' - ); - } - - /** - * @param \OC\Files\View $view - * @param string $path - */ - private function createAndCheckVersions(\OC\Files\View $view, $path) { - $view->file_put_contents($path, 'test file'); - $view->file_put_contents($path, 'version 1'); - $view->file_put_contents($path, 'version 2'); - - $this->loginAsUser(self::TEST_VERSIONS_USER); - - // need to scan for the versions - list($rootStorage,) = $this->rootView->resolvePath(self::TEST_VERSIONS_USER . '/files_versions'); - $rootStorage->getScanner()->scan('files_versions'); - - $versions = \OCA\Files_Versions\Storage::getVersions( - self::TEST_VERSIONS_USER, '/' . $path - ); - - // note: we cannot predict how many versions are created due to - // test run timing - $this->assertGreaterThan(0, count($versions)); - } - - /** - * @param string $user - * @param bool $create - */ - public static function loginHelper($user, $create = false) { - - if ($create) { - $backend = new \Test\Util\User\Dummy(); - $backend->createUser($user, $user); - \OC::$server->getUserManager()->registerBackend($backend); - } - - $storage = new \ReflectionClass('\OC\Files\Storage\Shared'); - $isInitialized = $storage->getProperty('initialized'); - $isInitialized->setAccessible(true); - $isInitialized->setValue($storage, false); - $isInitialized->setAccessible(false); - - \OC_Util::tearDownFS(); - \OC_User::setUserId(''); - \OC\Files\Filesystem::tearDown(); - \OC_User::setUserId($user); - \OC_Util::setupFS($user); - \OC::$server->getUserFolder($user); - } - -} - -// extend the original class to make it possible to test protected methods -class VersionStorageToTest extends \OCA\Files_Versions\Storage { - - /** - * @param integer $time - */ - public function callProtectedGetExpireList($time, $versions) { - return self::getExpireList($time, $versions); - - } -} |