Files
2026-11-14 09:56:45 +00:00

155 lines
5.8 KiB
JavaScript

#!/usr/bin/env node
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// Chromium + actual module/client, local synthetic HTTP. No provider/auth proof.
import assert from "node:assert/strict"
import { spawnSync } from "node:child_process"
import { createHash } from "node:crypto"
import { mkdir, mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises"
import http from "node:http"
import os from "node:os"
import path from "node:path"
import { fileURLToPath } from "node:url"
import { chromium } from "@playwright/test"
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..")
const evidence = process.env.LIFECYCLE_BROWSER_EVIDENCE
if (!evidence) throw new Error("LIFECYCLE_BROWSER_EVIDENCE must name a new external directory")
await mkdir(evidence, { mode: 0o700 })
const temp = await mkdtemp(path.join(os.tmpdir(), "conusai-knowledge-browser-"))
const build = spawnSync(
"bun",
[
"build",
"test/component-browser/knowledge.fixture.tsx",
"--target",
"browser",
"--format",
"iife",
"--outdir",
temp,
"--entry-naming",
"fixture.[ext]",
],
{ cwd: root, encoding: "utf8" },
)
if (build.status !== 0) throw new Error(`fixture build failed: ${build.stdout}${build.stderr}`)
const assets = new Set(await readdir(temp))
const network = []
const errors = []
const consoleErrors = []
const server = http.createServer(async (req, res) => {
if (req.url === "/api/v1/knowledge.search") {
const scenario = req.headers["x-fixture-scenario"]
const status = scenario === "configuration" ? 503 : 200
const body =
status === 503
? { status: 503, code: "configuration_missing", title: "Configuration required" }
: {
results:
scenario === "revoked"
? []
: [
{
fileId: "synthetic-file",
filename: "synthetic-source.txt",
collectionName: "sources",
snippet: "A synthetic passage with a current source citation.",
score: 0.9,
chunk: 1,
page: 3,
blockId: "p3-b2",
chunkId: "22222222-2222-4222-8222-222222222222",
},
],
}
network.push({ method: req.method, path: req.url, scenario, status })
res.writeHead(status, { "content-type": "application/json" })
res.end(JSON.stringify(body))
return
}
if (req.url === "/favicon.ico") {
res.writeHead(204)
res.end()
return
}
if (req.url?.startsWith("/") && assets.has(req.url.slice(1))) {
res.writeHead(200, {
"content-type": req.url.endsWith(".css")
? "text/css"
: req.url.endsWith(".woff2")
? "font/woff2"
: "application/javascript",
})
res.end(await readFile(path.join(temp, req.url.slice(1))))
return
}
res.writeHead(200, { "content-type": "text/html" })
res.end(
'<!doctype html><html lang="en"><head><title>Knowledge lifecycle fixture</title><link rel="stylesheet" href="/fixture.css"></head><body><main id="root"></main><script src="/fixture.js"></script></body></html>',
)
})
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve))
const browser = await chromium.launch({ headless: true })
try {
const page = await browser.newPage({ viewport: { width: 1200, height: 800 } })
page.on("pageerror", (error) => errors.push(error.message))
page.on("console", (message) => {
if (message.type() === "error") {
consoleErrors.push(message.text())
if (!/503/.test(message.text())) errors.push(message.text())
}
})
const base = `http://127.0.0.1:${server.address().port}`
for (const scenario of ["cited", "revoked", "configuration"]) {
await page.goto(`${base}/?scenario=${scenario}`)
await page.getByRole("searchbox", { name: "Question" }).fill("synthetic source")
await page.getByRole("button", { name: "Search", exact: true }).click()
if (scenario === "cited") {
await page.getByText("A synthetic passage with a current source citation.", { exact: true }).waitFor()
assert.match(await page.locator("ol").innerText(), /synthetic-source.txt.*result 1.*page 3/s)
await page.getByText("p3-b2", { exact: true }).waitFor()
} else if (scenario === "revoked") {
await page.getByText("No answers in this collection.", { exact: true }).waitFor()
assert.equal(await page.locator("blockquote").count(), 0)
} else {
await page.getByRole("alert").filter({ hasText: "Set CONUSAI_ENVIRONMENT" }).waitFor()
assert.equal(await page.locator("blockquote").count(), 0)
}
await page.screenshot({ path: path.join(evidence, `${scenario}.png`), fullPage: true })
}
assert.deepEqual(errors, [])
const sources = {}
for (const file of [
"modules/knowledge/ui/search-form.tsx",
"modules/knowledge/ui/labels.ts",
"modules/knowledge/contract/procedures.ts",
"test/component-browser/knowledge.fixture.tsx",
])
sources[file] = createHash("sha256")
.update(await readFile(path.join(root, file)))
.digest("hex")
await writeFile(
path.join(evidence, "result.json"),
JSON.stringify(
{
tier: "component-browser-synthetic-http",
sourceCommit:
process.env.LIFECYCLE_SOURCE_COMMIT ??
spawnSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" }).stdout.trim(),
sources,
network,
consoleErrors,
unexpectedErrors: errors,
result: "passed",
},
null,
2,
),
)
console.log("Knowledge citations, revocation and configuration refusals passed; fixture browser only")
} finally {
await browser.close()
await new Promise((resolve) => server.close(resolve))
await rm(temp, { recursive: true, force: true })
}