Download src/agents/utils/image-resize.test.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 5.33 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/agents/utils/image-resize.test.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/agents/utils/image-resize.test.ts
-
curl -L -o image-resize.test.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/agents/utils/image-resize.test.ts
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: [], | |
| }); | |
| }); | |
| }); | |