diff --git a/src/ImageResult.php b/src/ImageResult.php index ecb9a4c..7141572 100644 --- a/src/ImageResult.php +++ b/src/ImageResult.php @@ -4,6 +4,13 @@ final readonly class ImageResult { + /** + * @param float|null $psnr Peak signal-to-noise ratio in decibels between the + * input and the output, returned by optimize and convert. + * Higher means closer to the input. Null when the API + * does not send the field (resize, thumbnail) or when + * the loss cannot be measured. + */ public function __construct( public string $bytes, public string $format, @@ -11,6 +18,7 @@ public function __construct( public int $size, public int $width, public int $height, + public ?float $psnr = null, ) {} /** @@ -18,6 +26,8 @@ public function __construct( */ public static function fromResponse(array $data): self { + $psnr = data_get($data, 'psnr'); + return new self( bytes: (string) base64_decode((string) data_get($data, 'output.data'), true), format: (string) data_get($data, 'format'), @@ -25,6 +35,7 @@ public static function fromResponse(array $data): self size: (int) data_get($data, 'size'), width: (int) data_get($data, 'width'), height: (int) data_get($data, 'height'), + psnr: $psnr === null ? null : (float) $psnr, ); } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index c9ed9dd..86a0f0a 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -8,6 +8,7 @@ use MathiasGrimm\GlimpsePhp\ForbiddenException; use MathiasGrimm\GlimpsePhp\ImageFormat; use MathiasGrimm\GlimpsePhp\ImageInfo; +use MathiasGrimm\GlimpsePhp\ImageResult; use MathiasGrimm\GlimpsePhp\RateLimitException; use MathiasGrimm\GlimpsePhp\SizeEstimate; use MathiasGrimm\GlimpsePhp\Tests\Fixtures\Images; @@ -30,7 +31,7 @@ function client(Factory $http, Closure|string|null $token = 'test-token', string } test('convert posts the base64 envelope and returns a decoded ImageResult', function () { - $http = fakeHttp(['*/v1/convert' => Factory::response(fakeTransformResponse())]); + $http = fakeHttp(['*/v1/convert' => Factory::response(fakeTransformResponse(overrides: ['psnr' => 41.27]))]); $result = client($http)->convert(Images::png(), ImageFormat::Jpg); @@ -38,7 +39,8 @@ function client(Factory $http, Closure|string|null $token = 'test-token', string ->and($result->format)->toBe(ImageFormat::Jpg->value) ->and($result->mimeType)->toBe('image/jpeg') ->and($result->width)->toBe(1280) - ->and($result->height)->toBe(720); + ->and($result->height)->toBe(720) + ->and($result->psnr)->toBe(41.27); $http->assertSent(function (Request $request) { return $request->url() === 'https://glimpseimg.com/api/v1/convert' @@ -49,6 +51,33 @@ function client(Factory $http, Closure|string|null $token = 'test-token', string }); }); +test('a null psnr in the response stays null', function () { + $http = fakeHttp(['*/v1/optimize' => Factory::response(fakeTransformResponse(overrides: ['psnr' => null]))]); + + expect(client($http)->optimize(Images::png())->psnr)->toBeNull(); +}); + +test('a response without a psnr field maps to a null psnr', function () { + // Resize and thumbnail responses do not carry the field at all. + $http = fakeHttp(['*/v1/resize' => Factory::response(fakeTransformResponse())]); + + expect(client($http)->resize(Images::png(), width: 800)->psnr)->toBeNull(); +}); + +test('an integer psnr in the response is cast to float', function () { + $http = fakeHttp(['*/v1/optimize' => Factory::response(fakeTransformResponse(overrides: ['psnr' => 42]))]); + + expect(client($http)->optimize(Images::png())->psnr)->toBe(42.0); +}); + +test('the six-argument constructor stays valid and defaults psnr to null', function () { + // Locks backward compatibility: callers built against pre-3.1 must keep + // working without passing psnr. + $result = new ImageResult('bytes', 'jpg', 'image/jpeg', 100, 20, 10); + + expect($result->psnr)->toBeNull(); +}); + test('convert sends optimize and quality when given', function () { $http = fakeHttp(['*/v1/convert' => Factory::response(fakeTransformResponse())]); diff --git a/tests/Pest.php b/tests/Pest.php index ccab4f9..5579e9f 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -68,11 +68,12 @@ function fakeInfoResponse(array $overrides = []): array /** * A canned successful transform-endpoint response envelope. * + * @param array $overrides * @return array{data: array} */ -function fakeTransformResponse(string $format = 'jpg', string $mimeType = 'image/jpeg'): array +function fakeTransformResponse(string $format = 'jpg', string $mimeType = 'image/jpeg', array $overrides = []): array { - return ['data' => [ + return ['data' => $overrides + [ 'output' => ['type' => 'BASE64', 'data' => Images::JPG_BASE64], 'format' => $format, 'mime_type' => $mimeType,