Spaces:
Running
Running
| // Copyright 2026 SZL Holdings | |
| // SPDX-License-Identifier: Apache-2.0 | |
| // | |
| // a11oyClient.test.ts — integration tests for the operator-console data layer. | |
| // Boots a real node:http server that mimics a11oy's `serve` route surface and | |
| // exercises every A11oyClient method over real TCP via Node's global fetch. | |
| // No mocked transport: the request crosses a socket and the real JSON response | |
| // is parsed back. This proves the 5 console routes' backing calls work against | |
| // the a11oy serve contract. | |
| // | |
| // Run: node --experimental-strip-types src/a11oyClient.test.ts | |
| // | |
| // Authored for SZL Holdings. Signed-off per repository DCO. | |
| import * as http from "node:http"; | |
| import { test } from "node:test"; | |
| import assert from "node:assert/strict"; | |
| import { A11oyClient, CONSOLE_ROUTES } from "./a11oyClient.ts"; | |
| function bootA11oyLike(): Promise<{ base: string; close: () => Promise<void> }> { | |
| const server = http.createServer((req, res) => { | |
| const url = new URL(req.url ?? "/", "http://localhost"); | |
| const chunks: Buffer[] = []; | |
| req.on("data", (c) => chunks.push(c)); | |
| req.on("end", () => { | |
| const reply = (status: number, body: unknown) => { | |
| res.writeHead(status, { "content-type": "application/json" }); | |
| res.end(JSON.stringify(body)); | |
| }; | |
| if (req.method === "GET" && url.pathname === "/healthz") { | |
| return reply(200, { status: "ok", sha: "abc123", ts: "2026-05-31T00:00:00Z" }); | |
| } | |
| if (req.method === "GET" && url.pathname === "/readyz") { | |
| return reply(200, { status: "ready", ledger: "/tmp/x.jsonl" }); | |
| } | |
| if (req.method === "GET" && url.pathname === "/v1/ledger") { | |
| const limit = Number(url.searchParams.get("limit") ?? "20"); | |
| return reply(200, { count: 1, total: 1, receipts: [{ receipt_id: "r-1", merkle_root: "m-1", limit }] }); | |
| } | |
| if (req.method === "GET" && url.pathname.startsWith("/v1/ledger/")) { | |
| const hash = decodeURIComponent(url.pathname.slice("/v1/ledger/".length)); | |
| return reply(200, { receipt: { receipt_id: hash } }); | |
| } | |
| if (req.method === "POST" && url.pathname === "/v1/verify") { | |
| const body = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}"); | |
| const valid = Array.isArray(body.ledger) && body.ledger.length > 0; | |
| return reply(200, { valid, broken_at: valid ? null : 0, errors: valid ? [] : ["position 0: empty"] }); | |
| } | |
| if (req.method === "POST" && url.pathname === "/v1/policy/evaluate") { | |
| const body = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}"); | |
| const sev = body.action?.severity ?? "medium"; | |
| const allow = sev === "low" || sev === "medium"; | |
| return reply(200, { | |
| decision: allow ? "allow" : "deny", | |
| gate: "thresholdPolicySeverity", | |
| receipt_hash: allow ? "sig-xyz" : "", | |
| rationale: `severity=${sev}`, | |
| }); | |
| } | |
| reply(404, { error: "not found", path: url.pathname }); | |
| }); | |
| }); | |
| return new Promise((resolve) => { | |
| server.listen(0, "127.0.0.1", () => { | |
| const addr = server.address(); | |
| const port = typeof addr === "object" && addr ? addr.port : 0; | |
| resolve({ base: `http://127.0.0.1:${port}`, close: () => new Promise<void>((r) => server.close(() => r())) }); | |
| }); | |
| }); | |
| } | |
| test("there are exactly 5 console routes, each citing its a11oy source endpoint", () => { | |
| assert.equal(CONSOLE_ROUTES.length, 5); | |
| for (const r of CONSOLE_ROUTES) assert.ok(r.source.length > 0); | |
| }); | |
| test("health + readiness routes read /healthz and /readyz", async () => { | |
| const srv = await bootA11oyLike(); | |
| try { | |
| const c = new A11oyClient(srv.base); | |
| const h = await c.health(); | |
| assert.equal(h.status, "ok"); | |
| assert.equal(h.sha, "abc123"); | |
| const r = await c.readiness(); | |
| assert.equal(r.status, "ready"); | |
| } finally { | |
| await srv.close(); | |
| } | |
| }); | |
| test("proof-ledger route reads /v1/ledger with the limit", async () => { | |
| const srv = await bootA11oyLike(); | |
| try { | |
| const c = new A11oyClient(srv.base); | |
| const page = await c.ledger(5); | |
| assert.equal(page.count, 1); | |
| assert.equal(page.receipts[0].limit, 5); | |
| } finally { | |
| await srv.close(); | |
| } | |
| }); | |
| test("receipt route reads /v1/ledger/{hash}", async () => { | |
| const srv = await bootA11oyLike(); | |
| try { | |
| const c = new A11oyClient(srv.base); | |
| const out = await c.receipt("m-1"); | |
| assert.equal(out.receipt.receipt_id, "m-1"); | |
| } finally { | |
| await srv.close(); | |
| } | |
| }); | |
| test("verify route POSTs the ledger and returns the real verdict", async () => { | |
| const srv = await bootA11oyLike(); | |
| try { | |
| const c = new A11oyClient(srv.base); | |
| const ok = await c.verify([{ receipt_id: "r-1" }]); | |
| assert.equal(ok.valid, true); | |
| const empty = await c.verify([]); | |
| assert.equal(empty.valid, false); | |
| assert.equal(empty.broken_at, 0); | |
| } finally { | |
| await srv.close(); | |
| } | |
| }); | |
| test("policy route POSTs the action and returns allow/deny", async () => { | |
| const srv = await bootA11oyLike(); | |
| try { | |
| const c = new A11oyClient(srv.base); | |
| const allow = await c.evaluatePolicy({ actionId: "a", severity: "low", confidence: 0.9 }); | |
| assert.equal(allow.decision, "allow"); | |
| assert.equal(allow.gate, "thresholdPolicySeverity"); | |
| const deny = await c.evaluatePolicy({ actionId: "b", severity: "critical" }); | |
| assert.equal(deny.decision, "deny"); | |
| } finally { | |
| await srv.close(); | |
| } | |
| }); | |