You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

922 lines
33 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
12 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. * @author Björn Schießle <bjoern@schiessle.org>
  7. * @author Georg Ehrke <oc.list@georgehrke.com>
  8. * @author Joas Schilling <coding@schilljs.com>
  9. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  10. * @author Lukas Reschke <lukas@statuscode.ch>
  11. * @author Morris Jobke <hey@morrisjobke.de>
  12. * @author Robin Appelman <robin@icewind.nl>
  13. * @author Roeland Jago Douma <roeland@famdouma.nl>
  14. * @author Stefan Weil <sw@weilnetz.de>
  15. * @author Thomas Müller <thomas.mueller@tmit.eu>
  16. * @author Vincent Petry <pvince81@owncloud.com>
  17. *
  18. * @license AGPL-3.0
  19. *
  20. * This code is free software: you can redistribute it and/or modify
  21. * it under the terms of the GNU Affero General Public License, version 3,
  22. * as published by the Free Software Foundation.
  23. *
  24. * This program is distributed in the hope that it will be useful,
  25. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  27. * GNU Affero General Public License for more details.
  28. *
  29. * You should have received a copy of the GNU Affero General Public License, version 3,
  30. * along with this program. If not, see <http://www.gnu.org/licenses/>
  31. *
  32. */
  33. namespace OCA\Files_Versions\Tests;
  34. require_once __DIR__ . '/../appinfo/app.php';
  35. use OC\Files\Storage\Temporary;
  36. use OCP\IConfig;
  37. /**
  38. * Class Test_Files_versions
  39. * this class provide basic files versions test
  40. *
  41. * @group DB
  42. */
  43. class VersioningTest extends \Test\TestCase {
  44. const TEST_VERSIONS_USER = 'test-versions-user';
  45. const TEST_VERSIONS_USER2 = 'test-versions-user2';
  46. const USERS_VERSIONS_ROOT = '/test-versions-user/files_versions';
  47. /**
  48. * @var \OC\Files\View
  49. */
  50. private $rootView;
  51. public static function setUpBeforeClass() {
  52. parent::setUpBeforeClass();
  53. $application = new \OCA\Files_Sharing\AppInfo\Application();
  54. $application->registerMountProviders();
  55. // create test user
  56. self::loginHelper(self::TEST_VERSIONS_USER2, true);
  57. self::loginHelper(self::TEST_VERSIONS_USER, true);
  58. }
  59. public static function tearDownAfterClass() {
  60. // cleanup test user
  61. $user = \OC::$server->getUserManager()->get(self::TEST_VERSIONS_USER);
  62. if ($user !== null) { $user->delete(); }
  63. $user = \OC::$server->getUserManager()->get(self::TEST_VERSIONS_USER2);
  64. if ($user !== null) { $user->delete(); }
  65. parent::tearDownAfterClass();
  66. }
  67. protected function setUp() {
  68. parent::setUp();
  69. $config = \OC::$server->getConfig();
  70. $mockConfig = $this->createMock(IConfig::class);
  71. $mockConfig->expects($this->any())
  72. ->method('getSystemValue')
  73. ->will($this->returnCallback(function ($key, $default) use ($config) {
  74. if ($key === 'filesystem_check_changes') {
  75. return \OC\Files\Cache\Watcher::CHECK_ONCE;
  76. } else {
  77. return $config->getSystemValue($key, $default);
  78. }
  79. }));
  80. $this->overwriteService('AllConfig', $mockConfig);
  81. // clear hooks
  82. \OC_Hook::clear();
  83. \OC::registerShareHooks();
  84. \OCA\Files_Versions\Hooks::connectHooks();
  85. self::loginHelper(self::TEST_VERSIONS_USER);
  86. $this->rootView = new \OC\Files\View();
  87. if (!$this->rootView->file_exists(self::USERS_VERSIONS_ROOT)) {
  88. $this->rootView->mkdir(self::USERS_VERSIONS_ROOT);
  89. }
  90. }
  91. protected function tearDown() {
  92. $this->restoreService('AllConfig');
  93. if ($this->rootView) {
  94. $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files/');
  95. $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files/');
  96. $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files_versions/');
  97. $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files_versions/');
  98. }
  99. \OC_Hook::clear();
  100. parent::tearDown();
  101. }
  102. /**
  103. * @medium
  104. * test expire logic
  105. * @dataProvider versionsProvider
  106. */
  107. public function testGetExpireList($versions, $sizeOfAllDeletedFiles) {
  108. // last interval end at 2592000
  109. $startTime = 5000000;
  110. $testClass = new VersionStorageToTest();
  111. list($deleted, $size) = $testClass->callProtectedGetExpireList($startTime, $versions);
  112. // we should have deleted 16 files each of the size 1
  113. $this->assertEquals($sizeOfAllDeletedFiles, $size);
  114. // the deleted array should only contain versions which should be deleted
  115. foreach($deleted as $key => $path) {
  116. unset($versions[$key]);
  117. $this->assertEquals("delete", substr($path, 0, strlen("delete")));
  118. }
  119. // the versions array should only contain versions which should be kept
  120. foreach ($versions as $version) {
  121. $this->assertEquals("keep", $version['path']);
  122. }
  123. }
  124. public function versionsProvider() {
  125. return array(
  126. // first set of versions uniformly distributed versions
  127. array(
  128. array(
  129. // first slice (10sec) keep one version every 2 seconds
  130. array("version" => 4999999, "path" => "keep", "size" => 1),
  131. array("version" => 4999998, "path" => "delete", "size" => 1),
  132. array("version" => 4999997, "path" => "keep", "size" => 1),
  133. array("version" => 4999995, "path" => "keep", "size" => 1),
  134. array("version" => 4999994, "path" => "delete", "size" => 1),
  135. //next slice (60sec) starts at 4999990 keep one version every 10 secons
  136. array("version" => 4999988, "path" => "keep", "size" => 1),
  137. array("version" => 4999978, "path" => "keep", "size" => 1),
  138. array("version" => 4999975, "path" => "delete", "size" => 1),
  139. array("version" => 4999972, "path" => "delete", "size" => 1),
  140. array("version" => 4999967, "path" => "keep", "size" => 1),
  141. array("version" => 4999958, "path" => "delete", "size" => 1),
  142. array("version" => 4999957, "path" => "keep", "size" => 1),
  143. //next slice (3600sec) start at 4999940 keep one version every 60 seconds
  144. array("version" => 4999900, "path" => "keep", "size" => 1),
  145. array("version" => 4999841, "path" => "delete", "size" => 1),
  146. array("version" => 4999840, "path" => "keep", "size" => 1),
  147. array("version" => 4999780, "path" => "keep", "size" => 1),
  148. array("version" => 4996401, "path" => "keep", "size" => 1),
  149. // next slice (86400sec) start at 4996400 keep one version every 3600 seconds
  150. array("version" => 4996350, "path" => "delete", "size" => 1),
  151. array("version" => 4992800, "path" => "keep", "size" => 1),
  152. array("version" => 4989800, "path" => "delete", "size" => 1),
  153. array("version" => 4989700, "path" => "delete", "size" => 1),
  154. array("version" => 4989200, "path" => "keep", "size" => 1),
  155. // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds
  156. array("version" => 4913600, "path" => "keep", "size" => 1),
  157. array("version" => 4852800, "path" => "delete", "size" => 1),
  158. array("version" => 4827201, "path" => "delete", "size" => 1),
  159. array("version" => 4827200, "path" => "keep", "size" => 1),
  160. array("version" => 4777201, "path" => "delete", "size" => 1),
  161. array("version" => 4777501, "path" => "delete", "size" => 1),
  162. array("version" => 4740000, "path" => "keep", "size" => 1),
  163. // final slice starts at 2408000 keep one version every 604800 secons
  164. array("version" => 2408000, "path" => "keep", "size" => 1),
  165. array("version" => 1803201, "path" => "delete", "size" => 1),
  166. array("version" => 1803200, "path" => "keep", "size" => 1),
  167. array("version" => 1800199, "path" => "delete", "size" => 1),
  168. array("version" => 1800100, "path" => "delete", "size" => 1),
  169. array("version" => 1198300, "path" => "keep", "size" => 1),
  170. ),
  171. 16 // size of all deleted files (every file has the size 1)
  172. ),
  173. // second set of versions, here we have only really old versions
  174. array(
  175. array(
  176. // first slice (10sec) keep one version every 2 seconds
  177. // next slice (60sec) starts at 4999990 keep one version every 10 secons
  178. // next slice (3600sec) start at 4999940 keep one version every 60 seconds
  179. // next slice (86400sec) start at 4996400 keep one version every 3600 seconds
  180. array("version" => 4996400, "path" => "keep", "size" => 1),
  181. array("version" => 4996350, "path" => "delete", "size" => 1),
  182. array("version" => 4996350, "path" => "delete", "size" => 1),
  183. array("version" => 4992800, "path" => "keep", "size" => 1),
  184. array("version" => 4989800, "path" => "delete", "size" => 1),
  185. array("version" => 4989700, "path" => "delete", "size" => 1),
  186. array("version" => 4989200, "path" => "keep", "size" => 1),
  187. // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds
  188. array("version" => 4913600, "path" => "keep", "size" => 1),
  189. array("version" => 4852800, "path" => "delete", "size" => 1),
  190. array("version" => 4827201, "path" => "delete", "size" => 1),
  191. array("version" => 4827200, "path" => "keep", "size" => 1),
  192. array("version" => 4777201, "path" => "delete", "size" => 1),
  193. array("version" => 4777501, "path" => "delete", "size" => 1),
  194. array("version" => 4740000, "path" => "keep", "size" => 1),
  195. // final slice starts at 2408000 keep one version every 604800 secons
  196. array("version" => 2408000, "path" => "keep", "size" => 1),
  197. array("version" => 1803201, "path" => "delete", "size" => 1),
  198. array("version" => 1803200, "path" => "keep", "size" => 1),
  199. array("version" => 1800199, "path" => "delete", "size" => 1),
  200. array("version" => 1800100, "path" => "delete", "size" => 1),
  201. array("version" => 1198300, "path" => "keep", "size" => 1),
  202. ),
  203. 11 // size of all deleted files (every file has the size 1)
  204. ),
  205. // third set of versions, with some gaps between
  206. array(
  207. array(
  208. // first slice (10sec) keep one version every 2 seconds
  209. array("version" => 4999999, "path" => "keep", "size" => 1),
  210. array("version" => 4999998, "path" => "delete", "size" => 1),
  211. array("version" => 4999997, "path" => "keep", "size" => 1),
  212. array("version" => 4999995, "path" => "keep", "size" => 1),
  213. array("version" => 4999994, "path" => "delete", "size" => 1),
  214. //next slice (60sec) starts at 4999990 keep one version every 10 secons
  215. array("version" => 4999988, "path" => "keep", "size" => 1),
  216. array("version" => 4999978, "path" => "keep", "size" => 1),
  217. //next slice (3600sec) start at 4999940 keep one version every 60 seconds
  218. // next slice (86400sec) start at 4996400 keep one version every 3600 seconds
  219. array("version" => 4989200, "path" => "keep", "size" => 1),
  220. // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds
  221. array("version" => 4913600, "path" => "keep", "size" => 1),
  222. array("version" => 4852800, "path" => "delete", "size" => 1),
  223. array("version" => 4827201, "path" => "delete", "size" => 1),
  224. array("version" => 4827200, "path" => "keep", "size" => 1),
  225. array("version" => 4777201, "path" => "delete", "size" => 1),
  226. array("version" => 4777501, "path" => "delete", "size" => 1),
  227. array("version" => 4740000, "path" => "keep", "size" => 1),
  228. // final slice starts at 2408000 keep one version every 604800 secons
  229. array("version" => 2408000, "path" => "keep", "size" => 1),
  230. array("version" => 1803201, "path" => "delete", "size" => 1),
  231. array("version" => 1803200, "path" => "keep", "size" => 1),
  232. array("version" => 1800199, "path" => "delete", "size" => 1),
  233. array("version" => 1800100, "path" => "delete", "size" => 1),
  234. array("version" => 1198300, "path" => "keep", "size" => 1),
  235. ),
  236. 9 // size of all deleted files (every file has the size 1)
  237. ),
  238. );
  239. }
  240. public function testRename() {
  241. \OC\Files\Filesystem::file_put_contents("test.txt", "test file");
  242. $t1 = time();
  243. // second version is two weeks older, this way we make sure that no
  244. // version will be expired
  245. $t2 = $t1 - 60 * 60 * 24 * 14;
  246. // create some versions
  247. $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
  248. $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
  249. $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
  250. $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
  251. $this->rootView->file_put_contents($v1, 'version1');
  252. $this->rootView->file_put_contents($v2, 'version2');
  253. // execute rename hook of versions app
  254. \OC\Files\Filesystem::rename("test.txt", "test2.txt");
  255. $this->runCommands();
  256. $this->assertFalse($this->rootView->file_exists($v1), 'version 1 of old file does not exist');
  257. $this->assertFalse($this->rootView->file_exists($v2), 'version 2 of old file does not exist');
  258. $this->assertTrue($this->rootView->file_exists($v1Renamed), 'version 1 of renamed file exists');
  259. $this->assertTrue($this->rootView->file_exists($v2Renamed), 'version 2 of renamed file exists');
  260. }
  261. public function testRenameInSharedFolder() {
  262. \OC\Files\Filesystem::mkdir('folder1');
  263. \OC\Files\Filesystem::mkdir('folder1/folder2');
  264. \OC\Files\Filesystem::file_put_contents("folder1/test.txt", "test file");
  265. $t1 = time();
  266. // second version is two weeks older, this way we make sure that no
  267. // version will be expired
  268. $t2 = $t1 - 60 * 60 * 24 * 14;
  269. $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1');
  270. // create some versions
  271. $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1;
  272. $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2;
  273. $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t1;
  274. $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t2;
  275. $this->rootView->file_put_contents($v1, 'version1');
  276. $this->rootView->file_put_contents($v2, 'version2');
  277. $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1');
  278. $share = \OC::$server->getShareManager()->newShare();
  279. $share->setNode($node)
  280. ->setShareType(\OCP\Share::SHARE_TYPE_USER)
  281. ->setSharedBy(self::TEST_VERSIONS_USER)
  282. ->setSharedWith(self::TEST_VERSIONS_USER2)
  283. ->setPermissions(\OCP\Constants::PERMISSION_ALL);
  284. $share = \OC::$server->getShareManager()->createShare($share);
  285. self::loginHelper(self::TEST_VERSIONS_USER2);
  286. $this->assertTrue(\OC\Files\Filesystem::file_exists('folder1/test.txt'));
  287. // execute rename hook of versions app
  288. \OC\Files\Filesystem::rename('/folder1/test.txt', '/folder1/folder2/test.txt');
  289. $this->runCommands();
  290. self::loginHelper(self::TEST_VERSIONS_USER);
  291. $this->assertFalse($this->rootView->file_exists($v1), 'version 1 of old file does not exist');
  292. $this->assertFalse($this->rootView->file_exists($v2), 'version 2 of old file does not exist');
  293. $this->assertTrue($this->rootView->file_exists($v1Renamed), 'version 1 of renamed file exists');
  294. $this->assertTrue($this->rootView->file_exists($v2Renamed), 'version 2 of renamed file exists');
  295. \OC::$server->getShareManager()->deleteShare($share);
  296. }
  297. public function testMoveFolder() {
  298. \OC\Files\Filesystem::mkdir('folder1');
  299. \OC\Files\Filesystem::mkdir('folder2');
  300. \OC\Files\Filesystem::file_put_contents('folder1/test.txt', 'test file');
  301. $t1 = time();
  302. // second version is two weeks older, this way we make sure that no
  303. // version will be expired
  304. $t2 = $t1 - 60 * 60 * 24 * 14;
  305. // create some versions
  306. $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1');
  307. $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1;
  308. $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2;
  309. $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t1;
  310. $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t2;
  311. $this->rootView->file_put_contents($v1, 'version1');
  312. $this->rootView->file_put_contents($v2, 'version2');
  313. // execute rename hook of versions app
  314. \OC\Files\Filesystem::rename('folder1', 'folder2/folder1');
  315. $this->runCommands();
  316. $this->assertFalse($this->rootView->file_exists($v1));
  317. $this->assertFalse($this->rootView->file_exists($v2));
  318. $this->assertTrue($this->rootView->file_exists($v1Renamed));
  319. $this->assertTrue($this->rootView->file_exists($v2Renamed));
  320. }
  321. public function testMoveFileIntoSharedFolderAsRecipient() {
  322. \OC\Files\Filesystem::mkdir('folder1');
  323. $fileInfo = \OC\Files\Filesystem::getFileInfo('folder1');
  324. $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1');
  325. $share = \OC::$server->getShareManager()->newShare();
  326. $share->setNode($node)
  327. ->setShareType(\OCP\Share::SHARE_TYPE_USER)
  328. ->setSharedBy(self::TEST_VERSIONS_USER)
  329. ->setSharedWith(self::TEST_VERSIONS_USER2)
  330. ->setPermissions(\OCP\Constants::PERMISSION_ALL);
  331. $share = \OC::$server->getShareManager()->createShare($share);
  332. self::loginHelper(self::TEST_VERSIONS_USER2);
  333. $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions';
  334. \OC\Files\Filesystem::file_put_contents('test.txt', 'test file');
  335. $t1 = time();
  336. // second version is two weeks older, this way we make sure that no
  337. // version will be expired
  338. $t2 = $t1 - 60 * 60 * 24 * 14;
  339. $this->rootView->mkdir($versionsFolder2);
  340. // create some versions
  341. $v1 = $versionsFolder2 . '/test.txt.v' . $t1;
  342. $v2 = $versionsFolder2 . '/test.txt.v' . $t2;
  343. $this->rootView->file_put_contents($v1, 'version1');
  344. $this->rootView->file_put_contents($v2, 'version2');
  345. // move file into the shared folder as recipient
  346. \OC\Files\Filesystem::rename('/test.txt', '/folder1/test.txt');
  347. $this->assertFalse($this->rootView->file_exists($v1));
  348. $this->assertFalse($this->rootView->file_exists($v2));
  349. self::loginHelper(self::TEST_VERSIONS_USER);
  350. $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions';
  351. $v1Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t1;
  352. $v2Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t2;
  353. $this->assertTrue($this->rootView->file_exists($v1Renamed));
  354. $this->assertTrue($this->rootView->file_exists($v2Renamed));
  355. \OC::$server->getShareManager()->deleteShare($share);
  356. }
  357. public function testMoveFolderIntoSharedFolderAsRecipient() {
  358. \OC\Files\Filesystem::mkdir('folder1');
  359. $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1');
  360. $share = \OC::$server->getShareManager()->newShare();
  361. $share->setNode($node)
  362. ->setShareType(\OCP\Share::SHARE_TYPE_USER)
  363. ->setSharedBy(self::TEST_VERSIONS_USER)
  364. ->setSharedWith(self::TEST_VERSIONS_USER2)
  365. ->setPermissions(\OCP\Constants::PERMISSION_ALL);
  366. $share = \OC::$server->getShareManager()->createShare($share);
  367. self::loginHelper(self::TEST_VERSIONS_USER2);
  368. $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions';
  369. \OC\Files\Filesystem::mkdir('folder2');
  370. \OC\Files\Filesystem::file_put_contents('folder2/test.txt', 'test file');
  371. $t1 = time();
  372. // second version is two weeks older, this way we make sure that no
  373. // version will be expired
  374. $t2 = $t1 - 60 * 60 * 24 * 14;
  375. $this->rootView->mkdir($versionsFolder2);
  376. $this->rootView->mkdir($versionsFolder2 . '/folder2');
  377. // create some versions
  378. $v1 = $versionsFolder2 . '/folder2/test.txt.v' . $t1;
  379. $v2 = $versionsFolder2 . '/folder2/test.txt.v' . $t2;
  380. $this->rootView->file_put_contents($v1, 'version1');
  381. $this->rootView->file_put_contents($v2, 'version2');
  382. // move file into the shared folder as recipient
  383. \OC\Files\Filesystem::rename('/folder2', '/folder1/folder2');
  384. $this->assertFalse($this->rootView->file_exists($v1));
  385. $this->assertFalse($this->rootView->file_exists($v2));
  386. self::loginHelper(self::TEST_VERSIONS_USER);
  387. $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions';
  388. $v1Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t1;
  389. $v2Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t2;
  390. $this->assertTrue($this->rootView->file_exists($v1Renamed));
  391. $this->assertTrue($this->rootView->file_exists($v2Renamed));
  392. \OC::$server->getShareManager()->deleteShare($share);
  393. }
  394. public function testRenameSharedFile() {
  395. \OC\Files\Filesystem::file_put_contents("test.txt", "test file");
  396. $t1 = time();
  397. // second version is two weeks older, this way we make sure that no
  398. // version will be expired
  399. $t2 = $t1 - 60 * 60 * 24 * 14;
  400. $this->rootView->mkdir(self::USERS_VERSIONS_ROOT);
  401. // create some versions
  402. $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
  403. $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
  404. // the renamed versions should not exist! Because we only moved the mount point!
  405. $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
  406. $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
  407. $this->rootView->file_put_contents($v1, 'version1');
  408. $this->rootView->file_put_contents($v2, 'version2');
  409. $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('test.txt');
  410. $share = \OC::$server->getShareManager()->newShare();
  411. $share->setNode($node)
  412. ->setShareType(\OCP\Share::SHARE_TYPE_USER)
  413. ->setSharedBy(self::TEST_VERSIONS_USER)
  414. ->setSharedWith(self::TEST_VERSIONS_USER2)
  415. ->setPermissions(\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_SHARE);
  416. $share = \OC::$server->getShareManager()->createShare($share);
  417. self::loginHelper(self::TEST_VERSIONS_USER2);
  418. $this->assertTrue(\OC\Files\Filesystem::file_exists('test.txt'));
  419. // execute rename hook of versions app
  420. \OC\Files\Filesystem::rename('test.txt', 'test2.txt');
  421. self::loginHelper(self::TEST_VERSIONS_USER);
  422. $this->runCommands();
  423. $this->assertTrue($this->rootView->file_exists($v1));
  424. $this->assertTrue($this->rootView->file_exists($v2));
  425. $this->assertFalse($this->rootView->file_exists($v1Renamed));
  426. $this->assertFalse($this->rootView->file_exists($v2Renamed));
  427. \OC::$server->getShareManager()->deleteShare($share);
  428. }
  429. public function testCopy() {
  430. \OC\Files\Filesystem::file_put_contents("test.txt", "test file");
  431. $t1 = time();
  432. // second version is two weeks older, this way we make sure that no
  433. // version will be expired
  434. $t2 = $t1 - 60 * 60 * 24 * 14;
  435. // create some versions
  436. $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
  437. $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
  438. $v1Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
  439. $v2Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
  440. $this->rootView->file_put_contents($v1, 'version1');
  441. $this->rootView->file_put_contents($v2, 'version2');
  442. // execute copy hook of versions app
  443. \OC\Files\Filesystem::copy("test.txt", "test2.txt");
  444. $this->runCommands();
  445. $this->assertTrue($this->rootView->file_exists($v1), 'version 1 of original file exists');
  446. $this->assertTrue($this->rootView->file_exists($v2), 'version 2 of original file exists');
  447. $this->assertTrue($this->rootView->file_exists($v1Copied), 'version 1 of copied file exists');
  448. $this->assertTrue($this->rootView->file_exists($v2Copied), 'version 2 of copied file exists');
  449. }
  450. /**
  451. * test if we find all versions and if the versions array contain
  452. * the correct 'path' and 'name'
  453. */
  454. public function testGetVersions() {
  455. $t1 = time();
  456. // second version is two weeks older, this way we make sure that no
  457. // version will be expired
  458. $t2 = $t1 - 60 * 60 * 24 * 14;
  459. // create some versions
  460. $v1 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t1;
  461. $v2 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t2;
  462. $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/subfolder/');
  463. $this->rootView->file_put_contents($v1, 'version1');
  464. $this->rootView->file_put_contents($v2, 'version2');
  465. // execute copy hook of versions app
  466. $versions = \OCA\Files_Versions\Storage::getVersions(self::TEST_VERSIONS_USER, '/subfolder/test.txt');
  467. $this->assertCount(2, $versions);
  468. foreach ($versions as $version) {
  469. $this->assertSame('/subfolder/test.txt', $version['path']);
  470. $this->assertSame('test.txt', $version['name']);
  471. }
  472. //cleanup
  473. $this->rootView->deleteAll(self::USERS_VERSIONS_ROOT . '/subfolder');
  474. }
  475. /**
  476. * test if we find all versions and if the versions array contain
  477. * the correct 'path' and 'name'
  478. */
  479. public function testGetVersionsEmptyFile() {
  480. // execute copy hook of versions app
  481. $versions = \OCA\Files_Versions\Storage::getVersions(self::TEST_VERSIONS_USER, '');
  482. $this->assertCount(0, $versions);
  483. $versions = \OCA\Files_Versions\Storage::getVersions(self::TEST_VERSIONS_USER, null);
  484. $this->assertCount(0, $versions);
  485. }
  486. public function testExpireNonexistingFile() {
  487. $this->logout();
  488. // needed to have a FS setup (the background job does this)
  489. \OC_Util::setupFS(self::TEST_VERSIONS_USER);
  490. $this->assertFalse(\OCA\Files_Versions\Storage::expire('/void/unexist.txt', self::TEST_VERSIONS_USER));
  491. }
  492. /**
  493. * @expectedException \OC\User\NoUserException
  494. */
  495. public function testExpireNonexistingUser() {
  496. $this->logout();
  497. // needed to have a FS setup (the background job does this)
  498. \OC_Util::setupFS(self::TEST_VERSIONS_USER);
  499. \OC\Files\Filesystem::file_put_contents("test.txt", "test file");
  500. $this->assertFalse(\OCA\Files_Versions\Storage::expire('test.txt', 'unexist'));
  501. }
  502. public function testRestoreSameStorage() {
  503. \OC\Files\Filesystem::mkdir('sub');
  504. $this->doTestRestore();
  505. }
  506. public function testRestoreCrossStorage() {
  507. $storage2 = new Temporary(array());
  508. \OC\Files\Filesystem::mount($storage2, array(), self::TEST_VERSIONS_USER . '/files/sub');
  509. $this->doTestRestore();
  510. }
  511. public function testRestoreNoPermission() {
  512. $this->loginAsUser(self::TEST_VERSIONS_USER);
  513. $userHome = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER);
  514. $node = $userHome->newFolder('folder');
  515. $file = $node->newFile('test.txt');
  516. $share = \OC::$server->getShareManager()->newShare();
  517. $share->setNode($node)
  518. ->setShareType(\OCP\Share::SHARE_TYPE_USER)
  519. ->setSharedBy(self::TEST_VERSIONS_USER)
  520. ->setSharedWith(self::TEST_VERSIONS_USER2)
  521. ->setPermissions(\OCP\Constants::PERMISSION_READ);
  522. $share = \OC::$server->getShareManager()->createShare($share);
  523. $versions = $this->createAndCheckVersions(
  524. \OC\Files\Filesystem::getView(),
  525. 'folder/test.txt'
  526. );
  527. $file->putContent('test file');
  528. $this->loginAsUser(self::TEST_VERSIONS_USER2);
  529. $firstVersion = current($versions);
  530. $this->assertFalse(\OCA\Files_Versions\Storage::rollback('folder/test.txt', $firstVersion['version']), 'Revert did not happen');
  531. $this->loginAsUser(self::TEST_VERSIONS_USER);
  532. \OC::$server->getShareManager()->deleteShare($share);
  533. $this->assertEquals('test file', $file->getContent(), 'File content has not changed');
  534. }
  535. /**
  536. * @param string $hookName name of hook called
  537. * @param string $params variable to receive parameters provided by hook
  538. */
  539. private function connectMockHooks($hookName, &$params) {
  540. if ($hookName === null) {
  541. return;
  542. }
  543. $eventHandler = $this->getMockBuilder(\stdclass::class)
  544. ->setMethods(['callback'])
  545. ->getMock();
  546. $eventHandler->expects($this->any())
  547. ->method('callback')
  548. ->will($this->returnCallback(
  549. function($p) use (&$params) {
  550. $params = $p;
  551. }
  552. ));
  553. \OCP\Util::connectHook(
  554. '\OCP\Versions',
  555. $hookName,
  556. $eventHandler,
  557. 'callback'
  558. );
  559. }
  560. private function doTestRestore() {
  561. $filePath = self::TEST_VERSIONS_USER . '/files/sub/test.txt';
  562. $this->rootView->file_put_contents($filePath, 'test file');
  563. $t0 = $this->rootView->filemtime($filePath);
  564. // not exactly the same timestamp as the file
  565. $t1 = time() - 60;
  566. // second version is two weeks older
  567. $t2 = $t1 - 60 * 60 * 24 * 14;
  568. // create some versions
  569. $v1 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t1;
  570. $v2 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t2;
  571. $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/sub');
  572. $this->rootView->file_put_contents($v1, 'version1');
  573. $this->rootView->file_put_contents($v2, 'version2');
  574. $oldVersions = \OCA\Files_Versions\Storage::getVersions(
  575. self::TEST_VERSIONS_USER, '/sub/test.txt'
  576. );
  577. $this->assertCount(2, $oldVersions);
  578. $this->assertEquals('test file', $this->rootView->file_get_contents($filePath));
  579. $info1 = $this->rootView->getFileInfo($filePath);
  580. $params = array();
  581. $this->connectMockHooks('rollback', $params);
  582. $this->assertTrue(\OCA\Files_Versions\Storage::rollback('sub/test.txt', $t2));
  583. $expectedParams = array(
  584. 'path' => '/sub/test.txt',
  585. );
  586. $this->assertEquals($expectedParams['path'], $params['path']);
  587. $this->assertTrue(array_key_exists('revision', $params));
  588. $this->assertTrue($params['revision'] > 0);
  589. $this->assertEquals('version2', $this->rootView->file_get_contents($filePath));
  590. $info2 = $this->rootView->getFileInfo($filePath);
  591. $this->assertNotEquals(
  592. $info2['etag'],
  593. $info1['etag'],
  594. 'Etag must change after rolling back version'
  595. );
  596. $this->assertEquals(
  597. $info2['fileid'],
  598. $info1['fileid'],
  599. 'File id must not change after rolling back version'
  600. );
  601. $this->assertEquals(
  602. $info2['mtime'],
  603. $t2,
  604. 'Restored file has mtime from version'
  605. );
  606. $newVersions = \OCA\Files_Versions\Storage::getVersions(
  607. self::TEST_VERSIONS_USER, '/sub/test.txt'
  608. );
  609. $this->assertTrue(
  610. $this->rootView->file_exists(self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t0),
  611. 'A version file was created for the file before restoration'
  612. );
  613. $this->assertTrue(
  614. $this->rootView->file_exists($v1),
  615. 'Untouched version file is still there'
  616. );
  617. $this->assertFalse(
  618. $this->rootView->file_exists($v2),
  619. 'Restored version file gone from files_version folder'
  620. );
  621. $this->assertCount(2, $newVersions, 'Additional version created');
  622. $this->assertTrue(
  623. isset($newVersions[$t0 . '#' . 'test.txt']),
  624. 'A version was created for the file before restoration'
  625. );
  626. $this->assertTrue(
  627. isset($newVersions[$t1 . '#' . 'test.txt']),
  628. 'Untouched version is still there'
  629. );
  630. $this->assertFalse(
  631. isset($newVersions[$t2 . '#' . 'test.txt']),
  632. 'Restored version is not in the list any more'
  633. );
  634. }
  635. /**
  636. * Test whether versions are created when overwriting as owner
  637. */
  638. public function testStoreVersionAsOwner() {
  639. $this->loginAsUser(self::TEST_VERSIONS_USER);
  640. $this->createAndCheckVersions(
  641. \OC\Files\Filesystem::getView(),
  642. 'test.txt'
  643. );
  644. }
  645. /**
  646. * Test whether versions are created when overwriting as share recipient
  647. */
  648. public function testStoreVersionAsRecipient() {
  649. $this->loginAsUser(self::TEST_VERSIONS_USER);
  650. \OC\Files\Filesystem::mkdir('folder');
  651. \OC\Files\Filesystem::file_put_contents('folder/test.txt', 'test file');
  652. $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder');
  653. $share = \OC::$server->getShareManager()->newShare();
  654. $share->setNode($node)
  655. ->setShareType(\OCP\Share::SHARE_TYPE_USER)
  656. ->setSharedBy(self::TEST_VERSIONS_USER)
  657. ->setSharedWith(self::TEST_VERSIONS_USER2)
  658. ->setPermissions(\OCP\Constants::PERMISSION_ALL);
  659. $share = \OC::$server->getShareManager()->createShare($share);
  660. $this->loginAsUser(self::TEST_VERSIONS_USER2);
  661. $this->createAndCheckVersions(
  662. \OC\Files\Filesystem::getView(),
  663. 'folder/test.txt'
  664. );
  665. \OC::$server->getShareManager()->deleteShare($share);
  666. }
  667. /**
  668. * Test whether versions are created when overwriting anonymously.
  669. *
  670. * When uploading through a public link or publicwebdav, no user
  671. * is logged in. File modification must still be able to find
  672. * the owner and create versions.
  673. */
  674. public function testStoreVersionAsAnonymous() {
  675. $this->logout();
  676. // note: public link upload does this,
  677. // needed to make the hooks fire
  678. \OC_Util::setupFS(self::TEST_VERSIONS_USER);
  679. $userView = new \OC\Files\View('/' . self::TEST_VERSIONS_USER . '/files');
  680. $this->createAndCheckVersions(
  681. $userView,
  682. 'test.txt'
  683. );
  684. }
  685. /**
  686. * @param \OC\Files\View $view
  687. * @param string $path
  688. */
  689. private function createAndCheckVersions(\OC\Files\View $view, $path) {
  690. $view->file_put_contents($path, 'test file');
  691. $view->file_put_contents($path, 'version 1');
  692. $view->file_put_contents($path, 'version 2');
  693. $this->loginAsUser(self::TEST_VERSIONS_USER);
  694. // need to scan for the versions
  695. list($rootStorage,) = $this->rootView->resolvePath(self::TEST_VERSIONS_USER . '/files_versions');
  696. $rootStorage->getScanner()->scan('files_versions');
  697. $versions = \OCA\Files_Versions\Storage::getVersions(
  698. self::TEST_VERSIONS_USER, '/' . $path
  699. );
  700. // note: we cannot predict how many versions are created due to
  701. // test run timing
  702. $this->assertGreaterThan(0, count($versions));
  703. return $versions;
  704. }
  705. /**
  706. * @param string $user
  707. * @param bool $create
  708. */
  709. public static function loginHelper($user, $create = false) {
  710. if ($create) {
  711. $backend = new \Test\Util\User\Dummy();
  712. $backend->createUser($user, $user);
  713. \OC::$server->getUserManager()->registerBackend($backend);
  714. }
  715. $storage = new \ReflectionClass('\OCA\Files_Sharing\SharedStorage');
  716. $isInitialized = $storage->getProperty('initialized');
  717. $isInitialized->setAccessible(true);
  718. $isInitialized->setValue($storage, false);
  719. $isInitialized->setAccessible(false);
  720. \OC_Util::tearDownFS();
  721. \OC_User::setUserId('');
  722. \OC\Files\Filesystem::tearDown();
  723. \OC_User::setUserId($user);
  724. \OC_Util::setupFS($user);
  725. \OC::$server->getUserFolder($user);
  726. }
  727. }
  728. // extend the original class to make it possible to test protected methods
  729. class VersionStorageToTest extends \OCA\Files_Versions\Storage {
  730. /**
  731. * @param integer $time
  732. */
  733. public function callProtectedGetExpireList($time, $versions) {
  734. return self::getExpireList($time, $versions);
  735. }
  736. }