Files
oh-my-pi/packages/coding-agent/test/session-storage.test.ts
T
slact 2825c894f5 feat: session deletion (#448)
* feat: session deletion

* better session deletion cleanup error handling

* fix: complete deletion including artifacts
fix: native confirmation for session deletion

* even better native configmation for session picker

* fix(coding-agent): detach active session before deletion

* fix(coding-agent): keep failed session deletes visible

* fix(coding-agent): await session delete command

* fix(coding-agent): surface session artifact cleanup failures

---------

Co-authored-by: can1357 <me@can.ac>
Co-authored-by: Can Bölük <can1357@users.noreply.github.com>
2026-03-17 14:51:21 +01:00

65 lines
2.6 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, mock, vi } from "bun:test";
import * as fs from "node:fs";
import * as fsp from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
mock.module("@oh-my-pi/pi-utils", () => ({
isEnoent: (err: unknown) => typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT",
toError: (err: unknown) => (err instanceof Error ? err : new Error(String(err))),
}));
describe("FileSessionStorage.deleteSessionWithArtifacts", () => {
let tempDir: string;
let storage: { deleteSessionWithArtifacts(sessionPath: string): Promise<void> };
beforeEach(async () => {
tempDir = await fsp.mkdtemp(path.join(os.tmpdir(), "omp-session-storage-"));
const { FileSessionStorage } = await import("../src/session/session-storage");
storage = new FileSessionStorage();
});
afterEach(async () => {
vi.restoreAllMocks();
await fsp.rm(tempDir, { recursive: true, force: true });
});
async function createSessionFile(name: string): Promise<string> {
const sessionPath = path.join(tempDir, `${name}.jsonl`);
await Bun.write(
sessionPath,
`${JSON.stringify({ type: "session", id: "session-id", timestamp: "2025-01-01T00:00:00Z", cwd: tempDir })}\n`,
);
return sessionPath;
}
it("succeeds when the artifact directory is already absent", async () => {
const sessionPath = await createSessionFile("missing-artifacts");
const artifactsDir = sessionPath.slice(0, -6);
expect(fs.existsSync(sessionPath)).toBe(true);
expect(fs.existsSync(artifactsDir)).toBe(false);
await expect(storage.deleteSessionWithArtifacts(sessionPath)).resolves.toBeUndefined();
expect(fs.existsSync(sessionPath)).toBe(false);
expect(fs.existsSync(artifactsDir)).toBe(false);
});
it("throws when artifact cleanup fails after the session file is deleted", async () => {
const sessionPath = await createSessionFile("cleanup-failure");
const artifactsDir = sessionPath.slice(0, -6);
await fsp.mkdir(artifactsDir, { recursive: true });
await Bun.write(path.join(artifactsDir, "artifact.txt"), "artifact payload");
const rmError = new Error("permission denied");
const rmSpy = vi.spyOn(fsp, "rm").mockRejectedValueOnce(rmError);
await expect(storage.deleteSessionWithArtifacts(sessionPath)).rejects.toThrow(
`Session file deleted but failed to remove artifacts directory ${artifactsDir}: permission denied`,
);
expect(rmSpy).toHaveBeenCalledWith(artifactsDir, { recursive: true, force: true });
expect(fs.existsSync(sessionPath)).toBe(false);
expect(fs.existsSync(artifactsDir)).toBe(true);
});
});