StorageTest.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Bjoern Schiessle <bjoern@schiessle.org>
  6. * @author Björn Schießle <bjoern@schiessle.org>
  7. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  8. * @author Joas Schilling <coding@schilljs.com>
  9. * @author Julius Härtl <jus@bitgrid.net>
  10. * @author Morris Jobke <hey@morrisjobke.de>
  11. * @author Robin Appelman <robin@icewind.nl>
  12. * @author Roeland Jago Douma <roeland@famdouma.nl>
  13. * @author Stefan Weil <sw@weilnetz.de>
  14. * @author Thomas Müller <thomas.mueller@tmit.eu>
  15. * @author Vincent Petry <vincent@nextcloud.com>
  16. *
  17. * @license AGPL-3.0
  18. *
  19. * This code is free software: you can redistribute it and/or modify
  20. * it under the terms of the GNU Affero General Public License, version 3,
  21. * as published by the Free Software Foundation.
  22. *
  23. * This program is distributed in the hope that it will be useful,
  24. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. * GNU Affero General Public License for more details.
  27. *
  28. * You should have received a copy of the GNU Affero General Public License, version 3,
  29. * along with this program. If not, see <http://www.gnu.org/licenses/>
  30. *
  31. */
  32. namespace OCA\Files_Trashbin\Tests;
  33. use OC\Files\Filesystem;
  34. use OC\Files\Storage\Common;
  35. use OC\Files\Storage\Local;
  36. use OC\Files\Storage\Temporary;
  37. use OCA\Files_Trashbin\AppInfo\Application;
  38. use OCA\Files_Trashbin\Events\MoveToTrashEvent;
  39. use OCA\Files_Trashbin\Storage;
  40. use OCA\Files_Trashbin\Trash\ITrashManager;
  41. use OCP\AppFramework\Bootstrap\IBootContext;
  42. use OCP\AppFramework\Utility\ITimeFactory;
  43. use OCP\Files\Cache\ICache;
  44. use OCP\Files\Folder;
  45. use OCP\Files\IRootFolder;
  46. use OCP\Files\Node;
  47. use OCP\Files\Storage\IStorage;
  48. use OCP\ILogger;
  49. use OCP\IUserManager;
  50. use OCP\Lock\ILockingProvider;
  51. use OCP\Share\IShare;
  52. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  53. use Test\Traits\MountProviderTrait;
  54. class TemporaryNoCross extends Temporary {
  55. public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = null) {
  56. return Common::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime);
  57. }
  58. public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
  59. return Common::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
  60. }
  61. }
  62. /**
  63. * Class Storage
  64. *
  65. * @group DB
  66. *
  67. * @package OCA\Files_Trashbin\Tests
  68. */
  69. class StorageTest extends \Test\TestCase {
  70. use MountProviderTrait;
  71. /**
  72. * @var string
  73. */
  74. private $user;
  75. /**
  76. * @var \OC\Files\View
  77. */
  78. private $rootView;
  79. /**
  80. * @var \OC\Files\View
  81. */
  82. private $userView;
  83. protected function setUp(): void {
  84. parent::setUp();
  85. \OC_Hook::clear();
  86. \OC::$server->boot();
  87. // register trashbin hooks
  88. $trashbinApp = new Application();
  89. $trashbinApp->boot($this->createMock(IBootContext::class));
  90. $this->user = $this->getUniqueId('user');
  91. \OC::$server->getUserManager()->createUser($this->user, $this->user);
  92. // this will setup the FS
  93. $this->loginAsUser($this->user);
  94. \OCA\Files_Trashbin\Storage::setupStorage();
  95. $this->rootView = new \OC\Files\View('/');
  96. $this->userView = new \OC\Files\View('/' . $this->user . '/files/');
  97. $this->userView->file_put_contents('test.txt', 'foo');
  98. $this->userView->mkdir('folder');
  99. $this->userView->file_put_contents('folder/inside.txt', 'bar');
  100. }
  101. protected function tearDown(): void {
  102. \OC\Files\Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
  103. $this->logout();
  104. $user = \OC::$server->getUserManager()->get($this->user);
  105. if ($user !== null) {
  106. $user->delete();
  107. }
  108. \OC_Hook::clear();
  109. parent::tearDown();
  110. }
  111. /**
  112. * Test that deleting a file puts it into the trashbin.
  113. */
  114. public function testSingleStorageDeleteFile() {
  115. $this->assertTrue($this->userView->file_exists('test.txt'));
  116. $this->userView->unlink('test.txt');
  117. [$storage,] = $this->userView->resolvePath('test.txt');
  118. $storage->getScanner()->scan(''); // make sure we check the storage
  119. $this->assertFalse($this->userView->getFileInfo('test.txt'));
  120. // check if file is in trashbin
  121. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
  122. $this->assertEquals(1, count($results));
  123. $name = $results[0]->getName();
  124. $this->assertEquals('test.txt', substr($name, 0, strrpos($name, '.')));
  125. }
  126. /**
  127. * Test that deleting a folder puts it into the trashbin.
  128. */
  129. public function testSingleStorageDeleteFolder() {
  130. $this->assertTrue($this->userView->file_exists('folder/inside.txt'));
  131. $this->userView->rmdir('folder');
  132. [$storage,] = $this->userView->resolvePath('folder/inside.txt');
  133. $storage->getScanner()->scan(''); // make sure we check the storage
  134. $this->assertFalse($this->userView->getFileInfo('folder'));
  135. // check if folder is in trashbin
  136. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
  137. $this->assertEquals(1, count($results));
  138. $name = $results[0]->getName();
  139. $this->assertEquals('folder', substr($name, 0, strrpos($name, '.')));
  140. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/' . $name . '/');
  141. $this->assertEquals(1, count($results));
  142. $name = $results[0]->getName();
  143. $this->assertEquals('inside.txt', $name);
  144. }
  145. /**
  146. * Test that deleting a file from another mounted storage properly
  147. * lands in the trashbin. This is a cross-storage situation because
  148. * the trashbin folder is in the root storage while the mounted one
  149. * isn't.
  150. */
  151. public function testCrossStorageDeleteFile() {
  152. $storage2 = new Temporary([]);
  153. \OC\Files\Filesystem::mount($storage2, [], $this->user . '/files/substorage');
  154. $this->userView->file_put_contents('substorage/subfile.txt', 'foo');
  155. $storage2->getScanner()->scan('');
  156. $this->assertTrue($storage2->file_exists('subfile.txt'));
  157. $this->userView->unlink('substorage/subfile.txt');
  158. $storage2->getScanner()->scan('');
  159. $this->assertFalse($this->userView->getFileInfo('substorage/subfile.txt'));
  160. $this->assertFalse($storage2->file_exists('subfile.txt'));
  161. // check if file is in trashbin
  162. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
  163. $this->assertEquals(1, count($results));
  164. $name = $results[0]->getName();
  165. $this->assertEquals('subfile.txt', substr($name, 0, strrpos($name, '.')));
  166. }
  167. /**
  168. * Test that deleting a folder from another mounted storage properly
  169. * lands in the trashbin. This is a cross-storage situation because
  170. * the trashbin folder is in the root storage while the mounted one
  171. * isn't.
  172. */
  173. public function testCrossStorageDeleteFolder() {
  174. $storage2 = new Temporary([]);
  175. \OC\Files\Filesystem::mount($storage2, [], $this->user . '/files/substorage');
  176. $this->userView->mkdir('substorage/folder');
  177. $this->userView->file_put_contents('substorage/folder/subfile.txt', 'bar');
  178. $storage2->getScanner()->scan('');
  179. $this->assertTrue($storage2->file_exists('folder/subfile.txt'));
  180. $this->userView->rmdir('substorage/folder');
  181. $storage2->getScanner()->scan('');
  182. $this->assertFalse($this->userView->getFileInfo('substorage/folder'));
  183. $this->assertFalse($storage2->file_exists('folder/subfile.txt'));
  184. // check if folder is in trashbin
  185. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
  186. $this->assertEquals(1, count($results));
  187. $name = $results[0]->getName();
  188. $this->assertEquals('folder', substr($name, 0, strrpos($name, '.')));
  189. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/' . $name . '/');
  190. $this->assertEquals(1, count($results));
  191. $name = $results[0]->getName();
  192. $this->assertEquals('subfile.txt', $name);
  193. }
  194. /**
  195. * Test that deleted versions properly land in the trashbin.
  196. */
  197. public function testDeleteVersionsOfFile() {
  198. // trigger a version (multiple would not work because of the expire logic)
  199. $this->userView->file_put_contents('test.txt', 'v1');
  200. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/');
  201. $this->assertEquals(1, count($results));
  202. $this->userView->unlink('test.txt');
  203. // rescan trash storage
  204. [$rootStorage,] = $this->rootView->resolvePath($this->user . '/files_trashbin');
  205. $rootStorage->getScanner()->scan('');
  206. // check if versions are in trashbin
  207. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions');
  208. $this->assertEquals(1, count($results));
  209. $name = $results[0]->getName();
  210. $this->assertEquals('test.txt.v', substr($name, 0, strlen('test.txt.v')));
  211. // versions deleted
  212. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/');
  213. $this->assertEquals(0, count($results));
  214. }
  215. /**
  216. * Test that deleted versions properly land in the trashbin.
  217. */
  218. public function testDeleteVersionsOfFolder() {
  219. // trigger a version (multiple would not work because of the expire logic)
  220. $this->userView->file_put_contents('folder/inside.txt', 'v1');
  221. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/folder/');
  222. $this->assertEquals(1, count($results));
  223. $this->userView->rmdir('folder');
  224. // rescan trash storage
  225. [$rootStorage,] = $this->rootView->resolvePath($this->user . '/files_trashbin');
  226. $rootStorage->getScanner()->scan('');
  227. // check if versions are in trashbin
  228. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions');
  229. $this->assertEquals(1, count($results));
  230. $name = $results[0]->getName();
  231. $this->assertEquals('folder.d', substr($name, 0, strlen('folder.d')));
  232. // check if versions are in trashbin
  233. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions/' . $name . '/');
  234. $this->assertEquals(1, count($results));
  235. $name = $results[0]->getName();
  236. $this->assertEquals('inside.txt.v', substr($name, 0, strlen('inside.txt.v')));
  237. // versions deleted
  238. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/folder/');
  239. $this->assertEquals(0, count($results));
  240. }
  241. /**
  242. * Test that deleted versions properly land in the trashbin when deleting as share recipient.
  243. */
  244. public function testDeleteVersionsOfFileAsRecipient() {
  245. $this->userView->mkdir('share');
  246. // trigger a version (multiple would not work because of the expire logic)
  247. $this->userView->file_put_contents('share/test.txt', 'v1');
  248. $this->userView->file_put_contents('share/test.txt', 'v2');
  249. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/share/');
  250. $this->assertEquals(1, count($results));
  251. $recipientUser = $this->getUniqueId('recipient_');
  252. \OC::$server->getUserManager()->createUser($recipientUser, $recipientUser);
  253. $node = \OC::$server->getUserFolder($this->user)->get('share');
  254. $share = \OC::$server->getShareManager()->newShare();
  255. $share->setNode($node)
  256. ->setShareType(IShare::TYPE_USER)
  257. ->setSharedBy($this->user)
  258. ->setSharedWith($recipientUser)
  259. ->setPermissions(\OCP\Constants::PERMISSION_ALL);
  260. $share = \OC::$server->getShareManager()->createShare($share);
  261. \OC::$server->getShareManager()->acceptShare($share, $recipientUser);
  262. $this->loginAsUser($recipientUser);
  263. // delete as recipient
  264. $recipientView = new \OC\Files\View('/' . $recipientUser . '/files');
  265. $recipientView->unlink('share/test.txt');
  266. // rescan trash storage for both users
  267. [$rootStorage,] = $this->rootView->resolvePath($this->user . '/files_trashbin');
  268. $rootStorage->getScanner()->scan('');
  269. // check if versions are in trashbin for both users
  270. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions');
  271. $this->assertEquals(1, count($results), 'Versions in owner\'s trashbin');
  272. $name = $results[0]->getName();
  273. $this->assertEquals('test.txt.v', substr($name, 0, strlen('test.txt.v')));
  274. $results = $this->rootView->getDirectoryContent($recipientUser . '/files_trashbin/versions');
  275. $this->assertEquals(1, count($results), 'Versions in recipient\'s trashbin');
  276. $name = $results[0]->getName();
  277. $this->assertEquals('test.txt.v', substr($name, 0, strlen('test.txt.v')));
  278. // versions deleted
  279. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/share/');
  280. $this->assertEquals(0, count($results));
  281. }
  282. /**
  283. * Test that deleted versions properly land in the trashbin when deleting as share recipient.
  284. */
  285. public function testDeleteVersionsOfFolderAsRecipient() {
  286. $this->userView->mkdir('share');
  287. $this->userView->mkdir('share/folder');
  288. // trigger a version (multiple would not work because of the expire logic)
  289. $this->userView->file_put_contents('share/folder/test.txt', 'v1');
  290. $this->userView->file_put_contents('share/folder/test.txt', 'v2');
  291. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/share/folder/');
  292. $this->assertEquals(1, count($results));
  293. $recipientUser = $this->getUniqueId('recipient_');
  294. \OC::$server->getUserManager()->createUser($recipientUser, $recipientUser);
  295. $node = \OC::$server->getUserFolder($this->user)->get('share');
  296. $share = \OC::$server->getShareManager()->newShare();
  297. $share->setNode($node)
  298. ->setShareType(IShare::TYPE_USER)
  299. ->setSharedBy($this->user)
  300. ->setSharedWith($recipientUser)
  301. ->setPermissions(\OCP\Constants::PERMISSION_ALL);
  302. $share = \OC::$server->getShareManager()->createShare($share);
  303. \OC::$server->getShareManager()->acceptShare($share, $recipientUser);
  304. $this->loginAsUser($recipientUser);
  305. // delete as recipient
  306. $recipientView = new \OC\Files\View('/' . $recipientUser . '/files');
  307. $recipientView->rmdir('share/folder');
  308. // rescan trash storage
  309. [$rootStorage,] = $this->rootView->resolvePath($this->user . '/files_trashbin');
  310. $rootStorage->getScanner()->scan('');
  311. // check if versions are in trashbin for owner
  312. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions');
  313. $this->assertEquals(1, count($results));
  314. $name = $results[0]->getName();
  315. $this->assertEquals('folder.d', substr($name, 0, strlen('folder.d')));
  316. // check if file versions are in trashbin for owner
  317. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions/' . $name . '/');
  318. $this->assertEquals(1, count($results));
  319. $name = $results[0]->getName();
  320. $this->assertEquals('test.txt.v', substr($name, 0, strlen('test.txt.v')));
  321. // check if versions are in trashbin for recipient
  322. $results = $this->rootView->getDirectoryContent($recipientUser . '/files_trashbin/versions');
  323. $this->assertEquals(1, count($results));
  324. $name = $results[0]->getName();
  325. $this->assertEquals('folder.d', substr($name, 0, strlen('folder.d')));
  326. // check if file versions are in trashbin for recipient
  327. $results = $this->rootView->getDirectoryContent($recipientUser . '/files_trashbin/versions/' . $name . '/');
  328. $this->assertEquals(1, count($results));
  329. $name = $results[0]->getName();
  330. $this->assertEquals('test.txt.v', substr($name, 0, strlen('test.txt.v')));
  331. // versions deleted
  332. $results = $this->rootView->getDirectoryContent($recipientUser . '/files_versions/share/folder/');
  333. $this->assertEquals(0, count($results));
  334. }
  335. /**
  336. * Test that versions are not auto-trashed when moving a file between
  337. * storages. This is because rename() between storages would call
  338. * unlink() which should NOT trigger the version deletion logic.
  339. */
  340. public function testKeepFileAndVersionsWhenMovingFileBetweenStorages() {
  341. $storage2 = new Temporary([]);
  342. \OC\Files\Filesystem::mount($storage2, [], $this->user . '/files/substorage');
  343. // trigger a version (multiple would not work because of the expire logic)
  344. $this->userView->file_put_contents('test.txt', 'v1');
  345. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
  346. $this->assertEquals(0, count($results));
  347. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/');
  348. $this->assertEquals(1, count($results));
  349. // move to another storage
  350. $this->userView->rename('test.txt', 'substorage/test.txt');
  351. $this->assertTrue($this->userView->file_exists('substorage/test.txt'));
  352. // rescan trash storage
  353. [$rootStorage,] = $this->rootView->resolvePath($this->user . '/files_trashbin');
  354. $rootStorage->getScanner()->scan('');
  355. // versions were moved too
  356. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/substorage');
  357. $this->assertEquals(1, count($results));
  358. // check that nothing got trashed by the rename's unlink() call
  359. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
  360. $this->assertEquals(0, count($results));
  361. // check that versions were moved and not trashed
  362. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions/');
  363. $this->assertEquals(0, count($results));
  364. }
  365. /**
  366. * Test that versions are not auto-trashed when moving a file between
  367. * storages. This is because rename() between storages would call
  368. * unlink() which should NOT trigger the version deletion logic.
  369. */
  370. public function testKeepFileAndVersionsWhenMovingFolderBetweenStorages() {
  371. $storage2 = new Temporary([]);
  372. \OC\Files\Filesystem::mount($storage2, [], $this->user . '/files/substorage');
  373. // trigger a version (multiple would not work because of the expire logic)
  374. $this->userView->file_put_contents('folder/inside.txt', 'v1');
  375. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
  376. $this->assertEquals(0, count($results));
  377. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/folder/');
  378. $this->assertEquals(1, count($results));
  379. // move to another storage
  380. $this->userView->rename('folder', 'substorage/folder');
  381. $this->assertTrue($this->userView->file_exists('substorage/folder/inside.txt'));
  382. // rescan trash storage
  383. [$rootStorage,] = $this->rootView->resolvePath($this->user . '/files_trashbin');
  384. $rootStorage->getScanner()->scan('');
  385. // versions were moved too
  386. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/substorage/folder/');
  387. $this->assertEquals(1, count($results));
  388. // check that nothing got trashed by the rename's unlink() call
  389. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
  390. $this->assertEquals(0, count($results));
  391. // check that versions were moved and not trashed
  392. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions/');
  393. $this->assertEquals(0, count($results));
  394. }
  395. /**
  396. * Delete should fail if the source file can't be deleted.
  397. */
  398. public function testSingleStorageDeleteFileFail() {
  399. /**
  400. * @var \OC\Files\Storage\Temporary | \PHPUnit\Framework\MockObject\MockObject $storage
  401. */
  402. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  403. ->setConstructorArgs([[]])
  404. ->setMethods(['rename', 'unlink', 'moveFromStorage'])
  405. ->getMock();
  406. $storage->expects($this->any())
  407. ->method('rename')
  408. ->willReturn(false);
  409. $storage->expects($this->any())
  410. ->method('moveFromStorage')
  411. ->willReturn(false);
  412. $storage->expects($this->any())
  413. ->method('unlink')
  414. ->willReturn(false);
  415. $cache = $storage->getCache();
  416. Filesystem::mount($storage, [], '/' . $this->user);
  417. $storage->mkdir('files');
  418. $this->userView->file_put_contents('test.txt', 'foo');
  419. $this->assertTrue($storage->file_exists('files/test.txt'));
  420. $this->assertFalse($this->userView->unlink('test.txt'));
  421. $this->assertTrue($storage->file_exists('files/test.txt'));
  422. $this->assertTrue($cache->inCache('files/test.txt'));
  423. // file should not be in the trashbin
  424. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
  425. $this->assertEquals(0, count($results));
  426. }
  427. /**
  428. * Delete should fail if the source folder can't be deleted.
  429. */
  430. public function testSingleStorageDeleteFolderFail() {
  431. /**
  432. * @var \OC\Files\Storage\Temporary | \PHPUnit\Framework\MockObject\MockObject $storage
  433. */
  434. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  435. ->setConstructorArgs([[]])
  436. ->setMethods(['rename', 'unlink', 'rmdir'])
  437. ->getMock();
  438. $storage->expects($this->any())
  439. ->method('rmdir')
  440. ->willReturn(false);
  441. $cache = $storage->getCache();
  442. Filesystem::mount($storage, [], '/' . $this->user);
  443. $storage->mkdir('files');
  444. $this->userView->mkdir('folder');
  445. $this->userView->file_put_contents('folder/test.txt', 'foo');
  446. $this->assertTrue($storage->file_exists('files/folder/test.txt'));
  447. $this->assertFalse($this->userView->rmdir('files/folder'));
  448. $this->assertTrue($storage->file_exists('files/folder'));
  449. $this->assertTrue($storage->file_exists('files/folder/test.txt'));
  450. $this->assertTrue($cache->inCache('files/folder'));
  451. $this->assertTrue($cache->inCache('files/folder/test.txt'));
  452. // file should not be in the trashbin
  453. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
  454. $this->assertEquals(0, count($results));
  455. }
  456. /**
  457. * @dataProvider dataTestShouldMoveToTrash
  458. */
  459. public function testShouldMoveToTrash($mountPoint, $path, $userExists, $appDisablesTrash, $expected) {
  460. $fileID = 1;
  461. $cache = $this->createMock(ICache::class);
  462. $cache->expects($this->any())->method('getId')->willReturn($fileID);
  463. $tmpStorage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  464. ->disableOriginalConstructor()->getMock($cache);
  465. $tmpStorage->expects($this->any())->method('getCache')->willReturn($cache);
  466. $userManager = $this->getMockBuilder(IUserManager::class)
  467. ->disableOriginalConstructor()->getMock();
  468. $userManager->expects($this->any())
  469. ->method('userExists')->willReturn($userExists);
  470. $logger = $this->getMockBuilder(ILogger::class)->getMock();
  471. $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
  472. $rootFolder = $this->createMock(IRootFolder::class);
  473. $userFolder = $this->createMock(Folder::class);
  474. $node = $this->getMockBuilder(Node::class)->disableOriginalConstructor()->getMock();
  475. $trashManager = $this->createMock(ITrashManager::class);
  476. $event = $this->getMockBuilder(MoveToTrashEvent::class)->disableOriginalConstructor()->getMock();
  477. $event->expects($this->any())->method('shouldMoveToTrashBin')->willReturn(!$appDisablesTrash);
  478. $userFolder->expects($this->any())->method('getById')->with($fileID)->willReturn([$node]);
  479. $rootFolder->expects($this->any())->method('getUserFolder')->willReturn($userFolder);
  480. $storage = $this->getMockBuilder(Storage::class)
  481. ->setConstructorArgs(
  482. [
  483. ['mountPoint' => $mountPoint, 'storage' => $tmpStorage],
  484. $trashManager,
  485. $userManager,
  486. $logger,
  487. $eventDispatcher,
  488. $rootFolder
  489. ]
  490. )->setMethods(['createMoveToTrashEvent'])->getMock();
  491. $storage->expects($this->any())->method('createMoveToTrashEvent')->with($node)
  492. ->willReturn($event);
  493. $this->assertSame($expected,
  494. $this->invokePrivate($storage, 'shouldMoveToTrash', [$path])
  495. );
  496. }
  497. public function dataTestShouldMoveToTrash() {
  498. return [
  499. ['/schiesbn/', '/files/test.txt', true, false, true],
  500. ['/schiesbn/', '/files/test.txt', false, false, false],
  501. ['/schiesbn/', '/test.txt', true, false, false],
  502. ['/schiesbn/', '/test.txt', false, false, false],
  503. // other apps disables the trashbin
  504. ['/schiesbn/', '/files/test.txt', true, true, false],
  505. ['/schiesbn/', '/files/test.txt', false, true, false],
  506. ];
  507. }
  508. /**
  509. * Test that deleting a file doesn't error when nobody is logged in
  510. */
  511. public function testSingleStorageDeleteFileLoggedOut() {
  512. $this->logout();
  513. if (!$this->userView->file_exists('test.txt')) {
  514. $this->markTestSkipped('Skipping since the current home storage backend requires the user to logged in');
  515. } else {
  516. $this->userView->unlink('test.txt');
  517. $this->addToAssertionCount(1);
  518. }
  519. }
  520. public function testTrashbinCollision() {
  521. $this->userView->file_put_contents('test.txt', 'foo');
  522. $this->userView->file_put_contents('folder/test.txt', 'bar');
  523. $timeFactory = $this->createMock(ITimeFactory::class);
  524. $timeFactory->method('getTime')
  525. ->willReturn(1000);
  526. $lockingProvider = \OC::$server->getLockingProvider();
  527. $this->overwriteService(ITimeFactory::class, $timeFactory);
  528. $this->userView->unlink('test.txt');
  529. $this->assertTrue($this->rootView->file_exists('/' . $this->user . '/files_trashbin/files/test.txt.d1000'));
  530. /** @var \OC\Files\Storage\Storage $trashStorage */
  531. [$trashStorage, $trashInternalPath] = $this->rootView->resolvePath('/' . $this->user . '/files_trashbin/files/test.txt.d1000');
  532. /// simulate a concurrent delete
  533. $trashStorage->acquireLock($trashInternalPath, ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
  534. $this->userView->unlink('folder/test.txt');
  535. $trashStorage->releaseLock($trashInternalPath, ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
  536. $this->assertTrue($this->rootView->file_exists($this->user . '/files_trashbin/files/test.txt.d1001'));
  537. $this->assertEquals('foo', $this->rootView->file_get_contents($this->user . '/files_trashbin/files/test.txt.d1000'));
  538. $this->assertEquals('bar', $this->rootView->file_get_contents($this->user . '/files_trashbin/files/test.txt.d1001'));
  539. }
  540. public function testMoveFromStoragePreserveFileId() {
  541. if (!$this->userView->getMount('')->getStorage()->instanceOfStorage(Local::class)) {
  542. $this->markTestSkipped("Skipping on non-local users storage");
  543. }
  544. $this->userView->file_put_contents('test.txt', 'foo');
  545. $fileId = $this->userView->getFileInfo('test.txt')->getId();
  546. $externalStorage = new TemporaryNoCross([]);
  547. $externalStorage->getScanner()->scan('');
  548. Filesystem::mount($externalStorage, [], "/" . $this->user . "/files/storage");
  549. $this->assertTrue($this->userView->rename('test.txt', 'storage/test.txt'));
  550. $this->assertTrue($externalStorage->file_exists('test.txt'));
  551. $this->assertEquals($fileId, $this->userView->getFileInfo('storage/test.txt')->getId());
  552. }
  553. }