repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/notes/__tests__/debug.test.ts
Rawimport { mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, it, vi } from "vitest";
import { createTestSession, type TestSession } from "../../../test/harness";
it("records a terminal transition after session shutdown", async () => {
const root = mkdtempSync(join(tmpdir(), "pi-ext-notes-debug-"));
const original = {
debug: process.env.PI_NOTES_DEBUG,
state: process.env.XDG_STATE_HOME,
};
let session: TestSession | undefined;
try {
process.env.PI_NOTES_DEBUG = "1";
process.env.XDG_STATE_HOME = join(root, "state");
const { default: extension } = await import("../index.ts");
session = await createTestSession({
cwd: root,
env: {
PI_NOTES_DEBUG: "1",
XDG_STATE_HOME: join(root, "state"),
},
extensionFactories: [extension],
});
const runner = session.session.extensionRunner;
await runner.emit({ type: "session_start", reason: "startup" });
const command = runner.extensions[0].commands.get("note");
if (!command) throw new Error("missing note command");
await command.handler("add failed delivery", runner.createCommandContext());
const ctx = runner.createCommandContext() as any;
const manager = session.session.sessionManager as any;
ctx.newSession = vi.fn(async (options) => {
await runner.emit({
type: "session_shutdown",
reason: "new",
targetSessionFile: undefined,
});
manager.newSession();
await runner.emit({
type: "session_start",
reason: "new",
previousSessionFile: undefined,
});
await options.withSession({
...ctx,
sendUserMessage: async () => {
throw new Error("delivery failed");
},
});
return { cancelled: false };
});
await command.handler("new 1", ctx);
await command.handler("add later", runner.createCommandContext());
await command.handler("add", runner.createCommandContext());
const directory = join(root, "state", "pi-ext", "debug", "notes");
const files = readdirSync(directory);
expect(files).toHaveLength(2);
const events = files.flatMap((file) =>
readFileSync(join(directory, file), "utf8")
.trim()
.split("\n")
.map((line) => JSON.parse(line)),
);
const starts = events.filter(
(event) => event.event === "note.session_transition.start",
);
const terminals = events.filter(
(event) =>
event.event === "note.session_transition.finish" ||
event.event === "note.session_transition.error",
);
expect(starts).toHaveLength(1);
expect(events).toEqual(
expect.arrayContaining([
expect.objectContaining({
event: "note.change",
kind: "add",
outcome: "applied",
}),
expect.objectContaining({
event: "note.change",
kind: "add",
outcome: "invalid",
}),
]),
);
expect(terminals).toEqual([
expect.objectContaining({
extension: "notes",
event: "note.session_transition.finish",
kind: "new",
outcome: "delivery_failed",
spanId: starts[0]?.spanId,
durationMs: expect.any(Number),
}),
]);
} finally {
session?.dispose();
vi.restoreAllMocks();
if (original.debug === undefined) delete process.env.PI_NOTES_DEBUG;
else process.env.PI_NOTES_DEBUG = original.debug;
if (original.state === undefined) delete process.env.XDG_STATE_HOME;
else process.env.XDG_STATE_HOME = original.state;
rmSync(root, { recursive: true, force: true });
}
});