ldc-mesbr-app/tests/Unit/Domain/Models/ValueObjects/PicturePathTest.php

164 lines
5.2 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace Tests\Unit\Domain\Models\ValueObjects;
use Domain\Models\ValueObjects\PicturePath;
covers(PicturePath::class);
describe('picture path', function (): void {
it('creates a valid PicturePath', function (string $pathValue): void {
// WHEN
$path = new PicturePath($pathValue);
// THEN
expect($path)->toBeInstanceOf(PicturePath::class)
->and($path->value)->toBe($pathValue);
})->with([
'simple path' => 'coupons/logo.png',
'nested directory' => 'uploads/2026/03/avatar.jpg',
'safe punctuation' => 'images/my-photo_(1).webp',
'windows backslash' => 'coupons\sub\logo.jpeg',
]);
it('rejects an empty or whitespace-only path', function (string $pathValue): void {
// WHEN
$action = fn (): PicturePath => new PicturePath($pathValue);
// THEN
expect($action)
->toThrow(
\DomainException::class,
'Picture Path cannot be empty.'
);
})->with([
'empty' => '',
'spaces' => ' ',
]);
it('rejects a path that exceeds the maximum allowed length', function (): void {
// GIVEN
$pathValue = str_repeat('a', 197) . '.jpg'; // 201 chars
// WHEN
$action = fn (): PicturePath => new PicturePath($pathValue);
// THEN
expect($action)
->toThrow(
\DomainException::class,
'Picture Path exceeds the maximum allowed length.'
);
});
it('accepts a path of exactly the maximum length', function (): void {
// GIVEN
$pathValue = str_repeat('a', 196) . '.jpg'; // 200 chars
// WHEN
$path = new PicturePath($pathValue);
// THEN
expect($path)->toBeInstanceOf(PicturePath::class)
->and(mb_strlen($path->value))->toBe(200);
});
it('rejects null byte characters', function (): void {
// WHEN
$action = fn (): PicturePath => new PicturePath("coupons/logo\0.png");
// THEN
expect($action)
->toThrow(
\DomainException::class,
'Picture Path contains invalid null byte.'
);
});
it('rejects invalid, invisible, or forbidden filesystem characters', function (string $pathValue): void {
// WHEN
$action = fn (): PicturePath => new PicturePath($pathValue);
// THEN
expect($action)
->toThrow(
\DomainException::class,
'Picture Path contains invalid, non-printable, or dangerous characters.'
);
})->with([
'contains space' => 'coupons/my logo.png',
'contains newline' => "coupons/logo\n.png",
'contains asterisk' => 'coupons/*.png',
'contains question' => 'coupons/img?.png',
'contains pipe' => 'coupons/logo|1.png',
'contains colon' => 'coupons:logo.png',
'contains quotes' => 'coupons/"logo".png',
]);
it('rejects paths starting with double slashes', function (): void {
// WHEN
$action = fn (): PicturePath => new PicturePath('//coupons/logo.png');
// THEN
expect($action)
->toThrow(
\DomainException::class,
'Picture Path must not start with "//".'
);
});
it('rejects drive paths and URIs', function (string $pathValue): void {
// WHEN
$action = fn (): PicturePath => new PicturePath($pathValue);
// THEN
expect($action)
->toThrow(
\DomainException::class,
'Picture Path must not be an absolute drive path or a URI.'
);
})->with([
'windows drive letter' => 'C:/images/logo.png',
'windows backslash' => 'D:\images\logo.png',
'http uri' => 'http://example.com/logo.png',
'https uri' => 'https://example.com/logo.png',
'custom scheme' => 's3://bucket/logo.png',
]);
it('rejects directory traversal segments', function (string $pathValue): void {
// WHEN
$action = fn (): PicturePath => new PicturePath($pathValue);
// THEN
expect($action)
->toThrow(
\DomainException::class,
'Picture Path must not contain relative traversal segments ("." or "..").'
);
})->with([
'parent traversal' => '../logo.png',
'nested parent traversal' => 'coupons/../../secret.png',
'current directory segment' => 'coupons/./logo.png',
]);
it('rejects an invalid or missing image extension', function (string $pathValue): void {
// WHEN
$action = fn (): PicturePath => new PicturePath($pathValue);
// THEN
expect($action)
->toThrow(
\DomainException::class,
'Picture Path must end with one of: ' .
implode(', ', PicturePath::ALLOWED_EXTENSIONS)
);
})->with([
'no extension' => 'coupons/image',
'forbidden extension' => 'coupons/image.gif',
'executable extension' => 'coupons/shell.php',
'trailing dot' => 'coupons/image.',
]);
});