[TASK] Implement conflict handling into renameFile()
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Resource / ResourceStorageTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Resource;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Prophecy\Argument;
18 use TYPO3\CMS\Core\Resource\Driver\AbstractDriver;
19 use TYPO3\CMS\Core\Resource\Driver\LocalDriver;
20 use TYPO3\CMS\Core\Resource\DuplicationBehavior;
21 use TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException;
22 use TYPO3\CMS\Core\Resource\File;
23 use TYPO3\CMS\Core\Resource\FileInterface;
24 use TYPO3\CMS\Core\Resource\FileRepository;
25 use TYPO3\CMS\Core\Resource\Folder;
26 use TYPO3\CMS\Core\Resource\Index\FileIndexRepository;
27 use TYPO3\CMS\Core\Resource\ResourceFactory;
28 use TYPO3\CMS\Core\Resource\ResourceStorage;
29 use TYPO3\CMS\Core\Utility\ArrayUtility;
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31
32 /**
33 * Test case for ResourceStorage class
34 */
35 class ResourceStorageTest extends BaseTestCase
36 {
37 /**
38 * @var array A backup of registered singleton instances
39 */
40 protected $singletonInstances = [];
41
42 /**
43 * @var ResourceStorage|\PHPUnit_Framework_MockObject_MockObject
44 */
45 protected $subject;
46
47 protected function setUp()
48 {
49 parent::setUp();
50 $this->singletonInstances = GeneralUtility::getSingletonInstances();
51 /** @var FileRepository|\PHPUnit_Framework_MockObject_MockObject $fileRepositoryMock */
52 $fileRepositoryMock = $this->createMock(FileRepository::class);
53 GeneralUtility::setSingletonInstance(
54 FileRepository::class,
55 $fileRepositoryMock
56 );
57 }
58
59 protected function tearDown()
60 {
61 GeneralUtility::resetSingletonInstances($this->singletonInstances);
62 parent::tearDown();
63 }
64
65 /**
66 * Prepare ResourceStorage
67 *
68 * @param array $configuration
69 * @param bool $mockPermissionChecks
70 * @param AbstractDriver|\PHPUnit_Framework_MockObject_MockObject $driverObject
71 * @param array $storageRecord
72 * @param array $mockedMethods
73 */
74 protected function prepareSubject(array $configuration, $mockPermissionChecks = false, AbstractDriver $driverObject = null, array $storageRecord = [], array $mockedMethods = [])
75 {
76 $permissionMethods = ['assureFileAddPermissions', 'checkFolderActionPermission', 'checkFileActionPermission', 'checkUserActionPermission', 'checkFileExtensionPermission', 'isWithinFileMountBoundaries', 'assureFileRenamePermissions'];
77 $configuration = $this->convertConfigurationArrayToFlexformXml($configuration);
78 $overruleArray = ['configuration' => $configuration];
79 ArrayUtility::mergeRecursiveWithOverrule($storageRecord, $overruleArray);
80 if ($driverObject == null) {
81 $driverObject = $this->getMockForAbstractClass(AbstractDriver::class, [], '', false);
82 }
83 if ($mockPermissionChecks) {
84 $mockedMethods = array_merge($mockedMethods, $permissionMethods);
85 }
86 $mockedMethods[] = 'getIndexer';
87
88 $this->subject = $this->getMockBuilder(ResourceStorage::class)
89 ->setMethods(array_unique($mockedMethods))
90 ->setConstructorArgs([$driverObject, $storageRecord])
91 ->getMock();
92 $this->subject->expects($this->any())->method('getIndexer')->will($this->returnValue($this->createMock(\TYPO3\CMS\Core\Resource\Index\Indexer::class)));
93 if ($mockPermissionChecks) {
94 foreach ($permissionMethods as $method) {
95 $this->subject->expects($this->any())->method($method)->will($this->returnValue(true));
96 }
97 }
98 }
99
100 /**
101 * Converts a simple configuration array into a FlexForm data structure serialized as XML
102 *
103 * @param array $configuration
104 * @return string
105 * @see GeneralUtility::array2xml()
106 */
107 protected function convertConfigurationArrayToFlexformXml(array $configuration)
108 {
109 $flexFormArray = ['data' => ['sDEF' => ['lDEF' => []]]];
110 foreach ($configuration as $key => $value) {
111 $flexFormArray['data']['sDEF']['lDEF'][$key] = ['vDEF' => $value];
112 }
113 $configuration = GeneralUtility::array2xml($flexFormArray);
114 return $configuration;
115 }
116
117 /**
118 * Creates a driver fixture object, optionally using a given mount object.
119 *
120 * IMPORTANT: Call this only after setting up the virtual file system (with the addTo* methods)!
121 *
122 * @param $driverConfiguration
123 * @param ResourceStorage $storageObject
124 * @param array $mockedDriverMethods
125 * @return \TYPO3\CMS\Core\Resource\Driver\LocalDriver|\PHPUnit_Framework_MockObject_MockObject
126 */
127 protected function createDriverMock($driverConfiguration, ResourceStorage $storageObject = null, $mockedDriverMethods = [])
128 {
129 $this->initializeVfs();
130
131 if (!isset($driverConfiguration['basePath'])) {
132 $driverConfiguration['basePath'] = $this->getMountRootUrl();
133 }
134
135 if ($mockedDriverMethods === null) {
136 $driver = new LocalDriver($driverConfiguration);
137 } else {
138 // We are using the LocalDriver here because PHPUnit can't mock concrete methods in abstract classes, so
139 // when using the AbstractDriver we would be in trouble when wanting to mock away some concrete method
140 $driver = $this->getMockBuilder(LocalDriver::class)
141 ->setMethods($mockedDriverMethods)
142 ->setConstructorArgs([$driverConfiguration])
143 ->getMock();
144 }
145 if ($storageObject !== null) {
146 $storageObject->setDriver($driver);
147 }
148 $driver->setStorageUid(6);
149 $driver->processConfiguration();
150 $driver->initialize();
151 return $driver;
152 }
153
154 /**
155 * @return array
156 */
157 public function fileExtensionPermissionDataProvider()
158 {
159 return [
160 'Permissions evaluated, extension not in allowed list' => [
161 'fileName' => 'foo.txt',
162 'configuration' => ['allow' => 'jpg'],
163 'evaluatePermissions' => true,
164 'isAllowed' => true,
165 ],
166 'Permissions evaluated, extension in deny list' => [
167 'fileName' => 'foo.txt',
168 'configuration' => ['deny' => 'txt'],
169 'evaluatePermissions' => true,
170 'isAllowed' => false,
171 ],
172 'Permissions not evaluated, extension is php' => [
173 'fileName' => 'foo.php',
174 'configuration' => [],
175 'evaluatePermissions' => false,
176 'isAllowed' => false,
177 ],
178 'Permissions evaluated, extension is php' => [
179 'fileName' => 'foo.php',
180 // It is not possible to allow php file extension through configuration
181 'configuration' => ['allow' => 'php'],
182 'evaluatePermissions' => true,
183 'isAllowed' => false,
184 ],
185 ];
186 }
187
188 /**
189 * @param string $fileName
190 * @param array $configuration
191 * @param bool $evaluatePermissions
192 * @param bool $isAllowed
193 * @test
194 * @dataProvider fileExtensionPermissionDataProvider
195 */
196 public function fileExtensionPermissionIsWorkingCorrectly($fileName, array $configuration, $evaluatePermissions, $isAllowed)
197 {
198 $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace'] = $configuration;
199 $driverMock = $this->getMockForAbstractClass(AbstractDriver::class, [], '', false);
200 $subject = $this->getAccessibleMock(ResourceStorage::class, ['dummy'], [$driverMock, []]);
201 $subject->_set('evaluatePermissions', $evaluatePermissions);
202 $this->assertSame($isAllowed, $subject->_call('checkFileExtensionPermission', $fileName));
203 }
204
205 /**
206 * @return array
207 */
208 public function capabilitiesDataProvider()
209 {
210 return [
211 'only public' => [
212 [
213 'public' => true,
214 'writable' => false,
215 'browsable' => false
216 ]
217 ],
218 'only writable' => [
219 [
220 'public' => false,
221 'writable' => true,
222 'browsable' => false
223 ]
224 ],
225 'only browsable' => [
226 [
227 'public' => false,
228 'writable' => false,
229 'browsable' => true
230 ]
231 ],
232 'all capabilities' => [
233 [
234 'public' => true,
235 'writable' => true,
236 'browsable' => true
237 ]
238 ],
239 'none' => [
240 [
241 'public' => false,
242 'writable' => false,
243 'browsable' => false
244 ]
245 ]
246 ];
247 }
248
249 /**
250 * @test
251 * @dataProvider capabilitiesDataProvider
252 * @TODO: Rewrite or move to functional suite
253 */
254 public function capabilitiesOfStorageObjectAreCorrectlySet(array $capabilities)
255 {
256 $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
257 $storageRecord = [
258 'is_public' => $capabilities['public'],
259 'is_writable' => $capabilities['writable'],
260 'is_browsable' => $capabilities['browsable'],
261 'is_online' => true
262 ];
263 $mockedDriver = $this->createDriverMock(
264 [
265 'pathType' => 'relative',
266 'basePath' => 'fileadmin/',
267 ],
268 $this->subject,
269 null
270 );
271 $this->prepareSubject([], false, $mockedDriver, $storageRecord);
272 $this->assertEquals($capabilities['public'], $this->subject->isPublic(), 'Capability "public" is not correctly set.');
273 $this->assertEquals($capabilities['writable'], $this->subject->isWritable(), 'Capability "writable" is not correctly set.');
274 $this->assertEquals($capabilities['browsable'], $this->subject->isBrowsable(), 'Capability "browsable" is not correctly set.');
275 }
276
277 /**
278 * @test
279 * @TODO: Rewrite or move to functional suite
280 */
281 public function fileAndFolderListFiltersAreInitializedWithDefaultFilters()
282 {
283 $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
284 $this->prepareSubject([]);
285 $this->assertEquals($GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['defaultFilterCallbacks'], $this->subject->getFileAndFolderNameFilters());
286 }
287
288 /**
289 * @test
290 */
291 public function addFileFailsIfFileDoesNotExist()
292 {
293 /** @var Folder|\PHPUnit_Framework_MockObject_MockObject $mockedFolder */
294 $mockedFolder = $this->createMock(Folder::class);
295 $this->expectException(\InvalidArgumentException::class);
296 $this->expectExceptionCode(1319552745);
297 $this->prepareSubject([]);
298 $this->subject->addFile('/some/random/file', $mockedFolder);
299 }
300
301 /**
302 * @test
303 */
304 public function getPublicUrlReturnsNullIfStorageIsNotOnline()
305 {
306 /** @var $driver LocalDriver|\PHPUnit_Framework_MockObject_MockObject */
307 $driver = $this->getMockBuilder(LocalDriver::class)
308 ->setConstructorArgs([['basePath' => $this->getMountRootUrl()]])
309 ->getMock();
310 /** @var $subject ResourceStorage|\PHPUnit_Framework_MockObject_MockObject */
311 $subject = $this->getMockBuilder(ResourceStorage::class)
312 ->setMethods(['isOnline'])
313 ->setConstructorArgs([$driver, ['configuration' => []]])
314 ->getMock();
315 $subject->expects($this->once())->method('isOnline')->will($this->returnValue(false));
316
317 $sourceFileIdentifier = '/sourceFile.ext';
318 $sourceFile = $this->getSimpleFileMock($sourceFileIdentifier);
319 $result = $subject->getPublicUrl($sourceFile);
320 $this->assertSame($result, null);
321 }
322
323 /**
324 * Data provider for checkFolderPermissionsRespectsFilesystemPermissions
325 *
326 * @return array
327 */
328 public function checkFolderPermissionsFilesystemPermissionsDataProvider()
329 {
330 return [
331 'read action on readable/writable folder' => [
332 'read',
333 ['r' => true, 'w' => true],
334 true
335 ],
336 'read action on unreadable folder' => [
337 'read',
338 ['r' => false, 'w' => true],
339 false
340 ],
341 'write action on read-only folder' => [
342 'write',
343 ['r' => true, 'w' => false],
344 false
345 ]
346 ];
347 }
348
349 /**
350 * @test
351 * @dataProvider checkFolderPermissionsFilesystemPermissionsDataProvider
352 * @param string $action 'read' or 'write'
353 * @param array $permissionsFromDriver The permissions as returned from the driver
354 * @param bool $expectedResult
355 */
356 public function checkFolderPermissionsRespectsFilesystemPermissions($action, $permissionsFromDriver, $expectedResult)
357 {
358 /** @var $mockedDriver LocalDriver|\PHPUnit_Framework_MockObject_MockObject */
359 $mockedDriver = $this->createMock(LocalDriver::class);
360 $mockedDriver->expects($this->any())->method('getPermissions')->will($this->returnValue($permissionsFromDriver));
361 /** @var $mockedFolder Folder|\PHPUnit_Framework_MockObject_MockObject */
362 $mockedFolder = $this->createMock(Folder::class);
363 // Let all other checks pass
364 /** @var $subject ResourceStorage|\PHPUnit_Framework_MockObject_MockObject */
365 $subject = $this->getMockBuilder(ResourceStorage::class)
366 ->setMethods(['isWritable', 'isBrowsable', 'checkUserActionPermission'])
367 ->setConstructorArgs([$mockedDriver, []])
368 ->getMock();
369 $subject->expects($this->any())->method('isWritable')->will($this->returnValue(true));
370 $subject->expects($this->any())->method('isBrowsable')->will($this->returnValue(true));
371 $subject->expects($this->any())->method('checkUserActionPermission')->will($this->returnValue(true));
372 $subject->setDriver($mockedDriver);
373
374 $this->assertSame($expectedResult, $subject->checkFolderActionPermission($action, $mockedFolder));
375 }
376
377 /**
378 * @test
379 */
380 public function checkUserActionPermissionsAlwaysReturnsTrueIfNoUserPermissionsAreSet()
381 {
382 $this->prepareSubject([]);
383 $this->assertTrue($this->subject->checkUserActionPermission('read', 'folder'));
384 }
385
386 /**
387 * @test
388 */
389 public function checkUserActionPermissionReturnsFalseIfPermissionIsSetToZero()
390 {
391 $this->prepareSubject([]);
392 $this->subject->setUserPermissions(['readFolder' => true, 'writeFile' => true]);
393 $this->assertTrue($this->subject->checkUserActionPermission('read', 'folder'));
394 }
395
396 public function checkUserActionPermission_arbitraryPermissionDataProvider()
397 {
398 return [
399 'all lower cased' => [
400 ['readFolder' => true],
401 'read',
402 'folder'
403 ],
404 'all upper case' => [
405 ['readFolder' => true],
406 'READ',
407 'FOLDER'
408 ],
409 'mixed case' => [
410 ['readFolder' => true],
411 'ReaD',
412 'FoLdEr'
413 ]
414 ];
415 }
416
417 /**
418 * @param array $permissions
419 * @param string $action
420 * @param string $type
421 * @test
422 * @dataProvider checkUserActionPermission_arbitraryPermissionDataProvider
423 */
424 public function checkUserActionPermissionAcceptsArbitrarilyCasedArguments(array $permissions, $action, $type)
425 {
426 $this->prepareSubject([]);
427 $this->subject->setUserPermissions($permissions);
428 $this->assertTrue($this->subject->checkUserActionPermission($action, $type));
429 }
430
431 /**
432 * @test
433 */
434 public function userActionIsDisallowedIfPermissionIsSetToFalse()
435 {
436 $this->prepareSubject([]);
437 $this->subject->setEvaluatePermissions(true);
438 $this->subject->setUserPermissions(['readFolder' => false]);
439 $this->assertFalse($this->subject->checkUserActionPermission('read', 'folder'));
440 }
441
442 /**
443 * @test
444 */
445 public function userActionIsDisallowedIfPermissionIsNotSet()
446 {
447 $this->prepareSubject([]);
448 $this->subject->setEvaluatePermissions(true);
449 $this->subject->setUserPermissions(['readFolder' => true]);
450 $this->assertFalse($this->subject->checkUserActionPermission('write', 'folder'));
451 }
452
453 /**
454 * @test
455 */
456 public function getEvaluatePermissionsWhenSetFalse()
457 {
458 $this->prepareSubject([]);
459 $this->subject->setEvaluatePermissions(false);
460 $this->assertFalse($this->subject->getEvaluatePermissions());
461 }
462
463 /**
464 * @test
465 */
466 public function getEvaluatePermissionsWhenSetTrue()
467 {
468 $this->prepareSubject([]);
469 $this->subject->setEvaluatePermissions(true);
470 $this->assertTrue($this->subject->getEvaluatePermissions());
471 }
472
473 /**
474 * @test
475 * @group integration
476 * @TODO: Rewrite or move to functional suite
477 */
478 public function setFileContentsUpdatesObjectProperties()
479 {
480 $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
481 $this->initializeVfs();
482 $driverObject = $this->getMockForAbstractClass(AbstractDriver::class, [], '', false);
483 $this->subject = $this->getMockBuilder(ResourceStorage::class)
484 ->setMethods(['getFileIndexRepository', 'checkFileActionPermission'])
485 ->setConstructorArgs([$driverObject, []])
486 ->getMock();
487 $this->subject->expects($this->any())->method('checkFileActionPermission')->will($this->returnValue(true));
488 $fileInfo = [
489 'storage' => 'A',
490 'identifier' => 'B',
491 'mtime' => 'C',
492 'ctime' => 'D',
493 'mimetype' => 'E',
494 'size' => 'F',
495 'name' => 'G',
496 ];
497 $newProperties = [
498 'storage' => $fileInfo['storage'],
499 'identifier' => $fileInfo['identifier'],
500 'tstamp' => $fileInfo['mtime'],
501 'crdate' => $fileInfo['ctime'],
502 'mime_type' => $fileInfo['mimetype'],
503 'size' => $fileInfo['size'],
504 'name' => $fileInfo['name']
505 ];
506 $hash = 'asdfg';
507 /** @var $mockedDriver LocalDriver|\PHPUnit_Framework_MockObject_MockObject */
508 $mockedDriver = $this->getMockBuilder(LocalDriver::class)
509 ->setConstructorArgs([['basePath' => $this->getMountRootUrl()]])
510 ->getMock();
511 $mockedDriver->expects($this->once())->method('getFileInfoByIdentifier')->will($this->returnValue($fileInfo));
512 $mockedDriver->expects($this->once())->method('hash')->will($this->returnValue($hash));
513 $this->subject->setDriver($mockedDriver);
514 $indexFileRepositoryMock = $this->createMock(FileIndexRepository::class);
515 $this->subject->expects($this->any())->method('getFileIndexRepository')->will($this->returnValue($indexFileRepositoryMock));
516 /** @var $mockedFile File|\PHPUnit_Framework_MockObject_MockObject */
517 $mockedFile = $this->createMock(File::class);
518 $mockedFile->expects($this->any())->method('getIdentifier')->will($this->returnValue($fileInfo['identifier']));
519 // called by indexer because the properties are updated
520 $this->subject->expects($this->any())->method('getFileInfoByIdentifier')->will($this->returnValue($newProperties));
521 $mockedFile->expects($this->any())->method('getStorage')->will($this->returnValue($this->subject));
522 $mockedFile->expects($this->any())->method('getProperties')->will($this->returnValue(array_keys($fileInfo)));
523 $mockedFile->expects($this->any())->method('getUpdatedProperties')->will($this->returnValue(array_keys($newProperties)));
524 // do not update directly; that's up to the indexer
525 $indexFileRepositoryMock->expects($this->never())->method('update');
526 $this->subject->setFileContents($mockedFile, $this->getUniqueId());
527 }
528
529 /**
530 * @test
531 * @group integration
532 * @TODO: Rewrite or move to functional suite
533 */
534 public function moveFileCallsDriversMethodsWithCorrectArguments()
535 {
536 $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
537 $localFilePath = '/path/to/localFile';
538 $sourceFileIdentifier = '/sourceFile.ext';
539 $fileInfoDummy = [
540 'storage' => 'A',
541 'identifier' => 'B',
542 'mtime' => 'C',
543 'ctime' => 'D',
544 'mimetype' => 'E',
545 'size' => 'F',
546 'name' => 'G',
547 ];
548 $this->addToMount([
549 'targetFolder' => []
550 ]);
551 $this->initializeVfs();
552 $targetFolder = $this->getSimpleFolderMock('/targetFolder/');
553 /** @var $sourceDriver LocalDriver|\PHPUnit_Framework_MockObject_MockObject */
554 $sourceDriver = $this->createMock(LocalDriver::class);
555 $sourceDriver->expects($this->once())->method('deleteFile')->with($this->equalTo($sourceFileIdentifier));
556 $configuration = $this->convertConfigurationArrayToFlexformXml([]);
557 $sourceStorage = new ResourceStorage($sourceDriver, ['configuration' => $configuration]);
558 $sourceFile = $this->getSimpleFileMock($sourceFileIdentifier);
559 $sourceFile->expects($this->once())->method('getForLocalProcessing')->will($this->returnValue($localFilePath));
560 $sourceFile->expects($this->any())->method('getStorage')->will($this->returnValue($sourceStorage));
561 $sourceFile->expects($this->once())->method('getUpdatedProperties')->will($this->returnValue(array_keys($fileInfoDummy)));
562 $sourceFile->expects($this->once())->method('getProperties')->will($this->returnValue($fileInfoDummy));
563 /** @var $mockedDriver \TYPO3\CMS\Core\Resource\Driver\LocalDriver|\PHPUnit_Framework_MockObject_MockObject */
564 $mockedDriver = $this->getMockBuilder(LocalDriver::class)
565 ->setConstructorArgs([['basePath' => $this->getMountRootUrl()]])
566 ->getMock();
567 $mockedDriver->expects($this->once())->method('getFileInfoByIdentifier')->will($this->returnValue($fileInfoDummy));
568 $mockedDriver->expects($this->once())->method('addFile')->with($localFilePath, '/targetFolder/', $this->equalTo('file.ext'))->will($this->returnValue('/targetFolder/file.ext'));
569 /** @var $subject ResourceStorage */
570 $subject = $this->getMockBuilder(ResourceStorage::class)
571 ->setMethods(['assureFileMovePermissions'])
572 ->setConstructorArgs([$mockedDriver, ['configuration' => $configuration]])
573 ->getMock();
574 $subject->moveFile($sourceFile, $targetFolder, 'file.ext');
575 }
576
577 /**
578 * @test
579 * @group integration
580 * @TODO: Rewrite or move to functional suite
581 */
582 public function storageUsesInjectedFilemountsToCheckForMountBoundaries()
583 {
584 $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
585 $mockedFile = $this->getSimpleFileMock('/mountFolder/file');
586 $this->addToMount([
587 'mountFolder' => [
588 'file' => 'asdfg'
589 ]
590 ]);
591 $mockedDriver = $this->createDriverMock(['basePath' => $this->getMountRootUrl()], null, null);
592 $this->initializeVfs();
593 $this->prepareSubject([], null, $mockedDriver);
594 $this->subject->addFileMount('/mountFolder');
595 $this->assertEquals(1, count($this->subject->getFileMounts()));
596 $this->subject->isWithinFileMountBoundaries($mockedFile);
597 }
598
599 /**
600 * @test
601 * @TODO: Rewrite or move to functional suite
602 */
603 public function createFolderChecksIfParentFolderExistsBeforeCreatingFolder()
604 {
605 $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
606 $mockedParentFolder = $this->getSimpleFolderMock('/someFolder/');
607 $mockedDriver = $this->createDriverMock([]);
608 $mockedDriver->expects($this->once())->method('folderExists')->with($this->equalTo('/someFolder/'))->will($this->returnValue(true));
609 $mockedDriver->expects($this->once())->method('createFolder')->with($this->equalTo('newFolder'))->will($this->returnValue($mockedParentFolder));
610 $this->prepareSubject([], true);
611 $this->subject->setDriver($mockedDriver);
612 $this->subject->createFolder('newFolder', $mockedParentFolder);
613 }
614
615 /**
616 * @test
617 */
618 public function deleteFolderThrowsExceptionIfFolderIsNotEmptyAndRecursiveDeleteIsDisabled()
619 {
620 $this->expectException(\RuntimeException::class);
621 $this->expectExceptionCode(1325952534);
622
623 /** @var \TYPO3\CMS\Core\Resource\Folder|\PHPUnit_Framework_MockObject_MockObject $folderMock */
624 $folderMock = $this->createMock(Folder::class);
625 /** @var $mockedDriver \TYPO3\CMS\Core\Resource\Driver\AbstractDriver|\PHPUnit_Framework_MockObject_MockObject */
626 $mockedDriver = $this->getMockForAbstractClass(AbstractDriver::class);
627 $mockedDriver->expects($this->once())->method('isFolderEmpty')->will($this->returnValue(false));
628 /** @var $subject ResourceStorage|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\TestingFramework\Core\AccessibleObjectInterface */
629 $subject = $this->getAccessibleMock(ResourceStorage::class, ['checkFolderActionPermission'], [], '', false);
630 $subject->expects($this->any())->method('checkFolderActionPermission')->will($this->returnValue(true));
631 $subject->_set('driver', $mockedDriver);
632 $subject->deleteFolder($folderMock, false);
633 }
634
635 /**
636 * @test
637 * @TODO: Rewrite or move to functional suite
638 */
639 public function createFolderCallsDriverForFolderCreation()
640 {
641 $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
642 $mockedParentFolder = $this->getSimpleFolderMock('/someFolder/');
643 $this->prepareSubject([], true);
644 $mockedDriver = $this->createDriverMock([], $this->subject);
645 $mockedDriver->expects($this->once())->method('createFolder')->with($this->equalTo('newFolder'), $this->equalTo('/someFolder/'))->will($this->returnValue(true));
646 $mockedDriver->expects($this->once())->method('folderExists')->with($this->equalTo('/someFolder/'))->will($this->returnValue(true));
647 $this->subject->createFolder('newFolder', $mockedParentFolder);
648 }
649
650 /**
651 * @test
652 * @TODO: Rewrite or move to functional suite
653 */
654 public function createFolderCanRecursivelyCreateFolders()
655 {
656 $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
657 $this->addToMount(['someFolder' => []]);
658 $mockedDriver = $this->createDriverMock(['basePath' => $this->getMountRootUrl()], null, null);
659 $this->prepareSubject([], true, $mockedDriver);
660 $parentFolder = $this->subject->getFolder('/someFolder/');
661 $newFolder = $this->subject->createFolder('subFolder/secondSubfolder', $parentFolder);
662 $this->assertEquals('secondSubfolder', $newFolder->getName());
663 $this->assertFileExists($this->getUrlInMount('/someFolder/subFolder/'));
664 $this->assertFileExists($this->getUrlInMount('/someFolder/subFolder/secondSubfolder/'));
665 }
666
667 /**
668 * @test
669 * @TODO: Rewrite or move to functional suite
670 */
671 public function createFolderUsesRootFolderAsParentFolderIfNotGiven()
672 {
673 $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
674 $this->prepareSubject([], true);
675 $mockedDriver = $this->createDriverMock([], $this->subject);
676 $mockedDriver->expects($this->once())->method('getRootLevelFolder')->with()->will($this->returnValue('/'));
677 $mockedDriver->expects($this->once())->method('createFolder')->with($this->equalTo('someFolder'));
678 $this->subject->createFolder('someFolder');
679 }
680
681 /**
682 * @test
683 * @TODO: Rewrite or move to functional suite
684 */
685 public function createFolderCreatesNestedStructureEvenIfPartsAlreadyExist()
686 {
687 $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
688 $this->addToMount([
689 'existingFolder' => []
690 ]);
691 $this->initializeVfs();
692 $mockedDriver = $this->createDriverMock(['basePath' => $this->getMountRootUrl()], null, null);
693 $this->prepareSubject([], true, $mockedDriver);
694 $rootFolder = $this->subject->getFolder('/');
695 $newFolder = $this->subject->createFolder('existingFolder/someFolder', $rootFolder);
696 $this->assertEquals('someFolder', $newFolder->getName());
697 $this->assertFileExists($this->getUrlInMount('existingFolder/someFolder'));
698 }
699
700 /**
701 * @test
702 */
703 public function createFolderThrowsExceptionIfParentFolderDoesNotExist()
704 {
705 $this->expectException(\InvalidArgumentException::class);
706 $this->expectExceptionCode(1325689164);
707 $mockedParentFolder = $this->getSimpleFolderMock('/someFolder/');
708 $this->prepareSubject([], true);
709 $mockedDriver = $this->createDriverMock([], $this->subject);
710 $mockedDriver->expects($this->once())->method('folderExists')->with($this->equalTo('/someFolder/'))->will($this->returnValue(false));
711 $this->subject->createFolder('newFolder', $mockedParentFolder);
712 }
713
714 /**
715 * @test
716 */
717 public function renameFileRenamesFileAsRequested()
718 {
719 $mockedDriver = $this->createDriverMock([], $this->subject);
720 $mockedDriver->expects($this->once())->method('renameFile')->will($this->returnValue('bar'));
721 $this->prepareSubject([], true, $mockedDriver, [], ['emitPreFileRenameSignal', 'emitPostFileRenameSignal']);
722 /** @var File $file */
723 $file = new File(['identifier' => 'foo', 'name' => 'foo'], $this->subject);
724 $result = $this->subject->renameFile($file, 'bar');
725 // fake what the indexer does in updateIndexEntry
726 $result->updateProperties(['name' => $result->getIdentifier()]);
727 $this->assertSame('bar', $result->getName());
728 }
729
730 /**
731 * @test
732 */
733 public function renameFileRenamesWithUniqueNameIfConflictAndConflictModeIsRename()
734 {
735 $mockedDriver = $this->createDriverMock([], $this->subject);
736 $mockedDriver->expects($this->any())->method('renameFile')->will($this->onConsecutiveCalls($this->throwException(new ExistingTargetFileNameException('foo', 1489593090)), 'bar_01'));
737 //$mockedDriver->expects($this->at(1))->method('renameFile')->will($this->returnValue('bar_01'));
738 $mockedDriver->expects($this->any())->method('sanitizeFileName')->will($this->onConsecutiveCalls('bar', 'bar_01'));
739 $this->prepareSubject([], true, $mockedDriver, [], ['emitPreFileRenameSignal', 'emitPostFileRenameSignal', 'getUniqueName']);
740 /** @var File $file */
741 $file = new File(['identifier' => 'foo', 'name' => 'foo'], $this->subject);
742 $this->subject->expects($this->once())->method('getUniqueName')->will($this->returnValue('bar_01'));
743 $result = $this->subject->renameFile($file, 'bar');
744 // fake what the indexer does in updateIndexEntry
745 $result->updateProperties(['name' => $result->getIdentifier()]);
746 $this->assertSame('bar_01', $result->getName());
747 }
748
749 /**
750 * @test
751 */
752 public function renameFileThrowsExceptionIfConflictAndConflictModeIsCancel()
753 {
754 $mockedDriver = $this->createDriverMock([], $this->subject);
755 $mockedDriver->expects($this->once())->method('renameFile')->will($this->throwException(new ExistingTargetFileNameException('foo', 1489593099)));
756 $this->prepareSubject([], true, $mockedDriver, [], ['emitPreFileRenameSignal', 'emitPostFileRenameSignal']);
757 /** @var File $file */
758 $file = new File(['identifier' => 'foo', 'name' => 'foo'], $this->subject);
759 $this->expectException(ExistingTargetFileNameException::class);
760 $this->subject->renameFile($file, 'bar', DuplicationBehavior::CANCEL);
761 }
762
763 /**
764 * @test
765 */
766 public function renameFileReplacesIfConflictAndConflictModeIsReplace()
767 {
768 $mockedDriver = $this->createDriverMock([], $this->subject);
769 $mockedDriver->expects($this->once())->method('renameFile')->will($this->throwException(new ExistingTargetFileNameException('foo', 1489593098)));
770 $mockedDriver->expects($this->any())->method('sanitizeFileName')->will($this->returnValue('bar'));
771 $this->prepareSubject([], true, $mockedDriver, [], ['emitPreFileRenameSignal', 'emitPostFileRenameSignal', 'replaceFile', 'getPublicUrl', 'getResourceFactoryInstance']);
772 $this->subject->expects($this->once())->method('getPublicUrl')->will($this->returnValue('somePath'));
773 $resourceFactory = $this->prophesize(ResourceFactory::class);
774 $file = $this->prophesize(FileInterface::class);
775 $resourceFactory->getFileObjectFromCombinedIdentifier(Argument::any())->willReturn($file->reveal());
776 $this->subject->expects($this->once())->method('replaceFile')->will($this->returnValue($file->reveal()));
777 $this->subject->expects($this->any())->method('getResourceFactoryInstance')->will(self::returnValue($resourceFactory->reveal()));
778 /** @var File $file */
779 $file = new File(['identifier' => 'foo', 'name' => 'foo', 'missing' => false], $this->subject);
780 $this->subject->renameFile($file, 'bar', DuplicationBehavior::REPLACE);
781 }
782 }