@@ -10,6 +10,22 @@ import { createReadTool } from "../src/core/tools/read.ts";
|
||||
const TINY_PNG_BASE64 =
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==";
|
||||
|
||||
function createTinyBmp1x1Red24bpp(): Buffer {
|
||||
const buffer = Buffer.alloc(58);
|
||||
buffer.write("BM", 0, "ascii");
|
||||
buffer.writeUInt32LE(buffer.length, 2);
|
||||
buffer.writeUInt32LE(54, 10);
|
||||
buffer.writeUInt32LE(40, 14);
|
||||
buffer.writeInt32LE(1, 18);
|
||||
buffer.writeInt32LE(1, 22);
|
||||
buffer.writeUInt16LE(1, 26);
|
||||
buffer.writeUInt16LE(24, 28);
|
||||
buffer.writeUInt32LE(0, 30);
|
||||
buffer.writeUInt32LE(4, 34);
|
||||
buffer[56] = 0xff;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
describe("blockImages setting", () => {
|
||||
describe("SettingsManager", () => {
|
||||
it("should default blockImages to false", () => {
|
||||
@@ -106,6 +122,18 @@ describe("blockImages setting", () => {
|
||||
expect(result.images[0].type).toBe("image");
|
||||
});
|
||||
|
||||
it("should process BMP images from disk as PNG attachments", async () => {
|
||||
const imagePath = join(testDir, "test.bmp");
|
||||
writeFileSync(imagePath, createTinyBmp1x1Red24bpp());
|
||||
|
||||
const result = await processFileArguments([imagePath]);
|
||||
|
||||
expect(result.images).toHaveLength(1);
|
||||
expect(result.images[0].type).toBe("image");
|
||||
expect(result.images[0].mimeType).toBe("image/png");
|
||||
expect(result.text).toContain("[Image converted from image/bmp to image/png.]");
|
||||
});
|
||||
|
||||
it("should process text files normally", async () => {
|
||||
// Create test text file
|
||||
const textPath = join(testDir, "test.txt");
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { processImage } from "../src/utils/image-process.ts";
|
||||
import { detectSupportedImageMimeType } from "../src/utils/mime.ts";
|
||||
|
||||
function createTinyBmp1x1Red24bpp(): Buffer {
|
||||
const buffer = Buffer.alloc(58);
|
||||
buffer.write("BM", 0, "ascii");
|
||||
buffer.writeUInt32LE(buffer.length, 2);
|
||||
buffer.writeUInt32LE(54, 10);
|
||||
buffer.writeUInt32LE(40, 14);
|
||||
buffer.writeInt32LE(1, 18);
|
||||
buffer.writeInt32LE(1, 22);
|
||||
buffer.writeUInt16LE(1, 26);
|
||||
buffer.writeUInt16LE(24, 28);
|
||||
buffer.writeUInt32LE(0, 30);
|
||||
buffer.writeUInt32LE(4, 34);
|
||||
buffer[56] = 0xff;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
function expectPngMagic(base64Data: string): void {
|
||||
const buffer = Buffer.from(base64Data, "base64");
|
||||
expect(buffer[0]).toBe(0x89);
|
||||
expect(buffer[1]).toBe(0x50);
|
||||
expect(buffer[2]).toBe(0x4e);
|
||||
expect(buffer[3]).toBe(0x47);
|
||||
}
|
||||
|
||||
describe("image processing pipeline", () => {
|
||||
it("detects BMP files from magic bytes", () => {
|
||||
expect(detectSupportedImageMimeType(createTinyBmp1x1Red24bpp())).toBe("image/bmp");
|
||||
});
|
||||
|
||||
it("converts BMP files to PNG attachments when auto-resize is disabled", async () => {
|
||||
const result = await processImage(createTinyBmp1x1Red24bpp(), "image/bmp", { autoResizeImages: false });
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
expect(result.mimeType).toBe("image/png");
|
||||
expect(result.hints).toContain("[Image converted from image/bmp to image/png.]");
|
||||
expectPngMagic(result.data);
|
||||
});
|
||||
|
||||
it("converts BMP files before auto-resizing", async () => {
|
||||
const result = await processImage(createTinyBmp1x1Red24bpp(), "image/bmp");
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
expect(result.mimeType).toBe("image/png");
|
||||
expect(result.hints).toContain("[Image converted from image/bmp to image/png.]");
|
||||
expectPngMagic(result.data);
|
||||
});
|
||||
});
|
||||
@@ -34,6 +34,22 @@ function getTextOutput(result: any): string {
|
||||
);
|
||||
}
|
||||
|
||||
function createTinyBmp1x1Red24bpp(): Buffer {
|
||||
const buffer = Buffer.alloc(58);
|
||||
buffer.write("BM", 0, "ascii");
|
||||
buffer.writeUInt32LE(buffer.length, 2);
|
||||
buffer.writeUInt32LE(54, 10);
|
||||
buffer.writeUInt32LE(40, 14);
|
||||
buffer.writeInt32LE(1, 18);
|
||||
buffer.writeInt32LE(1, 22);
|
||||
buffer.writeUInt16LE(1, 26);
|
||||
buffer.writeUInt16LE(24, 28);
|
||||
buffer.writeUInt32LE(0, 30);
|
||||
buffer.writeUInt32LE(4, 34);
|
||||
buffer[56] = 0xff;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
describe("Coding Agent Tools", () => {
|
||||
let testDir: string;
|
||||
|
||||
@@ -191,6 +207,24 @@ describe("Coding Agent Tools", () => {
|
||||
expect((imageBlock?.data ?? "").length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should read BMP files from disk as PNG image attachments", async () => {
|
||||
const testFile = join(testDir, "image.bmp");
|
||||
writeFileSync(testFile, createTinyBmp1x1Red24bpp());
|
||||
|
||||
const result = await readTool.execute("test-call-img-bmp", { path: testFile });
|
||||
|
||||
expect(result.content[0]?.type).toBe("text");
|
||||
expect(getTextOutput(result)).toContain("Read image file [image/png]");
|
||||
expect(getTextOutput(result)).toContain("[Image converted from image/bmp to image/png.]");
|
||||
|
||||
const imageBlock = result.content.find(
|
||||
(c): c is { type: "image"; mimeType: string; data: string } => c.type === "image",
|
||||
);
|
||||
expect(imageBlock).toBeDefined();
|
||||
expect(imageBlock?.mimeType).toBe("image/png");
|
||||
expect(Buffer.from(imageBlock?.data ?? "", "base64")[0]).toBe(0x89);
|
||||
});
|
||||
|
||||
it("should treat files with image extension but non-image content as text", async () => {
|
||||
const testFile = join(testDir, "not-an-image.png");
|
||||
writeFileSync(testFile, "definitely not a png");
|
||||
|
||||
Reference in New Issue
Block a user