Files
automaker/apps/server/tests/unit/lib/conversation-utils.test.ts
Kacper 23ff99d2e2 feat: add comprehensive integration tests for auto-mode-service
- Created git-test-repo helper for managing test git repositories
- Added 13 integration tests covering:
  - Worktree operations (create, error handling, non-worktree mode)
  - Feature execution (status updates, model selection, duplicate prevention)
  - Auto loop (start/stop, pending features, max concurrency, events)
  - Error handling (provider errors, continue after failures)
- Integration tests use real git operations with temporary repos
- All 416 tests passing with 72.65% overall coverage
- Service coverage improved: agent-service 58%, auto-mode-service 44%, feature-loader 66%

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-13 13:34:27 +01:00

227 lines
7.6 KiB
TypeScript

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);
});
});
});