Files
local-cal/tests/ai-event-route.test.ts
Dmytro Stanchiev 513aafcebc feat: support multiple image uploads for AI event generation 🖼️
- Updated OpenRouter integration to accept an array of image URLs
- Updated ImagePicker to use the `multiple` attribute natively
- Added `appendImagesDeduped` for handling client-side image deduplication
- Enhanced clipboard pasting to extract multiple images at once
- Rendered multiple images in a horizontal thumbnail strip in the AIToolbar
- Added tests to cover multi-image logic and AI request mapping
2026-04-08 20:46:43 -04:00

97 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, test } from "bun:test";
import { buildMultimodalMessages } from "@/lib/ai-event-messages";
// ---------------------------------------------------------------------------
// buildMultimodalMessages behavioral tests
//
// Public behavior under test: given a system prompt, an optional text prompt,
// and an array of base64 image strings, returns a well-formed messages array
// for the OpenRouter chat API.
//
// We test WHAT the function produces (message structure), not HOW it does it.
// ---------------------------------------------------------------------------
const SYSTEM_PROMPT = "You are an assistant…";
const FAKE_PNG = "data:image/png;base64,abc123";
const FAKE_JPEG = "data:image/jpeg;base64,def456";
describe("buildMultimodalMessages message structure", () => {
test("first message is always the system prompt", () => {
const messages = buildMultimodalMessages(SYSTEM_PROMPT, "hello", [FAKE_PNG]);
expect(messages[0].role).toBe("system");
expect((messages[0].content as string)).toBe(SYSTEM_PROMPT);
});
test("second message is the user message", () => {
const messages = buildMultimodalMessages(SYSTEM_PROMPT, "hello", [FAKE_PNG]);
expect(messages[1].role).toBe("user");
});
test("user message content array starts with the text part", () => {
const messages = buildMultimodalMessages(SYSTEM_PROMPT, "any prompt", [FAKE_PNG]);
const userContent = messages[1].content as Array<{ type: string }>;
expect(userContent[0].type).toBe("text");
});
test("user message content array includes one image_url part per image", () => {
const messages = buildMultimodalMessages(SYSTEM_PROMPT, "prompt", [FAKE_PNG, FAKE_JPEG]);
const userContent = messages[1].content as Array<{ type: string }>;
const imageparts = userContent.filter((p) => p.type === "image_url");
expect(imageparts).toHaveLength(2);
});
test("each image_url part carries the correct base64 URL", () => {
const messages = buildMultimodalMessages(SYSTEM_PROMPT, undefined, [FAKE_PNG, FAKE_JPEG]);
const userContent = messages[1].content as Array<{
type: string;
imageUrl?: { url: string };
}>;
const imageParts = userContent.filter((p) => p.type === "image_url");
expect(imageParts[0].imageUrl?.url).toBe(FAKE_PNG);
expect(imageParts[1].imageUrl?.url).toBe(FAKE_JPEG);
});
test("text part uses a fallback when prompt is undefined", () => {
const messages = buildMultimodalMessages(SYSTEM_PROMPT, undefined, [FAKE_PNG]);
const userContent = messages[1].content as Array<{
type: string;
text?: string;
}>;
const textPart = userContent.find((p) => p.type === "text");
expect(typeof textPart?.text).toBe("string");
expect(textPart?.text?.length ?? 0).toBeGreaterThan(0);
});
test("text part carries the provided prompt text when given", () => {
const prompt = "Extract all events from these flyers";
const messages = buildMultimodalMessages(SYSTEM_PROMPT, prompt, [FAKE_PNG]);
const userContent = messages[1].content as Array<{
type: string;
text?: string;
}>;
const textPart = userContent.find((p) => p.type === "text");
expect(textPart?.text).toBe(prompt);
});
test("produces exactly 2 messages (system + user)", () => {
const messages = buildMultimodalMessages(SYSTEM_PROMPT, "x", [FAKE_PNG]);
expect(messages).toHaveLength(2);
});
test("single image produces content array of length 2 (1 text + 1 image)", () => {
const messages = buildMultimodalMessages(SYSTEM_PROMPT, "x", [FAKE_PNG]);
const userContent = messages[1].content as unknown[];
expect(userContent).toHaveLength(2);
});
test("three images produce content array of length 4 (1 text + 3 images)", () => {
const messages = buildMultimodalMessages(SYSTEM_PROMPT, "x", [
FAKE_PNG,
FAKE_JPEG,
FAKE_PNG,
]);
const userContent = messages[1].content as unknown[];
expect(userContent).toHaveLength(4);
});
});