import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { appendFeedbackMemory, feedbackMemoryPath, feedbackMemoryStats, writeFeedbackReceipt } from "./feedback-memory.js"; const roots: string[] = []; afterEach(() => { for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true }); }); function tmpRoot(): string { const root = fs.mkdtempSync(path.join(os.tmpdir(), "fable-feedback-memory-")); roots.push(root); return root; } describe("feedback memory", () => { it("appends feedback to the right markdown file", () => { const root = tmpRoot(); const entry = appendFeedbackMemory(root, "approved", "ship this\nnow", new Date("2026-06-18T00:00:00.000Z")); expect(entry.type).toBe("approved"); expect(fs.readFileSync(feedbackMemoryPath(root, "approved"), "utf-8")).toContain("ship this now"); }); it("counts entries per memory file", () => { const root = tmpRoot(); appendFeedbackMemory(root, "approved", "ok"); appendFeedbackMemory(root, "rejected", "bad"); appendFeedbackMemory(root, "rejected", "worse"); expect(feedbackMemoryStats(root)).toEqual({ approved: 1, rejected: 2, performance: 0 }); }); it("writes a feedback receipt beside feedback memory", () => { const root = tmpRoot(); const receipt = writeFeedbackReceipt(root, "performance", "users want receipts", "test", new Date("2026-06-18T00:00:00.000Z")); expect(receipt).toMatchObject({ schema: "fable.feedback.receipt.v1", source: "test", type: "performance", signal: "performance" }); expect(fs.existsSync(path.join(root, ".fable", "feedback", "2026-06-18T00-00-00-000Z-performance.json"))).toBe(true); expect(fs.readFileSync(receipt.memoryPath, "utf-8")).toContain("users want receipts"); }); });