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(); }); }