import { describe, it, expect } from "vitest"; import { extractTextFromContent, normalizeContentBlocks, formatHistoryAsText, convertHistoryToMessages, } from "@/lib/conversation-utils.js"; import { conversationHistoryFixture } from "../../fixtures/messages.js"; describe("conversation-utils.ts", () => { describe("extractTextFromContent", () => { it("should return string content as-is", () => { const result = extractTextFromContent("Hello world"); expect(result).toBe("Hello world"); }); it("should extract text from single text block", () => { const content = [{ type: "text", text: "Hello" }]; const result = extractTextFromContent(content); expect(result).toBe("Hello"); }); it("should extract and join multiple text blocks with newlines", () => { const content = [ { type: "text", text: "First block" }, { type: "text", text: "Second block" }, { type: "text", text: "Third block" }, ]; const result = extractTextFromContent(content); expect(result).toBe("First block\nSecond block\nThird block"); }); it("should ignore non-text blocks", () => { const content = [ { type: "text", text: "Text content" }, { type: "image", source: { type: "base64", data: "abc" } }, { type: "text", text: "More text" }, { type: "tool_use", name: "bash", input: {} }, ]; const result = extractTextFromContent(content); expect(result).toBe("Text content\nMore text"); }); it("should handle blocks without text property", () => { const content = [ { type: "text", text: "Valid" }, { type: "text" } as any, { type: "text", text: "Also valid" }, ]; const result = extractTextFromContent(content); expect(result).toBe("Valid\n\nAlso valid"); }); it("should handle empty array", () => { const result = extractTextFromContent([]); expect(result).toBe(""); }); it("should handle array with only non-text blocks", () => { const content = [ { type: "image", source: {} }, { type: "tool_use", name: "test" }, ]; const result = extractTextFromContent(content); expect(result).toBe(""); }); }); describe("normalizeContentBlocks", () => { it("should convert string to content block array", () => { const result = normalizeContentBlocks("Hello"); expect(result).toEqual([{ type: "text", text: "Hello" }]); }); it("should return array content as-is", () => { const content = [ { type: "text", text: "Hello" }, { type: "image", source: {} }, ]; const result = normalizeContentBlocks(content); expect(result).toBe(content); expect(result).toHaveLength(2); }); it("should handle empty string", () => { const result = normalizeContentBlocks(""); expect(result).toEqual([{ type: "text", text: "" }]); }); }); describe("formatHistoryAsText", () => { it("should return empty string for empty history", () => { const result = formatHistoryAsText([]); expect(result).toBe(""); }); it("should format single user message", () => { const history = [{ role: "user" as const, content: "Hello" }]; const result = formatHistoryAsText(history); expect(result).toContain("Previous conversation:"); expect(result).toContain("User: Hello"); expect(result).toContain("---"); }); it("should format single assistant message", () => { const history = [{ role: "assistant" as const, content: "Hi there" }]; const result = formatHistoryAsText(history); expect(result).toContain("Assistant: Hi there"); }); it("should format multiple messages with correct roles", () => { const history = conversationHistoryFixture.slice(0, 2); const result = formatHistoryAsText(history); expect(result).toContain("User: Hello, can you help me?"); expect(result).toContain("Assistant: Of course! How can I assist you today?"); expect(result).toContain("---"); }); it("should handle messages with array content (multipart)", () => { const history = [conversationHistoryFixture[2]]; // Has text + image const result = formatHistoryAsText(history); expect(result).toContain("What is in this image?"); expect(result).not.toContain("base64"); // Should not include image data }); it("should format all messages from fixture", () => { const result = formatHistoryAsText(conversationHistoryFixture); expect(result).toContain("Previous conversation:"); expect(result).toContain("User: Hello, can you help me?"); expect(result).toContain("Assistant: Of course!"); expect(result).toContain("User: What is in this image?"); expect(result).toContain("---"); }); it("should separate messages with double newlines", () => { const history = [ { role: "user" as const, content: "First" }, { role: "assistant" as const, content: "Second" }, ]; const result = formatHistoryAsText(history); expect(result).toMatch(/User: First\n\nAssistant: Second/); }); }); describe("convertHistoryToMessages", () => { it("should convert empty history", () => { const result = convertHistoryToMessages([]); expect(result).toEqual([]); }); it("should convert single message to SDK format", () => { const history = [{ role: "user" as const, content: "Hello" }]; const result = convertHistoryToMessages(history); expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ type: "user", session_id: "", message: { role: "user", content: [{ type: "text", text: "Hello" }], }, parent_tool_use_id: null, }); }); it("should normalize string content to array", () => { const history = [{ role: "assistant" as const, content: "Response" }]; const result = convertHistoryToMessages(history); expect(result[0].message.content).toEqual([ { type: "text", text: "Response" }, ]); }); it("should preserve array content", () => { const history = [ { role: "user" as const, content: [ { type: "text", text: "Hello" }, { type: "image", source: {} }, ], }, ]; const result = convertHistoryToMessages(history); expect(result[0].message.content).toHaveLength(2); expect(result[0].message.content[0]).toEqual({ type: "text", text: "Hello" }); }); it("should convert multiple messages", () => { const history = conversationHistoryFixture.slice(0, 2); const result = convertHistoryToMessages(history); expect(result).toHaveLength(2); expect(result[0].type).toBe("user"); expect(result[1].type).toBe("assistant"); }); it("should set correct fields for SDK format", () => { const history = [{ role: "user" as const, content: "Test" }]; const result = convertHistoryToMessages(history); expect(result[0].session_id).toBe(""); expect(result[0].parent_tool_use_id).toBeNull(); expect(result[0].type).toBe("user"); expect(result[0].message.role).toBe("user"); }); it("should handle all messages from fixture", () => { const result = convertHistoryToMessages(conversationHistoryFixture); expect(result).toHaveLength(3); expect(result[0].message.content).toBeInstanceOf(Array); expect(result[1].message.content).toBeInstanceOf(Array); expect(result[2].message.content).toBeInstanceOf(Array); }); }); });