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
+107
View File
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// What every spec does: sign in through the dev adapter, call a procedure the way the page's own
// forms do (with the page's CSRF token), and make an organization to work in.
import { randomBytes } from "node:crypto"
import { type APIRequestContext, expect, type Page } from "@playwright/test"
import axe from "axe-core"
export const unique = (prefix: string): string => `${prefix}-${randomBytes(3).toString("hex")}`
/** Signs in as `email` (created on first use) and lands on the home page. */
export async function signIn(page: Page, email: string): Promise<void> {
await page.goto(`/auth/dev-login?email=${encodeURIComponent(email)}`)
await expect(page.locator('meta[name="csrf-token"]')).toHaveCount(1)
}
/** The CSRF token of the page the browser is on: what the page's own forms send back. */
const csrfOf = (page: Page): Promise<string> =>
page
.locator('meta[name="csrf-token"]')
.getAttribute("content")
.then((value) => value ?? "")
/** Calls a procedure with the session of `page`. Throws on a refusal, with the problem's own words. */
export async function call<T = unknown>(page: Page, procedure: string, input: unknown): Promise<T> {
const response = await callRaw(page.request, await csrfOf(page), procedure, input)
const body = (await response.json().catch(() => null)) as T
if (!response.ok()) throw new Error(`${procedure} answered ${response.status()}: ${JSON.stringify(body)}`)
return body
}
export const callRaw = (request: APIRequestContext, csrf: string, procedure: string, input: unknown) =>
request.post(`/api/v1/${procedure}`, {
data: input,
headers: { "x-csrf-token": csrf, "content-type": "application/json", "sec-fetch-site": "same-origin" },
})
export interface Workspace {
org: string
workspace: string
project: string
}
/** The signed-in user's own organization with one workspace and one project. */
export async function makeOrganization(page: Page, prefix = "acme"): Promise<Workspace> {
const org = unique(prefix)
await call(page, "tenancy.org.create", { slug: org, name: `Org ${org}` })
const workspace = (
await call<{ slug: string }>(page, "tenancy.workspace.create", {
org,
slug: "main",
name: "Main",
visibility: "org",
})
).slug
const project = (
await call<{ slug: string }>(page, "tenancy.project.create", {
org,
workspace,
slug: "review",
name: "Review",
moduleKey: "documents",
})
).slug
return { org, workspace, project }
}
/** Puts axe into the page. */
async function injectAxe(page: Page): Promise<void> {
await page.addScriptTag({ content: axe.source })
}
/** WCAG 2.2 AA violations on the page the browser is on, as axe finds them. */
export async function violations(page: Page): Promise<string[]> {
await injectAxe(page)
const results = await page.evaluate(() =>
(
globalThis as unknown as {
axe: {
run(
context: unknown,
options: unknown,
): Promise<{ violations: Array<{ id: string; nodes: Array<{ target: unknown[] }> }> }>
}
}
).axe.run(document, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"] } }),
)
return results.violations.map(
(violation) => `${violation.id}: ${violation.nodes.map((node) => node.target.join(" ")).join(", ")}`,
)
}
/** What the fake platform has recorded: the audit events and usage facts the worker sent, and the blobs it stored. */
export interface PlatformState {
audit: Array<{ action: string; resource_id: string | null }>
usage: Array<Record<string, unknown>>
blobs: Array<{ pathname: string; contentType: string; size: number }>
otel: { logs: Array<Record<string, unknown>>; traces: Array<Record<string, unknown>> }
}
export async function platformState(): Promise<PlatformState> {
const base = process.env.E2E_PLATFORM_URL
if (!base) throw new Error("e2e: E2E_PLATFORM_URL is set by the global setup")
const response = await fetch(`${base}/__e2e/state`)
if (!response.ok) throw new Error(`e2e: the fake platform answered ${response.status}`)
return (await response.json()) as PlatformState
}
+20
View File
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// Starts the stack once for the whole run and stops it after (Playwright's global setup returns the
// teardown). The OCR the fake platform answers with is the documents module's own fixture: a three-page
// document with four labelled lines and a drawing page the extraction leaves out, which is what the specs review.
import { readFileSync } from "node:fs"
import { join } from "node:path"
import { APP_ROOT, startStack } from "./stack"
export default async function globalSetup(): Promise<() => Promise<void>> {
const document = JSON.parse(
readFileSync(join(APP_ROOT, "modules", "documents", "__tests__", "fixtures", "ocr-document.json"), "utf8"),
) as Record<string, unknown>
const stack = await startStack(document)
process.env.E2E_BASE_URL = stack.baseUrl
process.env.E2E_PLATFORM_URL = stack.platformUrl
return async () => {
await stack.stop()
}
}
+189
View File
@@ -0,0 +1,189 @@
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// The whole app, running, for the end-to-end suite: a Postgres with the three roles a linked managed
// database exposes, the fake platform of @conusai-kit/testing (Blob, OCR jobs, audit, usage), the
// migrate job, the worker and the web shell -- the same four things compose.yaml runs, as processes
// on this machine so the suite needs no image build.
//
// Postgres is the one thing that needs Docker. The web shell runs under `next dev`, because
// @conusai-kit/auth refuses the password-free dev sign-in the suite uses in a production start
// (AUTH_DEV_LOGIN with NODE_ENV=production fails at boot, spec §7), which is the right refusal and
// not one to work around.
import { type ChildProcess, execFileSync, spawn } from "node:child_process"
import { randomBytes } from "node:crypto"
import { mkdirSync, openSync, readFileSync, rmSync } from "node:fs"
import { join } from "node:path"
import { startFakePlatform } from "@conusai-kit/testing/fake-platform"
import { deterministicModel } from "../../test/support/fake-model"
export const APP_ROOT = new URL("../..", import.meta.url).pathname
const LOGS = join(APP_ROOT, "e2e", ".logs")
export const PORTS = {
web: Number(process.env.E2E_WEB_PORT ?? 3100),
postgres: Number(process.env.E2E_POSTGRES_PORT ?? 55432),
}
export interface Stack {
readonly baseUrl: string
readonly platformUrl: string
/** The fake platform, so a spec can change what its OCR reads. */
readonly platform: Awaited<ReturnType<typeof startFakePlatform>>
stop(): Promise<void>
}
const url = (role: string): string => `postgresql://appdev_${role}:appdev_${role}@127.0.0.1:${PORTS.postgres}/appdev`
function sh(command: string, args: string[]): string {
return execFileSync(command, args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim()
}
async function until(label: string, check: () => Promise<boolean> | boolean, seconds = 120): Promise<void> {
const deadline = Date.now() + seconds * 1000
for (;;) {
try {
if (await check()) return
} catch {
// not yet
}
if (Date.now() > deadline) throw new Error(`e2e: ${label} did not come up in ${seconds}s (logs in e2e/.logs)`)
await new Promise((resolve) => setTimeout(resolve, 500))
}
}
function spawnLogged(name: string, command: string, args: string[], cwd: string, env: NodeJS.ProcessEnv): ChildProcess {
mkdirSync(LOGS, { recursive: true })
const out = openSync(join(LOGS, `${name}.log`), "w")
// A process group of its own, so stopping it stops what it started (next dev forks).
return spawn(command, args, { cwd, env, stdio: ["ignore", out, out], detached: true })
}
function stopGroup(child: ChildProcess): void {
if (child.pid === undefined) return
try {
process.kill(-child.pid, "SIGTERM")
} catch {
// already gone
}
}
export async function startStack(document: Record<string, unknown>): Promise<Stack> {
const processes: ChildProcess[] = []
const name = `e2e-postgres-${randomBytes(4).toString("hex")}`
const platform = await startFakePlatform()
platform.ocr.setDocument(document)
// The model is the deterministic reader of the unit tests: the suite proves the app around a model, not a model.
platform.ai.respond(deterministicModel)
sh("docker", [
"run",
"-d",
"--rm",
"--name",
name,
"-p",
`127.0.0.1:${PORTS.postgres}:5432`,
"-e",
"POSTGRES_PASSWORD=postgres",
"-e",
"POSTGRES_DB=appdev",
"-v",
`${join(APP_ROOT, "dev", "postgres-roles.sql")}:/docker-entrypoint-initdb.d/10-roles.sql:ro`,
// pgvector: the run chooses the pgvector store (KNOWLEDGE_VECTOR_STORE below), whose table the
// migrate job creates in this Postgres.
"pgvector/pgvector:pg18",
])
const stopPostgres = (): void => {
try {
sh("docker", ["rm", "-f", name])
} catch {
// already gone
}
}
try {
// The init script creates the roles after the server first starts, then it restarts: "ready"
// is the app's own login working, not pg_isready.
await until("postgres", () => {
sh("docker", ["exec", name, "psql", "-U", "appdev_app", "-h", "127.0.0.1", "-d", "appdev", "-c", "select 1"])
return true
})
const common: NodeJS.ProcessEnv = {
...process.env,
// The fake platform is a development and test switch the app accepts in these modes alone (config/src/env.ts).
NODE_ENV: "development",
CONUSAI_FAKE_PLATFORM: "1",
// What the platform injects when the environment has OTLP: the web shell sends its logs and spans to
// the fake platform, where a spec reads them back with the request's trace id.
OTEL_EXPORTER_OTLP_ENDPOINT: `${platform.url}/otel`,
CONUSAI_API_URL: `${platform.url}/api`,
CONUSAI_API_TOKEN: "e2e-token",
CONUSAI_PROJECT_ID: "42",
// What the platform injects for usage metering (AT4.1): the fake platform keeps the usage of
// this organization beside the audit events, which is where a spec reads both.
CONUSAI_ORG_ID: "e2e",
CONUSAI_PUBLIC_URL: `http://127.0.0.1:${PORTS.web}`,
CONUSAI_RELEASE: "e2e",
POSTGRES_URL: url("app"),
POSTGRES_READER_URL: url("reader"),
KNOWLEDGE_VECTOR_STORE: "pgvector",
AUTH_SESSION_SECRET: randomBytes(32).toString("base64url"),
}
delete common.POSTGRES_MIGRATE_URL
// The migrate job holds the owner's URL and nothing else does.
execFileSync("node", ["apps/worker/dist/migrate.js"], {
cwd: APP_ROOT,
env: { ...common, POSTGRES_MIGRATE_URL: url("owner") },
stdio: "inherit",
})
// The demo data, written with the kit by the same script `pnpm dev` runs: the specs that look at it
// are proof it works, and the others each make their own organization beside it.
execFileSync("node", ["apps/worker/dist/seed-main.js"], { cwd: APP_ROOT, env: common, stdio: "inherit" })
// A dev server killed mid-write leaves a cache Turbopack panics on at the next start: begin clean.
rmSync(join(APP_ROOT, "apps", "web", ".next"), { recursive: true, force: true })
processes.push(
spawnLogged("worker", "node", ["apps/worker/dist/main.js"], APP_ROOT, {
...common,
WORKER_HEARTBEAT_FILE: join(LOGS, "worker-heartbeat"),
}),
)
processes.push(
spawnLogged(
"web",
process.execPath,
["node_modules/next/dist/bin/next", "dev", "-p", String(PORTS.web), "-H", "127.0.0.1"],
join(APP_ROOT, "apps", "web"),
{
...common,
AUTH_DEV_LOGIN: "1",
AUTH_INSECURE_COOKIES: "1",
},
),
)
const baseUrl = `http://127.0.0.1:${PORTS.web}`
await until("the web shell", async () => (await fetch(`${baseUrl}/api/ready`)).ok, 180)
await until("the worker", () => readFileSync(join(LOGS, "worker.log"), "utf8").includes("worker: serving"), 60)
return {
baseUrl,
platformUrl: platform.url,
platform,
async stop() {
for (const child of processes) stopGroup(child)
await platform.close()
stopPostgres()
},
}
} catch (error) {
for (const child of processes) stopGroup(child)
await platform.close()
stopPostgres()
throw error
}
}