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.
182 lines
5.2 KiB
TypeScript
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,
|
|
};
|
|
}
|