ConusAI vendor v0.1.0-dev.20261027+9b1131fb
ci / gate (push) Waiting to run

This commit is contained in:
ConusAI vendor v0.1.0-dev.20261027+9b1131fb
2026-10-27 18:05:17 +00:00
commit 3102273ec7
538 changed files with 80203 additions and 0 deletions
+466
View File
@@ -0,0 +1,466 @@
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
import { readFileSync } from "node:fs"
import { AppError, buildRegistry } from "@conusai-kit/core"
import { generateConvex } from "@conusai-kit/data-convex"
import { getFunctionName } from "convex/server"
import { ConvexError } from "convex/values"
import { describe, expect, it } from "vitest"
import { parse } from "yaml"
import { projectEntriesFor } from "../apps/web/src/ui/projects.js"
import {
CONVEX_ACTION_EFFECT_MODULES,
CONVEX_EXCLUDED_MODULES,
CONVEX_EXCLUDED_PROCEDURES,
convexBffModules,
convexModules,
} from "../convex/lib/convexModules.mjs"
import { appModules } from "../modules/index.js"
const root = new URL("..", import.meta.url)
describe("Convex deployable module closure", () => {
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 })',
)
})
it("keeps the documents review route visible in the Convex project navigation", () => {
expect(projectEntriesFor("convex")).toContainEqual({
href: "documents",
label: "documents.title",
module: "documents",
})
})
it("includes server-side auth handlers and only excludes modules with an explicit runtime gap", () => {
// Modules that declare `profiles: ["postgres"]` (knowledge, agent) ship fail-closed Convex
// binding files, but the integration review (2026-10-23) kept them Postgres-only: their
// search/run effects and jobs leave the process and have no composed Convex Node actions yet
// (the reasons are in their module READMEs). The generated closure is therefore the modules
// that support the profile.
const convexBound = appModules.filter(
(module) => module.profiles === undefined || module.profiles.includes("convex"),
)
const modules = convexModules(convexBound)
const closure = ["tenancy", "auth", "files", "proposals"]
closure.push("guides") // kit:guides -- its effect procedures are generated as Convex actions
closure.push("support") // kit:support -- its effect procedures are generated as Convex actions
expect(modules.map(({ id }) => id)).toEqual([...closure, "branding", "documents", "notes", "account"])
// The only effects generated as actions are those of the platform-call modules (convex/lib/effects.ts).
const effects = modules.flatMap(({ id, procedures = [] }) =>
procedures.filter(({ kind }) => kind === "effect").map(({ name }) => [id, name] as const),
)
expect(effects.every(([id]) => CONVEX_ACTION_EFFECT_MODULES.has(id))).toBe(true)
const effectNames = effects.map(([, name]) => name)
// The account module's export and deletion: actions over its bound store (convex/account.ts).
const expectedEffects: string[] = ["account.data.export", "account.deletion.request"]
// kit:guides:begin
expectedEffects.push(
"guides.builder.open",
"guides.runtime.bootstrap",
"guides.runtime.deleteFile",
"guides.runtime.record",
)
// A survey file's upload is a streamed body: not a Convex action on this profile.
expect(CONVEX_EXCLUDED_PROCEDURES.has("guides.runtime.uploadFile")).toBe(true)
// kit:guides:end
// kit:support:begin
expectedEffects.push("support.widgetSession.mint", "support.widget.config")
// kit:support:end
expect([...effectNames].sort()).toEqual(expectedEffects.sort())
const fileProcedures = modules.find(({ id }) => id === "files")?.procedures?.map(({ name }) => name)
expect(fileProcedures ?? []).toContain("files.file.get")
expect(fileProcedures ?? []).toContain("files.file.list")
expect(fileProcedures ?? []).not.toContain("files.file.delete")
expect(CONVEX_EXCLUDED_PROCEDURES.has("files.file.delete")).toBe(true)
expect([...CONVEX_EXCLUDED_MODULES.keys()]).toEqual([])
const branding = modules.find(({ id }) => id === "branding")?.procedures?.map(({ name }) => name)
expect(branding).toEqual(["branding.brand.get", "branding.brand.set"])
for (const name of ["branding.logo.get", "branding.logo.set", "branding.logo.clear"]) {
expect(CONVEX_EXCLUDED_PROCEDURES.has(name)).toBe(true)
expect(
convexBffModules(appModules)
.flatMap(({ procedures = [] }) => procedures)
.map(({ name }) => name),
).not.toContain(name)
}
expect(CONVEX_EXCLUDED_MODULES.get("auth")).toBeUndefined()
})
it("keeps composed file effects in the web BFF registry without generating Convex effect wrappers", () => {
const filtered = appModules.filter((module) => module.profiles === undefined || module.profiles.includes("convex"))
const backend = convexModules(filtered)
const bff = convexBffModules(filtered)
expect(
backend.some(
({ id, procedures = [] }) =>
!CONVEX_ACTION_EFFECT_MODULES.has(id) && procedures.some(({ kind }) => kind === "effect"),
),
).toBe(false)
const fileProcedures = bff.find(({ id }) => id === "files")?.procedures?.map(({ name }) => name)
expect(fileProcedures ?? []).toContain("files.file.beginUpload")
expect(fileProcedures ?? []).toContain("files.file.putPart")
expect(fileProcedures ?? []).toContain("files.file.completeUpload")
expect(fileProcedures ?? []).toContain("files.file.delete")
expect(bff.find(({ id }) => id === "proposals")?.procedures?.map(({ name }) => name)).toContain(
"proposals.proposal.approve",
)
expect(bff.find(({ id }) => id === "documents")?.procedures?.map(({ name }) => name)).toContain(
"documents.document.get",
)
})
it("registers scheduled mutations through generated internal function references", () => {
const crons = readFileSync(new URL("convex/crons.ts", root), "utf8")
expect(crons).toContain("internal.relay.relayPass")
expect(crons).toContain("internal.files.recoverRows")
// The account module's deletion sweep, the Postgres worker's DELETION_SWEEP_JOB.
expect(crons).toContain("internal.account.sweep")
expect(crons).not.toContain('from "./relay"')
})
it("keeps audit credentials behind a root internal action", () => {
const config = readFileSync(new URL("convex/convex.config.ts", root), "utf8")
const provider = readFileSync(new URL("convex/auditCredentials.ts", root), "utf8")
const relay = readFileSync(new URL("convex/relay.ts", root), "utf8")
expect(config).not.toContain("CONUSAI_API_TOKEN")
expect(provider).toContain("export const get = internalAction(")
expect(provider).toContain("apiUrl: process.env.CONUSAI_API_URL")
expect(provider).toContain("apiToken: process.env.CONUSAI_API_TOKEN")
expect(relay).toContain("credentialsProvider: internal.auditCredentials.get")
})
it("deploys functions before web and backup with the provider's server-only credential names", () => {
const compose = parse(readFileSync(new URL("profiles/convex/compose.yaml", root), "utf8")) as {
services: Record<
string,
{
command?: string[]
environment?: Record<string, string>
restart?: string
depends_on?: Record<string, { condition: string }>
}
>
}
const profile = JSON.parse(readFileSync(new URL("profiles/convex/conusai.json", root), "utf8")) as {
services: Record<string, unknown>
}
expect(Object.keys(compose.services).sort()).toEqual(["backup", "convex-deploy", "web"])
expect(Object.keys(profile.services).sort()).toEqual(Object.keys(compose.services).sort())
expect(compose.services["convex-deploy"]?.command).toEqual(["node", "profiles/convex/deploy.mjs"])
expect(compose.services["convex-deploy"]?.restart).toBe("no")
for (const name of ["web", "backup"] as const) {
expect(compose.services[name]?.depends_on?.["convex-deploy"]).toEqual({
condition: "service_completed_successfully",
})
}
for (const name of ["web", "backup", "convex-deploy"] as const) {
const environment = compose.services[name]?.environment ?? {}
expect(environment.CONVEX_SELF_HOSTED_URL).toBe("$" + "{CONVEX_SELF_HOSTED_URL}")
expect(environment.CONVEX_SELF_HOSTED_ADMIN_KEY).toBe("$" + "{CONVEX_SELF_HOSTED_ADMIN_KEY}")
expect(Object.keys(environment).some((key) => key.startsWith("NEXT_PUBLIC_") && key.includes("ADMIN"))).toBe(
false,
)
expect(environment.CONVEX_URL).toBeUndefined()
expect(environment.CONVEX_ADMIN_KEY).toBeUndefined()
}
expect(compose.services["convex-deploy"]?.environment).toMatchObject({
CONUSAI_API_TOKEN: expect.stringMatching(/^\$\{CONUSAI_API_TOKEN\}$/),
CONUSAI_PROJECT_ID: expect.stringMatching(/^\$\{CONUSAI_PROJECT_ID\}$/),
CONUSAI_ORG_SLUG: expect.stringMatching(/^\$\{CONUSAI_ORG_SLUG\}$/),
})
expect(compose.services.web?.environment?.CONUSAI_ORG_SLUG).toBe("$" + "{CONUSAI_ORG_SLUG}")
const deploy = readFileSync(new URL("profiles/convex/deploy.mjs", root), "utf8")
expect(deploy).toContain('"env", "set", "--from-file", envFile')
expect(deploy).toContain("mode: 0o600")
expect(deploy).toContain('"deploy", "--typecheck", "disable"')
const runtime = readFileSync(new URL("convex/lib/runtime.ts", root), "utf8")
expect(runtime).toContain('"filesActions:recoverUpload"')
})
})
describe("the account module on the Convex profile", () => {
it("binds a store whose every method is one internal query (a read) or mutation (a change)", async () => {
const { ACCOUNT_STORE_READS, ACCOUNT_STORE_WRITES, convexAccountStore } = await import("../convex/lib/effects")
const { createAccountStore } = await import("../modules/account/index.js")
const methods = Object.keys(createAccountStore({ data: {} as never, system: {} as never })).sort()
expect([...ACCOUNT_STORE_READS, ...ACCOUNT_STORE_WRITES].sort()).toEqual(methods)
const calls: Array<[string, string, unknown]> = []
const record = (kind: string) => async (reference: unknown, args: unknown) => {
calls.push([kind, getFunctionName(reference as never), args])
return null
}
const store = convexAccountStore({
auth: { getUserIdentity: async () => null },
runQuery: record("query") as never,
runMutation: record("mutation") as never,
})
await store.deletion("user-1")
await store.identities("user-1")
await store.changeDeletion("user-1", { kind: "step", step: "credentials" })
expect(calls).toEqual([
["query", "account:storeRead", { method: "deletion", args: ["user-1"] }],
["query", "account:storeRead", { method: "identities", args: ["user-1"] }],
[
"mutation",
"account:storeWrite",
{ method: "changeDeletion", args: ["user-1", { kind: "step", step: "credentials" }] },
],
])
})
it("brings a backend refusal back as the kernel's own problem", async () => {
const { convexAccountStore } = await import("../convex/lib/effects")
const store = convexAccountStore({
auth: { getUserIdentity: async () => null },
runQuery: (async () => {
throw new ConvexError({ type: "/problems/not_found", title: "Not found", status: 404, code: "not_found" })
}) as never,
runMutation: (async () => null) as never,
})
const error = await store.deletion("user-1").then(
() => null,
(thrown: unknown) => thrown,
)
expect(error).toBeInstanceOf(AppError)
expect((error as AppError).status).toBe(404)
})
it("reads the account credential from the BFF's identity claims, and nothing from an absent one", async () => {
const { accountCredentialOf } = await import("../convex/lib/runtime")
const identity = (claims: Record<string, unknown>) =>
({ issuer: "https://conusai.internal/bff", subject: "u", tokenIdentifier: "t", ...claims }) as never
expect(accountCredentialOf(null)).toEqual({ interactive: false })
expect(accountCredentialOf(identity({}))).toEqual({ interactive: false })
expect(accountCredentialOf(identity({ conusaiCredential: "interactive", conusaiSessionId: "s-1" }))).toEqual({
interactive: true,
sessionId: "s-1",
})
expect(
accountCredentialOf(
identity({ conusaiCredential: "interactive", conusaiSignedInAt: "2026-10-25T00:00:00.000Z" }),
),
).toEqual({ interactive: true, signedInAt: "2026-10-25T00:00:00.000Z" })
expect(accountCredentialOf(identity({ conusaiCredential: true, conusaiSessionId: "s-1" }))).toEqual({
interactive: false,
})
})
// kit:convex-better-auth:begin
it("installs the Better Auth deployment's variables in the backend only for a Better Auth app", () => {
const deploy = readFileSync(new URL("profiles/convex/deploy.mjs", root), "utf8")
expect(deploy).toContain(
'variables.push("AUTH_PROVIDER", "AUTH_IDENTITY_NAMESPACE", "BETTER_AUTH_CONVEX_URL", "BETTER_AUTH_CONVEX_ADMIN_KEY")',
)
})
// kit:convex-better-auth:end
})
/**
* The sign-out revocation outbox on the Convex profile (joint contract §14): the Postgres profile's
* own outbox (modules/account/core/revocation.ts) on Convex storage. The web records a revoked app
* session through the internal action `account:revokeSession`, which writes the row and delivers it
* at once from the backend; what a platform did not confirm the one-minute cron `account:revocationSweep`
* retries with the row's backoff, once per module, until 65 minutes have passed. Here the backend
* functions are the very store the Convex functions run, over a memory data source in place of ctx.db.
*/
describe("the revocation outbox on the Convex profile", () => {
it("keeps the outbox in the backend: every store method is one internal query or mutation", async () => {
const { REVOCATION_STORE_READS, REVOCATION_STORE_WRITES, convexRevocationStore } = await import(
"../convex/lib/effects"
)
const { createRevocationStore } = await import("../modules/account/index.js")
expect([...REVOCATION_STORE_READS, ...REVOCATION_STORE_WRITES].sort()).toEqual(
Object.keys(createRevocationStore({} as never)).sort(),
)
const calls: Array<[string, string, unknown]> = []
const record = (kind: string) => async (reference: unknown, args: unknown) => {
calls.push([kind, getFunctionName(reference as never), args])
return kind === "query" ? [] : null
}
const store = convexRevocationStore({
auth: { getUserIdentity: async () => null },
runQuery: record("query") as never,
runMutation: record("mutation") as never,
})
await store.due("2026-10-26T00:00:00.000Z", 10)
await store.purge("2026-10-25T00:00:00.000Z")
expect(calls).toEqual([
["query", "account:revocationRead", { method: "due", args: ["2026-10-26T00:00:00.000Z", 10] }],
["mutation", "account:revocationWrite", { method: "purge", args: ["2026-10-25T00:00:00.000Z"] }],
])
})
it("binds the device session a derived service session is minted for, from the BFF's identity", async () => {
const { requestSessionOf } = await import("../convex/lib/runtime")
const { effectContainer } = await import("../convex/lib/effects")
const { REQUEST_SESSION } = await import("@conusai-kit/tenancy")
const identity = (claims: Record<string, unknown>) =>
({ issuer: "https://conusai.internal/bff", subject: "u", tokenIdentifier: "t", ...claims }) as never
const fake = process.env.CONUSAI_FAKE_PLATFORM
process.env.CONUSAI_FAKE_PLATFORM = "1"
expect(requestSessionOf(null)).toBeNull()
expect(requestSessionOf(identity({}))).toBeNull()
expect(requestSessionOf(identity({ conusaiIdentitySession: 7 }))).toBeNull()
expect(requestSessionOf(identity({ conusaiIdentitySession: "4:oidc:36:s-1" }))).toBe("4:oidc:36:s-1")
const ctx = {
auth: { getUserIdentity: async () => null },
runQuery: (async () => null) as never,
runMutation: (async () => null) as never,
}
expect(effectContainer(ctx, { interactive: true }, "4:oidc:36:s-1").resolve(REQUEST_SESSION)).toBe("4:oidc:36:s-1")
expect(effectContainer(ctx).resolve(REQUEST_SESSION)).toBeNull()
if (fake === undefined) delete process.env.CONUSAI_FAKE_PLATFORM
else process.env.CONUSAI_FAKE_PLATFORM = fake
// The BFF names the device session of every call it makes for a signed-in person.
const caller = readFileSync(new URL("apps/web/src/server/convex-caller.ts", root), "utf8")
expect(caller).toContain("conusaiIdentitySession: options.identitySession")
const request = readFileSync(new URL("apps/web/src/server/request.ts", root), "utf8")
expect(request).toContain("identitySession: sessionRefKey(session.sessionRef)")
})
it("records a sign-out through an internal action and retries it on a one-minute cron", () => {
const account = readFileSync(new URL("convex/account.ts", root), "utf8")
for (const name of ["revocationRead = internalQuery", "revocationWrite = internalMutation"]) {
expect(account).toContain(`export const ${name}`)
}
expect(account).toContain("export const revokeSession = internalAction")
expect(account).toContain("export const revocationSweep = internalAction")
const crons = readFileSync(new URL("convex/crons.ts", root), "utf8")
expect(crons).toContain(
'crons.interval("account session revocations", { minutes: 1 }, internal.account.revocationSweep, {})',
)
// The web asks the backend's outbox, and falls back to the modules only when nothing was recorded.
const app = readFileSync(new URL("apps/web/src/server/app.ts", root), "utf8")
expect(app).toContain("durable: (session: string) => revokeConvexSession(convex, session)")
})
// kit:support:begin
describe("delivered from an action's container, against the fake platform", () => {
const START = Date.parse("2026-10-25T10:00:00.000Z")
type Platform = Awaited<ReturnType<typeof import("@conusai-kit/testing/fake-platform").startFakePlatform>>
type Clock = { at(ms: number): void }
/** One test against a fresh fake platform, the backend's deployment variables and a fixed clock. */
async function withPlatform(run: (platform: Platform, clock: Clock) => Promise<void>): Promise<void> {
const { vi } = await import("vitest")
const { startFakePlatform } = await import("@conusai-kit/testing/fake-platform")
const env = { ...process.env }
const platform = await startFakePlatform()
process.env.CONUSAI_API_URL = platform.url
process.env.CONUSAI_API_TOKEN = "cnsi_test"
process.env.CONUSAI_SUPPORT_CHANNEL = "web"
delete process.env.CONUSAI_FAKE_PLATFORM
vi.useFakeTimers({ toFake: ["Date"] })
vi.setSystemTime(START)
try {
await run(platform, { at: (ms) => vi.setSystemTime(START + ms) })
} finally {
vi.useRealTimers()
process.env = env
await platform.close()
}
}
/** The backend: `account:revocationRead`/`revocationWrite` over one store, as convex/account.ts runs them. */
async function backend() {
const { createMemoryDataSource } = await import("@conusai-kit/data")
const { accountSessionRevocations, createRevocationStore } = await import("../modules/account/index.js")
const data = createMemoryDataSource({ entities: [accountSessionRevocations] })
const store = createRevocationStore(data)
const run = (expected: string) => async (reference: unknown, input: unknown) => {
const name = getFunctionName(reference as never)
if (name !== expected) throw new Error(`unexpected backend call ${name}`)
const { method, args } = input as { method: keyof typeof store; args: unknown[] }
const call = store[method] as (...values: unknown[]) => Promise<unknown>
return JSON.parse(JSON.stringify((await call.apply(store, args)) ?? null))
}
const ctx = {
auth: { getUserIdentity: async () => null },
runQuery: run("account:revocationRead") as never,
runMutation: run("account:revocationWrite") as never,
}
const rows = async () => (await data.global(accountSessionRevocations).list({ limit: 50 })).items
return { ctx, rows }
}
const revocationsOf = async (platform: Platform, session: string) => {
const response = await fetch(new URL("/__e2e/state", platform.url), {
headers: { "x-conusai-organization": "e2e" },
})
const state = (await response.json()) as {
support: Array<{ route: string; body?: { identity_session?: string } }>
}
return state.support.filter((call) => call.route === "revoke" && call.body?.identity_session === session)
}
it("a platform down at sign-out gets the revocation once it is back, and nothing twice", () =>
withPlatform(async (platform, clock) => {
const { revokeSessionInBackend, sweepRevocationsInBackend } = await import("../convex/lib/effects")
const { ctx, rows } = await backend()
const session = "18:oidc:0199abcd-1234-7000-8000-00000000c001"
platform.outage("/v1/support/", true)
// The sign-out is not blocked: the action answers once the row is written.
await expect(revokeSessionInBackend(ctx, session)).resolves.toBeUndefined()
expect((await rows())[0]).toMatchObject({ session, state: "pending", pending: ["support"], attempts: 1 })
expect(await revocationsOf(platform, session)).toEqual([])
// Due, still down: the cron's run fails (the line an operator is alerted on), the row waits longer.
clock.at(31_000)
await expect(sweepRevocationsInBackend(ctx)).rejects.toMatchObject({ code: "account_revocations_waiting" })
expect((await rows())[0]).toMatchObject({ state: "pending", attempts: 2 })
platform.outage("/v1/support/", false)
clock.at(31_000 + 61_000)
await sweepRevocationsInBackend(ctx)
expect((await rows())[0]).toMatchObject({ state: "delivered", pending: [], delivered: ["support"] })
expect(await revocationsOf(platform, session)).toHaveLength(1)
clock.at(20 * 60_000)
await sweepRevocationsInBackend(ctx)
expect(await revocationsOf(platform, session)).toHaveLength(1)
// A second notification of the same revocation writes nothing and asks nobody.
await revokeSessionInBackend(ctx, session)
expect(await rows()).toHaveLength(1)
expect(await revocationsOf(platform, session)).toHaveLength(1)
}))
it("delivers at once when the platform is up", () =>
withPlatform(async (platform) => {
const { revokeSessionInBackend } = await import("../convex/lib/effects")
const { ctx, rows } = await backend()
const session = "18:oidc:0199abcd-1234-7000-8000-00000000c002"
await revokeSessionInBackend(ctx, session)
expect((await rows())[0]).toMatchObject({ state: "delivered", pending: [] })
expect(await revocationsOf(platform, session)).toHaveLength(1)
}))
it("lapses after 65 minutes, when no widget session it minted can still be alive", () =>
withPlatform(async (platform, clock) => {
const { revokeSessionInBackend, sweepRevocationsInBackend } = await import("../convex/lib/effects")
const { ctx, rows } = await backend()
const session = "18:oidc:0199abcd-1234-7000-8000-00000000c003"
platform.outage("/v1/support/", true)
await revokeSessionInBackend(ctx, session)
platform.outage("/v1/support/", false)
clock.at(66 * 60_000)
await sweepRevocationsInBackend(ctx)
expect((await rows())[0]).toMatchObject({ state: "lapsed" })
expect(await revocationsOf(platform, session)).toEqual([])
}))
})
// kit:support:end
})