Files
excalidraw-box/src/server/db.test.ts
2026-06-01 21:34:05 +00:00

250 lines
6.6 KiB
TypeScript

import { afterEach, describe, expect, test } from "bun:test";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createDrawingStore } from "./db";
const cleanup: string[] = [];
afterEach(() => {
while (cleanup.length > 0) {
const dir = cleanup.pop();
if (dir) {
rmSync(dir, { recursive: true, force: true });
}
}
});
function createTempStore() {
const dir = mkdtempSync(join(tmpdir(), "excali-"));
cleanup.push(dir);
return createDrawingStore(join(dir, "test.sqlite"));
}
describe("drawing store", () => {
test("creates, updates, lists, and deletes drawings", () => {
const store = createTempStore();
const created = store.createDrawing();
expect(created.title).toBe("Untitled");
expect(created.revision).toBe(0);
expect(created.scene.appState.theme).toBe("dark");
expect(store.listDrawings()).toHaveLength(1);
expect(store.listDrawings()[0]?.revision).toBe(0);
const updated = store.updateDrawing(created.id, {
title: "Flow",
scene: {
elements: [{ id: "one" }],
appState: { gridSize: 20 },
files: {},
},
});
expect(updated.ok).toBe(true);
expect(updated.ok ? updated.drawing.title : null).toBe("Flow");
expect(updated.ok ? updated.drawing.revision : null).toBe(1);
expect(updated.ok ? updated.drawing.scene.elements : []).toHaveLength(1);
const removed = store.deleteDrawing(created.id);
expect(removed.deleted).toBe(true);
expect(store.listDrawings()).toHaveLength(1);
expect(removed.next?.id).not.toBe(created.id);
});
test("rejects stale expected revisions without overwriting the scene", () => {
const store = createTempStore();
const created = store.createDrawing();
const first = store.updateDrawing(created.id, {
expectedRevision: 0,
scene: {
elements: [{ id: "first" }],
appState: {},
files: {},
},
});
expect(first.ok).toBe(true);
const stale = store.updateDrawing(created.id, {
expectedRevision: 0,
scene: {
elements: [{ id: "stale" }],
appState: {},
files: {},
},
});
expect(stale.ok).toBe(false);
expect(stale.ok ? null : stale.reason).toBe("conflict");
expect(store.getDrawing(created.id)?.revision).toBe(1);
expect(store.getDrawing(created.id)?.scene.elements).toEqual([
{ id: "first" },
]);
});
test("keeps the revision unchanged when the scene is already stored", () => {
const store = createTempStore();
const created = store.createDrawing();
const scene = {
elements: [{ id: "same" }],
appState: {},
files: {},
};
const first = store.updateDrawing(created.id, { scene });
expect(first.ok ? first.drawing.revision : null).toBe(1);
const repeated = store.updateDrawing(created.id, { scene });
expect(repeated.ok).toBe(true);
expect(repeated.ok ? repeated.drawing.revision : null).toBe(1);
expect(store.getDrawing(created.id)?.revision).toBe(1);
});
test("accepts stale expected revisions when the scene is already stored", () => {
const store = createTempStore();
const created = store.createDrawing();
const scene = {
elements: [{ id: "same" }],
appState: {},
files: {},
};
const first = store.updateDrawing(created.id, {
expectedRevision: 0,
scene,
});
expect(first.ok ? first.drawing.revision : null).toBe(1);
const repeated = store.updateDrawing(created.id, {
expectedRevision: 0,
scene,
});
expect(repeated.ok).toBe(true);
expect(repeated.ok ? repeated.drawing.revision : null).toBe(1);
expect(store.getDrawing(created.id)?.scene.elements).toEqual([
{ id: "same" },
]);
});
test("increments the revision when the title changes but the scene does not", () => {
const store = createTempStore();
const created = store.createDrawing();
const scene = {
elements: [{ id: "same" }],
appState: {},
files: {},
};
const first = store.updateDrawing(created.id, {
expectedRevision: 0,
scene,
});
expect(first.ok ? first.drawing.revision : null).toBe(1);
const renamed = store.updateDrawing(created.id, {
expectedRevision: 1,
title: "Renamed",
scene,
});
expect(renamed.ok).toBe(true);
expect(renamed.ok ? renamed.drawing.title : null).toBe("Renamed");
expect(renamed.ok ? renamed.drawing.revision : null).toBe(2);
});
test("persists publication fields and exposes public drawings by slug", () => {
const store = createTempStore();
const created = store.createDrawing("Flow");
store.updateDrawing(created.id, {
scene: {
elements: [{ id: "one" }],
appState: { theme: "dark" },
files: {},
},
});
const published = store.publishDrawing(created.id, { slug: "flow" });
expect(published.ok).toBe(true);
expect(store.getDrawingPublication(created.id)).toEqual({
enabled: true,
slug: "flow",
});
expect(store.getPublicDrawing("flow")).toEqual({
title: "Flow",
elements: [{ id: "one" }],
appState: { theme: "dark" },
files: {},
});
});
test("enforces unique active publication slugs", () => {
const store = createTempStore();
const first = store.createDrawing("First");
const second = store.createDrawing("Second");
expect(
store.publishDrawing(first.id, {
slug: "shared",
}),
).toEqual({
ok: true,
publication: {
enabled: true,
slug: "shared",
},
});
expect(
store.publishDrawing(second.id, {
slug: "shared",
}),
).toEqual({
ok: false,
reason: "slug_conflict",
});
expect(store.disableDrawingPublication(first.id)).toEqual({
ok: true,
publication: { enabled: false },
});
expect(
store.publishDrawing(second.id, {
slug: "shared",
}),
).toEqual({
ok: true,
publication: {
enabled: true,
slug: "shared",
},
});
});
test("clears public fields on disable", () => {
const store = createTempStore();
const created = store.createDrawing();
expect(
store.publishDrawing(created.id, {
slug: "note",
}),
).toEqual({
ok: true,
publication: {
enabled: true,
slug: "note",
},
});
expect(store.disableDrawingPublication(created.id)).toEqual({
ok: true,
publication: { enabled: false },
});
expect(store.getDrawingPublication(created.id)).toEqual({ enabled: false });
expect(store.getPublicDrawing("note")).toBeNull();
});
});