import { describe, expect, it } from "vitest"; import { z } from "zod/v4"; import { defineApp, defineFileType, defineTool } from "../define/define"; import { ui } from "../tree/tree"; import { createTestWorkspace } from "./test-workspace"; const createContact = defineTool({ name: "crm.create_contact", description: "Create a contact.", input: z.object({ name: z.string(), email: z.string().optional() }), effect: "write", execute: async (input, context) => { const file = await context.coline.files.create({ typeKey: "crm.contact", name: input.name, document: { name: input.name, email: input.email ?? null }, }); await context.coline.search.index.upsert({ documents: [ { documentKey: `contact:${file.fileId}`, documentType: "contact", fileId: file.fileId, title: input.name, body: input.email ?? "", }, ], }); return { output: { fileId: file.fileId }, card: ui.stack([ui.reference({ kind: "file", id: file.fileId })]), }; }, }); const app = defineApp({ key: "crm", name: "CRM", permissions: ["files.read", "files.write", "storage.app", "ai.tools", "search.index"], surfaces: { home: { tier: "tree" } }, files: [ defineFileType({ metadata: { typeKey: "crm.contact", name: "Contact", indexable: false, surfaces: { preview: { tier: "tree" }, inline: { tier: "tree" } }, }, runtime: { renderPreview: (context) => ui.heading(String(context.document.name ?? "Contact")), renderInline: (context) => ui.text(String(context.document.name ?? "Contact")), }, }), ], tools: [createContact], triggers: { schedules: [{ id: "digest", cron: "0 9 * * 1-5" }] }, handlers: { renderHome: async (context) => { const { files } = await context.coline.files.list({ typeKey: "crm.contact" }); return ui.stack([ui.heading("CRM"), ui.text(`${files.length} contacts`)]); }, onSchedule: async (context) => { await context.coline.storage.kv.set("last-digest", context.firedAt); }, }, }); describe("createTestWorkspace", () => { it("invokes tools through the schema and captures state", async () => { const workspace = createTestWorkspace(app); const result = await workspace.invokeTool("crm.create_contact", { name: "Sarah" }); expect(result.output).toMatchObject({ fileId: expect.stringContaining("file_test") }); expect(result.card?.type).toBe("Stack"); expect(workspace.files.byType("crm.contact")).toHaveLength(1); expect(workspace.state.indexDocuments.size).toBe(1); }); it("rejects invalid tool input with schema detail", async () => { const workspace = createTestWorkspace(app); await expect(workspace.invokeTool("crm.create_contact", { name: 42 })).rejects.toThrow( /invalid/i, ); }); it("renders home against live fake state", async () => { const workspace = createTestWorkspace(app); await workspace.invokeTool("crm.create_contact", { name: "A" }); await workspace.invokeTool("crm.create_contact", { name: "B" }); const tree = await workspace.renderHome(); expect(JSON.stringify(tree)).toContain("2 contacts"); }); it("renders file surfaces and enforces the inline subset", async () => { const workspace = createTestWorkspace(app); const preview = await workspace.renderFileSurface( "preview", { fileId: "f1", name: "Sarah", typeKey: "crm.contact" }, { name: "Sarah Chen" }, ); expect(JSON.stringify(preview)).toContain("Sarah Chen"); }); it("fires declared schedules with the schedule context", async () => { const workspace = createTestWorkspace(app); const at = new Date("2026-08-10T09:00:00Z"); await workspace.fireSchedule("digest", at); expect(workspace.state.kv.get("last-digest")).toBe(at.toISOString()); }); it("rejects undeclared schedules", async () => { const workspace = createTestWorkspace(app); await expect(workspace.fireSchedule("nope")).rejects.toThrow(/not declared/); }); });