Files

340 lines
14 KiB
TypeScript

// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// The platform's services are really bound (plan item AT4.1, spec §9): the AI gateway, OCR, Blob, the
// audit log and usage metering, built from the deployment contract by `platformClients`, bound to the
// tokens modules resolve them by in BOTH composition roots (apps/web/src/server/ports.ts and
// apps/worker/src/ports.ts), and each one reaching the platform over HTTP. The platform here is the
// fake of @conusai-kit/testing; nothing between a token and its HTTP request is stubbed.
import { readFileSync } from "node:fs"
import { loadAppEnv, PlatformEnvError, platformClients, probePlatform, publicUrlOf } from "@app/config"
import { appModules } from "@app/modules"
import { DOCUMENT_MODEL, DOCUMENT_OCR, MODEL_GATEWAY } from "@app/modules/documents"
import { buildRegistry, Container } from "@conusai-kit/core"
import { OBJECT_STORE } from "@conusai-kit/data"
import { FILES_AUDIT, FILES_REGISTRY, FILES_USAGE } from "@conusai-kit/files"
import { PROPOSALS_AUDIT, PROPOSALS_REGISTRY } from "@conusai-kit/proposals"
import { startFakePlatform } from "@conusai-kit/testing/fake-platform"
import { afterEach, beforeEach, describe, expect, it } from "vitest"
import { z } from "zod"
import { bindWebPorts } from "../apps/web/src/server/ports.js"
import { bindModulePorts } from "../apps/worker/src/ports.js"
const fixture = JSON.parse(
readFileSync(new URL("../modules/documents/__tests__/fixtures/ocr-document.json", import.meta.url), "utf8"),
) as Record<string, unknown>
type Platform = Awaited<ReturnType<typeof startFakePlatform>>
let platform: Platform
beforeEach(async () => {
platform = await startFakePlatform()
})
afterEach(async () => {
await platform.close()
})
const environment = (extra: Record<string, string> = {}): Record<string, string | undefined> => ({
NODE_ENV: "test",
CONUSAI_FAKE_PLATFORM: "1",
CONUSAI_API_URL: `${platform.url}/api`,
CONUSAI_API_TOKEN: "test-token",
CONUSAI_PROJECT_ID: "42",
CONUSAI_ORG_ID: "42",
CONUSAI_ORG_SLUG: "owner-org",
POSTGRES_URL: "postgresql://app_app:secret@db:5432/app",
POSTGRES_READER_URL: "postgresql://app_reader:secret@db:5432/app",
...extra,
})
const setup = (service: "web" | "worker") => {
const env = loadAppEnv(service, environment())
const registry = buildRegistry([...appModules])
const clients = platformClients(env, registry, environment())
return { env, registry, clients }
}
/**
* What the fake platform recorded for one organization of the platform. It keeps every tenant apart,
* as the real one does: usage arrives under the organization in its URL, everything else under the
* default one.
*/
const stateOf = async (organization?: string) =>
(await (
await fetch(
`${platform.url}/__e2e/state`,
organization ? { headers: { "x-conusai-organization": organization } } : {},
)
).json()) as {
audit: Array<{ action: string; resource_id: string | null }>
usage: Array<Record<string, unknown>>
blobs: Array<{ pathname: string; size: number }>
ocr: unknown[]
ai: Array<{ request?: { model?: string } }>
}
describe("platformClients", () => {
it("writes an audit event to the platform's audit log", async () => {
const { clients } = setup("web")
await clients.audit.log("tenancy.organization_created", {
actor: { id: "user-1", kind: "human" },
target: { id: "acme", type: "organization" },
outcome: "success",
})
await clients.audit.flush()
expect((await stateOf()).audit.map((event) => event.action)).toContain("tenancy.organization_created")
})
it("meters usage against the ConusAI organization that owns the deployment, not against a customer", async () => {
const { clients } = setup("web")
clients.usage.track({
customer: { externalId: "acme" },
meter: "notes_created",
quantity: 1,
idempotencyKey: "n-1",
})
await clients.usage.flush()
// The fake separates numeric ID from slug, matching the production billing route contract.
const [fact] = (await stateOf("owner-org")).usage
expect((await stateOf()).usage).toEqual([])
expect(fact).toMatchObject({ meter: "notes_created", quantity: "1", idempotency_key: "n-1" })
})
it("stores an object in Blob and reads its bytes back", async () => {
const { clients } = setup("worker")
const stored = await clients.objectStore.put("files/a.txt", "hello", { contentType: "text/plain" })
expect((await stateOf()).blobs.map((blob) => blob.pathname)).toContain("files/a.txt")
expect(await (await clients.objectStore.download(stored.url)).text()).toBe("hello")
})
it("asks the AI gateway for a validated object, naming the model the app is configured with", async () => {
platform.ai.respond(() => ({ content: JSON.stringify({ answer: "four" }) }))
const { clients } = setup("worker")
const reply = await clients.models.generateObject(z.object({ answer: z.string() }), [
{ role: "user", content: "2 + 2?" },
])
expect(reply).toEqual({ answer: "four" })
const [call] = (await stateOf()).ai
expect(call?.request?.model).toBe(clients.model)
})
it("reads a document through the OCR service", async () => {
platform.ocr.setDocument(fixture)
const { clients } = setup("worker")
const job = await clients.documents.submit(new TextEncoder().encode("%PDF-1.4"), {
filename: "a.pdf",
idempotencyKey: "k-1",
})
const read = await clients.documents.wait(job, { intervalMs: 10 })
expect(read.pages).toHaveLength((fixture as { pages: unknown[] }).pages.length)
expect((await stateOf()).ocr).toHaveLength(1)
})
})
describe("what each process binds", () => {
const resolvable = (container: Container, tokens: Array<Parameters<Container["resolve"]>[0]>): boolean[] =>
tokens.map((token) => {
try {
container.resolve(token)
return true
} catch {
return false
}
})
it("binds Blob, the AI gateway, OCR and the registries in the web process", () => {
const { registry, clients } = setup("web")
const container = new Container()
bindWebPorts(container, clients, registry)
expect(
resolvable(container, [
OBJECT_STORE,
MODEL_GATEWAY,
DOCUMENT_MODEL,
DOCUMENT_OCR,
FILES_REGISTRY,
PROPOSALS_REGISTRY,
]),
).toEqual([true, true, true, true, true, true])
// The sinks that belong to the outbox are the worker's alone.
expect(resolvable(container, [FILES_AUDIT, FILES_USAGE, PROPOSALS_AUDIT])).toEqual([false, false, false])
})
it("binds the same five and the audit and usage sinks in the worker process", () => {
const { registry, clients } = setup("worker")
const container = new Container()
bindModulePorts(container, clients, registry)
expect(
resolvable(container, [
OBJECT_STORE,
MODEL_GATEWAY,
DOCUMENT_MODEL,
DOCUMENT_OCR,
FILES_REGISTRY,
PROPOSALS_REGISTRY,
FILES_AUDIT,
FILES_USAGE,
PROPOSALS_AUDIT,
]),
).toEqual([true, true, true, true, true, true, true, true, true])
})
it("sends the worker's usage and audit sinks to the platform, through the bound tokens", async () => {
const { registry, clients } = setup("worker")
const container = new Container()
bindModulePorts(container, clients, registry)
const usage = container.resolve(FILES_USAGE)
await usage.track({
customer: { externalId: "acme" },
meter: "files_bytes_stored",
quantity: 12,
idempotencyKey: "f-1",
})
await usage.flush()
await container.resolve(FILES_AUDIT).log("files.file_uploaded", {
actor: { id: "user-1", kind: "human" },
target: { id: "acme/file-1", type: "file" },
})
await clients.audit.flush()
const state = await stateOf()
expect((await stateOf("owner-org")).usage).toEqual([
expect.objectContaining({ meter: "files_bytes_stored", quantity: "12" }),
])
expect(state.audit.map((event) => event.action)).toContain("files.file_uploaded")
})
})
describe("the platform contract at start-up", () => {
const fake = (): Record<string, string | undefined> => environment()
it("refuses to start in production without the platform URL, naming the variable", () => {
const { CONUSAI_API_URL: _url, CONUSAI_FAKE_PLATFORM: _fake, ...rest } = fake()
const error = (() => {
try {
loadAppEnv("web", { ...rest, NODE_ENV: "production" })
} catch (thrown) {
return thrown
}
})()
expect(error).toBeInstanceOf(PlatformEnvError)
expect(error).toMatchObject({ variable: "CONUSAI_API_URL" })
expect((error as Error).message).toMatch(/CONUSAI_API_URL is not set: link this project to the platform/)
})
it("refuses to start without the platform token, whichever mode, naming the variable", () => {
for (const NODE_ENV of ["production", "development", undefined]) {
const { CONUSAI_API_TOKEN: _token, ...rest } = fake()
const run = () =>
loadAppEnv("worker", { ...rest, ...(NODE_ENV ? { NODE_ENV } : {}), CONUSAI_FAKE_PLATFORM: undefined })
expect(run, String(NODE_ENV)).toThrow(PlatformEnvError)
expect(run, String(NODE_ENV)).toThrow(/CONUSAI_API_TOKEN is not set/)
}
})
it("treats a run with no NODE_ENV as production, so the fake platform must be asked for in a development run", () => {
const { NODE_ENV: _mode, ...rest } = fake()
expect(() => loadAppEnv("web", rest)).toThrow(/CONUSAI_FAKE_PLATFORM is set in a production run/)
})
it("points a development run that has no platform at `pnpm dev` and the explicit switch", () => {
const { CONUSAI_API_URL: _url, CONUSAI_FAKE_PLATFORM: _fake, ...rest } = fake()
expect(() => loadAppEnv("web", { ...rest, NODE_ENV: "development" })).toThrow(/pnpm dev.*CONUSAI_FAKE_PLATFORM=1/)
})
it("accepts the fake platform in development and in a test, and says which it is using", () => {
for (const NODE_ENV of ["development", "test"]) {
expect(loadAppEnv("web", { ...fake(), NODE_ENV })).toMatchObject({ platform: "fake", production: false })
}
const { CONUSAI_FAKE_PLATFORM: _fake, ...real } = fake()
expect(loadAppEnv("web", { ...real, NODE_ENV: "development" }).platform).toBe("platform")
})
it("refuses the fake platform in a production run even when everything else is set", () => {
expect(() => loadAppEnv("web", { ...fake(), NODE_ENV: "production" })).toThrow(PlatformEnvError)
expect(() => loadAppEnv("web", { ...fake(), NODE_ENV: "production" })).toThrow(/CONUSAI_FAKE_PLATFORM/)
})
it("refuses the fake switch when the platform URL is somebody's public address", () => {
expect(() => loadAppEnv("web", { ...fake(), CONUSAI_API_URL: "https://api.conusai.example/api" })).toThrow(
/CONUSAI_API_URL names api\.conusai\.example/,
)
// ... while a compose service name and loopback are what the fake platform runs at.
expect(() => loadAppEnv("web", { ...fake(), CONUSAI_API_URL: "http://platform:4010/api" })).not.toThrow()
expect(() => loadAppEnv("web", { ...fake(), CONUSAI_API_URL: "http://127.0.0.1:4010/api" })).not.toThrow()
})
it("requires the owning organization in production, where usage would otherwise be metered against nothing", () => {
const { CONUSAI_FAKE_PLATFORM: _fake, CONUSAI_ORG_ID: _org, ...rest } = fake()
const production = { ...rest, NODE_ENV: "production" }
expect(() => loadAppEnv("web", production)).toThrow(/CONUSAI_ORG_ID is not set/)
expect(() => loadAppEnv("worker", production)).toThrow(/CONUSAI_ORG_ID is not set/)
// The migrate job meters nothing.
expect(() =>
loadAppEnv("migrate", { ...production, POSTGRES_MIGRATE_URL: "postgresql://app_owner:secret@db:5432/app" }),
).not.toThrow()
})
})
describe("probePlatform", () => {
it("uses public readiness when the platform descriptor is protected", async () => {
const authorization: Array<string | null> = []
const answered = await probePlatform(
{ CONUSAI_API_URL: "https://platform.example.test/api" },
{
fetch: async (input, init) => {
authorization.push(new Headers(init?.headers).get("authorization"))
const path = new URL(String(input)).pathname
// The real platform mounts its descriptor under authenticated /api;
// /readyz is its public readiness contract.
if (path === "/readyz") return new Response("ready")
return new Response("not public", { status: path.startsWith("/api/") ? 401 : 404 })
},
},
)
expect(answered).toBe(true)
expect(authorization).toEqual([null])
})
it("is true when the platform answers its public readiness probe, and sends no credential", async () => {
const seen: Array<string | null> = []
const answered = await probePlatform(
{ CONUSAI_API_URL: `${platform.url}/api` },
{
fetch: async (input, init) => {
seen.push(new Headers(init?.headers).get("authorization"))
return fetch(input, init)
},
},
)
expect(answered).toBe(true)
expect(seen).toEqual([null])
})
it("is false when nothing answers, without throwing", async () => {
expect(await probePlatform({ CONUSAI_API_URL: "http://127.0.0.1:9/api" }, { timeoutMs: 500 })).toBe(false)
})
})
describe("the public address", () => {
it("is the one the deployment states", () => {
expect(publicUrlOf({ NODE_ENV: "production", CONUSAI_PUBLIC_URL: "https://app.example.test" })).toBe(
"https://app.example.test",
)
})
it("is refused by name in a production run that has none, and never guessed", () => {
for (const env of [{ NODE_ENV: "production" }, {}]) {
expect(() => publicUrlOf(env)).toThrow(PlatformEnvError)
try {
publicUrlOf(env)
} catch (error) {
expect((error as PlatformEnvError).variable).toBe("CONUSAI_PUBLIC_URL")
}
}
})
it("falls back to the development address only in development and tests", () => {
expect(publicUrlOf({ NODE_ENV: "development" })).toBe("http://localhost:3000")
expect(publicUrlOf({ NODE_ENV: "test" })).toBe("http://localhost:3000")
})
})