TrashbinTest.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. use OC\AllConfig;
  8. use OC\AppFramework\Bootstrap\BootContext;
  9. use OC\AppFramework\DependencyInjection\DIContainer;
  10. use OC\Files\Cache\Watcher;
  11. use OC\Files\Filesystem;
  12. use OC\Files\Storage\Local;
  13. use OC\Files\View;
  14. use OCA\Files_Sharing\AppInfo\Application;
  15. use OCA\Files_Trashbin\AppInfo\Application as TrashbinApplication;
  16. use OCA\Files_Trashbin\Expiration;
  17. use OCA\Files_Trashbin\Helper;
  18. use OCA\Files_Trashbin\Trashbin;
  19. use OCP\AppFramework\Utility\ITimeFactory;
  20. use OCP\Constants;
  21. use OCP\Files\FileInfo;
  22. use OCP\IConfig;
  23. use OCP\Share\IShare;
  24. /**
  25. * Class Test_Encryption
  26. *
  27. * @group DB
  28. */
  29. class TrashbinTest extends \Test\TestCase {
  30. public const TEST_TRASHBIN_USER1 = 'test-trashbin-user1';
  31. public const TEST_TRASHBIN_USER2 = 'test-trashbin-user2';
  32. private $trashRoot1;
  33. private $trashRoot2;
  34. private static $rememberRetentionObligation;
  35. /**
  36. * @var bool
  37. */
  38. private static $trashBinStatus;
  39. /**
  40. * @var View
  41. */
  42. private $rootView;
  43. public static function setUpBeforeClass(): void {
  44. parent::setUpBeforeClass();
  45. $appManager = \OC::$server->getAppManager();
  46. self::$trashBinStatus = $appManager->isEnabledForUser('files_trashbin');
  47. // reset backend
  48. \OC_User::clearBackends();
  49. \OC_User::useBackend('database');
  50. // clear share hooks
  51. \OC_Hook::clear('OCP\\Share');
  52. \OC::registerShareHooks(\OC::$server->getSystemConfig());
  53. // init files sharing
  54. new Application();
  55. //disable encryption
  56. \OC::$server->getAppManager()->disableApp('encryption');
  57. $config = \OC::$server->getConfig();
  58. //configure trashbin
  59. self::$rememberRetentionObligation = $config->getSystemValue('trashbin_retention_obligation', Expiration::DEFAULT_RETENTION_OBLIGATION);
  60. /** @var Expiration $expiration */
  61. $expiration = \OC::$server->query(Expiration::class);
  62. $expiration->setRetentionObligation('auto, 2');
  63. // register trashbin hooks
  64. $trashbinApp = new TrashbinApplication();
  65. $trashbinApp->boot(new BootContext(new DIContainer('', [], \OC::$server)));
  66. // create test user
  67. self::loginHelper(self::TEST_TRASHBIN_USER2, true);
  68. self::loginHelper(self::TEST_TRASHBIN_USER1, true);
  69. }
  70. public static function tearDownAfterClass(): void {
  71. // cleanup test user
  72. $user = \OC::$server->getUserManager()->get(self::TEST_TRASHBIN_USER1);
  73. if ($user !== null) {
  74. $user->delete();
  75. }
  76. /** @var Expiration $expiration */
  77. $expiration = \OC::$server->query(Expiration::class);
  78. $expiration->setRetentionObligation(self::$rememberRetentionObligation);
  79. \OC_Hook::clear();
  80. Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
  81. if (self::$trashBinStatus) {
  82. \OC::$server->getAppManager()->enableApp('files_trashbin');
  83. }
  84. parent::tearDownAfterClass();
  85. }
  86. protected function setUp(): void {
  87. parent::setUp();
  88. \OC::$server->getAppManager()->enableApp('files_trashbin');
  89. $config = \OC::$server->getConfig();
  90. $mockConfig = $this->createMock(IConfig::class);
  91. $mockConfig
  92. ->method('getSystemValue')
  93. ->willReturnCallback(static function ($key, $default) use ($config) {
  94. if ($key === 'filesystem_check_changes') {
  95. return Watcher::CHECK_ONCE;
  96. } else {
  97. return $config->getSystemValue($key, $default);
  98. }
  99. });
  100. $mockConfig
  101. ->method('getUserValue')
  102. ->willReturnCallback(static function ($userId, $appName, $key, $default = '') use ($config) {
  103. return $config->getUserValue($userId, $appName, $key, $default);
  104. });
  105. $mockConfig
  106. ->method('getAppValue')
  107. ->willReturnCallback(static function ($appName, $key, $default = '') use ($config) {
  108. return $config->getAppValue($appName, $key, $default);
  109. });
  110. $this->overwriteService(AllConfig::class, $mockConfig);
  111. $this->trashRoot1 = '/' . self::TEST_TRASHBIN_USER1 . '/files_trashbin';
  112. $this->trashRoot2 = '/' . self::TEST_TRASHBIN_USER2 . '/files_trashbin';
  113. $this->rootView = new View();
  114. self::loginHelper(self::TEST_TRASHBIN_USER1);
  115. }
  116. protected function tearDown(): void {
  117. $this->restoreService(AllConfig::class);
  118. // disable trashbin to be able to properly clean up
  119. \OC::$server->getAppManager()->disableApp('files_trashbin');
  120. $this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER1 . '/files');
  121. $this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER2 . '/files');
  122. $this->rootView->deleteAll($this->trashRoot1);
  123. $this->rootView->deleteAll($this->trashRoot2);
  124. // clear trash table
  125. $connection = \OC::$server->getDatabaseConnection();
  126. $connection->executeUpdate('DELETE FROM `*PREFIX*files_trash`');
  127. parent::tearDown();
  128. }
  129. /**
  130. * test expiration of files older then the max storage time defined for the trash
  131. */
  132. public function testExpireOldFiles(): void {
  133. /** @var ITimeFactory $time */
  134. $time = \OC::$server->query(ITimeFactory::class);
  135. $currentTime = $time->getTime();
  136. $expireAt = $currentTime - 2 * 24 * 60 * 60;
  137. $expiredDate = $currentTime - 3 * 24 * 60 * 60;
  138. // create some files
  139. Filesystem::file_put_contents('file1.txt', 'file1');
  140. Filesystem::file_put_contents('file2.txt', 'file2');
  141. Filesystem::file_put_contents('file3.txt', 'file3');
  142. // delete them so that they end up in the trash bin
  143. Filesystem::unlink('file1.txt');
  144. Filesystem::unlink('file2.txt');
  145. Filesystem::unlink('file3.txt');
  146. //make sure that files are in the trash bin
  147. $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name');
  148. $this->assertSame(3, count($filesInTrash));
  149. // every second file will get a date in the past so that it will get expired
  150. $manipulatedList = $this->manipulateDeleteTime($filesInTrash, $this->trashRoot1, $expiredDate);
  151. $testClass = new TrashbinForTesting();
  152. [$sizeOfDeletedFiles, $count] = $testClass->dummyDeleteExpiredFiles($manipulatedList, $expireAt);
  153. $this->assertSame(10, $sizeOfDeletedFiles);
  154. $this->assertSame(2, $count);
  155. // only file2.txt should be left
  156. $remainingFiles = array_slice($manipulatedList, $count);
  157. $this->assertSame(1, count($remainingFiles));
  158. $remainingFile = reset($remainingFiles);
  159. // TODO: failing test
  160. #$this->assertSame('file2.txt', $remainingFile['name']);
  161. // check that file1.txt and file3.txt was really deleted
  162. $newTrashContent = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1);
  163. $this->assertSame(1, count($newTrashContent));
  164. $element = reset($newTrashContent);
  165. // TODO: failing test
  166. #$this->assertSame('file2.txt', $element['name']);
  167. }
  168. /**
  169. * test expiration of files older then the max storage time defined for the trash
  170. * in this test we delete a shared file and check if both trash bins, the one from
  171. * the owner of the file and the one from the user who deleted the file get expired
  172. * correctly
  173. */
  174. public function testExpireOldFilesShared(): void {
  175. $currentTime = time();
  176. $folder = 'trashTest-' . $currentTime . '/';
  177. $expiredDate = $currentTime - 3 * 24 * 60 * 60;
  178. // create some files
  179. Filesystem::mkdir($folder);
  180. Filesystem::file_put_contents($folder . 'user1-1.txt', 'file1');
  181. Filesystem::file_put_contents($folder . 'user1-2.txt', 'file2');
  182. Filesystem::file_put_contents($folder . 'user1-3.txt', 'file3');
  183. Filesystem::file_put_contents($folder . 'user1-4.txt', 'file4');
  184. //share user1-4.txt with user2
  185. $node = \OC::$server->getUserFolder(self::TEST_TRASHBIN_USER1)->get($folder);
  186. $share = \OC::$server->getShareManager()->newShare();
  187. $share->setShareType(IShare::TYPE_USER)
  188. ->setNode($node)
  189. ->setSharedBy(self::TEST_TRASHBIN_USER1)
  190. ->setSharedWith(self::TEST_TRASHBIN_USER2)
  191. ->setPermissions(Constants::PERMISSION_ALL);
  192. $share = \OC::$server->getShareManager()->createShare($share);
  193. \OC::$server->getShareManager()->acceptShare($share, self::TEST_TRASHBIN_USER2);
  194. // delete them so that they end up in the trash bin
  195. Filesystem::unlink($folder . 'user1-1.txt');
  196. Filesystem::unlink($folder . 'user1-2.txt');
  197. Filesystem::unlink($folder . 'user1-3.txt');
  198. $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name');
  199. $this->assertSame(3, count($filesInTrash));
  200. // every second file will get a date in the past so that it will get expired
  201. $this->manipulateDeleteTime($filesInTrash, $this->trashRoot1, $expiredDate);
  202. // login as user2
  203. self::loginHelper(self::TEST_TRASHBIN_USER2);
  204. $this->assertTrue(Filesystem::file_exists($folder . 'user1-4.txt'));
  205. // create some files
  206. Filesystem::file_put_contents('user2-1.txt', 'file1');
  207. Filesystem::file_put_contents('user2-2.txt', 'file2');
  208. // delete them so that they end up in the trash bin
  209. Filesystem::unlink('user2-1.txt');
  210. Filesystem::unlink('user2-2.txt');
  211. $filesInTrashUser2 = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER2, 'name');
  212. $this->assertSame(2, count($filesInTrashUser2));
  213. // every second file will get a date in the past so that it will get expired
  214. $this->manipulateDeleteTime($filesInTrashUser2, $this->trashRoot2, $expiredDate);
  215. Filesystem::unlink($folder . 'user1-4.txt');
  216. $this->runCommands();
  217. $filesInTrashUser2AfterDelete = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER2);
  218. // user2-1.txt should have been expired
  219. $this->verifyArray($filesInTrashUser2AfterDelete, ['user2-2.txt', 'user1-4.txt']);
  220. self::loginHelper(self::TEST_TRASHBIN_USER1);
  221. // user1-1.txt and user1-3.txt should have been expired
  222. $filesInTrashUser1AfterDelete = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1);
  223. $this->verifyArray($filesInTrashUser1AfterDelete, ['user1-2.txt', 'user1-4.txt']);
  224. }
  225. /**
  226. * verify that the array contains the expected results
  227. *
  228. * @param FileInfo[] $result
  229. * @param string[] $expected
  230. */
  231. private function verifyArray($result, $expected) {
  232. $this->assertSame(count($expected), count($result));
  233. foreach ($expected as $expectedFile) {
  234. $found = false;
  235. foreach ($result as $fileInTrash) {
  236. if ($expectedFile === $fileInTrash['name']) {
  237. $found = true;
  238. break;
  239. }
  240. }
  241. if (!$found) {
  242. // if we didn't found the expected file, something went wrong
  243. $this->assertTrue(false, "can't find expected file '" . $expectedFile . "' in trash bin");
  244. }
  245. }
  246. }
  247. /**
  248. * @param FileInfo[] $files
  249. * @param string $trashRoot
  250. * @param integer $expireDate
  251. */
  252. private function manipulateDeleteTime($files, $trashRoot, $expireDate) {
  253. $counter = 0;
  254. foreach ($files as &$file) {
  255. // modify every second file
  256. $counter = ($counter + 1) % 2;
  257. if ($counter === 1) {
  258. $source = $trashRoot . '/files/' . $file['name'] . '.d' . $file['mtime'];
  259. $target = Filesystem::normalizePath($trashRoot . '/files/' . $file['name'] . '.d' . $expireDate);
  260. $this->rootView->rename($source, $target);
  261. $file['mtime'] = $expireDate;
  262. }
  263. }
  264. return \OCA\Files\Helper::sortFiles($files, 'mtime');
  265. }
  266. /**
  267. * test expiration of old files in the trash bin until the max size
  268. * of the trash bin is met again
  269. */
  270. public function testExpireOldFilesUtilLimitsAreMet(): void {
  271. // create some files
  272. Filesystem::file_put_contents('file1.txt', 'file1');
  273. Filesystem::file_put_contents('file2.txt', 'file2');
  274. Filesystem::file_put_contents('file3.txt', 'file3');
  275. // delete them so that they end up in the trash bin
  276. Filesystem::unlink('file3.txt');
  277. sleep(1); // make sure that every file has a unique mtime
  278. Filesystem::unlink('file2.txt');
  279. sleep(1); // make sure that every file has a unique mtime
  280. Filesystem::unlink('file1.txt');
  281. //make sure that files are in the trash bin
  282. $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
  283. $this->assertSame(3, count($filesInTrash));
  284. $testClass = new TrashbinForTesting();
  285. $sizeOfDeletedFiles = $testClass->dummyDeleteFiles($filesInTrash, -8);
  286. // the two oldest files (file3.txt and file2.txt) should be deleted
  287. $this->assertSame(10, $sizeOfDeletedFiles);
  288. $newTrashContent = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1);
  289. $this->assertSame(1, count($newTrashContent));
  290. $element = reset($newTrashContent);
  291. $this->assertSame('file1.txt', $element['name']);
  292. }
  293. /**
  294. * Test restoring a file
  295. */
  296. public function testRestoreFileInRoot(): void {
  297. $userFolder = \OC::$server->getUserFolder();
  298. $file = $userFolder->newFile('file1.txt');
  299. $file->putContent('foo');
  300. $this->assertTrue($userFolder->nodeExists('file1.txt'));
  301. $file->delete();
  302. $this->assertFalse($userFolder->nodeExists('file1.txt'));
  303. $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
  304. $this->assertCount(1, $filesInTrash);
  305. /** @var FileInfo */
  306. $trashedFile = $filesInTrash[0];
  307. $this->assertTrue(
  308. Trashbin::restore(
  309. 'file1.txt.d' . $trashedFile->getMtime(),
  310. $trashedFile->getName(),
  311. $trashedFile->getMtime()
  312. )
  313. );
  314. $file = $userFolder->get('file1.txt');
  315. $this->assertEquals('foo', $file->getContent());
  316. }
  317. /**
  318. * Test restoring a file in subfolder
  319. */
  320. public function testRestoreFileInSubfolder(): void {
  321. $userFolder = \OC::$server->getUserFolder();
  322. $folder = $userFolder->newFolder('folder');
  323. $file = $folder->newFile('file1.txt');
  324. $file->putContent('foo');
  325. $this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
  326. $file->delete();
  327. $this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
  328. $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
  329. $this->assertCount(1, $filesInTrash);
  330. /** @var FileInfo */
  331. $trashedFile = $filesInTrash[0];
  332. $this->assertTrue(
  333. Trashbin::restore(
  334. 'file1.txt.d' . $trashedFile->getMtime(),
  335. $trashedFile->getName(),
  336. $trashedFile->getMtime()
  337. )
  338. );
  339. $file = $userFolder->get('folder/file1.txt');
  340. $this->assertEquals('foo', $file->getContent());
  341. }
  342. /**
  343. * Test restoring a folder
  344. */
  345. public function testRestoreFolder(): void {
  346. $userFolder = \OC::$server->getUserFolder();
  347. $folder = $userFolder->newFolder('folder');
  348. $file = $folder->newFile('file1.txt');
  349. $file->putContent('foo');
  350. $this->assertTrue($userFolder->nodeExists('folder'));
  351. $folder->delete();
  352. $this->assertFalse($userFolder->nodeExists('folder'));
  353. $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
  354. $this->assertCount(1, $filesInTrash);
  355. /** @var FileInfo */
  356. $trashedFolder = $filesInTrash[0];
  357. $this->assertTrue(
  358. Trashbin::restore(
  359. 'folder.d' . $trashedFolder->getMtime(),
  360. $trashedFolder->getName(),
  361. $trashedFolder->getMtime()
  362. )
  363. );
  364. $file = $userFolder->get('folder/file1.txt');
  365. $this->assertEquals('foo', $file->getContent());
  366. }
  367. /**
  368. * Test restoring a file from inside a trashed folder
  369. */
  370. public function testRestoreFileFromTrashedSubfolder(): void {
  371. $userFolder = \OC::$server->getUserFolder();
  372. $folder = $userFolder->newFolder('folder');
  373. $file = $folder->newFile('file1.txt');
  374. $file->putContent('foo');
  375. $this->assertTrue($userFolder->nodeExists('folder'));
  376. $folder->delete();
  377. $this->assertFalse($userFolder->nodeExists('folder'));
  378. $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
  379. $this->assertCount(1, $filesInTrash);
  380. /** @var FileInfo */
  381. $trashedFile = $filesInTrash[0];
  382. $this->assertTrue(
  383. Trashbin::restore(
  384. 'folder.d' . $trashedFile->getMtime() . '/file1.txt',
  385. 'file1.txt',
  386. $trashedFile->getMtime()
  387. )
  388. );
  389. $file = $userFolder->get('file1.txt');
  390. $this->assertEquals('foo', $file->getContent());
  391. }
  392. /**
  393. * Test restoring a file whenever the source folder was removed.
  394. * The file should then land in the root.
  395. */
  396. public function testRestoreFileWithMissingSourceFolder(): void {
  397. $userFolder = \OC::$server->getUserFolder();
  398. $folder = $userFolder->newFolder('folder');
  399. $file = $folder->newFile('file1.txt');
  400. $file->putContent('foo');
  401. $this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
  402. $file->delete();
  403. $this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
  404. $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
  405. $this->assertCount(1, $filesInTrash);
  406. /** @var FileInfo */
  407. $trashedFile = $filesInTrash[0];
  408. // delete source folder
  409. $folder->delete();
  410. $this->assertTrue(
  411. Trashbin::restore(
  412. 'file1.txt.d' . $trashedFile->getMtime(),
  413. $trashedFile->getName(),
  414. $trashedFile->getMtime()
  415. )
  416. );
  417. $file = $userFolder->get('file1.txt');
  418. $this->assertEquals('foo', $file->getContent());
  419. }
  420. /**
  421. * Test restoring a file in the root folder whenever there is another file
  422. * with the same name in the root folder
  423. */
  424. public function testRestoreFileDoesNotOverwriteExistingInRoot(): void {
  425. $userFolder = \OC::$server->getUserFolder();
  426. $file = $userFolder->newFile('file1.txt');
  427. $file->putContent('foo');
  428. $this->assertTrue($userFolder->nodeExists('file1.txt'));
  429. $file->delete();
  430. $this->assertFalse($userFolder->nodeExists('file1.txt'));
  431. $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
  432. $this->assertCount(1, $filesInTrash);
  433. /** @var FileInfo */
  434. $trashedFile = $filesInTrash[0];
  435. // create another file
  436. $file = $userFolder->newFile('file1.txt');
  437. $file->putContent('bar');
  438. $this->assertTrue(
  439. Trashbin::restore(
  440. 'file1.txt.d' . $trashedFile->getMtime(),
  441. $trashedFile->getName(),
  442. $trashedFile->getMtime()
  443. )
  444. );
  445. $anotherFile = $userFolder->get('file1.txt');
  446. $this->assertEquals('bar', $anotherFile->getContent());
  447. $restoredFile = $userFolder->get('file1 (restored).txt');
  448. $this->assertEquals('foo', $restoredFile->getContent());
  449. }
  450. /**
  451. * Test restoring a file whenever there is another file
  452. * with the same name in the source folder
  453. */
  454. public function testRestoreFileDoesNotOverwriteExistingInSubfolder(): void {
  455. $userFolder = \OC::$server->getUserFolder();
  456. $folder = $userFolder->newFolder('folder');
  457. $file = $folder->newFile('file1.txt');
  458. $file->putContent('foo');
  459. $this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
  460. $file->delete();
  461. $this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
  462. $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
  463. $this->assertCount(1, $filesInTrash);
  464. /** @var FileInfo */
  465. $trashedFile = $filesInTrash[0];
  466. // create another file
  467. $file = $folder->newFile('file1.txt');
  468. $file->putContent('bar');
  469. $this->assertTrue(
  470. Trashbin::restore(
  471. 'file1.txt.d' . $trashedFile->getMtime(),
  472. $trashedFile->getName(),
  473. $trashedFile->getMtime()
  474. )
  475. );
  476. $anotherFile = $userFolder->get('folder/file1.txt');
  477. $this->assertEquals('bar', $anotherFile->getContent());
  478. $restoredFile = $userFolder->get('folder/file1 (restored).txt');
  479. $this->assertEquals('foo', $restoredFile->getContent());
  480. }
  481. /**
  482. * Test restoring a non-existing file from trashbin, returns false
  483. */
  484. public function testRestoreUnexistingFile(): void {
  485. $this->assertFalse(
  486. Trashbin::restore(
  487. 'unexist.txt.d123456',
  488. 'unexist.txt',
  489. '123456'
  490. )
  491. );
  492. }
  493. /**
  494. * Test restoring a file into a read-only folder, will restore
  495. * the file to root instead
  496. */
  497. public function testRestoreFileIntoReadOnlySourceFolder(): void {
  498. $userFolder = \OC::$server->getUserFolder();
  499. $folder = $userFolder->newFolder('folder');
  500. $file = $folder->newFile('file1.txt');
  501. $file->putContent('foo');
  502. $this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
  503. $file->delete();
  504. $this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
  505. $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
  506. $this->assertCount(1, $filesInTrash);
  507. /** @var FileInfo */
  508. $trashedFile = $filesInTrash[0];
  509. // delete source folder
  510. [$storage, $internalPath] = $this->rootView->resolvePath('/' . self::TEST_TRASHBIN_USER1 . '/files/folder');
  511. if ($storage instanceof Local) {
  512. $folderAbsPath = $storage->getSourcePath($internalPath);
  513. // make folder read-only
  514. chmod($folderAbsPath, 0555);
  515. $this->assertTrue(
  516. Trashbin::restore(
  517. 'file1.txt.d' . $trashedFile->getMtime(),
  518. $trashedFile->getName(),
  519. $trashedFile->getMtime()
  520. )
  521. );
  522. $file = $userFolder->get('file1.txt');
  523. $this->assertEquals('foo', $file->getContent());
  524. chmod($folderAbsPath, 0755);
  525. }
  526. }
  527. /**
  528. * @param string $user
  529. * @param bool $create
  530. */
  531. public static function loginHelper($user, $create = false) {
  532. if ($create) {
  533. try {
  534. \OC::$server->getUserManager()->createUser($user, $user);
  535. } catch (\Exception $e) { // catch username is already being used from previous aborted runs
  536. }
  537. }
  538. \OC_Util::tearDownFS();
  539. \OC_User::setUserId('');
  540. Filesystem::tearDown();
  541. \OC_User::setUserId($user);
  542. \OC_Util::setupFS($user);
  543. \OC::$server->getUserFolder($user);
  544. }
  545. }
  546. // just a dummy class to make protected methods available for testing
  547. class TrashbinForTesting extends Trashbin {
  548. /**
  549. * @param FileInfo[] $files
  550. * @param integer $limit
  551. */
  552. public function dummyDeleteExpiredFiles($files) {
  553. // dummy value for $retention_obligation because it is not needed here
  554. return parent::deleteExpiredFiles($files, TrashbinTest::TEST_TRASHBIN_USER1);
  555. }
  556. /**
  557. * @param FileInfo[] $files
  558. * @param integer $availableSpace
  559. */
  560. public function dummyDeleteFiles($files, $availableSpace) {
  561. return parent::deleteFiles($files, TrashbinTest::TEST_TRASHBIN_USER1, $availableSpace);
  562. }
  563. }