163 lines
7.4 KiB
TypeScript
163 lines
7.4 KiB
TypeScript
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
|
import { readFileSync } from "node:fs"
|
|
import { buildRegistry, type ModuleManifest, type Token } from "@conusai-kit/core"
|
|
import { generateConvex } from "@conusai-kit/data-convex"
|
|
import { guidesModule } from "@conusai-kit/guides" // kit:guides
|
|
import { supportModule } from "@conusai-kit/support" // kit:support
|
|
import { accountDataOf } from "@conusai-kit/tenancy"
|
|
import { describe, expect, it } from "vitest"
|
|
import { composedClientModules } from "../apps/web/src/ui/client-module-list.js"
|
|
import { helpFor } from "../apps/web/src/ui/help.js"
|
|
import { convexModules } from "../convex/lib/convexModules.mjs"
|
|
import { agentModule, assistantAppModule } from "../modules/agent/index.js" // kit:agent
|
|
import { custodyModule } from "../modules/custody.js"
|
|
import { documentsModule } from "../modules/documents/index.js" // kit:documents
|
|
import { moduleInclusion, OPTIONAL_MODULE_IDS, validateInclusion } from "../modules/inclusion.js"
|
|
import { appModules } from "../modules/index.js"
|
|
import { knowledgeModule } from "../modules/knowledge/index.js" // kit:knowledge
|
|
import { modulePages } from "../modules/pages.js"
|
|
|
|
describe("fresh module admission", () => {
|
|
it.runIf(moduleInclusion.everAdopted.length === 0)(
|
|
"registers no optional procedure, job, permission, page or rights obligation",
|
|
() => {
|
|
expect(moduleInclusion).toEqual({ schemaVersion: 1, selected: [], everAdopted: [] })
|
|
const config = JSON.parse(readFileSync(new URL("../conusai.json", import.meta.url), "utf8"))
|
|
expect(
|
|
config.token.permissions.filter((scope: string) =>
|
|
/^(ocr:|ai:|ai_tools:|knowledge[.:]|guides:|support:)/.test(scope),
|
|
),
|
|
).toEqual([])
|
|
const registry = buildRegistry([...appModules])
|
|
expect(appModules.some((m) => OPTIONAL_MODULE_IDS.some((id) => id === m.id) || m.id === "assistant")).toBe(false)
|
|
expect(Object.keys(modulePages)).toEqual(["notes"])
|
|
expect(accountDataOf(registry).some((share) => ["guides", "support", "assistant"].includes(share.key))).toBe(
|
|
false,
|
|
)
|
|
},
|
|
)
|
|
it.runIf(moduleInclusion.everAdopted.length === 0)("mounts no optional help section/client", () => {
|
|
expect(
|
|
helpFor({ locale: "en" } as never, { title: "Help", sections: "Sections", guides: "Guides", support: "Support" })
|
|
.any,
|
|
).toBe(false)
|
|
expect(composedClientModules.modules).toEqual([])
|
|
expect(composedClientModules.providers).toEqual([])
|
|
expect(composedClientModules.slots).toEqual([])
|
|
})
|
|
it("refuses unbounded fields, IDs, versions and selected-without-custody", () => {
|
|
for (const value of [
|
|
{ schemaVersion: 2, selected: [], everAdopted: [] },
|
|
{ schemaVersion: 1, selected: ["agent"], everAdopted: [] },
|
|
{ schemaVersion: 1, selected: [], everAdopted: ["customer"] },
|
|
{ schemaVersion: 1, selected: [], everAdopted: [], subject: "synthetic" },
|
|
])
|
|
expect(() => validateInclusion(value)).toThrow()
|
|
})
|
|
})
|
|
describe("retained manifests", () => {
|
|
it.each<ModuleManifest>([
|
|
documentsModule, // kit:documents
|
|
knowledgeModule, // kit:knowledge
|
|
agentModule, // kit:agent
|
|
assistantAppModule, // kit:agent
|
|
guidesModule, // kit:guides
|
|
supportModule, // kit:support
|
|
])("retains $id schema under its original identity without interactive admission", (original) => {
|
|
const retained = custodyModule(original)
|
|
expect(retained.id).toBe(original.id)
|
|
expect(retained.repositories).toBe(original.repositories)
|
|
expect(retained.migrations).toBe(original.migrations)
|
|
for (const key of [
|
|
"procedures",
|
|
"permissions",
|
|
"collections",
|
|
"ui",
|
|
"serviceRequirements",
|
|
"extensionPoints",
|
|
] as const)
|
|
expect(retained[key]).toBeUndefined()
|
|
expect((retained.extends ?? []).every((entry) => entry.point.id === "tenancy.account")).toBe(true)
|
|
})
|
|
it("retains exactly the original rights hooks and retention/removal jobs", () => {
|
|
const withRights: ModuleManifest[] = [
|
|
guidesModule, // kit:guides
|
|
supportModule, // kit:support
|
|
assistantAppModule, // kit:agent
|
|
]
|
|
for (const manifest of withRights)
|
|
expect(custodyModule(manifest).extends).toEqual(
|
|
manifest.extends?.filter((entry) => entry.point.id === "tenancy.account"),
|
|
)
|
|
// kit:agent:begin
|
|
expect(Object.keys(custodyModule(agentModule).jobs ?? {})).toEqual(["agent.runs.expire"])
|
|
expect(Object.keys(custodyModule(assistantAppModule).jobs ?? {})).toEqual(["assistant.conversations.expire"])
|
|
const schedules = custodyModule(agentModule).schedules
|
|
expect(typeof schedules === "function" ? schedules({}) : []).toHaveLength(1)
|
|
// kit:agent:end
|
|
// kit:knowledge:begin
|
|
const retained = custodyModule(knowledgeModule)
|
|
expect(Object.keys(retained.jobs ?? {})).toEqual(["knowledge.remove"])
|
|
expect(retained.events?.subscribes?.map((entry) => entry.event.name)).toEqual([
|
|
"files.FileDeleted",
|
|
"knowledge.documentRemoveRequested",
|
|
])
|
|
// kit:knowledge:end
|
|
})
|
|
})
|
|
|
|
it.runIf(moduleInclusion.everAdopted.length === 0)(
|
|
"does not construct optional provider ports on a fresh web/worker boot",
|
|
async () => {
|
|
const { Container } = await import("@conusai-kit/core")
|
|
const { bindWebPorts } = await import("../apps/web/src/server/ports.js")
|
|
const { bindModulePorts } = await import("../apps/worker/src/ports.js")
|
|
const optionalPorts: Token<unknown>[] = [
|
|
(await import("../modules/documents/index.js")).DOCUMENT_OCR, // kit:documents
|
|
(await import("@conusai-kit/guides")).GUIDES_PLATFORM, // kit:guides
|
|
(await import("@conusai-kit/support")).SUPPORT_PLATFORM, // kit:support
|
|
(await import("../modules/knowledge/index.js")).KNOWLEDGE_SEARCH, // kit:knowledge
|
|
]
|
|
const platform = {
|
|
mode: "fake",
|
|
deployment: { CONUSAI_API_URL: "http://127.0.0.1:1/api", CONUSAI_API_TOKEN: "synthetic" },
|
|
objectStore: {},
|
|
audit: {},
|
|
usage: {},
|
|
}
|
|
for (const key of ["models", "documents", "model"])
|
|
Object.defineProperty(platform, key, {
|
|
get: () => {
|
|
throw new Error("optional provider constructed at boot")
|
|
},
|
|
})
|
|
const registry = buildRegistry([...appModules])
|
|
for (const bind of [bindWebPorts, bindModulePorts]) {
|
|
const container = new Container()
|
|
bind(container, platform as never, registry)
|
|
for (const token of optionalPorts) expect(container.has(token)).toBe(false)
|
|
}
|
|
},
|
|
)
|
|
|
|
describe("committed Convex projection", () => {
|
|
const root = new URL("..", import.meta.url)
|
|
it("commits generated wrappers that forward explicit idempotency keys to the kernel", () => {
|
|
// The same profile filter as below: the Postgres-only modules are not Convex-deployed yet.
|
|
const convexBound = appModules.filter(
|
|
(module) => module.profiles === undefined || module.profiles.includes("convex"),
|
|
)
|
|
const registry = buildRegistry([...convexModules(convexBound)])
|
|
const generated = generateConvex(registry, {
|
|
header: "// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0\n",
|
|
})
|
|
for (const file of generated) {
|
|
expect(readFileSync(new URL(`convex/${file.path}`, root), "utf8"), file.path).toBe(file.content)
|
|
}
|
|
const proposal = generated.find(({ path }) => path === "generated/proposals.ts")
|
|
expect(proposal?.content).toContain(
|
|
'ctx.run("proposals.proposal.create", input, idempotencyKey === undefined ? undefined : { idempotencyKey })',
|
|
)
|
|
})
|
|
})
|