#!/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( 'Knowledge lifecycle fixture
', ) }) 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 }) }