Files
harness-tools/extensions/fast-browser/runtime.test.ts
T
jb fd63b0ccec
fast-browser scenarios / Offline scenario contract tests (push) Failing after 44s
fast-browser scenarios / Local Chromium headless scenarios (push) Failing after 23s
fast-browser-with-jev
2026-09-20 12:24:08 -04:00

219 lines
13 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { createBrowserRuntimeFactory, withRuntimeOriginPolicy } from "./runtime.ts";
import type {
BrowserOperation,
BrowserRuntimeOptions,
BrowserTransport,
BrowserTransportRequest,
BrowserTransportResultFor,
PrivateArtifactMetadata,
PrivateArtifactStore,
RuntimeOperationContext,
} from "./contracts.ts";
const tab = (id: string, url = "https://example.test/form") => ({
tabId: id as never, kind: "page" as const, selected: true, owned: true as const,
url, origin: new URL(url).origin, title: "Fixture",
});
function artifacts(): { store: PrivateArtifactStore; committed: PrivateArtifactMetadata[] } {
const committed: PrivateArtifactMetadata[] = [];
const store: PrivateArtifactStore = {
async open(owner, kind) {
let bytes = 0;
let done = false;
return {
owner,
kind,
async write(chunk) { if (done) throw new Error("closed"); bytes += chunk.byteLength; },
async commit() {
done = true;
const metadata: PrivateArtifactMetadata = {
artifactId: `${kind}-1`, kind,
contentType: kind === "screenshot" ? "image/png" : "application/json",
path: `/private/${kind}-1`, bytes, owner,
permissions: "0600", localOnly: true, modelForwarded: false,
};
committed.push(metadata);
return metadata;
},
async abort() { done = true; },
};
},
async close() {},
};
return { store, committed };
}
function transportFixture() {
const calls: BrowserTransportRequest[] = [];
let transportClosed = 0;
const transport: BrowserTransport = {
async dispatch<Operation extends BrowserOperation>(request: BrowserTransportRequest<Operation>, signal: AbortSignal): Promise<BrowserTransportResultFor<Operation>> {
calls.push(request as BrowserTransportRequest);
const op = request.operation;
if (op.op === "observe") return { op: "observe", result: { observationId: "o1", url: "https://example.test/form", title: "Fixture", candidates: [] } } as unknown as BrowserTransportResultFor<Operation>;
if (op.op === "read") return { op: "read", result: { tab: tab("tab-1"), documentId: "doc-1", url: "https://example.test/form", title: "Fixture", visibleText: "Hello", visibleTextTruncated: false } } as unknown as BrowserTransportResultFor<Operation>;
if (op.op === "listTabs") return { op: "listTabs", result: [tab("tab-1"), tab("tab-2", "https://other.test/")] } as unknown as BrowserTransportResultFor<Operation>;
if (op.op === "navigate") return { op: "navigate", result: { status: "committed", mutationState: "verified", tab: tab("tab-1", op.request.url), documentId: "doc-2" } } as unknown as BrowserTransportResultFor<Operation>;
if (op.op === "narrowOriginPolicy") return { op: "narrowOriginPolicy", result: { allowedOrigins: op.request.allowedOrigins } } as unknown as BrowserTransportResultFor<Operation>;
if (op.op === "captureEvidence") return { op: "captureEvidence", result: {
status: "complete", console: [{ level: "error", message: "password=secret https://example.test/?token=secret", count: 1, truncated: false }], failedRequests: [],
screenshot: { kind: "screenshot", contentType: "image/png", encoding: "base64", dataBase64: "aGk=", byteLength: 2, truncated: false },
} } as unknown as BrowserTransportResultFor<Operation>;
if (op.op === "close") return { op: "close", result: { closed: true } } as unknown as BrowserTransportResultFor<Operation>;
if (op.op === "assert") return { op: "assert", result: { status: "satisfied", readOnly: true, tab: tab("tab-1"), documentId: "doc-1" } } as unknown as BrowserTransportResultFor<Operation>;
if (op.op === "reload") return { op: "reload", result: { status: "committed", mutationState: "verified", tab: tab("tab-1"), documentId: "doc-2" } } as unknown as BrowserTransportResultFor<Operation>;
if (op.op === "selectTab") return { op: "selectTab", result: { selected: true, tab: tab(op.request.tabId) } } as unknown as BrowserTransportResultFor<Operation>;
if (op.op === "waitPopup") return { op: "waitPopup", result: { status: "timed_out", autoSwitched: false } } as unknown as BrowserTransportResultFor<Operation>;
if (op.op === "closeTab") return { op: "closeTab", result: { closed: true, tabId: op.request.tabId, mutationState: "verified" } } as unknown as BrowserTransportResultFor<Operation>;
throw new Error("unexpected operation");
},
async close() { transportClosed++; },
};
return { transport, calls, transportClosed: () => transportClosed };
}
function options(artifactsStore: PrivateArtifactStore, transport: BrowserTransport): BrowserRuntimeOptions {
return { sessionId: "session-1", cdpUrl: "http://127.0.0.1:9222", allowedOrigins: "*", artifacts: artifactsStore,
transportFactory: { create() { return transport; } } };
}
test("factory creates one persistent transport and serializes same-session calls", async () => {
const store = artifacts();
const fixture = transportFixture();
const runtime = createBrowserRuntimeFactory(options(store.store, fixture.transport).transportFactory).create(options(store.store, fixture.transport));
await Promise.all([runtime.observe(new AbortController().signal), runtime.read({}, new AbortController().signal)]);
assert.equal(fixture.calls.filter((call) => call.operation.op === "observe" || call.operation.op === "read").length, 2);
assert.deepEqual(Object.keys(fixture.calls[0] ?? {}).sort(), ["operation"]);
assert.equal((fixture.calls[0]?.operation as any).op, "observe");
assert.equal((fixture.calls[0] as any)?.op, undefined);
assert.equal(fixture.calls[1]?.operation.op, "read");
await runtime.close();
await runtime.close();
assert.equal(fixture.transportClosed(), 1);
});
test("aborted operations still make an independent bounded evidence attempt", async () => {
const store = artifacts(); const fixture = transportFixture();
const runtime = createBrowserRuntimeFactory({ create() { return fixture.transport; } }).create(options(store.store, fixture.transport));
const controller = new AbortController();
controller.abort(new Error("operation deadline exceeded"));
await assert.rejects(runtime.observe(controller.signal), (error: any) => {
assert.equal(error.details.code, "aborted");
assert.deepEqual(error.failureEvidence, { status: "complete" });
return true;
});
assert.equal(fixture.calls.filter((call) => call.operation.op === "captureEvidence").length, 1);
await runtime.close();
});
test("already-aborted parent does not create transport during failure evidence", async () => {
const store = artifacts();
const parent = new AbortController();
parent.abort(new Error("browser session closed"));
let created = 0;
const runtime = createBrowserRuntimeFactory({ create() { created++; return transportFixture().transport; } }).create({ ...options(store.store, transportFixture().transport) }, parent.signal);
await assert.rejects(runtime.observe(new AbortController().signal), (error: any) => {
assert.equal(error.details.code, "browser_closed");
assert.deepEqual(error.failureEvidence, { status: "unavailable", reason: "browser session is unavailable" });
return true;
});
assert.equal(created, 0);
await runtime.close();
});
test("destroyed transports report unavailable evidence without replacing the failure", async () => {
const store = artifacts();
const transport: BrowserTransport = {
async dispatch<Operation extends BrowserOperation>(request: BrowserTransportRequest<Operation>): Promise<BrowserTransportResultFor<Operation>> {
if (request.operation.op === "captureEvidence") throw new Error("browser bridge is closed");
throw new Error("browser worker request timed out; worker was torn down");
},
async close() {},
};
const runtime = createBrowserRuntimeFactory({ create() { return transport; } }).create(options(store.store, transport));
const controller = new AbortController();
controller.abort(new Error("operation deadline exceeded"));
await assert.rejects(runtime.observe(controller.signal), (error: any) => {
assert.equal(error.details.code, "aborted");
assert.deepEqual(error.failureEvidence, { status: "unavailable", reason: "browser session is unavailable" });
return true;
});
await runtime.close();
});
test("factory forwards pinned Python selection to the transport", async () => {
const store = artifacts(); const fixture = transportFixture();
let received: unknown;
const runtime = createBrowserRuntimeFactory({ create(transportOptions) { received = transportOptions; return fixture.transport; } }).create({ ...options(store.store, fixture.transport), python: "/tmp/pinned-python" });
await runtime.observe(new AbortController().signal);
assert.equal((received as any).python, "/tmp/pinned-python");
await runtime.close();
});
test("wildcard narrowing is scoped while restricted bases cannot broaden", async () => {
const first = artifacts();
const fixture = transportFixture();
const runtime = createBrowserRuntimeFactory(options(first.store, fixture.transport).transportFactory).create(options(first.store, fixture.transport));
await withRuntimeOriginPolicy(runtime, ["https://example.test"], (context) => runtime.read({}, new AbortController().signal, context));
const reads = fixture.calls.filter((call) => call.operation.op === "read");
assert.deepEqual((reads[0]?.context as RuntimeOperationContext).allowedOrigins, ["https://example.test"]);
await runtime.read({}, new AbortController().signal);
assert.equal(fixture.calls.at(-1)?.context, undefined);
const restricted = createBrowserRuntimeFactory({ create() { return fixture.transport; } }).create({ ...options(first.store, fixture.transport), allowedOrigins: ["https://example.test"] });
await assert.rejects(restricted.narrowOriginPolicy({ allowedOrigins: "*" }, new AbortController().signal), /broadened/);
await runtime.close(); await restricted.close();
});
test("policy is enforced before navigation and tabs are origin filtered", async () => {
const store = artifacts(); const fixture = transportFixture();
const runtime = createBrowserRuntimeFactory(options(store.store, fixture.transport).transportFactory).create(options(store.store, fixture.transport));
const context = await runtime.narrowOriginPolicy({ allowedOrigins: ["https://example.test"] }, new AbortController().signal);
await assert.rejects(runtime.navigate({ url: "https://other.test/out" }, new AbortController().signal, context), (error: any) => error.details.code === "policy_denied");
const restricted = createBrowserRuntimeFactory({ create() { return fixture.transport; } }).create({ ...options(store.store, fixture.transport), allowedOrigins: ["https://example.test"] });
await assert.rejects(restricted.navigate({ url: "https://example.test/out" }, new AbortController().signal, { allowedOrigins: "*" }), (error: any) => {
assert.equal(error.details.code, "policy_denied");
assert.equal(error.details.mutationState, "not_attempted");
assert.equal(error.details.retry, "never");
return true;
});
const tabs = await runtime.listTabs(new AbortController().signal, context);
assert.deepEqual(tabs.map((item) => item.origin), ["https://example.test"]);
await runtime.close();
await restricted.close();
});
test("requested evidence commits local metadata and redacts untrusted console text", async () => {
const store = artifacts(); const fixture = transportFixture();
const runtime = createBrowserRuntimeFactory(options(store.store, fixture.transport).transportFactory).create(options(store.store, fixture.transport));
const result = await runtime.captureEvidence({ runId: "run-1", reason: "requested" }, new AbortController().signal);
assert.equal(result.screenshot?.bytes, 2);
assert.equal(result.screenshot?.modelForwarded, false);
assert.equal(result.console[0]?.message.includes("secret"), false);
assert.equal(store.committed.length, 1);
await runtime.close();
});
test("transport failures become safe errors and are not mutation retried", async () => {
const store = artifacts(); let actions = 0;
const transport: BrowserTransport = {
async dispatch<Operation extends BrowserOperation>(request: BrowserTransportRequest<Operation>): Promise<BrowserTransportResultFor<Operation>> {
actions++;
if (request.operation.op === "captureEvidence") return { op: "captureEvidence", result: { status: "unavailable", console: [], failedRequests: [] } } as unknown as BrowserTransportResultFor<Operation>;
throw new Error("private page secret and mutation may have run");
},
async close() {},
};
const runtime = createBrowserRuntimeFactory({ create() { return transport; } }).create(options(store.store, transport));
await assert.rejects(runtime.act({ observationId: "o", candidateId: "c", action: "click", postcondition: { kind: "text", value: "done" } }, new AbortController().signal), (error: any) => {
assert.equal(error.details.code, "worker_failure");
assert.equal(error.details.mutationState, "unknown");
assert.equal(String(error).includes("private"), false);
return true;
});
assert.equal(actions, 2, "one action dispatch plus one best-effort evidence request; no mutation retry");
await runtime.close();
});