/** * Tests for the per-session .drawio auto-save (src/persistence.ts). */ import { existsSync, mkdtempSync, readdirSync, readFileSync, utimesSync, writeFileSync, } from "node:fs" import { tmpdir } from "node:os" import { join } from "node:path" import { afterEach, describe, expect, it } from "vitest" import { Autosaver, defaultDataDir } from "../src/persistence.js" const DIAGRAM = `` const BLANK = `` const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)) const tempDir = () => mkdtempSync(join(tmpdir(), "mcp-autosave-")) describe("Autosaver", () => { it("writes the latest XML once changes settle", async () => { const dir = tempDir() const saver = new Autosaver(dir, 30) saver.schedule("mcp-a", DIAGRAM.replace('id="a"', 'id="first"')) saver.schedule("mcp-a", DIAGRAM) const path = saver.pathFor("mcp-a") as string expect(existsSync(path)).toBe(false) await sleep(80) expect(readFileSync(path, "utf-8")).toBe(DIAGRAM) expect(readdirSync(dir)).toEqual(["mcp-a.drawio"]) }) it("skips a blank page that was never saved, but saves clearing a diagram", async () => { const dir = tempDir() const saver = new Autosaver(dir, 10) saver.schedule("mcp-blank", BLANK) await sleep(40) expect(existsSync(saver.pathFor("mcp-blank") as string)).toBe(false) saver.schedule("mcp-b", DIAGRAM) saver.flush() saver.schedule("mcp-b", BLANK) saver.flush() expect(readFileSync(saver.pathFor("mcp-b") as string, "utf-8")).toBe( BLANK, ) }) it("keeps only the newest files", () => { const dir = tempDir() const saver = new Autosaver(dir, 10, 2) for (const [i, id] of ["mcp-old", "mcp-mid"].entries()) { writeFileSync(join(dir, `${id}.drawio`), DIAGRAM) utimesSync(join(dir, `${id}.drawio`), 1000 + i, 1000 + i) } saver.schedule("mcp-new", DIAGRAM) saver.flush() expect(readdirSync(dir).sort()).toEqual([ "mcp-mid.drawio", "mcp-new.drawio", ]) }) it("does nothing when saving is off", () => { const saver = new Autosaver(null) expect(saver.pathFor("mcp-x")).toBeNull() saver.schedule("mcp-x", DIAGRAM) saver.flush() }) }) describe("defaultDataDir", () => { const original = process.env.DRAWIO_DATA_DIR afterEach(() => { if (original === undefined) delete process.env.DRAWIO_DATA_DIR else process.env.DRAWIO_DATA_DIR = original }) it("reads DRAWIO_DATA_DIR, where off disables saving", () => { process.env.DRAWIO_DATA_DIR = "off" expect(defaultDataDir()).toBeNull() process.env.DRAWIO_DATA_DIR = "/tmp/x" expect(defaultDataDir()).toBe("/tmp/x") delete process.env.DRAWIO_DATA_DIR expect(defaultDataDir()).toMatch(/\.next-ai-drawio$/) }) })