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