repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/bak/__tests__/debug.test.ts
Rawimport {
mkdirSync,
mkdtempSync,
readdirSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
createTestSession,
sandboxEnv,
type TestSession,
withProcessEnv,
} from "../../../test/harness";
describe("bak debug", () => {
let root: string | undefined;
let session: TestSession | undefined;
afterEach(() => {
session?.dispose();
if (root) rmSync(root, { recursive: true, force: true });
session = undefined;
root = undefined;
});
it("writes its lifecycle JSONL through the standalone factory", async () => {
root = mkdtempSync(path.join(tmpdir(), "pi-ext-bak-debug-"));
const env = sandboxEnv(root, { env: { PI_BAK_DEBUG: "1" } });
vi.resetModules();
const factory = await withProcessEnv(
env,
async () => (await import("../index.ts")).default,
);
session = await createTestSession({
cwd: root,
extensionFactories: [factory],
env: { PI_BAK_DEBUG: "1" },
});
await session.session.extensionRunner.emit({
type: "session_start",
reason: "startup",
});
await session.session.extensionRunner.emit({
type: "session_shutdown",
reason: "quit",
});
if (!env.XDG_STATE_HOME) throw new Error("missing sandbox state home");
const directory = path.join(env.XDG_STATE_HOME, "pi-ext", "debug", "bak");
const [file] = readdirSync(directory);
const events = readFileSync(path.join(directory, file), "utf8")
.trim()
.split("\n")
.map((line) => JSON.parse(line).event);
expect(events).toEqual(
expect.arrayContaining(["session.start", "session.shutdown"]),
);
});
it.each([
["true", true],
["false", false],
])(
"attempts a shutdown backup only when bak.autoBackup is %s",
async (_name, autoBackup) => {
root = mkdtempSync(path.join(tmpdir(), "pi-ext-bak-debug-"));
const env = sandboxEnv(root, { env: { PI_BAK_DEBUG: "1" } });
const agentDir = path.join(env.HOME ?? "", ".pi", "agent");
mkdirSync(agentDir, { recursive: true });
writeFileSync(
path.join(agentDir, "settings.json"),
JSON.stringify({ bak: { autoBackup } }),
);
vi.resetModules();
const factory = await withProcessEnv(
env,
async () => (await import("../index.ts")).default,
);
session = await createTestSession({
cwd: root,
extensionFactories: [factory],
env: { PI_BAK_DEBUG: "1" },
});
const runner = session.session.extensionRunner;
await runner.emit({ type: "session_start", reason: "startup" });
await runner.emit({ type: "session_shutdown", reason: "quit" });
if (!env.XDG_STATE_HOME) throw new Error("missing sandbox state home");
const directory = path.join(env.XDG_STATE_HOME, "pi-ext", "debug", "bak");
const [file] = readdirSync(directory);
// Every automatic attempt records an outcome, even when it has nothing to upload.
const attempts = readFileSync(path.join(directory, file), "utf8")
.trim()
.split("\n")
.map((line) => JSON.parse(line))
.filter((entry) => entry.operation === "automatic-backup");
expect(attempts.length > 0).toBe(autoBackup);
},
);
it("records skipped, successful, and failed command outcomes", async () => {
root = mkdtempSync(path.join(tmpdir(), "pi-ext-bak-debug-"));
const env = sandboxEnv(root, { env: { PI_BAK_DEBUG: "1" } });
vi.resetModules();
session = await createTestSession({
cwd: root,
env: { PI_BAK_DEBUG: "1" },
});
const ctx = session.session.extensionRunner.createContext();
const { createBakRuntime } = await withProcessEnv(
env,
() => import("../implementation.ts"),
);
const { closeDebug } = await withProcessEnv(
env,
() => import("../src/debug.ts"),
);
const disabled = createBakRuntime({
missing: ["PI_BAK_S3_ENDPOINT"],
});
disabled.sessionStart(ctx);
await disabled.command.handler("backup", ctx);
const configured = createBakRuntime({
missing: [],
config: {
endpoint: "https://example.invalid",
bucket: "test",
region: "auto",
accessKeyId: "id",
secretAccessKey: "secret",
},
});
configured.sessionStart(ctx);
await configured.command.handler("status", ctx);
await configured.command.handler("init", ctx);
closeDebug();
if (!env.XDG_STATE_HOME) throw new Error("missing sandbox state home");
const directory = path.join(env.XDG_STATE_HOME, "pi-ext", "debug", "bak");
const [file] = readdirSync(directory);
const outcomes = readFileSync(path.join(directory, file), "utf8")
.trim()
.split("\n")
.map((line) => JSON.parse(line))
.filter((entry) => entry.event === "operation.finish");
expect(outcomes).toEqual(
expect.arrayContaining([
expect.objectContaining({
operation: "command",
subcommand: "backup",
outcome: "skipped",
}),
expect.objectContaining({
operation: "command",
subcommand: "status",
outcome: "success",
}),
expect.objectContaining({
operation: "command",
subcommand: "init",
outcome: "failed",
}),
]),
);
});
});