import { describe, expect, it } from "vitest"; import { colineAppManifestV2Schema, colineAppRegistrationPayloadV2Schema, } from "./manifest"; const minimalManifest = { key: "crm", name: "CRM", }; describe("colineAppManifestV2Schema", () => { it("accepts a minimal manifest and applies defaults", () => { const manifest = colineAppManifestV2Schema.parse(minimalManifest); expect(manifest.permissions).toEqual([]); expect(manifest.hosting.default).toBe("coline"); expect(manifest.network.allowedHosts).toEqual([]); expect(manifest.files).toEqual([]); expect(manifest.tools).toEqual([]); expect(manifest.triggers).toEqual({ webhooks: [], schedules: [] }); }); it("accepts a full manifest", () => { const manifest = colineAppManifestV2Schema.parse({ key: "crm", name: "CRM", description: "Customer relationships inside Coline.", icon: "users", permissions: [ "files.read", "files.write", "ai.tools", "commands.register", "notifications.write", ], surfaces: { home: { tier: "react", entry: "./views/home", mobile: { tier: "tree" } }, }, files: [ { typeKey: "crm.contact", name: "Contact", storage: "json_document", surfaces: { editor: { tier: "react", entry: "./files/contact-editor" }, preview: { tier: "tree" }, inline: { tier: "tree" }, }, }, ], tools: [ { name: "crm.create_contact", description: "Create a contact in the CRM.", inputSchema: { type: "object", properties: { name: { type: "string" } } }, effect: "write", }, ], commands: [ { id: "new-contact", name: "Create contact", target: { kind: "tool", tool: "crm.create_contact" }, }, ], notifications: { channels: [{ key: "deal-updates", name: "Deal updates" }], }, hosting: { default: "coline", overrides: { "crm.create_contact": "external" }, external: { baseUrl: "https://crm.example.com" }, }, kairo: { instructions: "This workspace tracks customers in the CRM." }, }); expect(manifest.surfaces.home?.tier).toBe("react"); expect(manifest.notifications.channels[0]?.defaultDeliveries).toEqual(["in_app"]); }); it("rejects tools that are not namespaced under the app key", () => { const result = colineAppManifestV2Schema.safeParse({ ...minimalManifest, permissions: ["ai.tools"], tools: [ { name: "other.create_contact", description: "Wrong namespace.", inputSchema: {}, effect: "read", }, ], }); expect(result.success).toBe(false); }); it("rejects tools without the ai.tools permission", () => { const result = colineAppManifestV2Schema.safeParse({ ...minimalManifest, tools: [ { name: "crm.list_contacts", description: "List contacts.", inputSchema: {}, effect: "read", }, ], }); expect(result.success).toBe(false); expect(JSON.stringify(result.error?.issues)).toContain("ai.tools"); }); it("rejects commands that target undeclared tools", () => { const result = colineAppManifestV2Schema.safeParse({ ...minimalManifest, permissions: ["commands.register"], commands: [ { id: "new-contact", name: "Create contact", target: { kind: "tool", tool: "crm.create_contact" }, }, ], }); expect(result.success).toBe(false); }); it("requires external hosting when any capability overrides to external", () => { const result = colineAppManifestV2Schema.safeParse({ ...minimalManifest, permissions: ["ai.tools"], tools: [ { name: "crm.sync_salesforce", description: "Sync with Salesforce.", inputSchema: {}, effect: "external", }, ], hosting: { default: "coline", overrides: { "crm.sync_salesforce": "external" }, }, }); expect(result.success).toBe(false); expect(JSON.stringify(result.error?.issues)).toContain("hosting.external"); }); it("rejects hosting overrides for undeclared capabilities", () => { const result = colineAppManifestV2Schema.safeParse({ ...minimalManifest, hosting: { default: "coline", overrides: { "crm.nonexistent_tool": "external" }, external: { baseUrl: "https://crm.example.com" }, }, }); expect(result.success).toBe(false); }); it("requires external hosting for web-tier surfaces", () => { const result = colineAppManifestV2Schema.safeParse({ ...minimalManifest, surfaces: { home: { tier: "web", path: "/embed/home" } }, }); expect(result.success).toBe(false); }); it("requires external hosting for webhook triggers", () => { const result = colineAppManifestV2Schema.safeParse({ ...minimalManifest, permissions: ["webhooks.subscribe"], triggers: { webhooks: [{ event: "file.created" }] }, }); expect(result.success).toBe(false); }); it("rejects unknown top-level keys (strict manifest)", () => { const result = colineAppManifestV2Schema.safeParse({ ...minimalManifest, views: {}, }); expect(result.success).toBe(false); }); it("rejects file types outside the app namespace", () => { const result = colineAppManifestV2Schema.safeParse({ ...minimalManifest, files: [{ typeKey: "other.contact", name: "Contact" }], }); expect(result.success).toBe(false); }); }); describe("colineAppRegistrationPayloadV2Schema", () => { it("requires protocolVersion 2", () => { expect( colineAppRegistrationPayloadV2Schema.safeParse({ protocolVersion: 1, manifest: minimalManifest, }).success, ).toBe(false); expect( colineAppRegistrationPayloadV2Schema.safeParse({ protocolVersion: 2, manifest: minimalManifest, }).success, ).toBe(true); }); });