Files
oh-my-pi/packages/coding-agent/test/session-storage.test.ts
T
roboomp 3bcbf1515d fix(tui): reduced large transcript stalls
Tail appended transcript JSONL instead of rebuilding rendered history on every poll, collapse compacted history for live chat rendering, and replace synchronous session rewrites so tailers detect historical changes.

Fixes #3258
2026-06-22 12:01:34 +00:00

86 lines
3.2 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, 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";
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("@oh-my-pi/pi-coding-agent/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);
});
});
describe("FileSessionStorage.writeTextSync", () => {
let tempDir: string;
beforeEach(async () => {
tempDir = await fsp.mkdtemp(path.join(os.tmpdir(), "omp-session-storage-"));
});
afterEach(async () => {
await fsp.rm(tempDir, { recursive: true, force: true });
});
it("replaces the file identity so transcript tailers detect rewrites", async () => {
const { FileSessionStorage } = await import("@oh-my-pi/pi-coding-agent/session/session-storage");
const storage = new FileSessionStorage();
const sessionPath = path.join(tempDir, "session.jsonl");
storage.writeTextSync(sessionPath, "first\n");
const first = fs.statSync(sessionPath);
storage.writeTextSync(sessionPath, "second\n");
const second = fs.statSync(sessionPath);
expect(second.ino).not.toBe(first.ino);
expect(await Bun.file(sessionPath).text()).toBe("second\n");
});
});