repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/chrome-cdp/__tests__/debug.test.ts
Rawimport { mkdtemp, readdir, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
createTestSession,
sandboxEnv,
type TestSession,
withProcessEnv,
} from "../../../test/harness";
let root = "";
let session: TestSession | undefined;
async function tracePath(): Promise<string> {
const directory = join(root, ".test-state", "pi-ext", "debug", "chrome-cdp");
const entries = await readdir(directory);
expect(entries).toHaveLength(1);
return join(directory, entries[0]);
}
async function records(): Promise<Record<string, unknown>[]> {
return (await readFile(await tracePath(), "utf8"))
.trim()
.split("\n")
.map((line) => JSON.parse(line));
}
async function sandbox(debug: "0" | "1"): Promise<NodeJS.ProcessEnv> {
root = await mkdtemp(join(tmpdir(), "pi-ext-chrome-cdp-debug-"));
return sandboxEnv(root, { env: { PI_CHROME_CDP_DEBUG: debug } });
}
afterEach(async () => {
vi.doUnmock("node:fs");
session?.dispose();
session = undefined;
if (root) await rm(root, { recursive: true, force: true });
root = "";
});
describe("chrome-cdp debug", () => {
it("writes session lifecycle events to sandbox user state", async () => {
const env = await sandbox("1");
vi.resetModules();
const factory = await withProcessEnv(
env,
async () => (await import("../index.ts")).default,
);
session = await createTestSession({
cwd: root,
extensionFactories: [factory],
env: { PI_CHROME_CDP_DEBUG: "1" },
});
await session.session.extensionRunner.emit({
type: "session_start",
reason: "startup",
});
await session.session.extensionRunner.emit({
type: "session_shutdown",
reason: "quit",
});
expect((await records()).map((record) => record.event)).toEqual(
expect.arrayContaining(["session.start", "session.shutdown"]),
);
});
it("pairs monotonic operation spans", async () => {
const env = await sandbox("1");
vi.resetModules();
await withProcessEnv(env, async () => {
const { closeDebug, span } = await import("../src/pi-ext-debug.ts");
const end = span?.("operation", { index: 2, kind: "cdp" });
await new Promise((resolve) => setTimeout(resolve, 1));
end?.("finish", { status: "ok" });
closeDebug();
});
const [start, finish] = await records();
expect(start).toMatchObject({
event: "operation.start",
index: 2,
kind: "cdp",
});
expect(finish).toMatchObject({
event: "operation.finish",
spanId: start.spanId,
status: "ok",
});
expect(finish.durationMs).toEqual(expect.any(Number));
});
it("does no debug work when disabled", async () => {
const env = await sandbox("0");
vi.resetModules();
await withProcessEnv(env, async () => {
const { dbg } = await import("../src/pi-ext-debug.ts");
let built = false;
dbg?.(
"test.disabled",
(() => {
built = true;
return {};
})(),
);
expect(dbg).toBeUndefined();
expect(built).toBe(false);
});
await expect(tracePath()).rejects.toMatchObject({ code: "ENOENT" });
});
it("absorbs writer failures", async () => {
const env = await sandbox("1");
vi.resetModules();
const actual = await vi.importActual<typeof import("node:fs")>("node:fs");
const writeSync = vi.fn(() => {
throw new Error("write failed");
});
vi.doMock("node:fs", () => ({ ...actual, writeSync }));
await withProcessEnv(env, async () => {
const { dbg } = await import("../src/pi-ext-debug.ts");
expect(() => dbg?.("test.failure")).not.toThrow();
expect(() => dbg?.("test.failure")).not.toThrow();
});
expect(writeSync).toHaveBeenCalledOnce();
});
});