Source of file AssetStoreTest.php
Size: 45,020 Bytes - Last Modified: 2021-12-23T10:27:40+00:00
/var/www/docs.ssmods.com/process/src/tests/php/Storage/AssetStoreTest.php
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199 | <?php namespace SilverStripe\Assets\Tests\Storage; use Exception; use InvalidArgumentException; use League\Flysystem\Filesystem; use Silverstripe\Assets\Dev\TestAssetStore; use SilverStripe\Assets\File; use SilverStripe\Assets\FilenameParsing\FileIDHelper; use SilverStripe\Assets\FilenameParsing\HashFileIDHelper; use SilverStripe\Assets\FilenameParsing\LegacyFileIDHelper; use SilverStripe\Assets\FilenameParsing\NaturalFileIDHelper; use SilverStripe\Assets\FilenameParsing\ParsedFileID; use SilverStripe\Assets\Flysystem\FlysystemAssetStore; use SilverStripe\Assets\Storage\AssetStore; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Injector\Injector; use SilverStripe\Dev\SapphireTest; class AssetStoreTest extends SapphireTest { /** * @skipUpgrade */ protected function setUp(): void { parent::setUp(); // Set backend and base url TestAssetStore::activate('AssetStoreTest'); } protected function tearDown(): void { TestAssetStore::reset(); parent::tearDown(); } /** * @return TestAssetStore */ protected function getBackend() { return Injector::inst()->get(AssetStore::class); } /** * Test different storage methods */ public function testStorageMethods() { $backend = $this->getBackend(); // Test setFromContent $puppies1 = 'puppies'; $puppies1Tuple = $backend->setFromString($puppies1, 'pets/my-puppy.txt'); $this->assertEquals( [ 'Hash' => '2a17a9cb4be918774e73ba83bd1c1e7d000fdd53', 'Filename' => 'pets/my-puppy.txt', 'Variant' => '', ], $puppies1Tuple ); // Test setFromStream (seekable) $fish1 = realpath(__DIR__ . '/../ImageTest/test-image-high-quality.jpg'); $fish1Stream = fopen($fish1, 'r'); $fish1Tuple = $backend->setFromStream($fish1Stream, 'parent/awesome-fish.jpg'); fclose($fish1Stream); $this->assertEquals( [ 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1', 'Filename' => 'parent/awesome-fish.jpg', 'Variant' => '', ], $fish1Tuple ); // Test with non-seekable streams TestAssetStore::$seekable_override = false; $fish2 = realpath(__DIR__ . '/../ImageTest/test-image-low-quality.jpg'); $fish2Stream = fopen($fish2, 'r'); $fish2Tuple = $backend->setFromStream($fish2Stream, 'parent/mediocre-fish.jpg'); fclose($fish2Stream); $this->assertEquals( [ 'Hash' => '33be1b95cba0358fe54e8b13532162d52f97421c', 'Filename' => 'parent/mediocre-fish.jpg', 'Variant' => '', ], $fish2Tuple ); TestAssetStore::$seekable_override = null; } /** * Test that the backend correctly resolves conflicts */ public function testConflictResolution() { $backend = $this->getBackend(); // Put a file in $fish1 = realpath(__DIR__ . '/../ImageTest/test-image-high-quality.jpg'); $this->assertFileExists($fish1); $fish1Tuple = $backend->setFromLocalFile($fish1, 'directory/lovely-fish.jpg'); $this->assertEquals( [ 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1', 'Filename' => 'directory/lovely-fish.jpg', 'Variant' => '', ], $fish1Tuple ); $this->assertEquals( '/assets/directory/a870de278b/lovely-fish.jpg', $backend->getAsURL($fish1Tuple['Filename'], $fish1Tuple['Hash']), 'Files should default to being written to the protected store' ); // Write a different file with same name. Should not detect duplicates since sha are different $fish2 = realpath(__DIR__ . '/../ImageTest/test-image-low-quality.jpg'); $fish2Tuple = $backend->setFromLocalFile( $fish2, 'directory/lovely-fish.jpg', '', null, ['conflict' => AssetStore::CONFLICT_EXCEPTION] ); $this->assertEquals( [ 'Hash' => '33be1b95cba0358fe54e8b13532162d52f97421c', 'Filename' => 'directory/lovely-fish.jpg', 'Variant' => '', ], $fish2Tuple ); $this->assertEquals( '/assets/directory/33be1b95cb/lovely-fish.jpg', $backend->getAsURL($fish2Tuple['Filename'], $fish2Tuple['Hash']) ); // Write original file back with rename $this->assertFileExists($fish1); $fish3Tuple = $backend->setFromLocalFile( $fish1, 'directory/lovely-fish.jpg', null, null, ['conflict' => AssetStore::CONFLICT_RENAME] ); $this->assertEquals( [ 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1', 'Filename' => 'directory/lovely-fish-v2.jpg', 'Variant' => '', ], $fish3Tuple ); $this->assertEquals( '/assets/directory/a870de278b/lovely-fish-v2.jpg', $backend->getAsURL($fish3Tuple['Filename'], $fish3Tuple['Hash']) ); // Write another file should increment to -v3 $fish4Tuple = $backend->setFromLocalFile( $fish1, 'directory/lovely-fish.jpg', null, null, ['conflict' => AssetStore::CONFLICT_RENAME] ); $this->assertEquals( [ 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1', 'Filename' => 'directory/lovely-fish-v3.jpg', 'Variant' => '', ], $fish4Tuple ); $this->assertEquals( '/assets/directory/a870de278b/lovely-fish-v3.jpg', $backend->getAsURL($fish4Tuple['Filename'], $fish4Tuple['Hash']) ); // Test conflict use existing file $fish5Tuple = $backend->setFromLocalFile( $fish1, 'directory/lovely-fish.jpg', null, null, ['conflict' => AssetStore::CONFLICT_USE_EXISTING] ); $this->assertEquals( [ 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1', 'Filename' => 'directory/lovely-fish.jpg', 'Variant' => '', ], $fish5Tuple ); $this->assertEquals( '/assets/directory/a870de278b/lovely-fish.jpg', $backend->getAsURL($fish5Tuple['Filename'], $fish5Tuple['Hash']) ); // Test conflict use existing file $fish6Tuple = $backend->setFromLocalFile( $fish1, 'directory/lovely-fish.jpg', null, null, ['conflict' => AssetStore::CONFLICT_OVERWRITE] ); $this->assertEquals( [ 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1', 'Filename' => 'directory/lovely-fish.jpg', 'Variant' => '', ], $fish6Tuple ); $this->assertEquals( '/assets/directory/a870de278b/lovely-fish.jpg', $backend->getAsURL($fish6Tuple['Filename'], $fish6Tuple['Hash']) ); } /** * Test that flysystem can regenerate the original filename from fileID */ public function testGetOriginalFilename() { $store = new TestAssetStore(); $this->assertEquals( 'directory/lovely-fish.jpg', $store->getOriginalFilename('directory/a870de278b/lovely-fish.jpg') ); $this->assertEquals( 'directory/lovely-fish.jpg', $store->getOriginalFilename('directory/a870de278b/lovely-fish__variant.jpg') ); $this->assertEquals( 'directory/lovely_fish.jpg', $store->getOriginalFilename('directory/a870de278b/lovely_fish__vari_ant.jpg') ); $this->assertEquals( 'directory/lovely_fish.jpg', $store->getOriginalFilename('directory/a870de278b/lovely_fish.jpg') ); $this->assertEquals( 'lovely-fish.jpg', $store->getOriginalFilename('a870de278b/lovely-fish.jpg') ); $this->assertEquals( 'lovely-fish.jpg', $store->getOriginalFilename('a870de278b/lovely-fish__variant.jpg') ); $this->assertEquals( 'lovely_fish.jpg', $store->getOriginalFilename('a870de278b/lovely_fish__vari__ant.jpg') ); $this->assertEquals( 'lovely_fish.jpg', $store->getOriginalFilename('a870de278b/lovely_fish.jpg') ); } /** * Data provider for reversible file ids * * @return array */ public function dataProviderFileIDs() { return [ [ 'directory/2a17a9cb4b/file.jpg', [ 'Filename' => 'directory/file.jpg', 'Hash' => substr(sha1('puppies'), 0, 10), 'Variant' => null ], ], [ '2a17a9cb4b/file.jpg', [ 'Filename' => 'file.jpg', 'Hash' => substr(sha1('puppies'), 0, 10), 'Variant' => null ], ], [ 'dir_ectory/2a17a9cb4b/file_e.jpg', [ 'Filename' => 'dir_ectory/file_e.jpg', 'Hash' => substr(sha1('puppies'), 0, 10), 'Variant' => null ], ], [ 'directory/2a17a9cb4b/file__variant.jpg', [ 'Filename' => 'directory/file.jpg', 'Hash' => substr(sha1('puppies'), 0, 10), 'Variant' => 'variant' ], ], [ '2a17a9cb4b/file__var__iant.jpg', [ 'Filename' => 'file.jpg', 'Hash' => substr(sha1('puppies'), 0, 10), 'Variant' => 'var__iant' ], ], [ '2a17a9cb4b/file__var__iant', [ 'Filename' => 'file', 'Hash' => substr(sha1('puppies'), 0, 10), 'Variant' => 'var__iant' ], ], [ '2a17a9cb4b/file', [ 'Filename' => 'file', 'Hash' => substr(sha1('puppies'), 0, 10), 'Variant' => null ], ] ]; } /** * Data providers for files which need cleaning up (only when generating fileID) */ public function dataProviderDirtyFileIDs() { return [ [ 'directory/2a17a9cb4b/file_variant.jpg', [ 'Filename' => 'directory/file__variant.jpg', // '__' in filename is invalid, will get collapsed 'Hash' => sha1('puppies'), 'Variant' => null ], ] ]; } /** * Data provider for non-file IDs */ public function dataProviderHashlessFileIDs() { return [ [ 'some/folder/file.jpg', ['Filename' => 'some/folder/file.jpg', 'Hash' => '', 'Variant' => '' ] ], [ 'file.jpg', ['Filename' => 'file.jpg', 'Hash' => '', 'Variant' => '' ] ] ]; } /** * Test internal file Id generation * * @dataProvider dataProviderFileIDs * @dataProvider dataProviderDirtyFileIDs * @param string $fileID Expected file ID * @param array $tuple Tuple that generates this file ID */ public function testGetFileID($fileID, $tuple) { /** @var TestAssetStore $store */ $store = Injector::inst()->get(AssetStore::class); $this->assertEquals( $fileID, $store->getFileID($tuple['Filename'], $tuple['Hash'], $tuple['Variant']) ); } /** * Test reversing of FileIDs * * @dataProvider dataProviderFileIDs * @dataProvider dataProviderHashlessFileIDs * @param string $fileID File ID to parse * @param array|null $tuple expected parsed tuple, or null if invalid */ public function testParseFileID($fileID, $tuple) { $store = new TestAssetStore(); $result = $store->parseFileID($fileID); if (is_null($tuple)) { $this->assertNull($result, "$fileID should be treated as invalid fileID"); } else { $this->assertEquals($tuple['Filename'], $result['Filename']); $this->assertEquals($tuple['Variant'], $result['Variant']); $this->assertEquals($tuple['Hash'], $result['Hash']); } } public function testGetMetadata() { $backend = $this->getBackend(); // jpg $fish = realpath(__DIR__ . '/../ImageTest/test-image-high-quality.jpg'); $fishTuple = $backend->setFromLocalFile($fish, 'parent/awesome-fish.jpg'); $this->assertEquals( 'image/jpeg', $backend->getMimeType($fishTuple['Filename'], $fishTuple['Hash']) ); $fishMeta = $backend->getMetadata($fishTuple['Filename'], $fishTuple['Hash']); $this->assertEquals(151889, $fishMeta['size']); $this->assertEquals('file', $fishMeta['type']); $this->assertNotEmpty($fishMeta['timestamp']); // text $puppies = 'puppies'; $puppiesTuple = $backend->setFromString($puppies, 'pets/my-puppy.txt'); $this->assertEquals( 'text/plain', $backend->getMimeType($puppiesTuple['Filename'], $puppiesTuple['Hash']) ); $puppiesMeta = $backend->getMetadata($puppiesTuple['Filename'], $puppiesTuple['Hash']); $this->assertEquals(7, $puppiesMeta['size']); $this->assertEquals('file', $puppiesMeta['type']); $this->assertNotEmpty($puppiesMeta['timestamp']); } /** * Test that legacy filenames work as expected. This test is somewhate reduntant now because legacy filename * should be ignored. */ public function testLegacyFilenames() { Config::modify()->set(FlysystemAssetStore::class, 'legacy_filenames', true); $backend = $this->getBackend(); // Put a file in $fish1 = realpath(__DIR__ . '/../ImageTest/test-image-high-quality.jpg'); $this->assertFileExists($fish1); $fish1Tuple = $backend->setFromLocalFile($fish1, 'directory/lovely-fish.jpg'); $this->assertEquals( [ 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1', 'Filename' => 'directory/lovely-fish.jpg', 'Variant' => '', ], $fish1Tuple ); $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/.protected/directory/a870de278b/lovely-fish.jpg'); $this->assertEquals( '/assets/directory/a870de278b/lovely-fish.jpg', $backend->getAsURL($fish1Tuple['Filename'], $fish1Tuple['Hash']) ); // Write a different file with same name. // Since we are using legacy filenames, this should generate a new filename $fish2 = realpath(__DIR__ . '/../ImageTest/test-image-low-quality.jpg'); try { $backend->setFromLocalFile( $fish2, 'directory/lovely-fish.jpg', null, null, ['conflict' => AssetStore::CONFLICT_EXCEPTION] ); $this->fail('Writing file with different sha to same location should throw exception'); return; } catch (Exception $ex) { // Success } // Re-attempt this file write with conflict_rename $fish3Tuple = $backend->setFromLocalFile( $fish2, 'directory/lovely-fish.jpg', null, null, ['conflict' => AssetStore::CONFLICT_RENAME] ); $this->assertEquals( [ 'Hash' => '33be1b95cba0358fe54e8b13532162d52f97421c', 'Filename' => 'directory/lovely-fish-v2.jpg', 'Variant' => '', ], $fish3Tuple ); $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/.protected/directory/33be1b95cb/lovely-fish-v2.jpg'); $this->assertEquals( '/assets/directory/33be1b95cb/lovely-fish-v2.jpg', $backend->getAsURL($fish3Tuple['Filename'], $fish3Tuple['Hash']) ); // Write back original file, but with CONFLICT_EXISTING. The file should not change $backend->publish('directory/lovely-fish-v2.jpg', '33be1b95cba0358fe54e8b13532162d52f97421c'); $fish4Tuple = $backend->setFromLocalFile( $fish1, 'directory/lovely-fish-v2.jpg', null, null, ['conflict' => AssetStore::CONFLICT_USE_EXISTING, 'visibility' => AssetStore::VISIBILITY_PUBLIC] ); $this->assertEquals( [ 'Hash' => '33be1b95cba0358fe54e8b13532162d52f97421c', 'Filename' => 'directory/lovely-fish-v2.jpg', 'Variant' => '', ], $fish4Tuple ); $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/directory/lovely-fish-v2.jpg'); $this->assertEquals( '/assets/AssetStoreTest/directory/lovely-fish-v2.jpg', $backend->getAsURL($fish4Tuple['Filename'], $fish4Tuple['Hash']) ); // Write back original file with CONFLICT_OVERWRITE. The file sha should now be updated $fish5Tuple = $backend->setFromLocalFile( $fish1, 'directory/lovely-fish-v2.jpg', null, null, ['conflict' => AssetStore::CONFLICT_OVERWRITE, 'visibility' => AssetStore::VISIBILITY_PUBLIC] ); $this->assertEquals( [ 'Hash' => 'a870de278b475cb75f5d9f451439b2d378e13af1', 'Filename' => 'directory/lovely-fish-v2.jpg', 'Variant' => '', ], $fish5Tuple ); $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/directory/lovely-fish-v2.jpg'); $this->assertEquals( '/assets/AssetStoreTest/directory/lovely-fish-v2.jpg', $backend->getAsURL($fish5Tuple['Filename'], $fish5Tuple['Hash']) ); } /** * Test default conflict resolution */ public function testDefaultConflictResolution() { $store = $this->getBackend(); // Disable legacy filenames Config::modify()->set(FlysystemAssetStore::class, 'legacy_filenames', false); $this->assertEquals(AssetStore::CONFLICT_OVERWRITE, $store->getDefaultConflictResolution(null)); $this->assertEquals(AssetStore::CONFLICT_OVERWRITE, $store->getDefaultConflictResolution('somevariant')); // Enable legacy filenames -- legacy filename used to have different conflict resolution prior to 1.4.0 Config::modify()->set(FlysystemAssetStore::class, 'legacy_filenames', true); $this->assertEquals(AssetStore::CONFLICT_OVERWRITE, $store->getDefaultConflictResolution(null)); $this->assertEquals(AssetStore::CONFLICT_OVERWRITE, $store->getDefaultConflictResolution('somevariant')); } /** * Test protect / publish mechanisms */ public function testProtect() { $backend = $this->getBackend(); $fish = realpath(__DIR__ . '/../ImageTest/test-image-high-quality.jpg'); $fishTuple = $backend->setFromLocalFile( $fish, 'parent/lovely-fish.jpg', null, null, ['visibility' => AssetStore::VISIBILITY_PUBLIC] ); $fishVariantTuple = $backend->setFromLocalFile($fish, $fishTuple['Filename'], $fishTuple['Hash'], 'copy'); // Test public file storage $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/parent/lovely-fish.jpg'); $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/parent/lovely-fish__copy.jpg'); $this->assertEquals( AssetStore::VISIBILITY_PUBLIC, $backend->getVisibility($fishTuple['Filename'], $fishTuple['Hash']) ); $this->assertEquals( '/assets/AssetStoreTest/parent/lovely-fish.jpg', $backend->getAsURL($fishTuple['Filename'], $fishTuple['Hash']) ); $this->assertEquals( '/assets/AssetStoreTest/parent/lovely-fish__copy.jpg', $backend->getAsURL($fishVariantTuple['Filename'], $fishVariantTuple['Hash'], $fishVariantTuple['Variant']) ); // Test access rights to public files cannot be revoked $backend->revoke($fishTuple['Filename'], $fishTuple['Hash']); // can't revoke public assets $this->assertTrue($backend->canView($fishTuple['Filename'], $fishTuple['Hash'])); // Test protected file storage $backend->protect($fishTuple['Filename'], $fishTuple['Hash']); $this->assertFileDoesNotExist(ASSETS_PATH . '/AssetStoreTest/parent/lovely-fish.jpg'); $this->assertFileDoesNotExist(ASSETS_PATH . '/AssetStoreTest/parent/lovely-fish__copy.jpg'); $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/.protected/parent/a870de278b/lovely-fish.jpg'); $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/.protected/parent/a870de278b/lovely-fish__copy.jpg'); $this->assertEquals( AssetStore::VISIBILITY_PROTECTED, $backend->getVisibility($fishTuple['Filename'], $fishTuple['Hash']) ); // Test access rights $backend->revoke($fishTuple['Filename'], $fishTuple['Hash']); $this->assertFalse($backend->canView($fishTuple['Filename'], $fishTuple['Hash'])); $backend->grant($fishTuple['Filename'], $fishTuple['Hash']); $this->assertTrue($backend->canView($fishTuple['Filename'], $fishTuple['Hash'])); // Protected urls should go through asset routing mechanism $this->assertEquals( '/assets/parent/a870de278b/lovely-fish.jpg', $backend->getAsURL($fishTuple['Filename'], $fishTuple['Hash']) ); $this->assertEquals( '/assets/parent/a870de278b/lovely-fish__copy.jpg', $backend->getAsURL($fishVariantTuple['Filename'], $fishVariantTuple['Hash'], $fishVariantTuple['Variant']) ); // Publish reverts visibility $backend->publish($fishTuple['Filename'], $fishTuple['Hash']); $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/parent/lovely-fish.jpg'); $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/parent/lovely-fish__copy.jpg'); $this->assertFileDoesNotExist(ASSETS_PATH . '/AssetStoreTest/.protected/parent/a870de278b/lovely-fish.jpg'); $this->assertFileDoesNotExist(ASSETS_PATH . '/AssetStoreTest/.protected/parent/a870de278b/lovely-fish__copy.jpg'); $this->assertEquals( AssetStore::VISIBILITY_PUBLIC, $backend->getVisibility($fishTuple['Filename'], $fishTuple['Hash']) ); // Protected urls should go through asset routing mechanism $this->assertEquals( '/' . ASSETS_DIR . '/AssetStoreTest/parent/lovely-fish.jpg', $backend->getAsURL($fishTuple['Filename'], $fishTuple['Hash']) ); $this->assertEquals( '/' . ASSETS_DIR . '/AssetStoreTest/parent/lovely-fish__copy.jpg', $backend->getAsURL($fishVariantTuple['Filename'], $fishVariantTuple['Hash'], $fishVariantTuple['Variant']) ); } public function testRename() { $backend = $this->getBackend(); $fish1 = realpath(__DIR__ . '/../ImageTest/test-image-high-quality.jpg'); // Create file with various variants $fish1Tuple = $backend->setFromLocalFile($fish1, 'directory/lovely-fish.jpg'); $backend->setFromLocalFile($fish1, $fish1Tuple['Filename'], $fish1Tuple['Hash'], 'somevariant'); $backend->setFromLocalFile($fish1, $fish1Tuple['Filename'], $fish1Tuple['Hash'], 'anothervariant'); // Move to new filename $newFilename = 'another-file.jpg'; $confirmedFilename = $backend->rename($fish1Tuple['Filename'], $fish1Tuple['Hash'], $newFilename); // Check result $this->assertEquals($newFilename, $confirmedFilename); $this->assertNotEquals($fish1Tuple['Filename'], $confirmedFilename); // Check old files no longer exist $this->assertFalse($backend->exists($fish1Tuple['Filename'], $fish1Tuple['Hash'])); $this->assertFalse($backend->exists($fish1Tuple['Filename'], $fish1Tuple['Hash'], 'somevariant')); $this->assertFalse($backend->exists($fish1Tuple['Filename'], $fish1Tuple['Hash'], 'anothervariant')); // New files exist $this->assertTrue($backend->exists($newFilename, $fish1Tuple['Hash'])); $this->assertTrue($backend->exists($newFilename, $fish1Tuple['Hash'], 'somevariant')); $this->assertTrue($backend->exists($newFilename, $fish1Tuple['Hash'], 'anothervariant')); // Ensure we aren't getting false positives for exists() $this->assertFalse($backend->exists($fish1Tuple['Filename'], $fish1Tuple['Hash'], 'nonvariant')); $this->assertFalse($backend->exists($fish1Tuple['Filename'], sha1('nothash'))); $this->assertFalse($backend->exists($newFilename, $fish1Tuple['Hash'], 'nonvariant')); $this->assertFalse($backend->exists('notfilename.jpg', $fish1Tuple['Hash'])); } public function testCopy() { $backend = $this->getBackend(); $fish1 = realpath(__DIR__ . '/../ImageTest/test-image-high-quality.jpg'); // Create file with various variants $fish1Tuple = $backend->setFromLocalFile($fish1, 'directory/lovely-fish.jpg'); $backend->setFromLocalFile($fish1, $fish1Tuple['Filename'], $fish1Tuple['Hash'], 'somevariant'); $backend->setFromLocalFile($fish1, $fish1Tuple['Filename'], $fish1Tuple['Hash'], 'anothervariant'); // Move to new filename $newFilename = 'another-file.jpg'; $confirmedFilename = $backend->copy($fish1Tuple['Filename'], $fish1Tuple['Hash'], $newFilename); // Check result $this->assertEquals($newFilename, $confirmedFilename); $this->assertNotEquals($fish1Tuple['Filename'], $confirmedFilename); // Check old files haven't been deleted $this->assertTrue($backend->exists($fish1Tuple['Filename'], $fish1Tuple['Hash'])); $this->assertTrue($backend->exists($fish1Tuple['Filename'], $fish1Tuple['Hash'], 'somevariant')); $this->assertTrue($backend->exists($fish1Tuple['Filename'], $fish1Tuple['Hash'], 'anothervariant')); // New files exist $this->assertTrue($backend->exists($newFilename, $fish1Tuple['Hash'])); $this->assertTrue($backend->exists($newFilename, $fish1Tuple['Hash'], 'somevariant')); $this->assertTrue($backend->exists($newFilename, $fish1Tuple['Hash'], 'anothervariant')); } public function testStoreLocationWritingLogic() { $backend = $this->getBackend(); // Test defaults $tuple = $backend->setFromString('defaultsToProtectedStore', 'defaultsToProtectedStore.txt'); $this->assertEquals( AssetStore::VISIBILITY_PROTECTED, $backend->getVisibility($tuple['Filename'], $tuple['Hash']) ); // Test protected $tuple = $backend->setFromString( 'explicitely Protected Store', 'explicitelyProtectedStore.txt', null, null, ['visibility' => AssetStore::VISIBILITY_PROTECTED] ); $this->assertEquals( AssetStore::VISIBILITY_PROTECTED, $backend->getVisibility($tuple['Filename'], $tuple['Hash']) ); $tuple = $backend->setFromString( 'variant Protected Store', 'explicitelyProtectedStore.txt', $tuple['Hash'], 'variant' ); $hash = substr($tuple['Hash'], 0, 10); $this->assertFileExists( ASSETS_PATH . "/AssetStoreTest/.protected/$hash/explicitelyProtectedStore__variant.txt" ); // Test public $tuple = $backend->setFromString( 'explicitely public Store', 'explicitelyPublicStore.txt', null, null, ['visibility' => AssetStore::VISIBILITY_PUBLIC] ); $this->assertEquals( AssetStore::VISIBILITY_PUBLIC, $backend->getVisibility($tuple['Filename'], $tuple['Hash']) ); $tuple = $backend->setFromString( 'variant public Store', 'explicitelyPublicStore.txt', $tuple['Hash'], 'variant' ); $this->assertFileExists(ASSETS_PATH . '/AssetStoreTest/explicitelyPublicStore__variant.txt'); } public function testGetFilesystemFor() { $store = $this->getBackend(); $publicFs = $store->getPublicFilesystem(); $protectedFs = $store->getProtectedFilesystem(); $hash = sha1('hello'); $hashPath = substr($hash, 0, 10) . '/hello.txt'; $naturalPath = 'hello.txt'; $file = new File(); $file->File->Filename = $naturalPath; $file->File->Hash = $hash; $file->write(); $file->publishRecursive(); // Protected only $protectedFs->write($hashPath, 'hello'); $this->assertEquals( $protectedFs, $store->getFilesystemFor($hashPath), $hashPath . ' is protected and does not exist on public store' ); $this->assertEquals( $protectedFs, $store->getFilesystemFor($naturalPath), $naturalPath . ' should be rewritten to its hash path which exists on the protected' ); // Public and protected $publicFs->write($naturalPath, 'hello'); $store->setFromString( 'hello', 'hello.txt', null, null, ['visibility' => AssetStore::VISIBILITY_PUBLIC] ); $this->assertEquals( $protectedFs, $store->getFilesystemFor($hashPath), $hashPath . ' exists on protected store and even if it has a resolvable public equivalent' ); $this->assertEquals( $publicFs, $store->getFilesystemFor($naturalPath), $naturalPath . ' exists on public store even it has a protected resovlable equivalent' ); // Public only $protectedFs->delete($hashPath); $store->setFromString( 'hello', 'hello.txt', 'abcdef7890', null, ['visibility' => AssetStore::VISIBILITY_PUBLIC] ); $this->assertEquals( $publicFs, $store->getFilesystemFor($hashPath), $hashPath . ' should be rewritten to its natural path which exists on the public store' ); $this->assertEquals( $publicFs, $store->getFilesystemFor($naturalPath), $naturalPath . ' exists on public store' ); } public function listOfVariantsToWrite() { $content = "The quick brown fox jumps over the lazy dog."; $hash = sha1($content); $filename = 'folder/file.txt'; $variant = 'uppercase'; $parsedFiledID = new ParsedFileID($filename, $hash); $variantParsedFiledID = $parsedFiledID->setVariant($variant); $hashHelper = new HashFileIDHelper(); $hashPath = $hashHelper->buildFileID($parsedFiledID); $variantHashPath = $hashHelper->buildFileID($variantParsedFiledID); $naturalHelper = new NaturalFileIDHelper(); $naturalPath = $naturalHelper->buildFileID($parsedFiledID); $variantNaturalPath = $naturalHelper->buildFileID($variantParsedFiledID); return [ ['Public', $hashPath, $content, $variantParsedFiledID, $variantHashPath], ['Public', $variantNaturalPath, $content, $variantParsedFiledID, $variantNaturalPath], ['Protected', $hashPath, $content, $variantParsedFiledID, $variantHashPath], ['Protected', $variantNaturalPath, $content, $variantParsedFiledID, $variantNaturalPath], ]; } /** * Make sure that variants are written next to their parent file * @dataProvider listOfVariantsToWrite */ public function testVariantWriteNextToFile( $fsName, $mainFilePath, $content, ParsedFileID $variantParsedFileID, $expectedVariantPath ) { $fsMethod = "get{$fsName}Filesystem"; /** @var Filesystem $fs */ $fs = $this->getBackend()->$fsMethod(); $fs->write($mainFilePath, $content); $this->getBackend()->setFromString( 'variant content', $variantParsedFileID->getFilename(), $variantParsedFileID->getHash(), $variantParsedFileID->getVariant() ); $this->assertTrue($fs->has($expectedVariantPath)); } public function listOfFilesToNormalise() { $public = AssetStore::VISIBILITY_PUBLIC; $protected = AssetStore::VISIBILITY_PROTECTED; /** @var FileIDHelper $hashHelper */ $hashHelper = new HashFileIDHelper(); $naturalHelper = new NaturalFileIDHelper(); $legacyHelper = new LegacyFileIDHelper(); $content = "The quick brown fox jumps over the lazy dog."; $hash = sha1($content); $filename = 'folder/file.txt'; $hashPath = $hashHelper->buildFileID($filename, $hash); $legacyPath = $legacyHelper->buildFileID($filename, $hash); $variant = 'uppercase'; $vContent = strtoupper($content); $vNatural = $naturalHelper->buildFileID($filename, $hash, $variant); $vHash = $hashHelper->buildFileID($filename, $hash, $variant); $vLegacy = $legacyHelper->buildFileID($filename, $hash, $variant); return [ // Main file only [$public, [$filename => $content], $filename, $hash, [$filename], [$hashPath, dirname($hashPath)]], [$public, [$hashPath => $content], $filename, $hash, [$filename], [$hashPath, dirname($hashPath)]], [$protected, [$filename => $content], $filename, $hash, [$hashPath], [$filename]], [$protected, [$hashPath => $content], $filename, $hash, [$hashPath], [$filename]], // Main File with variant [ $public, [$filename => $content, $vNatural => $vContent], $filename, $hash, [$filename, $vNatural], [$hashPath, $vHash, dirname($hashPath)] ], [ $public, [$hashPath => $content, $vHash => $vContent], $filename, $hash, [$filename, $vNatural], [$hashPath, $vHash, dirname($hashPath)] ], [ $protected, [$filename => $content, $vNatural => $vContent], $filename, $hash, [$hashPath, $vHash], [$filename, $vNatural] ], [ $protected, [$hashPath => $content, $vHash => $vContent], $filename, $hash, [$hashPath, $vHash], [$filename, $vNatural] ], // SS3 variants ... the protected store doesn't resolve SS3 paths [ $public, [$legacyPath => $content, $vLegacy => $vContent], $filename, $hash, [$filename, $vNatural], [$vLegacy, dirname($vLegacy), dirname(dirname($vLegacy))] ], ]; } /** * @dataProvider listOfFilesToNormalise * @param string $fsName * @param array $contents * @param string $filename * @param string $hash * @param array $expected * @param array $notExpected */ public function testNormalise($fsName, array $contents, $filename, $hash, array $expected, array $notExpected = []) { $this->writeDummyFiles($fsName, $contents); $results = $this->getBackend()->normalise($filename, $hash); $this->assertEquals($filename, $results['Filename']); $this->assertEquals($hash, $results['Hash']); $fs = $this->getFilesystem($fsName); foreach ($expected as $expectedFile) { $this->assertTrue($fs->has($expectedFile), "$expectedFile should exists"); $this->assertNotEmpty($fs->read($expectedFile), "$expectedFile should be non empty"); } foreach ($notExpected as $notExpectedFile) { $this->assertFalse($fs->has($notExpectedFile), "$notExpectedFile should NOT exists"); } } public function listOfFileIDsToNormalise() { $public = AssetStore::VISIBILITY_PUBLIC; $protected = AssetStore::VISIBILITY_PROTECTED; /** @var FileIDHelper $hashHelper */ $hashHelper = new HashFileIDHelper(); $naturalHelper = new NaturalFileIDHelper(); $legacyHelper = new LegacyFileIDHelper(); $content = "The quick brown fox jumps over the lazy dog."; $hash = sha1($content); $filename = 'folder/file.txt'; $hashPath = $hashHelper->buildFileID($filename, $hash); $legacyPath = $legacyHelper->buildFileID($filename, $hash); $variant = 'uppercase'; $vContent = strtoupper($content); $vNatural = $naturalHelper->buildFileID($filename, $hash, $variant); $vHash = $hashHelper->buildFileID($filename, $hash, $variant); $vLegacy = $legacyHelper->buildFileID($filename, $hash, $variant); return [ // Main file only [$public, [$filename => $content], $filename, [$filename], [$hashPath, dirname($hashPath)]], [$public, [$hashPath => $content], $hashPath, [$filename], [$hashPath, dirname($hashPath)]], [$protected, [$filename => $content], $filename, [$hashPath], [$filename]], [$protected, [$hashPath => $content], $hashPath, [$hashPath], [$filename]], // Main File with variant [ $public, [$filename => $content, $vNatural => $vContent], $filename, [$filename, $vNatural], [$hashPath, $vHash, dirname($hashPath)] ], [ $public, [$hashPath => $content, $vHash => $vContent], $hashPath, [$filename, $vNatural], [$hashPath, $vHash, dirname($hashPath)] ], [ $protected, [$filename => $content, $vNatural => $vContent], $filename, [$hashPath, $vHash], [$filename, $vNatural] ], [ $protected, [$hashPath => $content, $vHash => $vContent], $hashPath, [$hashPath, $vHash], [$filename, $vNatural] ], // SS3 variants ... the protected store doesn't resolve SS3 paths [ $public, [$legacyPath => $content, $vLegacy => $vContent], $legacyPath, [$filename, $vNatural], [$vLegacy, dirname($vLegacy), dirname(dirname($vLegacy))] ], // Test files with a parent folder that could be confused for an hash folder 'natural path in public store with 10-char folder' => [ $public, ['multimedia/video.mp4' => $content], 'multimedia/video.mp4', ['multimedia/video.mp4'], [], 'multimedia/video.mp4' ], 'natural path in protected store with 10-char folder' => [ $protected, ['multimedia/video.mp4' => $content], 'multimedia/video.mp4', [$hashHelper->buildFileID('multimedia/video.mp4', $hash)], [], 'multimedia/video.mp4' ], 'natural path in public store with 10-hexadecimal-char folder' => [ $public, ['0123456789/video.mp4' => $content], '0123456789/video.mp4', ['0123456789/video.mp4'], [], '0123456789/video.mp4' ], 'natural path in protected store with 10-hexadecimal-char folder' => [ $protected, ['abcdef7890/video.mp4' => $content], 'abcdef7890/video.mp4', [$hashHelper->buildFileID('abcdef7890/video.mp4', $hash)], [], 'abcdef7890/video.mp4' ], ]; } /** * @dataProvider listOfFileIDsToNormalise * @param string $fsName * @param array $contents * @param string $fileID * @param array $expected * @param array $notExpected */ public function testNormalisePath( $fsName, array $contents, $fileID, array $expected, array $notExpected = [], $expectedFilename = 'folder/file.txt' ) { $this->writeDummyFiles($fsName, $contents); $results = $this->getBackend()->normalisePath($fileID); $this->assertEquals($expectedFilename, $results['Filename']); $this->assertTrue( strpos(sha1("The quick brown fox jumps over the lazy dog."), $results['Hash']) === 0 ); $fs = $this->getFilesystem($fsName); foreach ($expected as $expectedFile) { $this->assertTrue($fs->has($expectedFile), "$expectedFile should exists"); $this->assertNotEmpty($fs->read($expectedFile), "$expectedFile should be non empty"); } foreach ($notExpected as $notExpectedFile) { $this->assertFalse($fs->has($notExpectedFile), "$notExpectedFile should NOT exists"); } } /** * @param $fs * @return Filesystem */ private function getFilesystem($fs) { switch (strtolower($fs)) { case AssetStore::VISIBILITY_PUBLIC: return $this->getBackend()->getPublicFilesystem(); case AssetStore::VISIBILITY_PROTECTED: return $this->getBackend()->getProtectedFilesystem(); default: new InvalidArgumentException('getFilesystem(): $fs must be an equal to a know visibility.'); } } private function writeDummyFiles($fsName, array $contents) { $fs = $this->getFilesystem($fsName); foreach ($contents as $path => $content) { $fs->write($path, $content); } } public function testExist() { // "decade1980" could be confused for an hash because it's 10 hexa-decimal characters $filename = 'decade1980/pangram.txt'; $content = 'The quick brown fox jumps over a lazy dog'; $hash = sha1($content); $store = $this->getBackend(); // File haven't been created yet $this->assertFalse($store->exists($filename, $hash)); $this->assertFalse($store->exists($filename, $hash, 'variant')); // Create main file on protected store $store->setFromString($content, $filename); $this->assertTrue($store->exists($filename, $hash)); $this->assertFalse($store->exists($filename, $hash, 'variant')); // Create variant on protected store $store->setFromString(strtoupper($content), $filename, $hash, 'variant'); $this->assertTrue($store->exists($filename, $hash, 'variant')); // Publish file to public store $store->publish($filename, $hash); $this->assertTrue($store->exists($filename, $hash)); $this->assertTrue($store->exists($filename, $hash, 'variant')); // Files should be gone $store->delete($filename, $hash); $this->assertFalse($store->exists($filename, $hash)); $this->assertFalse($store->exists($filename, $hash, 'variant')); } } |