170 lines
6.4 KiB
TypeScript
170 lines
6.4 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import {
|
|
domainInitial,
|
|
formatDuration,
|
|
formatToolValue,
|
|
getDomainColor,
|
|
getFaviconUrl,
|
|
getToolDisplayInfo,
|
|
parseToolValue,
|
|
readSearchSources,
|
|
} from "../app/utils/tool-rendering";
|
|
|
|
describe("stored tool values", () => {
|
|
test("handles missing, text, valid JSON and streaming JSON", () => {
|
|
expect(parseToolValue(null)).toBeNull();
|
|
expect(parseToolValue({ type: "text", value: "hello" })).toBe("hello");
|
|
expect(parseToolValue({ type: "json", value: '{"query":"gold"}' })).toEqual({ query: "gold" });
|
|
expect(formatToolValue({ type: "json", value: '{"query":' })).toBe('{"query":');
|
|
});
|
|
});
|
|
|
|
describe("search sources", () => {
|
|
test("distinguishes unavailable results from empty results", () => {
|
|
expect(readSearchSources(null)).toBeNull();
|
|
expect(readSearchSources("partial")).toBeNull();
|
|
expect(readSearchSources([])).toEqual([]);
|
|
});
|
|
|
|
test("rejects unsafe links, tolerates malformed entries and deduplicates pages", () => {
|
|
expect(readSearchSources([
|
|
null,
|
|
{ link: "javascript:alert(1)" },
|
|
{ link: "data:text/html,hello" },
|
|
{ link: "/relative" },
|
|
{ link: "https://example.com/page#one", title: "Example", snippet: "Evidence" },
|
|
{ link: "https://example.com/page#two", title: "Duplicate" },
|
|
{ link: "https://other.example/", title: 1 },
|
|
])).toEqual([
|
|
{ url: "https://example.com/page", hostname: "example.com", title: "Example", snippet: "Evidence" },
|
|
{ url: "https://other.example/", hostname: "other.example", title: "other.example", snippet: "" },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("duration formatting", () => {
|
|
test("formats duration in seconds with 1 decimal", () => {
|
|
const start = new Date("2026-09-25T12:00:00.000Z");
|
|
const end = new Date("2026-09-25T12:00:02.100Z");
|
|
expect(formatDuration(start, end)).toBe("2.1s");
|
|
});
|
|
|
|
test("formats numeric timestamps", () => {
|
|
const now = 1700000000000;
|
|
expect(formatDuration(now, now + 1500)).toBe("1.5s");
|
|
expect(formatDuration(new Date(now).toISOString(), now + 3200)).toBe("3.2s");
|
|
});
|
|
|
|
test("formats short duration", () => {
|
|
const start = new Date("2026-09-25T12:00:00.000Z");
|
|
const end = new Date("2026-09-25T12:00:00.400Z");
|
|
expect(formatDuration(start, end)).toBe("0.4s");
|
|
});
|
|
|
|
test("returns null for invalid or negative or sub-threshold duration", () => {
|
|
expect(formatDuration(null, null)).toBeNull();
|
|
const start = new Date("2026-09-25T12:00:02.000Z");
|
|
const end = new Date("2026-09-25T12:00:01.000Z");
|
|
expect(formatDuration(start, end)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("tool display info", () => {
|
|
test("handles search with results", () => {
|
|
const info = getToolDisplayInfo({
|
|
toolName: "search",
|
|
status: "completed",
|
|
input: { type: "json", value: '{"query":"drizzle pg enum best practices"}' },
|
|
output: {
|
|
type: "json",
|
|
value: JSON.stringify([
|
|
{ link: "https://orm.drizzle.team/docs/enum", title: "PostgreSQL enums | Drizzle ORM" },
|
|
]),
|
|
},
|
|
});
|
|
expect(info.verb).toBe("Searched 1 site");
|
|
expect(info.target).toBe('"drizzle pg enum best practices"');
|
|
expect(info.icon).toBe("i-mynaui-search");
|
|
});
|
|
|
|
test("handles read file", () => {
|
|
const info = getToolDisplayInfo({
|
|
toolName: "read_file",
|
|
status: "completed",
|
|
input: { type: "json", value: '{"path":"drizzle/schema.ts"}' },
|
|
});
|
|
expect(info.verb).toBe("Read");
|
|
expect(info.target).toBe("drizzle/schema.ts");
|
|
expect(info.icon).toBe("i-mynaui-file");
|
|
});
|
|
|
|
test("handles file edit pending", () => {
|
|
const info = getToolDisplayInfo({
|
|
toolName: "edit_file",
|
|
status: "pending",
|
|
input: { type: "json", value: '{"path":"server/api/chat/generate.ts"}' },
|
|
});
|
|
expect(info.verb).toBe("Editing");
|
|
expect(info.target).toBe("server/api/chat/generate.ts");
|
|
expect(info.icon).toBe("i-mynaui-edit");
|
|
});
|
|
|
|
test("handles query failed", () => {
|
|
const info = getToolDisplayInfo({
|
|
toolName: "query",
|
|
status: "failed",
|
|
input: { type: "json", value: '{"query":"select * from tool_calls"}' },
|
|
error: { type: "text", value: 'relation "tool_calls" does not exist' },
|
|
});
|
|
expect(info.verb).toBe("Query failed");
|
|
expect(info.target).toBe("select * from tool_calls");
|
|
expect(info.icon).toBe("i-mynaui-database");
|
|
});
|
|
|
|
test("handles fetch cancelled", () => {
|
|
const info = getToolDisplayInfo({
|
|
toolName: "fetchUrl",
|
|
status: "cancelled",
|
|
input: { type: "json", value: '{"url":"https://orm.drizzle.team/docs/..."}' },
|
|
});
|
|
expect(info.verb).toBe("Fetch cancelled");
|
|
expect(info.target).toBe("https://orm.drizzle.team/docs/...");
|
|
expect(info.icon).toBe("i-mynaui-link");
|
|
});
|
|
|
|
test("handles python execution", () => {
|
|
const info = getToolDisplayInfo({
|
|
toolName: "python",
|
|
status: "completed",
|
|
input: { type: "json", value: '{"code":"2 + 2"}' },
|
|
output: { type: "json", value: '{"output":"4"}' },
|
|
});
|
|
expect(info.verb).toBe("Ran Python");
|
|
expect(info.target).toBe("2 + 2");
|
|
expect(info.icon).toBe("i-mynaui-terminal");
|
|
});
|
|
});
|
|
|
|
describe("domain utilities", () => {
|
|
test("extracts domain initial correctly", () => {
|
|
expect(domainInitial("goldprice.org")).toBe("G");
|
|
expect(domainInitial("www.kitco.com")).toBe("K");
|
|
expect(domainInitial("apmex.com")).toBe("A");
|
|
});
|
|
|
|
test("returns consistent domain colors", () => {
|
|
const color1 = getDomainColor("goldprice.org");
|
|
const color2 = getDomainColor("goldprice.org");
|
|
expect(color1).toBe(color2);
|
|
expect(typeof color1).toBe("string");
|
|
expect(color1.startsWith("#")).toBe(true);
|
|
});
|
|
|
|
test("formats DuckDuckGo favicon service url", () => {
|
|
expect(getFaviconUrl("goldprice.org")).toBe("https://icons.duckduckgo.com/ip3/goldprice.org.ico");
|
|
expect(getFaviconUrl("www.kitco.com")).toBe("https://icons.duckduckgo.com/ip3/kitco.com.ico");
|
|
});
|
|
});
|
|
|
|
|