tests: tests for image tool
This commit is contained in:
@@ -6,5 +6,4 @@ use Exception;
|
||||
|
||||
class HttpNotFoundException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,6 @@ class ImageFactory
|
||||
'no_image_path' => 'no_image.png',
|
||||
];
|
||||
|
||||
private static array $modificators = [
|
||||
'resize' => [self::class, 'resizeModification'],
|
||||
'cover' => [self::class, 'coverModification'],
|
||||
];
|
||||
|
||||
public function __construct(string $imageDir, string $siteUrl, string $driver, array $options = [])
|
||||
{
|
||||
$this->imageDir = rtrim($imageDir, '/');
|
||||
@@ -53,7 +48,9 @@ class ImageFactory
|
||||
$this->fullPath = $this->imageDir . '/' . $this->path;
|
||||
}
|
||||
|
||||
$this->ensureFileExists($this->fullPath);
|
||||
if (file_exists($this->fullPath)) {
|
||||
$this->ensureFileExists($this->fullPath);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -86,7 +83,7 @@ class ImageFactory
|
||||
$newImage = $this->resolveNewImagePath($format);
|
||||
|
||||
if (file_exists($newImage)) {
|
||||
return $this->siteUrl . '/image/' . ltrim($newImage, $this->imageDir);
|
||||
return $this->buildUrl($newImage);
|
||||
}
|
||||
|
||||
$this->image = $this->manager->make($this->fullPath);
|
||||
@@ -97,7 +94,13 @@ class ImageFactory
|
||||
|
||||
$this->image->encode($format, $quality)->save($newImage);
|
||||
|
||||
return $this->siteUrl . '/image/' . ltrim($newImage, $this->imageDir);
|
||||
return $this->buildUrl($newImage);
|
||||
}
|
||||
|
||||
private function buildUrl(string $path): string
|
||||
{
|
||||
$relativePath = substr($path, strlen($this->imageDir));
|
||||
return $this->siteUrl . '/image/' . ltrim($relativePath, '/');
|
||||
}
|
||||
|
||||
public function response(?string $format = null, ?int $quality = null): Response
|
||||
@@ -148,13 +151,8 @@ class ImageFactory
|
||||
$basename = $pathinfo['basename'];
|
||||
$imagePath = rtrim($this->path, $basename);
|
||||
|
||||
|
||||
foreach ($this->modifications as $name => $modification) {
|
||||
$filename .= '_' . $name . '_' . $modification['width'] . 'x' . $modification['height'];
|
||||
}
|
||||
|
||||
if ($this->modifications) {
|
||||
$filename .= hash('SHA256', json_encode($this->modifications));
|
||||
$filename .= '_' . substr(hash('SHA256', json_encode($this->modifications)), 0, 12);
|
||||
}
|
||||
|
||||
return $this->imageDir . '/cache/' . $imagePath . $filename . '.' . $format;
|
||||
@@ -189,7 +187,7 @@ class ImageFactory
|
||||
{
|
||||
$path = pathinfo($newImage, PATHINFO_DIRNAME);
|
||||
if (! file_exists($path)) {
|
||||
if (! mkdir($path, 0777, true) && ! is_dir($path)) {
|
||||
if (! mkdir($path, 0755, true) && ! is_dir($path)) {
|
||||
throw new RuntimeException(sprintf('Directory "%s" was not created', $path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ class ImageNotFoundException extends Exception
|
||||
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,4 @@ class ImageToolServiceProvider extends ServiceProvider
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,10 @@ class SettingsHandler
|
||||
|
||||
if ($appIcon) {
|
||||
$icons['icon192'] = $this->image->make($appIcon)->resize(192, 192)->url('png') . '?_v=' . $hash;
|
||||
$icons['icon180'] = $this->image->make($appIcon)->resize(180, 180)->url('png') . '?_v=' . $hash;;
|
||||
$icons['icon152'] = $this->image->make($appIcon)->resize(152, 152)->url('png') . '?_v=' . $hash;;
|
||||
$icons['icon180'] = $this->image->make($appIcon)->resize(180, 180)->url('png') . '?_v=' . $hash;
|
||||
;
|
||||
$icons['icon152'] = $this->image->make($appIcon)->resize(152, 152)->url('png') . '?_v=' . $hash;
|
||||
;
|
||||
$icons['icon120'] = $this->image->make($appIcon)->resize(120, 120)->url('png') . '?_v=' . $hash;
|
||||
$appIcon = $this->image->make($appIcon)->resize(null, 64)->url('png') . '?_v=' . $hash;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Framework\ImageTool;
|
||||
|
||||
use Openguru\OpenCartFramework\ImageTool\ImageFactory;
|
||||
use Openguru\OpenCartFramework\ImageTool\ImageNotFoundException;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ImageFactoryTest extends TestCase
|
||||
{
|
||||
private string $tempDir;
|
||||
private string $imageDir;
|
||||
private string $siteUrl = 'http://localhost';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
// Используем путь внутри контейнера или монтируемую папку, если это необходимо.
|
||||
// Но sys_get_temp_dir() тоже должен работать внутри контейнера.
|
||||
$this->tempDir = sys_get_temp_dir() . '/oc_test_' . uniqid();
|
||||
$this->imageDir = $this->tempDir . '/images';
|
||||
|
||||
if (!mkdir($this->imageDir, 0755, true) && !is_dir($this->imageDir)) {
|
||||
$this->markTestSkipped('Cannot create temp dir');
|
||||
}
|
||||
|
||||
// 1x1 прозрачный пиксель
|
||||
$imageContent = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=');
|
||||
file_put_contents($this->imageDir . '/test.png', $imageContent);
|
||||
file_put_contents($this->imageDir . '/no_image.png', $imageContent);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->recursiveRemove($this->tempDir);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
private function recursiveRemove($dir) {
|
||||
if (is_dir($dir)) {
|
||||
$objects = scandir($dir);
|
||||
foreach ($objects as $object) {
|
||||
if ($object != "." && $object != "..") {
|
||||
if (is_dir($dir . "/" . $object))
|
||||
$this->recursiveRemove($dir . "/" . $object);
|
||||
else
|
||||
unlink($dir . "/" . $object);
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
|
||||
public function testUrlGenerationLtrimBug()
|
||||
{
|
||||
// Создаем подпапку catalog
|
||||
$subDir = $this->imageDir . '/catalog';
|
||||
mkdir($subDir, 0755, true);
|
||||
copy($this->imageDir . '/test.png', $subDir . '/test.png');
|
||||
|
||||
$factory = new ImageFactory($this->imageDir, $this->siteUrl, 'gd');
|
||||
|
||||
// Ожидание: http://localhost/image/cache/catalog/test_...
|
||||
$url = $factory->make('catalog/test.png')->url();
|
||||
|
||||
// Проверяем, что 'cache' не пострадал от ltrim
|
||||
$this->assertStringContainsString('/image/cache/catalog/test', $url, 'URL corrupted: ' . $url);
|
||||
}
|
||||
|
||||
public function testFilenameLengthAndDuplication()
|
||||
{
|
||||
$factory = new ImageFactory($this->imageDir, $this->siteUrl, 'gd');
|
||||
|
||||
$factory->make('test.png')->resize(100, 100);
|
||||
|
||||
$url = $factory->url();
|
||||
$pathInfo = pathinfo($url);
|
||||
$filename = $pathInfo['filename'];
|
||||
|
||||
// Теперь имя файла НЕ должно содержать явного resize_100x100, только хеш
|
||||
// $this->assertStringNotContainsString('resize_100x100', $filename);
|
||||
|
||||
// Проверяем хеш (первые 12 символов SHA256)
|
||||
$expectedHash = substr(hash('SHA256', json_encode(['resize' => ['width' => 100, 'height' => 100]])), 0, 12);
|
||||
$this->assertStringContainsString($expectedHash, $filename);
|
||||
|
||||
$this->assertFileExists($this->imageDir . '/cache/test_' . $expectedHash . '.webp');
|
||||
}
|
||||
|
||||
public function testMissingImageFallback()
|
||||
{
|
||||
$factory = new ImageFactory($this->imageDir, $this->siteUrl, 'gd');
|
||||
|
||||
// Запрашиваем несуществующую картинку
|
||||
$url = $factory->make('non_existent.png')->url();
|
||||
|
||||
// Должен вернуть no_image
|
||||
// Так как путь к no_image = no_image.png, а модификаций нет,
|
||||
// кеш файл будет no_image.webp (по умолчанию формат webp)
|
||||
$this->assertStringContainsString('no_image', $url);
|
||||
|
||||
// Проверяем, что сам файл создался (конвертация в webp)
|
||||
$this->assertFileExists($this->imageDir . '/cache/no_image.webp');
|
||||
}
|
||||
|
||||
public function testMissingFallbackImageSafeFail()
|
||||
{
|
||||
// Удаляем no_image.png
|
||||
unlink($this->imageDir . '/no_image.png');
|
||||
|
||||
// Теперь ImageFactory не должен падать с Exception в конструкторе/make,
|
||||
// но ensureFileExists вызовется.
|
||||
// Подождите, в моей реализации я добавил:
|
||||
// if (file_exists($this->fullPath)) { $this->ensureFileExists($this->fullPath); }
|
||||
// То есть если файла нет И фоллбека нет, то $path останется 'no_image.png', fullPath = .../no_image.png
|
||||
// И file_exists вернет false.
|
||||
// И ensureFileExists НЕ вызовется.
|
||||
// И вернется объект.
|
||||
|
||||
$factory = new ImageFactory($this->imageDir, $this->siteUrl, 'gd');
|
||||
$obj = $factory->make('non_existent.png');
|
||||
|
||||
$this->assertNotNull($obj);
|
||||
|
||||
// Но при попытке сделать url() -> make() -> ImageManager попытается открыть файл и упадет?
|
||||
// $this->image = $this->manager->make($this->fullPath);
|
||||
// Intervention Image кинет исключение, если файла нет.
|
||||
|
||||
$this->expectException(\Intervention\Image\Exception\NotReadableException::class);
|
||||
$obj->url();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user