StorageTest.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  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. // register trashbin hooks
  87. $trashbinApp = new Application();
  88. $trashbinApp->boot($this->createMock(IBootContext::class));
  89. $this->user = $this->getUniqueId('user');
  90. \OC::$server->getUserManager()->createUser($this->user, $this->user);
  91. // this will setup the FS
  92. $this->loginAsUser($this->user);
  93. \OCA\Files_Trashbin\Storage::setupStorage();
  94. $this->rootView = new \OC\Files\View('/');
  95. $this->userView = new \OC\Files\View('/' . $this->user . '/files/');
  96. $this->userView->file_put_contents('test.txt', 'foo');
  97. $this->userView->mkdir('folder');
  98. $this->userView->file_put_contents('folder/inside.txt', 'bar');
  99. }
  100. protected function tearDown(): void {
  101. \OC\Files\Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
  102. $this->logout();
  103. $user = \OC::$server->getUserManager()->get($this->user);
  104. if ($user !== null) {
  105. $user->delete();
  106. }
  107. \OC_Hook::clear();
  108. parent::tearDown();
  109. }
  110. /**
  111. * Test that deleting a file puts it into the trashbin.
  112. */
  113. public function testSingleStorageDeleteFile() {
  114. $this->assertTrue($this->userView->file_exists('test.txt'));
  115. $this->userView->unlink('test.txt');
  116. [$storage,] = $this->userView->resolvePath('test.txt');
  117. $storage->getScanner()->scan(''); // make sure we check the storage
  118. $this->assertFalse($this->userView->getFileInfo('test.txt'));
  119. // check if file is in trashbin
  120. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
  121. $this->assertEquals(1, count($results));
  122. $name = $results[0]->getName();
  123. $this->assertEquals('test.txt', substr($name, 0, strrpos($name, '.')));
  124. }
  125. /**
  126. * Test that deleting a folder puts it into the trashbin.
  127. */
  128. public function testSingleStorageDeleteFolder() {
  129. $this->assertTrue($this->userView->file_exists('folder/inside.txt'));
  130. $this->userView->rmdir('folder');
  131. [$storage,] = $this->userView->resolvePath('folder/inside.txt');
  132. $storage->getScanner()->scan(''); // make sure we check the storage
  133. $this->assertFalse($this->userView->getFileInfo('folder'));
  134. // check if folder is in trashbin
  135. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
  136. $this->assertEquals(1, count($results));
  137. $name = $results[0]->getName();
  138. $this->assertEquals('folder', substr($name, 0, strrpos($name, '.')));
  139. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/' . $name . '/');
  140. $this->assertEquals(1, count($results));
  141. $name = $results[0]->getName();
  142. $this->assertEquals('inside.txt', $name);
  143. }
  144. /**
  145. * Test that deleting a file from another mounted storage properly
  146. * lands in the trashbin. This is a cross-storage situation because
  147. * the trashbin folder is in the root storage while the mounted one
  148. * isn't.
  149. */
  150. public function testCrossStorageDeleteFile() {
  151. $storage2 = new Temporary([]);
  152. \OC\Files\Filesystem::mount($storage2, [], $this->user . '/files/substorage');
  153. $this->userView->file_put_contents('substorage/subfile.txt', 'foo');
  154. $storage2->getScanner()->scan('');
  155. $this->assertTrue($storage2->file_exists('subfile.txt'));
  156. $this->userView->unlink('substorage/subfile.txt');
  157. $storage2->getScanner()->scan('');
  158. $this->assertFalse($this->userView->getFileInfo('substorage/subfile.txt'));
  159. $this->assertFalse($storage2->file_exists('subfile.txt'));
  160. // check if file is in trashbin
  161. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
  162. $this->assertEquals(1, count($results));
  163. $name = $results[0]->getName();
  164. $this->assertEquals('subfile.txt', substr($name, 0, strrpos($name, '.')));
  165. }
  166. /**
  167. * Test that deleting a folder from another mounted storage properly
  168. * lands in the trashbin. This is a cross-storage situation because
  169. * the trashbin folder is in the root storage while the mounted one
  170. * isn't.
  171. */
  172. public function testCrossStorageDeleteFolder() {
  173. $storage2 = new Temporary([]);
  174. \OC\Files\Filesystem::mount($storage2, [], $this->user . '/files/substorage');
  175. $this->userView->mkdir('substorage/folder');
  176. $this->userView->file_put_contents('substorage/folder/subfile.txt', 'bar');
  177. $storage2->getScanner()->scan('');
  178. $this->assertTrue($storage2->file_exists('folder/subfile.txt'));
  179. $this->userView->rmdir('substorage/folder');
  180. $storage2->getScanner()->scan('');
  181. $this->assertFalse($this->userView->getFileInfo('substorage/folder'));
  182. $this->assertFalse($storage2->file_exists('folder/subfile.txt'));
  183. // check if folder is in trashbin
  184. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
  185. $this->assertEquals(1, count($results));
  186. $name = $results[0]->getName();
  187. $this->assertEquals('folder', substr($name, 0, strrpos($name, '.')));
  188. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/' . $name . '/');
  189. $this->assertEquals(1, count($results));
  190. $name = $results[0]->getName();
  191. $this->assertEquals('subfile.txt', $name);
  192. }
  193. /**
  194. * Test that deleted versions properly land in the trashbin.
  195. */
  196. public function testDeleteVersionsOfFile() {
  197. \OCA\Files_Versions\Hooks::connectHooks();
  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. \OCA\Files_Versions\Hooks::connectHooks();
  220. // trigger a version (multiple would not work because of the expire logic)
  221. $this->userView->file_put_contents('folder/inside.txt', 'v1');
  222. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/folder/');
  223. $this->assertEquals(1, count($results));
  224. $this->userView->rmdir('folder');
  225. // rescan trash storage
  226. [$rootStorage,] = $this->rootView->resolvePath($this->user . '/files_trashbin');
  227. $rootStorage->getScanner()->scan('');
  228. // check if versions are in trashbin
  229. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions');
  230. $this->assertEquals(1, count($results));
  231. $name = $results[0]->getName();
  232. $this->assertEquals('folder.d', substr($name, 0, strlen('folder.d')));
  233. // check if versions are in trashbin
  234. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions/' . $name . '/');
  235. $this->assertEquals(1, count($results));
  236. $name = $results[0]->getName();
  237. $this->assertEquals('inside.txt.v', substr($name, 0, strlen('inside.txt.v')));
  238. // versions deleted
  239. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/folder/');
  240. $this->assertEquals(0, count($results));
  241. }
  242. /**
  243. * Test that deleted versions properly land in the trashbin when deleting as share recipient.
  244. */
  245. public function testDeleteVersionsOfFileAsRecipient() {
  246. \OCA\Files_Versions\Hooks::connectHooks();
  247. $this->userView->mkdir('share');
  248. // trigger a version (multiple would not work because of the expire logic)
  249. $this->userView->file_put_contents('share/test.txt', 'v1');
  250. $this->userView->file_put_contents('share/test.txt', 'v2');
  251. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/share/');
  252. $this->assertEquals(1, count($results));
  253. $recipientUser = $this->getUniqueId('recipient_');
  254. \OC::$server->getUserManager()->createUser($recipientUser, $recipientUser);
  255. $node = \OC::$server->getUserFolder($this->user)->get('share');
  256. $share = \OC::$server->getShareManager()->newShare();
  257. $share->setNode($node)
  258. ->setShareType(IShare::TYPE_USER)
  259. ->setSharedBy($this->user)
  260. ->setSharedWith($recipientUser)
  261. ->setPermissions(\OCP\Constants::PERMISSION_ALL);
  262. $share = \OC::$server->getShareManager()->createShare($share);
  263. \OC::$server->getShareManager()->acceptShare($share, $recipientUser);
  264. $this->loginAsUser($recipientUser);
  265. // delete as recipient
  266. $recipientView = new \OC\Files\View('/' . $recipientUser . '/files');
  267. $recipientView->unlink('share/test.txt');
  268. // rescan trash storage for both users
  269. [$rootStorage,] = $this->rootView->resolvePath($this->user . '/files_trashbin');
  270. $rootStorage->getScanner()->scan('');
  271. // check if versions are in trashbin for both users
  272. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions');
  273. $this->assertEquals(1, count($results), 'Versions in owner\'s trashbin');
  274. $name = $results[0]->getName();
  275. $this->assertEquals('test.txt.v', substr($name, 0, strlen('test.txt.v')));
  276. $results = $this->rootView->getDirectoryContent($recipientUser . '/files_trashbin/versions');
  277. $this->assertEquals(1, count($results), 'Versions in recipient\'s trashbin');
  278. $name = $results[0]->getName();
  279. $this->assertEquals('test.txt.v', substr($name, 0, strlen('test.txt.v')));
  280. // versions deleted
  281. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/share/');
  282. $this->assertEquals(0, count($results));
  283. }
  284. /**
  285. * Test that deleted versions properly land in the trashbin when deleting as share recipient.
  286. */
  287. public function testDeleteVersionsOfFolderAsRecipient() {
  288. \OCA\Files_Versions\Hooks::connectHooks();
  289. $this->userView->mkdir('share');
  290. $this->userView->mkdir('share/folder');
  291. // trigger a version (multiple would not work because of the expire logic)
  292. $this->userView->file_put_contents('share/folder/test.txt', 'v1');
  293. $this->userView->file_put_contents('share/folder/test.txt', 'v2');
  294. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/share/folder/');
  295. $this->assertEquals(1, count($results));
  296. $recipientUser = $this->getUniqueId('recipient_');
  297. \OC::$server->getUserManager()->createUser($recipientUser, $recipientUser);
  298. $node = \OC::$server->getUserFolder($this->user)->get('share');
  299. $share = \OC::$server->getShareManager()->newShare();
  300. $share->setNode($node)
  301. ->setShareType(IShare::TYPE_USER)
  302. ->setSharedBy($this->user)
  303. ->setSharedWith($recipientUser)
  304. ->setPermissions(\OCP\Constants::PERMISSION_ALL);
  305. $share = \OC::$server->getShareManager()->createShare($share);
  306. \OC::$server->getShareManager()->acceptShare($share, $recipientUser);
  307. $this->loginAsUser($recipientUser);
  308. // delete as recipient
  309. $recipientView = new \OC\Files\View('/' . $recipientUser . '/files');
  310. $recipientView->rmdir('share/folder');
  311. // rescan trash storage
  312. [$rootStorage,] = $this->rootView->resolvePath($this->user . '/files_trashbin');
  313. $rootStorage->getScanner()->scan('');
  314. // check if versions are in trashbin for owner
  315. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions');
  316. $this->assertEquals(1, count($results));
  317. $name = $results[0]->getName();
  318. $this->assertEquals('folder.d', substr($name, 0, strlen('folder.d')));
  319. // check if file versions are in trashbin for owner
  320. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions/' . $name . '/');
  321. $this->assertEquals(1, count($results));
  322. $name = $results[0]->getName();
  323. $this->assertEquals('test.txt.v', substr($name, 0, strlen('test.txt.v')));
  324. // check if versions are in trashbin for recipient
  325. $results = $this->rootView->getDirectoryContent($recipientUser . '/files_trashbin/versions');
  326. $this->assertEquals(1, count($results));
  327. $name = $results[0]->getName();
  328. $this->assertEquals('folder.d', substr($name, 0, strlen('folder.d')));
  329. // check if file versions are in trashbin for recipient
  330. $results = $this->rootView->getDirectoryContent($recipientUser . '/files_trashbin/versions/' . $name . '/');
  331. $this->assertEquals(1, count($results));
  332. $name = $results[0]->getName();
  333. $this->assertEquals('test.txt.v', substr($name, 0, strlen('test.txt.v')));
  334. // versions deleted
  335. $results = $this->rootView->getDirectoryContent($recipientUser . '/files_versions/share/folder/');
  336. $this->assertEquals(0, count($results));
  337. }
  338. /**
  339. * Test that versions are not auto-trashed when moving a file between
  340. * storages. This is because rename() between storages would call
  341. * unlink() which should NOT trigger the version deletion logic.
  342. */
  343. public function testKeepFileAndVersionsWhenMovingFileBetweenStorages() {
  344. \OCA\Files_Versions\Hooks::connectHooks();
  345. $storage2 = new Temporary([]);
  346. \OC\Files\Filesystem::mount($storage2, [], $this->user . '/files/substorage');
  347. // trigger a version (multiple would not work because of the expire logic)
  348. $this->userView->file_put_contents('test.txt', 'v1');
  349. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
  350. $this->assertEquals(0, count($results));
  351. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/');
  352. $this->assertEquals(1, count($results));
  353. // move to another storage
  354. $this->userView->rename('test.txt', 'substorage/test.txt');
  355. $this->assertTrue($this->userView->file_exists('substorage/test.txt'));
  356. // rescan trash storage
  357. [$rootStorage,] = $this->rootView->resolvePath($this->user . '/files_trashbin');
  358. $rootStorage->getScanner()->scan('');
  359. // versions were moved too
  360. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/substorage');
  361. $this->assertEquals(1, count($results));
  362. // check that nothing got trashed by the rename's unlink() call
  363. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
  364. $this->assertEquals(0, count($results));
  365. // check that versions were moved and not trashed
  366. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions/');
  367. $this->assertEquals(0, count($results));
  368. }
  369. /**
  370. * Test that versions are not auto-trashed when moving a file between
  371. * storages. This is because rename() between storages would call
  372. * unlink() which should NOT trigger the version deletion logic.
  373. */
  374. public function testKeepFileAndVersionsWhenMovingFolderBetweenStorages() {
  375. \OCA\Files_Versions\Hooks::connectHooks();
  376. $storage2 = new Temporary([]);
  377. \OC\Files\Filesystem::mount($storage2, [], $this->user . '/files/substorage');
  378. // trigger a version (multiple would not work because of the expire logic)
  379. $this->userView->file_put_contents('folder/inside.txt', 'v1');
  380. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
  381. $this->assertEquals(0, count($results));
  382. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/folder/');
  383. $this->assertEquals(1, count($results));
  384. // move to another storage
  385. $this->userView->rename('folder', 'substorage/folder');
  386. $this->assertTrue($this->userView->file_exists('substorage/folder/inside.txt'));
  387. // rescan trash storage
  388. [$rootStorage,] = $this->rootView->resolvePath($this->user . '/files_trashbin');
  389. $rootStorage->getScanner()->scan('');
  390. // versions were moved too
  391. $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/substorage/folder/');
  392. $this->assertEquals(1, count($results));
  393. // check that nothing got trashed by the rename's unlink() call
  394. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
  395. $this->assertEquals(0, count($results));
  396. // check that versions were moved and not trashed
  397. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions/');
  398. $this->assertEquals(0, count($results));
  399. }
  400. /**
  401. * Delete should fail if the source file can't be deleted.
  402. */
  403. public function testSingleStorageDeleteFileFail() {
  404. /**
  405. * @var \OC\Files\Storage\Temporary | \PHPUnit\Framework\MockObject\MockObject $storage
  406. */
  407. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  408. ->setConstructorArgs([[]])
  409. ->setMethods(['rename', 'unlink', 'moveFromStorage'])
  410. ->getMock();
  411. $storage->expects($this->any())
  412. ->method('rename')
  413. ->willReturn(false);
  414. $storage->expects($this->any())
  415. ->method('moveFromStorage')
  416. ->willReturn(false);
  417. $storage->expects($this->any())
  418. ->method('unlink')
  419. ->willReturn(false);
  420. $cache = $storage->getCache();
  421. Filesystem::mount($storage, [], '/' . $this->user);
  422. $storage->mkdir('files');
  423. $this->userView->file_put_contents('test.txt', 'foo');
  424. $this->assertTrue($storage->file_exists('files/test.txt'));
  425. $this->assertFalse($this->userView->unlink('test.txt'));
  426. $this->assertTrue($storage->file_exists('files/test.txt'));
  427. $this->assertTrue($cache->inCache('files/test.txt'));
  428. // file should not be in the trashbin
  429. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
  430. $this->assertEquals(0, count($results));
  431. }
  432. /**
  433. * Delete should fail if the source folder can't be deleted.
  434. */
  435. public function testSingleStorageDeleteFolderFail() {
  436. /**
  437. * @var \OC\Files\Storage\Temporary | \PHPUnit\Framework\MockObject\MockObject $storage
  438. */
  439. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  440. ->setConstructorArgs([[]])
  441. ->setMethods(['rename', 'unlink', 'rmdir'])
  442. ->getMock();
  443. $storage->expects($this->any())
  444. ->method('rmdir')
  445. ->willReturn(false);
  446. $cache = $storage->getCache();
  447. Filesystem::mount($storage, [], '/' . $this->user);
  448. $storage->mkdir('files');
  449. $this->userView->mkdir('folder');
  450. $this->userView->file_put_contents('folder/test.txt', 'foo');
  451. $this->assertTrue($storage->file_exists('files/folder/test.txt'));
  452. $this->assertFalse($this->userView->rmdir('files/folder'));
  453. $this->assertTrue($storage->file_exists('files/folder'));
  454. $this->assertTrue($storage->file_exists('files/folder/test.txt'));
  455. $this->assertTrue($cache->inCache('files/folder'));
  456. $this->assertTrue($cache->inCache('files/folder/test.txt'));
  457. // file should not be in the trashbin
  458. $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
  459. $this->assertEquals(0, count($results));
  460. }
  461. /**
  462. * @dataProvider dataTestShouldMoveToTrash
  463. */
  464. public function testShouldMoveToTrash($mountPoint, $path, $userExists, $appDisablesTrash, $expected) {
  465. $fileID = 1;
  466. $cache = $this->createMock(ICache::class);
  467. $cache->expects($this->any())->method('getId')->willReturn($fileID);
  468. $tmpStorage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  469. ->disableOriginalConstructor()->getMock($cache);
  470. $tmpStorage->expects($this->any())->method('getCache')->willReturn($cache);
  471. $userManager = $this->getMockBuilder(IUserManager::class)
  472. ->disableOriginalConstructor()->getMock();
  473. $userManager->expects($this->any())
  474. ->method('userExists')->willReturn($userExists);
  475. $logger = $this->getMockBuilder(ILogger::class)->getMock();
  476. $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
  477. $rootFolder = $this->createMock(IRootFolder::class);
  478. $userFolder = $this->createMock(Folder::class);
  479. $node = $this->getMockBuilder(Node::class)->disableOriginalConstructor()->getMock();
  480. $trashManager = $this->createMock(ITrashManager::class);
  481. $event = $this->getMockBuilder(MoveToTrashEvent::class)->disableOriginalConstructor()->getMock();
  482. $event->expects($this->any())->method('shouldMoveToTrashBin')->willReturn(!$appDisablesTrash);
  483. $userFolder->expects($this->any())->method('getById')->with($fileID)->willReturn([$node]);
  484. $rootFolder->expects($this->any())->method('getUserFolder')->willReturn($userFolder);
  485. $storage = $this->getMockBuilder(Storage::class)
  486. ->setConstructorArgs(
  487. [
  488. ['mountPoint' => $mountPoint, 'storage' => $tmpStorage],
  489. $trashManager,
  490. $userManager,
  491. $logger,
  492. $eventDispatcher,
  493. $rootFolder
  494. ]
  495. )->setMethods(['createMoveToTrashEvent'])->getMock();
  496. $storage->expects($this->any())->method('createMoveToTrashEvent')->with($node)
  497. ->willReturn($event);
  498. $this->assertSame($expected,
  499. $this->invokePrivate($storage, 'shouldMoveToTrash', [$path])
  500. );
  501. }
  502. public function dataTestShouldMoveToTrash() {
  503. return [
  504. ['/schiesbn/', '/files/test.txt', true, false, true],
  505. ['/schiesbn/', '/files/test.txt', false, false, false],
  506. ['/schiesbn/', '/test.txt', true, false, false],
  507. ['/schiesbn/', '/test.txt', false, false, false],
  508. // other apps disables the trashbin
  509. ['/schiesbn/', '/files/test.txt', true, true, false],
  510. ['/schiesbn/', '/files/test.txt', false, true, false],
  511. ];
  512. }
  513. /**
  514. * Test that deleting a file doesn't error when nobody is logged in
  515. */
  516. public function testSingleStorageDeleteFileLoggedOut() {
  517. $this->logout();
  518. if (!$this->userView->file_exists('test.txt')) {
  519. $this->markTestSkipped('Skipping since the current home storage backend requires the user to logged in');
  520. } else {
  521. $this->userView->unlink('test.txt');
  522. $this->addToAssertionCount(1);
  523. }
  524. }
  525. public function testTrashbinCollision() {
  526. $this->userView->file_put_contents('test.txt', 'foo');
  527. $this->userView->file_put_contents('folder/test.txt', 'bar');
  528. $timeFactory = $this->createMock(ITimeFactory::class);
  529. $timeFactory->method('getTime')
  530. ->willReturn(1000);
  531. $lockingProvider = \OC::$server->getLockingProvider();
  532. $this->overwriteService(ITimeFactory::class, $timeFactory);
  533. $this->userView->unlink('test.txt');
  534. $this->assertTrue($this->rootView->file_exists('/' . $this->user . '/files_trashbin/files/test.txt.d1000'));
  535. /** @var \OC\Files\Storage\Storage $trashStorage */
  536. [$trashStorage, $trashInternalPath] = $this->rootView->resolvePath('/' . $this->user . '/files_trashbin/files/test.txt.d1000');
  537. /// simulate a concurrent delete
  538. $trashStorage->acquireLock($trashInternalPath, ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
  539. $this->userView->unlink('folder/test.txt');
  540. $trashStorage->releaseLock($trashInternalPath, ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
  541. $this->assertTrue($this->rootView->file_exists($this->user . '/files_trashbin/files/test.txt.d1001'));
  542. $this->assertEquals('foo', $this->rootView->file_get_contents($this->user . '/files_trashbin/files/test.txt.d1000'));
  543. $this->assertEquals('bar', $this->rootView->file_get_contents($this->user . '/files_trashbin/files/test.txt.d1001'));
  544. }
  545. public function testMoveFromStoragePreserveFileId() {
  546. if (!$this->userView->getMount('')->getStorage()->instanceOfStorage(Local::class)) {
  547. $this->markTestSkipped("Skipping on non-local users storage");
  548. }
  549. $this->userView->file_put_contents('test.txt', 'foo');
  550. $fileId = $this->userView->getFileInfo('test.txt')->getId();
  551. $externalStorage = new TemporaryNoCross([]);
  552. $externalStorage->getScanner()->scan('');
  553. Filesystem::mount($externalStorage, [], "/" . $this->user . "/files/storage");
  554. $this->assertTrue($this->userView->rename('test.txt', 'storage/test.txt'));
  555. $this->assertTrue($externalStorage->file_exists('test.txt'));
  556. $this->assertEquals($fileId, $this->userView->getFileInfo('storage/test.txt')->getId());
  557. }
  558. }