"use strict"; const { test, describe, beforeEach, afterEach } = require("node:test"); const assert = require("node:assert/strict"); const fs = require("fs"); const path = require("path"); const os = require("os"); const { HistoryStore } = require("../src/core/history/history-store"); const { HistoryService, migrateLegacyHistory } = require("../src/core/history/history-service"); describe("history-store", () => { let tempDir; beforeEach(() => { tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "kadr-history-test-")); }); afterEach(() => { try { fs.rmSync(tempDir, { recursive: true, force: true }); } catch { // ignore } }); test("appends versions atomically and handles pruning", async () => { const store = new HistoryStore({ dir: tempDir, limit: 3 }); const service = new HistoryService({ store }); const dummyImg = Buffer.from("dummy image content"); const v1 = await service.createVersion({ kind: "original", label: "V1", buffer: dummyImg, ext: "jpg", width: 100, height: 100, }); const v2 = await service.createVersion({ kind: "edit", label: "V2", parentId: v1.id, buffer: dummyImg, ext: "jpg", width: 100, height: 100, }); const v3 = await service.createVersion({ kind: "edit", label: "V3", parentId: v2.id, buffer: dummyImg, ext: "jpg", width: 100, height: 100, }); assert.equal(store.load().length, 3); assert.ok(fs.existsSync(store.filePath(v1))); assert.ok(fs.existsSync(store.filePath(v2))); assert.ok(fs.existsSync(store.filePath(v3))); // Appending 4th version should prune v1 const v4 = await service.createVersion({ kind: "upscale", label: "V4", parentId: v3.id, buffer: dummyImg, ext: "png", width: 400, height: 400, }); const history = store.load(); assert.equal(history.length, 3); assert.deepEqual(history.map((v) => v.id), [v2.id, v3.id, v4.id]); // v1 file should be deleted on disk assert.equal(fs.existsSync(store.filePath(v1)), false); assert.ok(fs.existsSync(store.filePath(v4))); // Check no leftover .tmp files const tmpFiles = fs.readdirSync(tempDir).filter((f) => f.endsWith(".tmp")); assert.equal(tmpFiles.length, 0); }); test("cascade deletion removes version and all its children", async () => { const store = new HistoryStore({ dir: tempDir, limit: 10 }); const service = new HistoryService({ store }); const dummyImg = Buffer.from("dummy image content"); const root = await service.createVersion({ kind: "original", label: "Root", buffer: dummyImg }); const child1 = await service.createVersion({ kind: "edit", label: "Child 1", parentId: root.id, buffer: dummyImg }); const child2 = await service.createVersion({ kind: "upscale", label: "Child 2", parentId: child1.id, buffer: dummyImg }); const independent = await service.createVersion({ kind: "original", label: "Independent", buffer: dummyImg }); assert.equal(store.load().length, 4); // Delete child1 -> should delete child1 and child2, but keep root and independent const result = await service.removeCascade(child1.id); assert.equal(result.removed.length, 2); assert.equal(result.remaining.length, 2); assert.deepEqual(result.remaining.map((v) => v.id), [root.id, independent.id]); // Verify deleted files are gone from disk assert.ok(fs.existsSync(store.filePath(root))); assert.ok(fs.existsSync(store.filePath(independent))); assert.equal(fs.existsSync(store.filePath(child1)), false); assert.equal(fs.existsSync(store.filePath(child2)), false); }); test("migrateLegacyHistory copies files without deleting original", () => { const legacyDir = path.join(tempDir, "legacy"); const targetDir = path.join(tempDir, "target"); fs.mkdirSync(legacyDir, { recursive: true }); const mockManifest = [{ id: "v-legacy-1", kind: "original", label: "Legacy" }]; fs.writeFileSync(path.join(legacyDir, "history.json"), JSON.stringify(mockManifest)); fs.writeFileSync(path.join(legacyDir, "v-legacy-1.jpg"), "fake jpg content"); migrateLegacyHistory(legacyDir, targetDir); assert.ok(fs.existsSync(path.join(targetDir, "history.json"))); assert.ok(fs.existsSync(path.join(targetDir, "v-legacy-1.jpg"))); // Legacy files still exist assert.ok(fs.existsSync(path.join(legacyDir, "history.json"))); assert.ok(fs.existsSync(path.join(legacyDir, "v-legacy-1.jpg"))); }); });