import { describe, it, expect, beforeEach, afterEach } from "vitest"; import * as fs from "node:fs"; import * as path from "node:path"; import * as os from "node:os"; import { FlatLedger, setLedgerDir } from "./flat-ledger.js"; describe("flat ledger", () => { const testDir = path.join(os.tmpdir(), "fable-ledger-test"); beforeEach(() => { fs.mkdirSync(testDir, { recursive: true }); setLedgerDir(testDir); }); afterEach(() => { fs.rmSync(testDir, { recursive: true, force: true }); }); it("writes and reads entries", () => { const ledger = new FlatLedger("test-session"); const entry = ledger.append({ timestamp: new Date().toISOString(), agent: "test-agent", action: "build", target: "src/main.ts", result: "ok", durationMs: 100, }); expect(entry.step).toBe(1); expect(entry.result).toBe("ok"); const state = ledger.readState(); expect(state.length).toBe(1); expect(state[0].step).toBe(1); }); it("returns last step", () => { const ledger = new FlatLedger("test-session"); expect(ledger.lastStep()).toBeNull(); ledger.append({ timestamp: new Date().toISOString(), agent: "a", action: "build", target: "x", result: "ok", durationMs: 10 }); ledger.append({ timestamp: new Date().toISOString(), agent: "a", action: "test", target: "x", result: "fail", durationMs: 20 }); const last = ledger.lastStep(); expect(last?.step).toBe(2); expect(last?.result).toBe("fail"); expect(ledger.lastActionResult()).toBe("fail"); }); it("produces summary", () => { const ledger = new FlatLedger("test-session"); ledger.append({ timestamp: "t1", agent: "a", action: "b", target: "t", result: "ok", durationMs: 1 }); ledger.append({ timestamp: "t2", agent: "a", action: "b", target: "t", result: "ok", durationMs: 1 }); ledger.append({ timestamp: "t3", agent: "a", action: "b", target: "t", result: "fail", durationMs: 1 }); ledger.append({ timestamp: "t4", agent: "a", action: "b", target: "t", result: "skip", durationMs: 1 }); const summary = ledger.summary(); expect(summary).toContain("4 steps"); expect(summary).toContain("2 ok"); expect(summary).toContain("1 fail"); expect(summary).toContain("1 skip"); }); it("clear resets state", () => { const ledger = new FlatLedger("test-session"); ledger.append({ timestamp: "t1", agent: "a", action: "b", target: "t", result: "ok", durationMs: 1 }); expect(ledger.readState().length).toBe(1); ledger.clear(); expect(ledger.readState().length).toBe(0); }); it("sessionNameFromTask generates safe names", () => { const name = FlatLedger.sessionNameFromTask("Fix: broken auth on /api/v2/users"); expect(name).toBe("fix-broken-auth-on-api-v2-users"); expect(name.length).toBeLessThanOrEqual(40); }); });