Spaces:
Runtime error
Runtime error
| /** | |
| * Persistence Tests (Section 2 of TESTS.md) | |
| * | |
| * Tests debouncedSave, local file read/write, and flushAll. | |
| * Uses a temporary directory to avoid polluting real data. | |
| */ | |
| import { describe, it, expect, beforeEach, afterEach } from "vitest"; | |
| import { mkdirSync, existsSync, readFileSync, writeFileSync } from "fs"; | |
| import { mkdtempSync, rmSync } from "fs"; | |
| import { tmpdir } from "os"; | |
| import { join } from "path"; | |
| import * as Y from "yjs"; | |
| import { setDataDir, getDataDir, docPath } from "../src/utils.js"; | |
| import { debouncedSave, resetSaveTimers } from "../src/create-app.js"; | |
| let tmpDir: string; | |
| beforeEach(() => { | |
| tmpDir = mkdtempSync(join(tmpdir(), "collab-test-")); | |
| mkdirSync(tmpDir, { recursive: true }); | |
| setDataDir(tmpDir); | |
| }); | |
| afterEach(() => { | |
| resetSaveTimers(); | |
| setDataDir(undefined); | |
| try { | |
| rmSync(tmpDir, { recursive: true, force: true }); | |
| } catch {} | |
| }); | |
| function makeDoc(text: string): Y.Doc { | |
| const ydoc = new Y.Doc(); | |
| const fragment = ydoc.getXmlFragment("default"); | |
| const el = new Y.XmlElement("paragraph"); | |
| el.insert(0, [new Y.XmlText(text)]); | |
| fragment.insert(0, [el]); | |
| return ydoc; | |
| } | |
| describe("2.1 Local persistence", () => { | |
| it("2.1.1 debouncedSave writes .yjs file after debounce", async () => { | |
| const ydoc = makeDoc("Hello world"); | |
| debouncedSave("test-doc", ydoc); | |
| // File should NOT exist immediately (debounce is 2s) | |
| const p = docPath("test-doc"); | |
| expect(existsSync(p)).toBe(false); | |
| // Wait for debounce to fire | |
| await new Promise((r) => setTimeout(r, 2500)); | |
| expect(existsSync(p)).toBe(true); | |
| const buf = readFileSync(p); | |
| expect(buf.length).toBeGreaterThan(0); | |
| // Verify it's valid Yjs binary by applying it to a new doc | |
| const restored = new Y.Doc(); | |
| Y.applyUpdate(restored, new Uint8Array(buf)); | |
| const fragment = restored.getXmlFragment("default"); | |
| expect(fragment.length).toBeGreaterThan(0); | |
| }); | |
| it("2.1.2 docPath reads from configured DATA_DIR", () => { | |
| // Write a .yjs file manually, then verify docPath points to it | |
| const ydoc = makeDoc("Existing content"); | |
| const state = Y.encodeStateAsUpdate(ydoc); | |
| const p = docPath("existing"); | |
| writeFileSync(p, Buffer.from(state)); | |
| // Read it back and verify content | |
| expect(existsSync(p)).toBe(true); | |
| const buf = readFileSync(p); | |
| const restored = new Y.Doc(); | |
| Y.applyUpdate(restored, new Uint8Array(buf)); | |
| const fragment = restored.getXmlFragment("default"); | |
| expect(fragment.length).toBeGreaterThan(0); | |
| }); | |
| it("2.1.3 debounce collapses rapid saves into one write", async () => { | |
| const ydoc = makeDoc("Version 1"); | |
| // Trigger 3 rapid saves | |
| debouncedSave("rapid", ydoc); | |
| const ydoc2 = makeDoc("Version 2"); | |
| debouncedSave("rapid", ydoc2); | |
| const ydoc3 = makeDoc("Version 3"); | |
| debouncedSave("rapid", ydoc3); | |
| const p = docPath("rapid"); | |
| // Nothing written yet | |
| expect(existsSync(p)).toBe(false); | |
| // Wait for the single debounced write | |
| await new Promise((r) => setTimeout(r, 2500)); | |
| expect(existsSync(p)).toBe(true); | |
| // The file should contain the last version (ydoc3) | |
| const buf = readFileSync(p); | |
| const restored = new Y.Doc(); | |
| Y.applyUpdate(restored, new Uint8Array(buf)); | |
| const fragment = restored.getXmlFragment("default"); | |
| expect(fragment.length).toBeGreaterThan(0); | |
| }); | |
| }); | |