// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0 import { readdirSync, readFileSync } from "node:fs" import { generateConvex } from "@conusai-kit/data-convex" import { describe, expect, it, vi } from "vitest" const inclusion = vi.hoisted(() => ({ schemaVersion: 1, selected: [] as string[], everAdopted: [] as string[], })) vi.mock("../../dist/inclusion.js", () => ({ moduleInclusion: inclusion, moduleSelected: (id: string) => inclusion.selected.includes(id), })) const load = async (selected: string[], adopted = selected) => { inclusion.selected = selected inclusion.everAdopted = adopted vi.resetModules() const runtime = await import("../../../convex/lib/runtime.js") const { accountDataOf } = await import("@conusai-kit/tenancy") return { ...runtime, accountShares: accountDataOf(runtime.registry) } } describe("Convex optional inclusion projection (preview only)", () => { it("commits exactly the generated schema and wrappers for the selected defaults", async () => { const defaults = JSON.parse(readFileSync(new URL("../../inclusion.json", import.meta.url), "utf8")) const fresh = await load(defaults.selected, defaults.everAdopted) const root = new URL("../../../convex/", import.meta.url) const files = generateConvex(fresh.registry, { header: "// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0\n", }) for (const file of files) expect(readFileSync(new URL(file.path, root), "utf8"), file.path).toBe(file.content) expect( readdirSync(new URL("generated/", root)) .filter((name) => name.endsWith(".ts")) .sort(), ).toEqual( [ ...files.filter(({ path }) => path.startsWith("generated/")).map(({ path }) => path.split("/").at(-1)), "procedures.ts", ...["documents.ts", "guides.ts", "support.ts"].filter( (name) => !files.some(({ path }) => path === `generated/${name}`), ), ].sort(), ) }) it("omits fresh optional behavior and preserves original adopted schema and rights on off", async () => { const ids = ["documents", "guides", "support"] const fresh = await load([]) // An explicitly empty selection has no callable wrappers, independently of the // source template's default-on assistance selection. const freshFiles = generateConvex(fresh.registry) for (const id of ids) expect(freshFiles.some(({ path }) => path === `generated/${id}.ts`)).toBe(false) expect(fresh.deployableModules.map(({ id }) => id)).toEqual([ "tenancy", "auth", "files", "proposals", "notifications", "branding", "notes", // kit:notes "account", ]) expect([...fresh.registry.procedures.keys()].filter((name) => /^(documents|guides|support)\./.test(name))).toEqual( [], ) expect(fresh.deployableModules.find(({ id }) => id === "notes")?.extends ?? []).toEqual([]) // kit:notes expect(fresh.convexCapabilities.actions).not.toContain("documentsActions:process") const on = await load(ids) expect(on.convexCapabilities.procedures).toContain("documents.document.list") const guidesRequirements = on.deployableModules.find(({ id }) => id === "guides")?.serviceRequirements ?? [] expect(guidesRequirements.flatMap(({ operations }) => Object.keys(operations))).toContain( "guides.runtime.bootstrap", ) expect(guidesRequirements.flatMap(({ operations }) => Object.keys(operations))).not.toContain( "guides.runtime.uploadFile", ) expect(on.deployableModules.find(({ id }) => id === "notes")?.extends?.length).toBeGreaterThan(0) // kit:notes const off = await load([], ids) expect(off.convexCapabilities.schema).toEqual(on.convexCapabilities.schema) expect(off.deployableModules.filter(({ id }) => ids.includes(id)).map(({ id }) => id)).toEqual(ids) expect(off.convexCapabilities.procedures.filter((name) => /^(documents|guides|support)\./.test(name))).toEqual([]) expect(off.accountShares.map(({ key }) => key)).toEqual(on.accountShares.map(({ key }) => key)) expect(off.convexCapabilities.actions).not.toContain("documentsActions:process") }) })