- Introduced prefix property in StorageServiceConfig. - Implemented prefix validation and trimming logic. - Updated path generators to utilize the configured prefix. - Added tests for prefix validation and retrieval.
163 lines
5.4 KiB
PHP
163 lines
5.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Uwil\Storage\Tests\Unit\Lib\PathGenerators;
|
|
|
|
use Uwil\Storage\Config\StorageServiceConfig;
|
|
use Uwil\Storage\Lib\PathGenerators\DateHashPathGenerator;
|
|
use Uwil\Storage\Lib\ValueObjects\DiskName;
|
|
use Uwil\Storage\Lib\ValueObjects\FileStream;
|
|
use Uwil\Storage\Lib\ValueObjects\SanitizedFile;
|
|
use Uwil\Storage\Lib\ValueObjects\StorageFilePath;
|
|
|
|
covers(DateHashPathGenerator::class);
|
|
|
|
describe('DateHashPathGenerator generation without directory', function (): void {
|
|
it('generates a storage file path with UTC date segments and hash prefix', function (): void {
|
|
// GIVEN
|
|
$generator = new DateHashPathGenerator();
|
|
$disk = new DiskName('local', 'local');
|
|
$sanitizedFile = new SanitizedFile(
|
|
originalName: 'document.pdf',
|
|
sanitizedName: 'document.pdf',
|
|
contents: FileStream::fromString('test content')
|
|
);
|
|
|
|
$expectedYear = gmdate('Y');
|
|
$expectedMonth = gmdate('m');
|
|
$expectedDay = gmdate('d');
|
|
$expectedHash = substr(hash('sha256', 'document.pdf'), 0, 2);
|
|
|
|
// WHEN
|
|
$result = $generator->generate($disk, $sanitizedFile);
|
|
|
|
// THEN
|
|
expect($result)->toBeInstanceOf(StorageFilePath::class);
|
|
|
|
$pattern = sprintf(
|
|
'#^%s/%s/%s/%s/document-[a-f0-9]{8}\.pdf$#',
|
|
$expectedYear,
|
|
$expectedMonth,
|
|
$expectedDay,
|
|
$expectedHash
|
|
);
|
|
expect($result->value)->toMatch($pattern);
|
|
});
|
|
|
|
it('generates a path for a file without extension', function (): void {
|
|
// GIVEN
|
|
$generator = new DateHashPathGenerator();
|
|
$disk = new DiskName('local', 'local');
|
|
$sanitizedFile = new SanitizedFile(
|
|
originalName: 'LICENSE',
|
|
sanitizedName: 'LICENSE',
|
|
contents: FileStream::fromString('MIT License')
|
|
);
|
|
|
|
$expectedHash = substr(hash('sha256', 'LICENSE'), 0, 2);
|
|
|
|
// WHEN
|
|
$result = $generator->generate($disk, $sanitizedFile);
|
|
|
|
// THEN
|
|
expect($result)->toBeInstanceOf(StorageFilePath::class);
|
|
|
|
$pattern = sprintf(
|
|
'#^%s/%s/%s/%s/license-[a-f0-9]{8}$#',
|
|
gmdate('Y'),
|
|
gmdate('m'),
|
|
gmdate('d'),
|
|
$expectedHash
|
|
);
|
|
expect($result->value)->toMatch($pattern);
|
|
});
|
|
|
|
it('generates unique paths for identical files generated consecutively', function (): void {
|
|
// GIVEN
|
|
$generator = new DateHashPathGenerator();
|
|
$disk = new DiskName('local', 'local');
|
|
$sanitizedFile = new SanitizedFile(
|
|
originalName: 'photo.jpg',
|
|
sanitizedName: 'photo.jpg',
|
|
contents: FileStream::fromString('image bytes')
|
|
);
|
|
|
|
// WHEN
|
|
$path1 = $generator->generate($disk, $sanitizedFile);
|
|
$path2 = $generator->generate($disk, $sanitizedFile);
|
|
|
|
// THEN
|
|
expect($path1->value)->not->toBe($path2->value);
|
|
});
|
|
});
|
|
|
|
describe('DateHashPathGenerator generation with prefix', function (): void {
|
|
it('prepends the configured prefix to the path', function (): void {
|
|
// GIVEN
|
|
$config = new StorageServiceConfig(
|
|
defaultDisk: 'local',
|
|
eligibleDisks: ['local'],
|
|
laravelExistingDisks: [(object) ['disk' => 'local', 'driver' => 'local']],
|
|
allowedExtensions: ['png'],
|
|
prefix: 'avatars/users'
|
|
);
|
|
$generator = new DateHashPathGenerator($config);
|
|
$disk = new DiskName('local', 'local');
|
|
$sanitizedFile = new SanitizedFile(
|
|
originalName: 'avatar.png',
|
|
sanitizedName: 'avatar.png',
|
|
contents: FileStream::fromString('image data')
|
|
);
|
|
|
|
// WHEN
|
|
$result = $generator->generate($disk, $sanitizedFile);
|
|
|
|
// THEN
|
|
$expectedHash = substr(hash('sha256', 'avatar.png'), 0, 2);
|
|
$pattern = sprintf(
|
|
'#^avatars/users/%s/%s/%s/%s/avatar-[a-f0-9]{8}\.png$#',
|
|
gmdate('Y'),
|
|
gmdate('m'),
|
|
gmdate('d'),
|
|
$expectedHash
|
|
);
|
|
expect($result->value)->toMatch($pattern);
|
|
});
|
|
});
|
|
|
|
describe('DateHashPathGenerator filename slugging', function (): void {
|
|
it('slugs filename containing underscores or mixed case', function (): void {
|
|
// GIVEN
|
|
$generator = new DateHashPathGenerator();
|
|
$disk = new DiskName('local', 'local');
|
|
$sanitizedFile = new SanitizedFile(
|
|
originalName: 'My_Monthly_Report_2026.csv',
|
|
sanitizedName: 'My_Monthly_Report_2026.csv',
|
|
contents: FileStream::fromString('a,b,c')
|
|
);
|
|
|
|
// WHEN
|
|
$result = $generator->generate($disk, $sanitizedFile);
|
|
|
|
// THEN
|
|
expect($result->value)->toMatch('#/my-monthly-report-2026-[a-f0-9]{8}\.csv$#');
|
|
});
|
|
|
|
it('falls back to "file" when filename produces an empty slug', function (): void {
|
|
// GIVEN
|
|
$generator = new DateHashPathGenerator();
|
|
$disk = new DiskName('local', 'local');
|
|
$sanitizedFile = new SanitizedFile(
|
|
originalName: '.env.backup',
|
|
sanitizedName: '.backup',
|
|
contents: FileStream::fromString('KEY=VAL')
|
|
);
|
|
|
|
// WHEN
|
|
$result = $generator->generate($disk, $sanitizedFile);
|
|
|
|
// THEN
|
|
expect($result->value)->toMatch('#/file-[a-f0-9]{8}\.backup$#');
|
|
});
|
|
});
|