Files
reverse-engineering-anything/tests/boundary/browser/cdpEndpointResponseBounds.test.ts
T
morluto 60149282fc test: strengthen integration oracles and verification (#8295)
* test: strengthen integration oracles and verification

* test: bound lifecycle gates and strengthen workflow checks

* test: verify complete paged and extracted results

* test: preserve bytecode and cancellation evidence

* test: strengthen retained evidence and fixture cleanup

* test: require cross-projection bridge identities

* test: retain fixture owners through cleanup failures

* test: verify graph examples and project selectors

* test: tighten provider and lifecycle regressions

* test: consolidate hex coverage and bound cutter cleanup

* test: bound subprocess output waits and strengthen graph checks

* test: check mcp recovery and preserve cleanup owners

* test: verify structured mcp argument failures

* test: preserve artifact cleanup and gdb limit evidence

* test: preserve evidence identities and bound provider gates

* test: retain browser and comparison cleanup owners

* test: bound startup and strengthen fixture oracles

* test: bound lifecycle gates and verify recovered evidence

* test: verify public outcomes and lifecycle ownership

* test: assert precise evidence and release lifecycle gates

* test: verify selected facts and bound cleanup waits

* test: check retained evidence and decoded records

* test: verify final reports and retain cleanup ownership

* test: bound reviewed startup gates and retain cleanup

* test: verify PE errors and preserve MCP cleanup

* test: preserve MCP fixture cleanup ownership

* test: release provider gates before late startup
2026-11-01 00:15:28 +08:00

263 lines
8.2 KiB
TypeScript

import { createServer } from "node:http";
import type { RequestListener, Server } from "node:http";
import { afterEach, describe, expect, it } from "vitest";
import { withVerifierCleanup } from "../../../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../../fixtures/lifecycleAcknowledgement.js";
import {
discoverCdpEndpoint,
readCdpJson,
} from "../../../src/browser/CdpEndpoint.js";
const servers: Server[] = [];
afterEach(async () => {
await Promise.all(
servers.splice(0).map(
(server) =>
new Promise<void>((resolve, reject) => {
server.closeAllConnections();
server.close((error) =>
error === undefined ? resolve() : reject(error),
);
}),
),
);
});
describe("CDP discovery response UTF-8", () => {
it.each(["title", "url"] as const)(
"rejects malformed UTF-8 in a discovered target %s and permits the next discovery",
async (field) => {
const target = {
id: "selected",
type: "page",
title: "Selected target",
url: "https://example.test/",
[field]:
field === "url" ? "https://example.test/target-slot" : "target-slot",
};
const valid = Buffer.from(JSON.stringify([target]));
const malformed = Buffer.from(valid);
malformed[malformed.indexOf("target-slot")] =
field === "title" ? 0xff : 0xc3;
let sendMalformed = true;
let responseClosed = false;
const server = await startServer((request, response) => {
if (request.url === "/json/version") {
response.end(
JSON.stringify({
Browser: "Fixture Chromium",
"Protocol-Version": "1.3",
"User-Agent": "Fixture",
"V8-Version": "1",
"WebKit-Version": "1",
webSocketDebuggerUrl: `ws://${request.headers.host}/devtools/browser/fixture`,
}),
);
} else if (request.url === "/json/list") {
if (sendMalformed)
response.once("close", () => {
responseClosed = true;
});
response.end(sendMalformed ? malformed : valid);
} else {
response.writeHead(404).end();
}
});
await expect(
discoverCdpEndpoint(server.url.href, "list_browser_targets"),
).rejects.toMatchObject({
_tag: "BrowserObservationError",
operation: "list_browser_targets",
reason: "invalid_endpoint_response",
});
await expect.poll(() => responseClosed, { timeout: 2_000 }).toBe(true);
sendMalformed = false;
await expect(
discoverCdpEndpoint(server.url.href, "list_browser_targets"),
).resolves.toMatchObject({ targets: [target] });
},
);
it("preserves valid replacement characters and multibyte text across HTTP chunks", async () => {
const value = {
title: "Literal \uFFFD and 🛰️",
url: "https://example.test/空",
};
const body = Buffer.from(JSON.stringify(value));
const split = body.indexOf(Buffer.from("🛰")) + 1;
const server = await startServer((_request, response) => {
response.write(body.subarray(0, split));
setImmediate(() => response.end(body.subarray(split)));
});
await expect(
readCdpJson(server.url, "list_browser_targets"),
).resolves.toEqual(value);
});
it("keeps rejecting a leading UTF-8 BOM instead of normalizing it away", async () => {
const server = await startServer((_request, response) => {
response.end(
Buffer.concat([Buffer.from([0xef, 0xbb, 0xbf]), Buffer.from("{}")]),
);
});
await expect(
readCdpJson(server.url, "list_browser_targets"),
).rejects.toMatchObject({ reason: "invalid_endpoint_response" });
});
});
describe("CDP discovery response bounds", () => {
it("accepts a complete JSON response exactly at the byte limit", async () => {
const body = JSON.stringify("x".repeat(14));
expect(Buffer.byteLength(body)).toBe(16);
const server = await startServer((_request, response) =>
response.end(body),
);
await expect(
readCdpJson(server.url, "list_browser_targets", undefined, {
bytes: 16,
timeoutMs: 1_000,
}),
).resolves.toBe("x".repeat(14));
});
it("rejects and closes an oversized chunked response without parsing a prefix", async () => {
let responseClosed = false;
const server = await startServer((_request, response) => {
response.once("close", () => {
responseClosed = true;
});
response.write(JSON.stringify("x".repeat(15)));
});
await expect(
readCdpJson(server.url, "list_browser_targets", undefined, {
bytes: 16,
timeoutMs: 1_000,
}),
).rejects.toMatchObject({
_tag: "BrowserObservationError",
reason: "payload_limit",
});
await expect.poll(() => responseClosed, { timeout: 2_000 }).toBe(true);
});
it("ends a response that exceeds the total elapsed deadline", async () => {
let responseClosed = false;
let requestStarted: (() => void) | undefined;
const started = new Promise<void>((resolve) => {
requestStarted = resolve;
});
const server = await startServer((_request, response) => {
response.once("close", () => {
responseClosed = true;
});
requestStarted?.();
});
const controller = new AbortController();
const pending = readCdpJson(
server.url,
"list_browser_targets",
controller.signal,
{
bytes: 16,
timeoutMs: 50,
},
);
await withVerifierCleanup(async () => {
await awaitAcknowledgement(started, pending, "CDP request");
await expect(pending).rejects.toMatchObject({
_tag: "BrowserObservationError",
reason: "timeout",
});
await expect.poll(() => responseClosed, { timeout: 2_000 }).toBe(true);
}, [
() => controller.abort(),
async () => {
await pending.catch(() => undefined);
},
]);
});
it("preserves caller cancellation and closes the response", async () => {
let responseClosed = false;
let requestStarted: (() => void) | undefined;
const started = new Promise<void>((resolve) => {
requestStarted = resolve;
});
const server = await startServer((_request, response) => {
response.once("close", () => {
responseClosed = true;
});
requestStarted?.();
});
const controller = new AbortController();
const pending = readCdpJson(
server.url,
"list_browser_targets",
controller.signal,
{
bytes: 16,
timeoutMs: 1_000,
},
);
await withVerifierCleanup(async () => {
await awaitAcknowledgement(started, pending, "CDP request");
controller.abort();
await expect(pending).rejects.toMatchObject({
_tag: "AnalysisCancelledError",
operation: "list_browser_targets",
});
await expect.poll(() => responseClosed, { timeout: 2_000 }).toBe(true);
}, [
() => controller.abort(),
async () => {
await pending.catch(() => undefined);
},
]);
});
it("destroys an unsuccessful HTTP response", async () => {
let responseClosed = false;
const server = await startServer((_request, response) => {
response.once("close", () => {
responseClosed = true;
});
response.writeHead(503).flushHeaders();
});
await expect(
readCdpJson(server.url, "list_browser_targets", undefined, {
bytes: 16,
timeoutMs: 1_000,
}),
).rejects.toMatchObject({
_tag: "BrowserObservationError",
reason: "invalid_endpoint_response",
});
await expect.poll(() => responseClosed, { timeout: 2_000 }).toBe(true);
});
});
const startServer = async (
handler: RequestListener,
): Promise<{ readonly url: URL }> => {
const server = createServer(handler);
servers.push(server);
await new Promise<void>((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", resolve);
});
const address = server.address();
if (address === null || typeof address === "string")
throw new TypeError("Expected a TCP listener address");
return { url: new URL(`http://127.0.0.1:${String(address.port)}`) };
};