mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 06:42:03 +00:00
Added extensive test coverage for previously untested files: Platform package (94.69% coverage, +47 tests): - paths.test.ts: 22 tests for path construction and directory creation - security.test.ts: 25 tests for path validation and security Utils package (94.3% coverage, +109 tests): - logger.test.ts: 23 tests for logging with levels - fs-utils.test.ts: 20 tests for safe file operations - conversation-utils.test.ts: 24 tests for message formatting - image-handler.test.ts: 25 tests for image processing - prompt-builder.test.ts: 17 tests for prompt construction Coverage improvements: - Platform: 63.71% → 94.69% stmts, 40% → 97.14% funcs - Utils: 19.51% → 94.3% stmts, 18.51% → 100% funcs Updated thresholds to enforce high quality: - Platform: 90% lines/stmts, 95% funcs, 75% branches - Utils: 90% lines/stmts, 95% funcs, 85% branches Total new tests: 156 (platform: 47, utils: 109) All tests passing with new coverage thresholds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
317 lines
9.5 KiB
TypeScript
317 lines
9.5 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
import fs from "fs/promises";
|
|
import path from "path";
|
|
import os from "os";
|
|
import { buildPromptWithImages } from "../src/prompt-builder";
|
|
|
|
describe("prompt-builder.ts", () => {
|
|
let tempDir: string;
|
|
|
|
beforeEach(async () => {
|
|
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "prompt-builder-test-"));
|
|
});
|
|
|
|
afterEach(async () => {
|
|
try {
|
|
await fs.rm(tempDir, { recursive: true, force: true });
|
|
} catch (error) {
|
|
// Ignore cleanup errors
|
|
}
|
|
});
|
|
|
|
describe("buildPromptWithImages - no images", () => {
|
|
it("should return plain text when no images provided", async () => {
|
|
const basePrompt = "Hello, world!";
|
|
|
|
const result = await buildPromptWithImages(basePrompt);
|
|
|
|
expect(result.content).toBe("Hello, world!");
|
|
expect(result.hasImages).toBe(false);
|
|
});
|
|
|
|
it("should return plain text when empty image array provided", async () => {
|
|
const basePrompt = "Test prompt";
|
|
|
|
const result = await buildPromptWithImages(basePrompt, []);
|
|
|
|
expect(result.content).toBe("Test prompt");
|
|
expect(result.hasImages).toBe(false);
|
|
});
|
|
|
|
it("should handle multiline prompts", async () => {
|
|
const basePrompt = "Line 1\nLine 2\nLine 3";
|
|
|
|
const result = await buildPromptWithImages(basePrompt);
|
|
|
|
expect(result.content).toBe("Line 1\nLine 2\nLine 3");
|
|
});
|
|
});
|
|
|
|
describe("buildPromptWithImages - with images", () => {
|
|
it("should build content blocks with single image", async () => {
|
|
const imagePath = path.join(tempDir, "test.png");
|
|
await fs.writeFile(imagePath, Buffer.from("image data"));
|
|
|
|
const result = await buildPromptWithImages(
|
|
"Check this image",
|
|
[imagePath]
|
|
);
|
|
|
|
expect(result.hasImages).toBe(true);
|
|
expect(Array.isArray(result.content)).toBe(true);
|
|
|
|
const blocks = result.content as Array<{
|
|
type: string;
|
|
text?: string;
|
|
source?: object;
|
|
}>;
|
|
expect(blocks).toHaveLength(2);
|
|
expect(blocks[0]).toMatchObject({
|
|
type: "text",
|
|
text: "Check this image",
|
|
});
|
|
expect(blocks[1]).toMatchObject({
|
|
type: "image",
|
|
});
|
|
});
|
|
|
|
it("should build content blocks with multiple images", async () => {
|
|
const image1 = path.join(tempDir, "img1.jpg");
|
|
const image2 = path.join(tempDir, "img2.png");
|
|
|
|
await fs.writeFile(image1, Buffer.from("jpg data"));
|
|
await fs.writeFile(image2, Buffer.from("png data"));
|
|
|
|
const result = await buildPromptWithImages("Two images", [
|
|
image1,
|
|
image2,
|
|
]);
|
|
|
|
expect(result.hasImages).toBe(true);
|
|
|
|
const blocks = result.content as Array<{
|
|
type: string;
|
|
text?: string;
|
|
source?: object;
|
|
}>;
|
|
expect(blocks).toHaveLength(3); // 1 text + 2 images
|
|
expect(blocks[0].type).toBe("text");
|
|
expect(blocks[1].type).toBe("image");
|
|
expect(blocks[2].type).toBe("image");
|
|
});
|
|
|
|
it("should resolve relative paths with workDir", async () => {
|
|
const imagePath = "test.png";
|
|
const fullPath = path.join(tempDir, imagePath);
|
|
await fs.writeFile(fullPath, Buffer.from("data"));
|
|
|
|
const result = await buildPromptWithImages(
|
|
"Test",
|
|
[imagePath],
|
|
tempDir
|
|
);
|
|
|
|
expect(result.hasImages).toBe(true);
|
|
expect(Array.isArray(result.content)).toBe(true);
|
|
});
|
|
|
|
it("should handle absolute paths without workDir", async () => {
|
|
const imagePath = path.join(tempDir, "absolute.png");
|
|
await fs.writeFile(imagePath, Buffer.from("data"));
|
|
|
|
const result = await buildPromptWithImages("Test", [imagePath]);
|
|
|
|
expect(result.hasImages).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("buildPromptWithImages - includeImagePaths option", () => {
|
|
it("should not include image paths by default", async () => {
|
|
const imagePath = path.join(tempDir, "test.png");
|
|
await fs.writeFile(imagePath, Buffer.from("data"));
|
|
|
|
const result = await buildPromptWithImages("Prompt", [imagePath]);
|
|
|
|
const blocks = result.content as Array<{
|
|
type: string;
|
|
text?: string;
|
|
}>;
|
|
const textBlock = blocks.find((b) => b.type === "text");
|
|
|
|
expect(textBlock?.text).not.toContain("Attached images:");
|
|
expect(textBlock?.text).toBe("Prompt");
|
|
});
|
|
|
|
it("should include image paths when requested", async () => {
|
|
const imagePath = path.join(tempDir, "test.png");
|
|
await fs.writeFile(imagePath, Buffer.from("data"));
|
|
|
|
const result = await buildPromptWithImages(
|
|
"Prompt",
|
|
[imagePath],
|
|
undefined,
|
|
true
|
|
);
|
|
|
|
const blocks = result.content as Array<{
|
|
type: string;
|
|
text?: string;
|
|
}>;
|
|
const textBlock = blocks.find((b) => b.type === "text");
|
|
|
|
expect(textBlock?.text).toContain("Prompt");
|
|
expect(textBlock?.text).toContain("Attached images:");
|
|
expect(textBlock?.text).toContain(imagePath);
|
|
});
|
|
|
|
it("should format multiple image paths when included", async () => {
|
|
const img1 = path.join(tempDir, "img1.png");
|
|
const img2 = path.join(tempDir, "img2.jpg");
|
|
|
|
await fs.writeFile(img1, Buffer.from("data1"));
|
|
await fs.writeFile(img2, Buffer.from("data2"));
|
|
|
|
const result = await buildPromptWithImages(
|
|
"Test",
|
|
[img1, img2],
|
|
undefined,
|
|
true
|
|
);
|
|
|
|
const blocks = result.content as Array<{
|
|
type: string;
|
|
text?: string;
|
|
}>;
|
|
const textBlock = blocks.find((b) => b.type === "text");
|
|
|
|
expect(textBlock?.text).toContain("Attached images:");
|
|
expect(textBlock?.text).toContain(img1);
|
|
expect(textBlock?.text).toContain(img2);
|
|
});
|
|
});
|
|
|
|
describe("buildPromptWithImages - edge cases", () => {
|
|
it("should handle empty prompt with images", async () => {
|
|
const imagePath = path.join(tempDir, "test.png");
|
|
await fs.writeFile(imagePath, Buffer.from("data"));
|
|
|
|
const result = await buildPromptWithImages("", [imagePath]);
|
|
|
|
expect(result.hasImages).toBe(true);
|
|
|
|
const blocks = result.content as Array<{
|
|
type: string;
|
|
text?: string;
|
|
source?: object;
|
|
}>;
|
|
// Should only have image block, no text block for empty string
|
|
expect(blocks.length).toBeGreaterThan(0);
|
|
expect(blocks.some((b) => b.type === "image")).toBe(true);
|
|
});
|
|
|
|
it("should handle whitespace-only prompt with images", async () => {
|
|
const imagePath = path.join(tempDir, "test.png");
|
|
await fs.writeFile(imagePath, Buffer.from("data"));
|
|
|
|
const result = await buildPromptWithImages(" ", [imagePath]);
|
|
|
|
expect(result.hasImages).toBe(true);
|
|
|
|
const blocks = result.content as Array<{
|
|
type: string;
|
|
text?: string;
|
|
source?: object;
|
|
}>;
|
|
// Whitespace-only is trimmed, so no text block should be added
|
|
expect(blocks.every((b) => b.type !== "text")).toBe(true);
|
|
});
|
|
|
|
it("should skip failed image loads", async () => {
|
|
const validImage = path.join(tempDir, "valid.png");
|
|
const invalidImage = path.join(tempDir, "nonexistent.png");
|
|
|
|
await fs.writeFile(validImage, Buffer.from("data"));
|
|
|
|
const result = await buildPromptWithImages("Test", [
|
|
validImage,
|
|
invalidImage,
|
|
]);
|
|
|
|
expect(result.hasImages).toBe(true);
|
|
|
|
const blocks = result.content as Array<{
|
|
type: string;
|
|
text?: string;
|
|
source?: object;
|
|
}>;
|
|
const imageBlocks = blocks.filter((b) => b.type === "image");
|
|
|
|
// Only valid image should be included
|
|
expect(imageBlocks).toHaveLength(1);
|
|
});
|
|
|
|
it("should handle mixed case in includeImagePaths parameter", async () => {
|
|
const imagePath = path.join(tempDir, "test.png");
|
|
await fs.writeFile(imagePath, Buffer.from("data"));
|
|
|
|
const resultFalse = await buildPromptWithImages(
|
|
"Test",
|
|
[imagePath],
|
|
undefined,
|
|
false
|
|
);
|
|
const resultTrue = await buildPromptWithImages(
|
|
"Test",
|
|
[imagePath],
|
|
undefined,
|
|
true
|
|
);
|
|
|
|
const blocksFalse = resultFalse.content as Array<{
|
|
type: string;
|
|
text?: string;
|
|
}>;
|
|
const blocksTrue = resultTrue.content as Array<{
|
|
type: string;
|
|
text?: string;
|
|
}>;
|
|
|
|
expect(blocksFalse[0].text).not.toContain("Attached images:");
|
|
expect(blocksTrue[0].text).toContain("Attached images:");
|
|
});
|
|
});
|
|
|
|
describe("buildPromptWithImages - content format", () => {
|
|
it("should return string when only text and includeImagePaths false", async () => {
|
|
const result = await buildPromptWithImages("Just text", undefined);
|
|
|
|
expect(typeof result.content).toBe("string");
|
|
});
|
|
|
|
it("should return array when has images", async () => {
|
|
const imagePath = path.join(tempDir, "test.png");
|
|
await fs.writeFile(imagePath, Buffer.from("data"));
|
|
|
|
const result = await buildPromptWithImages("Text", [imagePath]);
|
|
|
|
expect(Array.isArray(result.content)).toBe(true);
|
|
});
|
|
|
|
it("should preserve prompt formatting", async () => {
|
|
const basePrompt = "Line 1\n\nLine 2\n Indented line";
|
|
const imagePath = path.join(tempDir, "test.png");
|
|
await fs.writeFile(imagePath, Buffer.from("data"));
|
|
|
|
const result = await buildPromptWithImages(basePrompt, [imagePath]);
|
|
|
|
const blocks = result.content as Array<{
|
|
type: string;
|
|
text?: string;
|
|
}>;
|
|
const textBlock = blocks.find((b) => b.type === "text");
|
|
|
|
expect(textBlock?.text).toBe(basePrompt);
|
|
});
|
|
});
|
|
});
|