openclaw / src /agents /utils /image-resize.test.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
eb3f11e verified
Raw History Blame Contribute Delete
5.33 kB
// Image resize utility tests cover Rastermill probing/encoding decisions,
// base64 byte budgets, and coordinate notes for resized inline images.
import { beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
convertImageToPng: vi.fn(),
encode: vi.fn(),
probe: vi.fn(),
}));
vi.mock("../../media/image-ops.js", () => ({
convertImageToPng: mocks.convertImageToPng,
createImageProcessor: () => ({
encode: mocks.encode,
probe: mocks.probe,
}),
}));
import { processImage } from "./image-resize.js";
describe("image resize utility", () => {
beforeEach(() => {
mocks.convertImageToPng.mockReset();
mocks.encode.mockReset();
mocks.probe.mockReset();
});
it("keeps images that exactly fit the inline limits", async () => {
const input = "a".repeat(4.5 * 1024 * 1024);
mocks.probe.mockResolvedValue({
bytes: Buffer.byteLength(input, "base64"),
format: "png",
hasAlpha: false,
height: 20,
orientation: null,
width: 10,
});
const result = await processImage(
{ type: "image", data: input, mimeType: "image/png" },
{ autoResizeImages: true },
);
expect(result).toStrictEqual({
ok: true,
image: { type: "image", data: input, mimeType: "image/png" },
hints: [],
});
expect(mocks.encode).not.toHaveBeenCalled();
});
it("uses Rastermill limits, base64 budget, and orientation-aware source dimensions", async () => {
// EXIF orientation swaps source dimensions before coordinate mapping so UI
// click coordinates can be translated back to the original image.
const inputBuffer = Buffer.from("large image");
const outputBuffer = Buffer.from("jpeg output");
mocks.probe.mockResolvedValue({
bytes: inputBuffer.byteLength,
format: "jpeg",
hasAlpha: false,
height: 1200,
orientation: 6,
width: 3000,
});
mocks.encode.mockResolvedValue({
base64Bytes: Buffer.byteLength(outputBuffer.toString("base64"), "utf8"),
bytes: outputBuffer.byteLength,
chosen: { format: "jpeg", quality: 70 },
data: outputBuffer,
format: "jpeg",
height: 1600,
metadata: "stripped",
mimeType: "image/jpeg",
resized: true,
width: 640,
withinBudget: true,
});
const result = await processImage(
{ type: "image", data: inputBuffer.toString("base64"), mimeType: "image/jpeg" },
{ autoResizeImages: true },
);
expect(mocks.encode).toHaveBeenCalledWith(inputBuffer, {
format: "auto",
limits: {
maxHeight: 2_000,
maxWidth: 2_000,
},
maxBase64Bytes: 4.5 * 1024 * 1024,
opaque: { format: "jpeg", quality: 80 },
search: {
compressionLevel: [6, 9],
quality: [80, 85, 70, 55, 40, 35],
},
transparent: { format: "png" },
});
expect(result).toStrictEqual({
ok: true,
image: {
type: "image",
data: outputBuffer.toString("base64"),
mimeType: "image/jpeg",
},
hints: [
"[Image: original 1200x3000, displayed at 640x1600. Multiply coordinates by 1.88 to map to original image.]",
],
});
});
it("omits images when Rastermill cannot satisfy the base64 budget", async () => {
const inputBuffer = Buffer.from("too large");
mocks.probe.mockResolvedValue({
bytes: inputBuffer.byteLength,
format: "png",
hasAlpha: false,
height: 4000,
orientation: null,
width: 4000,
});
mocks.encode.mockResolvedValue({
base64Bytes: 120,
bytes: 90,
chosen: { format: "png" },
data: Buffer.alloc(90),
format: "png",
height: 1,
metadata: "stripped",
mimeType: "image/png",
resized: true,
width: 1,
withinBudget: false,
});
await expect(
processImage(
{ type: "image", data: inputBuffer.toString("base64"), mimeType: "image/png" },
{ autoResizeImages: true },
),
).resolves.toStrictEqual({
ok: false,
message: "[Image omitted: could not be resized below the inline image size limit.]",
});
});
it("does not add coordinate hints when Rastermill only re-encodes the image", async () => {
const input = "a".repeat(4.5 * 1024 * 1024 + 1);
const outputBuffer = Buffer.from("re-encoded");
mocks.probe.mockResolvedValue({
bytes: Buffer.byteLength(input, "base64"),
format: "png",
hasAlpha: false,
height: 20,
orientation: null,
width: 10,
});
mocks.encode.mockResolvedValue({
base64Bytes: Buffer.byteLength(outputBuffer.toString("base64"), "utf8"),
bytes: outputBuffer.byteLength,
chosen: { format: "jpeg", quality: 80 },
data: outputBuffer,
format: "jpeg",
height: 20,
metadata: "stripped",
mimeType: "image/jpeg",
resized: false,
width: 10,
withinBudget: true,
});
await expect(
processImage(
{ type: "image", data: input, mimeType: "image/png" },
{ autoResizeImages: true },
),
).resolves.toStrictEqual({
ok: true,
image: {
type: "image",
data: outputBuffer.toString("base64"),
mimeType: "image/jpeg",
},
hints: [],
});
});
});