Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/notes/__tests__/debug.test.ts

Raw
import { readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { afterEach, expect, it, vi } from "vitest";
import {
	createPiSession,
	type PiSession,
} from "../../../test/fixture/create-pi-session.ts";

const extensionPath = fileURLToPath(new URL("../index.ts", import.meta.url));

let session: PiSession | undefined;
afterEach(async () => {
	await session?.dispose();
	session = undefined;
	vi.restoreAllMocks();
});

it("records a terminal transition after session shutdown", async () => {
	session = await createPiSession({
		extensionPaths: [extensionPath],
		env: { PI_NOTES_DEBUG: "1" },
	});
	const runner = session.session.extensionRunner;
	await runner.emit({ type: "session_start", reason: "startup" });
	const command = session.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",
		});
		manager.newSession();
		await runner.emit({
			type: "session_start",
			reason: "new",
		});
		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(
		session.env["XDG_STATE_HOME"] as string,
		"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),
		}),
	]);
});