2000, 'qr.image.jpeg_quality' => 82]); } /** Write a real JPEG of the given size to a temp file and wrap it as an upload. */ private function jpeg(int $width, int $height): UploadedFile { $img = imagecreatetruecolor($width, $height); // Some actual detail, so the encoder cannot trivially compress it to nothing. for ($i = 0; $i < 60; $i++) { $colour = imagecolorallocate($img, random_int(0, 255), random_int(0, 255), random_int(0, 255)); imagefilledrectangle( $img, random_int(0, $width - 1), random_int(0, $height - 1), random_int(0, $width - 1), random_int(0, $height - 1), $colour, ); } $path = tempnam(sys_get_temp_dir(), 'img').'.jpg'; imagejpeg($img, $path, 92); imagedestroy($img); return new UploadedFile($path, 'cover.jpg', 'image/jpeg', null, true); } private function png(int $width, int $height): UploadedFile { $img = imagecreatetruecolor($width, $height); imagealphablending($img, false); imagesavealpha($img, true); imagefilledrectangle($img, 0, 0, $width, $height, imagecolorallocatealpha($img, 0, 0, 0, 127)); imagefilledrectangle($img, 10, 10, (int) ($width / 2), (int) ($height / 2), imagecolorallocate($img, 255, 0, 0)); $path = tempnam(sys_get_temp_dir(), 'img').'.png'; imagepng($img, $path); imagedestroy($img); return new UploadedFile($path, 'logo.png', 'image/png', null, true); } public function test_an_oversized_cover_is_downscaled_and_shrunk(): void { // The shape of the file that caused the incident: print resolution. $file = $this->jpeg(4000, 3000); $originalBytes = $file->getSize(); UploadedImageOptimizer::store($file, '1/bookshop-covers/cover.jpg'); $stored = Storage::disk('qr')->get('1/bookshop-covers/cover.jpg'); $this->assertNotNull($stored); [$w, $h] = getimagesizefromstring($stored); $this->assertSame(2000, $w, 'Long edge should be capped at max_dimension.'); $this->assertSame(1500, $h, 'Aspect ratio must be preserved.'); $this->assertLessThan($originalBytes, strlen($stored)); } public function test_a_small_image_is_stored_untouched(): void { $file = $this->jpeg(400, 300); $originalBytes = $file->getSize(); $optimised = UploadedImageOptimizer::store($file, '1/logos/small.jpg'); $this->assertFalse($optimised, 'Small files should not be re-encoded.'); $this->assertSame($originalBytes, strlen(Storage::disk('qr')->get('1/logos/small.jpg'))); } public function test_png_transparency_survives_resizing(): void { $file = $this->png(3000, 3000); UploadedImageOptimizer::store($file, '1/logos/logo.png'); $stored = Storage::disk('qr')->get('1/logos/logo.png'); $img = imagecreatefromstring($stored); $this->assertNotFalse($img); [$w] = getimagesizefromstring($stored); $this->assertSame(2000, $w); // Top-left was fully transparent; it must not have become black. $alpha = (imagecolorat($img, 1, 1) >> 24) & 0x7F; imagedestroy($img); $this->assertGreaterThan(0, $alpha, 'Alpha channel was lost during resize.'); } public function test_format_is_preserved(): void { UploadedImageOptimizer::store($this->png(3000, 3000), '1/logos/keep.png'); $info = getimagesizefromstring(Storage::disk('qr')->get('1/logos/keep.png')); $this->assertSame(IMAGETYPE_PNG, $info[2], 'A PNG must not be converted to JPEG.'); } public function test_a_non_image_is_stored_unchanged(): void { $file = UploadedFile::fake()->create('book.pdf', 40, 'application/pdf'); $optimised = UploadedImageOptimizer::store($file, '1/books/book.pdf'); $this->assertFalse($optimised); $this->assertTrue(Storage::disk('qr')->exists('1/books/book.pdf')); } public function test_the_incident_file_shape_drops_below_a_sane_size(): void { // 9000x6600 was the real cover; assert the outcome we actually needed. $file = $this->jpeg(9000, 6600); UploadedImageOptimizer::store($file, '1/bookshop-covers/huge.jpg'); $stored = Storage::disk('qr')->get('1/bookshop-covers/huge.jpg'); [$w, $h] = getimagesizefromstring($stored); $this->assertSame(2000, $w); $this->assertSame(1467, $h); $this->assertLessThan( 1_500_000, strlen($stored), 'A bio-page cover served to every visitor must not be megabytes.', ); } }