evOo-local/packages/adapters/src/scratchpad-tools.test.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

182 lines
5.2 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import {
addScratchpadItemFromTool,
completeScratchpadItemFromTool,
listScratchpadItems,
listScratchpadItemsFromTool,
removeScratchpadItemFromTool,
updateScratchpadItemFromTool,
} from "./scratchpad-tools.js";
describe("scratchpad tools store", () => {
it("lists open and parked items by default and omits done", async () => {
const findMany = vi.fn(async () => [
row({ id: "1", title: "Open", status: "open" }),
row({ id: "2", title: "Parked", status: "parked" }),
]);
const prisma = {
scratchpadItem: { findMany },
};
const items = await listScratchpadItems(
{ prisma: prisma as never },
{ spaceId: "ws", botId: "bot" },
);
expect(findMany).toHaveBeenCalledWith({
where: { spaceId: "ws", botId: "bot", status: { in: ["open", "parked"] } },
orderBy: [{ updatedAt: "desc" }, { createdAt: "desc" }],
});
expect(items).toEqual([
expect.objectContaining({ id: "1", title: "Open", status: "open" }),
expect.objectContaining({ id: "2", title: "Parked", status: "parked" }),
]);
});
it("adds an item with trimmed title and default open status", async () => {
const create = vi.fn(async ({ data }: { data: Record<string, unknown> }) =>
row({
id: "new",
title: String(data.title),
status: String(data.status),
notes: String(data.notes),
}),
);
const prisma = { scratchpadItem: { create } };
const result = await addScratchpadItemFromTool(
{ prisma: prisma as never },
{
spaceId: "ws",
botId: "bot",
userId: "user",
title: " Ship PR ",
notes: " after review ",
},
);
expect(create).toHaveBeenCalledWith({
data: {
spaceId: "ws",
botId: "bot",
userId: "user",
title: "Ship PR",
status: "open",
notes: "after review",
},
});
expect(result).toEqual({
item: expect.objectContaining({ id: "new", title: "Ship PR", status: "open" }),
});
});
it("rejects invalid status on add", async () => {
const result = await addScratchpadItemFromTool(
{ prisma: { scratchpadItem: { create: vi.fn() } } as never },
{
spaceId: "ws",
botId: "bot",
userId: "user",
title: "Nope",
status: "later",
},
);
expect(result).toEqual({ error: "status must be open, parked, or done." });
});
it("updates, completes, and removes scoped items", async () => {
const existing = row({ id: "item-1", title: "Draft", status: "open" });
const findFirst = vi.fn(async () => existing);
const update = vi.fn(async ({ data }: { data: Record<string, unknown> }) =>
row({ ...existing, ...data, status: String(data.status ?? existing.status) }),
);
const del = vi.fn(async () => existing);
const prisma = {
scratchpadItem: { findFirst, update, delete: del },
};
const updated = await updateScratchpadItemFromTool(
{ prisma: prisma as never },
{
spaceId: "ws",
botId: "bot",
userId: "user",
itemId: "item-1",
title: "Draft v2",
status: "parked",
},
);
expect(updated).toEqual({
item: expect.objectContaining({ title: "Draft v2", status: "parked" }),
});
const completed = await completeScratchpadItemFromTool(
{ prisma: prisma as never },
{ spaceId: "ws", botId: "bot", userId: "user", itemId: "item-1" },
);
expect(completed).toEqual({
item: expect.objectContaining({ status: "done" }),
});
const removed = await removeScratchpadItemFromTool(
{ prisma: prisma as never },
{ spaceId: "ws", botId: "bot", userId: "user", itemId: "item-1" },
);
expect(removed).toEqual({ ok: true, itemId: "item-1", title: "Draft" });
expect(findFirst).toHaveBeenCalledWith({
where: {
id: "item-1",
spaceId: "ws",
botId: "bot",
userId: "user",
},
});
});
it("returns not found when item is outside the bot scope", async () => {
const prisma = {
scratchpadItem: { findFirst: vi.fn(async () => null) },
};
await expect(
updateScratchpadItemFromTool(
{ prisma: prisma as never },
{
spaceId: "ws",
botId: "bot",
userId: "user",
itemId: "missing",
title: "x",
},
),
).resolves.toEqual({ error: "Scratchpad item not found." });
});
it("lists via tool wrapper", async () => {
const findMany = vi.fn(async () => [row({ id: "1", title: "Open", status: "open" })]);
const result = await listScratchpadItemsFromTool(
{ prisma: { scratchpadItem: { findMany } } as never },
{ spaceId: "ws", botId: "bot" },
);
expect(result.items).toHaveLength(1);
});
});
function row(partial: {
id: string;
title: string;
status: string;
notes?: string;
botId?: string;
}) {
const now = new Date("2026-08-25T12:00:00.000Z");
return {
id: partial.id,
botId: partial.botId ?? "bot",
title: partial.title,
status: partial.status,
notes: partial.notes ?? "",
createdAt: now,
updatedAt: now,
};
}