Add extensive controller and model tests
All checks were successful
CI/CD Pipeline / test (push) Successful in 10s
CI/CD Pipeline / deploy (push) Successful in 25s

This commit is contained in:
2026-02-07 22:14:42 +01:00
parent 9c60a8944e
commit 160430e128
39 changed files with 3941 additions and 1 deletions

View File

@@ -0,0 +1,214 @@
<?php
namespace App\Services {
if (!isset($GLOBALS['attachment_thumbnail_overrides'])) {
$GLOBALS['attachment_thumbnail_overrides'] = [
'force_imagecreatetruecolor_fail' => false,
'force_imagejpeg_fail' => false,
'force_imagecreatefromjpeg_fail' => false,
'fake_getimagesize' => null,
];
}
if (!function_exists(__NAMESPACE__ . '\\imagecreatetruecolor')) {
function imagecreatetruecolor($width, $height)
{
if (!empty($GLOBALS['attachment_thumbnail_overrides']['force_imagecreatetruecolor_fail'])) {
return false;
}
return \imagecreatetruecolor($width, $height);
}
}
if (!function_exists(__NAMESPACE__ . '\\imagecreatefromjpeg')) {
function imagecreatefromjpeg($path)
{
if (!empty($GLOBALS['attachment_thumbnail_overrides']['force_imagecreatefromjpeg_fail'])) {
return false;
}
return \imagecreatefromjpeg($path);
}
}
if (!function_exists(__NAMESPACE__ . '\\getimagesize')) {
function getimagesize($path)
{
$override = $GLOBALS['attachment_thumbnail_overrides']['fake_getimagesize'] ?? null;
if ($override !== null) {
return $override;
}
return \getimagesize($path);
}
}
if (!function_exists(__NAMESPACE__ . '\\imagejpeg')) {
function imagejpeg($image, $to = null, $quality = null)
{
if (!empty($GLOBALS['attachment_thumbnail_overrides']['force_imagejpeg_fail'])) {
return false;
}
if ($quality === null) {
return \imagejpeg($image, $to);
}
return \imagejpeg($image, $to, $quality);
}
}
}
namespace {
use App\Models\Attachment;
use App\Models\Forum;
use App\Models\Post;
use App\Models\Setting;
use App\Models\Thread;
use App\Services\AttachmentThumbnailService;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
if (!function_exists('imagewebp')) {
function imagewebp($image, $to = null, $quality = null)
{
return false;
}
}
beforeEach(function (): void {
$GLOBALS['attachment_thumbnail_overrides'] = [
'force_imagecreatetruecolor_fail' => false,
'force_imagejpeg_fail' => false,
'force_imagecreatefromjpeg_fail' => false,
'fake_getimagesize' => null,
];
});
it('uses misc scope for attachments without thread or post', function (): void {
if (!function_exists('imagecreatetruecolor')) {
$this->markTestSkipped('GD extension not available.');
}
Storage::fake('local');
Setting::updateOrCreate(['key' => 'attachments.create_thumbnails'], ['value' => 'true']);
Setting::updateOrCreate(['key' => 'attachments.thumbnail_max_width'], ['value' => '100']);
Setting::updateOrCreate(['key' => 'attachments.thumbnail_max_height'], ['value' => '100']);
$image = UploadedFile::fake()->image('photo.jpg', 800, 600);
$path = 'attachments/misc/photo.jpg';
Storage::disk('local')->put($path, file_get_contents($image->getPathname()));
$attachment = Attachment::create([
'thread_id' => null,
'post_id' => null,
'attachment_extension_id' => null,
'attachment_group_id' => null,
'user_id' => null,
'disk' => 'local',
'path' => $path,
'original_name' => 'photo.jpg',
'extension' => 'jpg',
'mime_type' => 'image/jpeg',
'size_bytes' => 1234,
]);
$service = new AttachmentThumbnailService();
$result = $service->createForAttachment($attachment, true);
expect($result)->not->toBeNull();
expect($result['path'])->toContain('attachments/misc/thumbs/');
});
it('returns null when image dimensions are zero', function (): void {
Storage::fake('local');
Setting::updateOrCreate(['key' => 'attachments.create_thumbnails'], ['value' => 'true']);
Setting::updateOrCreate(['key' => 'attachments.thumbnail_max_width'], ['value' => '100']);
Setting::updateOrCreate(['key' => 'attachments.thumbnail_max_height'], ['value' => '100']);
$GLOBALS['attachment_thumbnail_overrides']['fake_getimagesize'] = [0, 0, IMAGETYPE_JPEG];
$service = new AttachmentThumbnailService();
$file = UploadedFile::fake()->image('photo.jpg', 800, 600);
$result = $service->createForUpload($file, 'threads/1', 'local');
expect($result)->toBeNull();
});
it('returns null when thumbnail image creation fails', function (): void {
if (!function_exists('imagecreatetruecolor')) {
$this->markTestSkipped('GD extension not available.');
}
Storage::fake('local');
Setting::updateOrCreate(['key' => 'attachments.create_thumbnails'], ['value' => 'true']);
Setting::updateOrCreate(['key' => 'attachments.thumbnail_max_width'], ['value' => '100']);
Setting::updateOrCreate(['key' => 'attachments.thumbnail_max_height'], ['value' => '100']);
$GLOBALS['attachment_thumbnail_overrides']['force_imagecreatetruecolor_fail'] = true;
$service = new AttachmentThumbnailService();
$file = UploadedFile::fake()->image('photo.jpg', 800, 600);
$result = $service->createForUpload($file, 'threads/1', 'local');
expect($result)->toBeNull();
});
it('returns null when renderer fails to encode', function (): void {
if (!function_exists('imagecreatetruecolor')) {
$this->markTestSkipped('GD extension not available.');
}
Storage::fake('local');
Setting::updateOrCreate(['key' => 'attachments.create_thumbnails'], ['value' => 'true']);
Setting::updateOrCreate(['key' => 'attachments.thumbnail_max_width'], ['value' => '100']);
Setting::updateOrCreate(['key' => 'attachments.thumbnail_max_height'], ['value' => '100']);
$GLOBALS['attachment_thumbnail_overrides']['force_imagejpeg_fail'] = true;
$service = new AttachmentThumbnailService();
$file = UploadedFile::fake()->image('photo.jpg', 800, 600);
$result = $service->createForUpload($file, 'threads/1', 'local');
expect($result)->toBeNull();
});
it('handles webp branch in image loader', function (): void {
$service = new AttachmentThumbnailService();
$ref = new ReflectionMethod($service, 'createImageFromFile');
$ref->setAccessible(true);
$temp = tempnam(sys_get_temp_dir(), 'webp');
file_put_contents($temp, 'not-a-real-webp');
$result = $ref->invoke($service, $temp, 'image/webp');
expect($result === null || $result === false)->toBeTrue();
unlink($temp);
});
it('handles webp branch in renderer when available', function (): void {
$service = new AttachmentThumbnailService();
$ref = new ReflectionMethod($service, 'renderImageBinary');
$ref->setAccessible(true);
$image = \imagecreatetruecolor(10, 10);
$data = $ref->invoke($service, $image, 'image/webp', 80);
expect($data === null || is_string($data))->toBeTrue();
imagedestroy($image);
});
it('returns default when setting is missing', function (): void {
$service = new AttachmentThumbnailService();
$ref = new ReflectionMethod($service, 'settingBool');
$ref->setAccessible(true);
$result = $ref->invoke($service, 'attachments.missing_flag', true);
expect($result)->toBeTrue();
});
}