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
+252
View File
@@ -0,0 +1,252 @@
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// biome-ignore-all lint/suspicious/noTemplateCurlyInString: a ${NAME} in this file is what the
// compose document holds verbatim -- compose interpolates it from the deployment's env file -- so
// the assertions compare against that text and not against a value.
// compose.yaml: the ordering the platform does not give (decision AT-D14), and the keys the
// platform's own compose model accepts.
//
// The model is crates/conusai-deployer/src/compose_model.rs: a service key it has no field for
// fails the deployment's parse, and so does a host binding in `ports`. This suite is that list,
// read from the document, so a key added here in a hurry fails in review and not on stage.
import { readFileSync } from "node:fs"
import { describe, expect, it } from "vitest"
import { parse } from "yaml"
interface Service {
build?: { context: string; dockerfile: string; target?: string }
image?: string
command?: string[]
environment?: Record<string, string>
expose?: (string | number)[]
ports?: unknown[]
depends_on?: Record<string, { condition: string }>
restart?: string
healthcheck?: { test: string[]; interval?: string }
deploy?: { resources?: { limits?: { cpus?: string; memory?: string } } }
init?: boolean
working_dir?: string
stop_grace_period?: string
}
const document = parse(readFileSync(new URL("../compose.yaml", import.meta.url), "utf8")) as {
services: Record<string, Service>
}
const services = document.services
const optional = (name: string): string => `\${${name}:-}`
/**
* The passthroughs every profile owes each process (BUG-APP-02): a variable the code reads but
* compose never names never reaches the container, so the deployment's value silently loses to
* the default. `${VAR}` where boot refuses to run without it, `${VAR:-}` where a default stands.
*/
function expectPassthroughs(named: Record<string, Service>): void {
for (const name of [
"OIDC_WEB_CLIENT_SECRET",
"OIDC_RESOURCE",
"OIDC_INTROSPECTION_CLIENT_ID",
"OIDC_INTROSPECTION_CLIENT_SECRET",
"OIDC_RESOURCE_STATUS_URL",
"OIDC_SCOPES",
"AUTH_PROVIDER_GENERATION",
"AUTH_COOKIE_NAME",
"AUTH_SESSION_IDLE_SECONDS",
"AUTH_SESSION_ABSOLUTE_SECONDS",
]) {
expect(named.web?.environment?.[name], `web ${name}`).toBe(optional(name))
}
for (const name of ["web", "worker"] as const) {
if (named[name]) expect(named[name]?.environment?.CONUSAI_ORG_SLUG).toBe(optional("CONUSAI_ORG_SLUG"))
}
if (named.worker) {
expect(named.worker.environment?.CONUSAI_PUBLIC_URL).toBe("${CONUSAI_PUBLIC_URL}")
expect(named.worker.environment?.DOCUMENTS_EXCLUDED_PAGE_CLASSES).toBe(optional("DOCUMENTS_EXCLUDED_PAGE_CLASSES"))
}
if (named.migrate) expect(named.migrate.environment?.CONUSAI_RELEASE).toBe("${CONUSAI_RELEASE}")
for (const name of [
"CONUSAI_ENVIRONMENT",
"CONUSAI_DSN",
"OTEL_EXPORTER_OTLP_ENDPOINT",
"OTEL_EXPORTER_OTLP_PROTOCOL",
"OTEL_EXPORTER_OTLP_HEADERS",
"OTEL_SERVICE_NAME",
"OTEL_SERVICE_VERSION",
]) {
if (named.migrate) expect(named.migrate.environment?.[name], `migrate ${name}`).toBe(optional(name))
}
}
/** Every key TenantService models (compose_model.rs). Anything else fails the platform's parse. */
const MODELLED = new Set([
"image",
"build",
"command",
"environment",
"ports",
"expose",
"volumes",
"depends_on",
"restart",
"healthcheck",
"deploy",
"init",
"stop_grace_period",
"stop_signal",
"working_dir",
"user",
])
describe("compose.yaml", () => {
it("has the three services of conusai.json and nothing else", () => {
expect(Object.keys(services).sort()).toEqual(["migrate", "web", "worker"])
})
it("uses only the keys the platform's compose model has a field for", () => {
for (const [name, service] of Object.entries(services)) {
for (const key of Object.keys(service)) {
expect(MODELLED.has(key), `${name}.${key} is not in the platform's compose model`).toBe(true)
}
}
})
it("builds every service from one Dockerfile, so one image serves all three", () => {
const builds = Object.values(services).map((service) => JSON.stringify(service.build))
expect(new Set(builds).size).toBe(1)
expect(Object.values(services).every((service) => service.image === undefined)).toBe(true)
})
it("starts each process with its own argv, never a shell string", () => {
for (const [name, service] of Object.entries(services)) {
expect(Array.isArray(service.command), `${name} has no argv command`).toBe(true)
}
expect(services.migrate?.command?.at(-1)).toContain("migrate.js")
})
it("runs the migrate job to completion before web and worker start", () => {
// `kind: job` in conusai.json is inert at deploy time: one `docker compose up -d --wait`
// starts everything at once, and `pre_deploy_command` has no executor at all. This is the
// ordering (decision AT-D14).
expect(services.migrate?.restart).toBe("no")
for (const name of ["web", "worker"] as const) {
expect(services[name]?.depends_on?.migrate).toEqual({ condition: "service_completed_successfully" })
}
})
it("publishes no host port", () => {
for (const service of Object.values(services)) expect(service.ports).toBeUndefined()
expect(services.web?.expose).toEqual(["3000"])
})
it("gives every service a limit, and the two long-running ones a healthcheck", () => {
for (const [name, service] of Object.entries(services)) {
expect(service.deploy?.resources?.limits?.memory, `${name} has no memory limit`).toBeTruthy()
expect(service.deploy?.resources?.limits?.cpus, `${name} has no cpu limit`).toBeTruthy()
expect(service.init, `${name} does not reap its children`).toBe(true)
}
expect(services.web?.healthcheck?.test).toBeTruthy()
expect(services.worker?.healthcheck?.test).toBeTruthy()
// A job is not healthy or unhealthy; it succeeds or it fails.
expect(services.migrate?.healthcheck).toBeUndefined()
})
it("hands the owner's URL to the migrate job alone", () => {
expect(services.migrate?.environment?.POSTGRES_MIGRATE_URL).toBe("${POSTGRES_MIGRATE_URL}")
expect(services.web?.environment?.POSTGRES_MIGRATE_URL).toBeUndefined()
expect(services.worker?.environment?.POSTGRES_MIGRATE_URL).toBeUndefined()
for (const name of ["web", "worker", "migrate"] as const) {
expect(services[name]?.environment?.POSTGRES_URL).toBe("${POSTGRES_URL}")
expect(services[name]?.environment?.POSTGRES_READER_URL).toBe("${POSTGRES_READER_URL}")
}
})
it("passes every variable each process reads through to its container", () => {
expectPassthroughs(services)
})
})
describe("profiles/mariadb/compose.yaml", () => {
const profile = parse(readFileSync(new URL("../profiles/mariadb/compose.yaml", import.meta.url), "utf8")) as {
services: Record<string, Service>
}
it("hands the one MariaDB login to every process and the Valkey queue to web and worker", () => {
for (const name of ["web", "worker", "migrate"] as const) {
expect(profile.services[name]?.environment?.MARIADB_URL).toBe("${MARIADB_URL}")
}
for (const name of ["web", "worker"] as const) {
expect(profile.services[name]?.environment?.REDIS_URL).toBe("${REDIS_URL}")
}
// MariaDB has no row-level security: there is no owner/runtime/reader split to hand out.
expect(profile.services.migrate?.environment?.POSTGRES_MIGRATE_URL).toBeUndefined()
})
it("passes every variable each process reads through to its container", () => {
expectPassthroughs(profile.services)
})
})
for (const name of ["mongodb", "convex"] as const) {
describe(`profiles/${name}/compose.yaml`, () => {
const profile = parse(readFileSync(new URL(`../profiles/${name}/compose.yaml`, import.meta.url), "utf8")) as {
services: Record<string, Service>
}
it("passes the platform billing slug and web auth settings through to the containers", () => {
for (const service of ["web", "worker"] as const) {
if (profile.services[service]) {
expect(profile.services[service]?.environment?.CONUSAI_ORG_SLUG).toBe(
name === "convex" ? `\${CONUSAI_ORG_SLUG}` : optional("CONUSAI_ORG_SLUG"),
)
}
}
for (const variable of [
"OIDC_WEB_CLIENT_SECRET",
"OIDC_RESOURCE",
"OIDC_INTROSPECTION_CLIENT_ID",
"OIDC_INTROSPECTION_CLIENT_SECRET",
"OIDC_RESOURCE_STATUS_URL",
"OIDC_SCOPES",
"AUTH_PROVIDER_GENERATION",
"AUTH_COOKIE_NAME",
"AUTH_SESSION_IDLE_SECONDS",
"AUTH_SESSION_ABSOLUTE_SECONDS",
]) {
expect(profile.services.web?.environment?.[variable], `web ${variable}`).toBe(optional(variable))
}
})
})
}
describe("the local stack", () => {
const dev = readFileSync(new URL("../compose.dev.yaml", import.meta.url), "utf8")
const roles = readFileSync(new URL("../dev/postgres-roles.sql", import.meta.url), "utf8")
const example = readFileSync(new URL("../.env.example", import.meta.url), "utf8")
it("creates the three roles the platform creates, with the same powers", () => {
for (const role of ["appdev_owner", "appdev_app", "appdev_reader"]) {
expect(roles, `${role} is not created`).toMatch(new RegExp(`CREATE ROLE ${role}\\s+LOGIN`))
}
// The three are LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOBYPASSRLS NOINHERIT NOREPLICATION,
// as data-services DS21 creates them.
expect(roles.match(/NOSUPERUSER NOCREATEDB NOCREATEROLE NOBYPASSRLS NOINHERIT NOREPLICATION/g)).toHaveLength(3)
// The owner owns schema public; the runtime role gets rows, never ownership.
expect(roles).toContain('ALTER SCHEMA public OWNER TO "appdev_owner"')
expect(roles).toContain('GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "appdev_app"')
// The reader gets no table privilege here: the app's own migrations grant it what it reads.
expect(roles).not.toMatch(/GRANT SELECT ON (ALL )?TABLES? [^;]*"appdev_reader"/)
})
it("hands each local process the same URL the platform would", () => {
expect(example).toContain("POSTGRES_URL=postgresql://appdev_app:")
expect(example).toContain("POSTGRES_READER_URL=postgresql://appdev_reader:")
expect(example).toContain("POSTGRES_MIGRATE_URL=postgresql://appdev_owner:")
})
it("keeps the development stack out of what is deployed", () => {
// conusai.json names compose.yaml alone, so nothing in compose.dev.yaml is ever deployed.
expect(dev).toContain("image: pgvector/pgvector:pg18")
expect(readFileSync(new URL("../conusai.json", import.meta.url), "utf8")).not.toContain("compose.dev.yaml")
})
})