512 lines
17 KiB
PHP
512 lines
17 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Uwil\Storage\Tests\Unit\Config;
|
|
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Uwil\Storage\Config\StorageServiceConfig;
|
|
use Uwil\Storage\Exceptions\UnexpectedStorageException;
|
|
use Uwil\Storage\Lib\ValueObjects\DiskName;
|
|
|
|
covers(StorageServiceConfig::class);
|
|
|
|
describe('StorageServiceConfig instantiation', function (): void {
|
|
it('can instantiate with valid configuration', function (): void {
|
|
// GIVEN
|
|
$defaultDisk = 'local';
|
|
$eligibleDisks = ['local'];
|
|
$laravelExistingDisks = [(object) ['disk' => 'local', 'driver' => 'local']];
|
|
$allowedExtensions = ['jpg', 'png', 'pdf'];
|
|
|
|
// WHEN
|
|
$config = new StorageServiceConfig(
|
|
defaultDisk: $defaultDisk,
|
|
eligibleDisks: $eligibleDisks,
|
|
laravelExistingDisks: $laravelExistingDisks,
|
|
allowedExtensions: $allowedExtensions
|
|
);
|
|
|
|
// THEN
|
|
expect($config)->toBeInstanceOf(StorageServiceConfig::class)
|
|
->and($config->getDefaultDisk())->toBeInstanceOf(DiskName::class)
|
|
->and($config->getDefaultDisk()->value)->toBe('local')
|
|
->and($config->getEligibleDisks())->toHaveCount(1)
|
|
->and($config->getEligibleDisks()[0]->value)->toBe('local')
|
|
->and($config->getAllowedExtensions())->toBe(['jpg', 'png', 'pdf']);
|
|
});
|
|
});
|
|
|
|
describe('StorageServiceConfig disk validation', function (): void {
|
|
it('throws exception if default disk does not exist', function (): void {
|
|
// GIVEN
|
|
$defaultDisk = 'non_existent_disk';
|
|
$eligibleDisks = ['local'];
|
|
$laravelExistingDisks = [(object) ['disk' => 'local', 'driver' => 'local']];
|
|
$allowedExtensions = ['pdf'];
|
|
|
|
// WHEN
|
|
$action = fn () => new StorageServiceConfig(
|
|
defaultDisk: $defaultDisk,
|
|
eligibleDisks: $eligibleDisks,
|
|
laravelExistingDisks: $laravelExistingDisks,
|
|
allowedExtensions: $allowedExtensions
|
|
);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(
|
|
UnexpectedStorageException::class,
|
|
'Default disk does not exist'
|
|
);
|
|
});
|
|
|
|
it('throws exception if an eligible disk does not exist', function (): void {
|
|
// GIVEN
|
|
$defaultDisk = 'local';
|
|
$eligibleDisks = ['local', 'unknown_disk'];
|
|
$laravelExistingDisks = [(object) ['disk' => 'local', 'driver' => 'local']];
|
|
$allowedExtensions = ['pdf'];
|
|
|
|
// WHEN
|
|
$action = fn () => new StorageServiceConfig(
|
|
defaultDisk: $defaultDisk,
|
|
eligibleDisks: $eligibleDisks,
|
|
laravelExistingDisks: $laravelExistingDisks,
|
|
allowedExtensions: $allowedExtensions
|
|
);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(
|
|
UnexpectedStorageException::class,
|
|
'Eligible disk unknown_disk does not exist'
|
|
);
|
|
});
|
|
|
|
it('throws exception if a Laravel existing disk does not exist', function (): void {
|
|
// GIVEN
|
|
$defaultDisk = 'local';
|
|
$eligibleDisks = ['local'];
|
|
$laravelExistingDisks = [
|
|
(object) ['disk' => 'local', 'driver' => 'local'],
|
|
(object) ['disk' => 'phantom_disk', 'driver' => 'local'],
|
|
];
|
|
$allowedExtensions = ['pdf'];
|
|
|
|
// WHEN
|
|
$action = fn () => new StorageServiceConfig(
|
|
defaultDisk: $defaultDisk,
|
|
eligibleDisks: $eligibleDisks,
|
|
laravelExistingDisks: $laravelExistingDisks,
|
|
allowedExtensions: $allowedExtensions
|
|
);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(
|
|
UnexpectedStorageException::class,
|
|
'Laravel existing disk phantom_disk does not exist'
|
|
);
|
|
});
|
|
|
|
it('throws exception if laravelExistingDisks is empty', function (): void {
|
|
// GIVEN
|
|
$defaultDisk = 'local';
|
|
$eligibleDisks = ['local'];
|
|
$laravelExistingDisks = [];
|
|
$allowedExtensions = ['pdf'];
|
|
|
|
// WHEN
|
|
$action = fn () => new StorageServiceConfig(
|
|
defaultDisk: $defaultDisk,
|
|
eligibleDisks: $eligibleDisks,
|
|
laravelExistingDisks: $laravelExistingDisks,
|
|
allowedExtensions: $allowedExtensions
|
|
);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(
|
|
UnexpectedStorageException::class,
|
|
'Laravel existing disks cannot be empty'
|
|
);
|
|
});
|
|
|
|
it('throws exception if eligibleDisks is empty', function (): void {
|
|
// GIVEN
|
|
$defaultDisk = 'local';
|
|
$eligibleDisks = [];
|
|
$laravelExistingDisks = [(object) ['disk' => 'local', 'driver' => 'local']];
|
|
$allowedExtensions = ['pdf'];
|
|
|
|
// WHEN
|
|
$action = fn () => new StorageServiceConfig(
|
|
defaultDisk: $defaultDisk,
|
|
eligibleDisks: $eligibleDisks,
|
|
laravelExistingDisks: $laravelExistingDisks,
|
|
allowedExtensions: $allowedExtensions
|
|
);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(
|
|
UnexpectedStorageException::class,
|
|
'Eligible disks cannot be empty'
|
|
);
|
|
});
|
|
|
|
it('throws exception if no eligible disks exist in Laravel existing disks', function (): void {
|
|
// GIVEN
|
|
$defaultDisk = 'local';
|
|
$eligibleDisks = ['public'];
|
|
$laravelExistingDisks = [(object) ['disk' => 'local', 'driver' => 'local']];
|
|
$allowedExtensions = ['pdf'];
|
|
|
|
// WHEN
|
|
$action = fn () => new StorageServiceConfig(
|
|
defaultDisk: $defaultDisk,
|
|
eligibleDisks: $eligibleDisks,
|
|
laravelExistingDisks: $laravelExistingDisks,
|
|
allowedExtensions: $allowedExtensions
|
|
);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(
|
|
UnexpectedStorageException::class,
|
|
'No eligible disks exist in Laravel existing disks'
|
|
);
|
|
});
|
|
|
|
it('throws exception if default disk does not exist in Laravel existing disks', function (): void {
|
|
// GIVEN
|
|
$defaultDisk = 'local';
|
|
$eligibleDisks = ['public'];
|
|
$laravelExistingDisks = [(object) ['disk' => 'public', 'driver' => 'local']];
|
|
$allowedExtensions = ['pdf'];
|
|
|
|
// WHEN
|
|
$action = fn () => new StorageServiceConfig(
|
|
defaultDisk: $defaultDisk,
|
|
eligibleDisks: $eligibleDisks,
|
|
laravelExistingDisks: $laravelExistingDisks,
|
|
allowedExtensions: $allowedExtensions
|
|
);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(
|
|
UnexpectedStorageException::class,
|
|
'Default disk does not exist in Laravel existing disks'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('StorageServiceConfig allowed extensions validation', function (): void {
|
|
it('throws exception if allowedExtensions is empty', function (): void {
|
|
// GIVEN
|
|
$defaultDisk = 'local';
|
|
$eligibleDisks = ['local'];
|
|
$laravelExistingDisks = [(object) ['disk' => 'local', 'driver' => 'local']];
|
|
$allowedExtensions = [];
|
|
|
|
// WHEN
|
|
$action = fn () => new StorageServiceConfig(
|
|
defaultDisk: $defaultDisk,
|
|
eligibleDisks: $eligibleDisks,
|
|
laravelExistingDisks: $laravelExistingDisks,
|
|
allowedExtensions: $allowedExtensions
|
|
);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(
|
|
UnexpectedStorageException::class,
|
|
'Allowed extensions cannot be empty'
|
|
);
|
|
});
|
|
|
|
it('throws exception if an allowed extension is empty or invalid', function (string $invalidExtension): void {
|
|
// GIVEN
|
|
$defaultDisk = 'local';
|
|
$eligibleDisks = ['local'];
|
|
$laravelExistingDisks = [(object) ['disk' => 'local', 'driver' => 'local']];
|
|
$allowedExtensions = ['jpg', $invalidExtension];
|
|
|
|
// WHEN
|
|
$action = fn () => new StorageServiceConfig(
|
|
defaultDisk: $defaultDisk,
|
|
eligibleDisks: $eligibleDisks,
|
|
laravelExistingDisks: $laravelExistingDisks,
|
|
allowedExtensions: $allowedExtensions
|
|
);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(
|
|
UnexpectedStorageException::class,
|
|
"Allowed extension {$invalidExtension} is not valid"
|
|
);
|
|
})->with([
|
|
'empty string' => '',
|
|
'whitespace' => ' ',
|
|
]);
|
|
|
|
it('throws exception if an allowed extension is forbidden (svg or xml)', function (string $forbidden): void {
|
|
// GIVEN
|
|
$defaultDisk = 'local';
|
|
$eligibleDisks = ['local'];
|
|
$laravelExistingDisks = [(object) ['disk' => 'local', 'driver' => 'local']];
|
|
$allowedExtensions = ['jpg', $forbidden];
|
|
|
|
// WHEN
|
|
$action = fn () => new StorageServiceConfig(
|
|
defaultDisk: $defaultDisk,
|
|
eligibleDisks: $eligibleDisks,
|
|
laravelExistingDisks: $laravelExistingDisks,
|
|
allowedExtensions: $allowedExtensions
|
|
);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(
|
|
UnexpectedStorageException::class,
|
|
"Extension '{$forbidden}' is forbidden: XML/SVG sanitization is currently not supported."
|
|
);
|
|
})->with([
|
|
'svg' => 'svg',
|
|
'svg with .' => '.svg',
|
|
'SVG upper' => 'SVG',
|
|
'xml' => 'xml',
|
|
'xml with .' => '.xml',
|
|
'XML upper' => 'XML',
|
|
]);
|
|
});
|
|
|
|
describe('StorageServiceConfig isDiskEligible', function (): void {
|
|
it('returns true when checking eligible disk with string or DiskName', function (): void {
|
|
// GIVEN
|
|
$config = new StorageServiceConfig(
|
|
defaultDisk: 'local',
|
|
eligibleDisks: ['local'],
|
|
laravelExistingDisks: [(object) ['disk' => 'local', 'driver' => 'local']],
|
|
allowedExtensions: ['pdf']
|
|
);
|
|
$diskNameVo = new DiskName('local', 'local');
|
|
|
|
// WHEN
|
|
$eligibleFromString = $config->isDiskEligible('local');
|
|
$eligibleFromVo = $config->isDiskEligible($diskNameVo);
|
|
|
|
// THEN
|
|
expect($eligibleFromString)->toBeTrue()
|
|
->and($eligibleFromVo)->toBeTrue();
|
|
});
|
|
|
|
it('returns false when checking non-eligible disk', function (): void {
|
|
// GIVEN
|
|
$config = new StorageServiceConfig(
|
|
defaultDisk: 'local',
|
|
eligibleDisks: ['local'],
|
|
laravelExistingDisks: [(object) ['disk' => 'local', 'driver' => 'local']],
|
|
allowedExtensions: ['pdf']
|
|
);
|
|
|
|
// WHEN
|
|
$isEligible = $config->isDiskEligible('public');
|
|
|
|
// THEN
|
|
expect($isEligible)->toBeFalse();
|
|
});
|
|
});
|
|
|
|
describe('StorageServiceConfig isExtensionAllowed', function (): void {
|
|
it('returns true for allowed extensions regardless of case or leading dot', function (string $extension): void {
|
|
// GIVEN
|
|
$config = new StorageServiceConfig(
|
|
defaultDisk: 'local',
|
|
eligibleDisks: ['local'],
|
|
laravelExistingDisks: [(object) ['disk' => 'local', 'driver' => 'local']],
|
|
allowedExtensions: ['jpg', 'png', 'pdf']
|
|
);
|
|
|
|
// WHEN
|
|
$isAllowed = $config->isExtensionAllowed($extension);
|
|
|
|
// THEN
|
|
expect($isAllowed)->toBeTrue();
|
|
})->with([
|
|
'exact' => 'jpg',
|
|
'with dot' => '.jpg',
|
|
'uppercase' => 'PNG',
|
|
'upper dot' => '.PDF',
|
|
]);
|
|
|
|
it('returns false for non-allowed extensions', function (string $extension): void {
|
|
// GIVEN
|
|
$config = new StorageServiceConfig(
|
|
defaultDisk: 'local',
|
|
eligibleDisks: ['local'],
|
|
laravelExistingDisks: [(object) ['disk' => 'local', 'driver' => 'local']],
|
|
allowedExtensions: ['jpg', 'png', 'pdf']
|
|
);
|
|
|
|
// WHEN
|
|
$isAllowed = $config->isExtensionAllowed($extension);
|
|
|
|
// THEN
|
|
expect($isAllowed)->toBeFalse();
|
|
})->with([
|
|
'exe' => 'exe',
|
|
'zip' => 'zip',
|
|
'svg' => 'svg',
|
|
'xml' => 'xml',
|
|
'.svg' => '.svg',
|
|
]);
|
|
});
|
|
|
|
describe('StorageServiceConfig prefix validation and retrieval', function (): void {
|
|
it('returns null when prefix is omitted or null', function (?string $prefix): void {
|
|
// GIVEN
|
|
$config = new StorageServiceConfig(
|
|
defaultDisk: 'local',
|
|
eligibleDisks: ['local'],
|
|
laravelExistingDisks: [(object) ['disk' => 'local', 'driver' => 'local']],
|
|
allowedExtensions: ['pdf'],
|
|
prefix: $prefix
|
|
);
|
|
|
|
// THEN
|
|
expect($config->getPrefix())->toBeNull();
|
|
})->with([
|
|
'null' => null,
|
|
'empty' => '',
|
|
'spaces' => ' ',
|
|
'slashes' => '///',
|
|
]);
|
|
|
|
it('trims leading and trailing slashes and spaces from prefix', function (string $input, string $expected): void {
|
|
// GIVEN
|
|
$config = new StorageServiceConfig(
|
|
defaultDisk: 'local',
|
|
eligibleDisks: ['local'],
|
|
laravelExistingDisks: [(object) ['disk' => 'local', 'driver' => 'local']],
|
|
allowedExtensions: ['pdf'],
|
|
prefix: $input
|
|
);
|
|
|
|
// THEN
|
|
expect($config->getPrefix())->toBe($expected);
|
|
})->with([
|
|
'simple' => ['media', 'media'],
|
|
'leading slash' => ['/media', 'media'],
|
|
'trailing slash' => ['media/', 'media'],
|
|
'both slashes' => ['/media/', 'media'],
|
|
'nested' => ['/uploads/images/', 'uploads/images'],
|
|
'with spaces' => [' media ', 'media'],
|
|
]);
|
|
|
|
it('throws exception if prefix contains invalid sequences', function (string $invalidPrefix): void {
|
|
// GIVEN
|
|
$action = fn () => new StorageServiceConfig(
|
|
defaultDisk: 'local',
|
|
eligibleDisks: ['local'],
|
|
laravelExistingDisks: [(object) ['disk' => 'local', 'driver' => 'local']],
|
|
allowedExtensions: ['pdf'],
|
|
prefix: $invalidPrefix
|
|
);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(UnexpectedStorageException::class);
|
|
})->with([
|
|
'dot dot' => '../media',
|
|
'nested dot dot' => 'media/../sub',
|
|
'backslash' => 'media\\sub',
|
|
'double slash' => 'media//sub',
|
|
]);
|
|
});
|
|
|
|
describe('StorageServiceConfig real driver resolution', function (): void {
|
|
it('resolves direct drivers of default and eligible disks', function (): void {
|
|
// GIVEN
|
|
Storage::fake('s3');
|
|
|
|
// WHEN
|
|
$config = new StorageServiceConfig(
|
|
defaultDisk: 'local',
|
|
eligibleDisks: ['local', 's3'],
|
|
laravelExistingDisks: [
|
|
(object) ['disk' => 'local', 'driver' => 'local'],
|
|
(object) ['disk' => 's3', 'driver' => 's3'],
|
|
],
|
|
allowedExtensions: ['pdf']
|
|
);
|
|
$localDisk = new DiskName('local', 'local');
|
|
$s3Disk = new DiskName('s3', 's3');
|
|
|
|
// THEN
|
|
expect($config->getRealDriver($localDisk))->toBe('local')
|
|
->and($config->isS3Driver($localDisk))->toBeFalse()
|
|
->and($config->getRealDriver($s3Disk))->toBe('s3')
|
|
->and($config->isS3Driver($s3Disk))->toBeTrue();
|
|
});
|
|
|
|
it('resolves the underlying driver of a scoped disk at boot', function (): void {
|
|
// GIVEN
|
|
config()->set('filesystems.disks.s3_parent', ['driver' => 's3']);
|
|
config()->set('filesystems.disks.media', [
|
|
'driver' => 'scoped',
|
|
'disk' => 's3_parent',
|
|
'prefix' => 'media',
|
|
]);
|
|
Storage::fake('media');
|
|
|
|
// WHEN
|
|
$config = new StorageServiceConfig(
|
|
defaultDisk: 'media',
|
|
eligibleDisks: ['media'],
|
|
laravelExistingDisks: [(object) ['disk' => 'media', 'driver' => 'scoped']],
|
|
allowedExtensions: ['pdf']
|
|
);
|
|
$media = new DiskName('media', 'scoped');
|
|
|
|
// THEN
|
|
expect($config->getRealDriver($media))->toBe('s3')
|
|
->and($config->isS3Driver($media))->toBeTrue();
|
|
});
|
|
|
|
it('fails at boot when a scoped disk is misconfigured', function (): void {
|
|
// GIVEN
|
|
config()->set('filesystems.disks.broken', ['driver' => 'scoped']);
|
|
Storage::fake('broken');
|
|
|
|
// WHEN
|
|
$action = fn () => new StorageServiceConfig(
|
|
defaultDisk: 'local',
|
|
eligibleDisks: ['local', 'broken'],
|
|
laravelExistingDisks: [
|
|
(object) ['disk' => 'local', 'driver' => 'local'],
|
|
(object) ['disk' => 'broken', 'driver' => 'scoped'],
|
|
],
|
|
allowedExtensions: ['pdf']
|
|
);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(
|
|
UnexpectedStorageException::class,
|
|
"Scoped disk 'broken' is missing a valid parent 'disk' configuration."
|
|
);
|
|
});
|
|
|
|
it('throws when asking the driver of a disk that is neither default nor eligible', function (): void {
|
|
// GIVEN
|
|
$config = new StorageServiceConfig(
|
|
defaultDisk: 'local',
|
|
eligibleDisks: ['local'],
|
|
laravelExistingDisks: [(object) ['disk' => 'local', 'driver' => 'local']],
|
|
allowedExtensions: ['pdf']
|
|
);
|
|
Storage::fake('other');
|
|
$other = new DiskName('other', 'local');
|
|
|
|
// WHEN
|
|
$action = fn () => $config->getRealDriver($other);
|
|
|
|
// THEN
|
|
expect($action)->toThrow(
|
|
UnexpectedStorageException::class,
|
|
'Disk other is neither the default nor an eligible disk'
|
|
);
|
|
});
|
|
});
|