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