clone

a browser-first diagram editor with live svg previews, local drafts and encrypted, accountless sharing

main
diagramzip / apps / editor / test / local-documents.test.js
local-documents.test.js 4.6K
1import assert from 'node:assert/strict'2import test from 'node:test'3import { createLocalDocuments, LEGACY_DRAFT_KEY, LOCAL_DOCUMENT_PREFIX, RECENTS_KEY, WRITE_CAPABILITY_PREFIX } from '../src/local-documents.js'45class MemoryStorage {6 constructor(limit = Infinity) { this.values = new Map(); this.limit = limit }7 get length() { return this.values.size }8 key(index) { return [...this.values.keys()][index] ?? null }9 getItem(key) { return this.values.get(key) ?? null }10 removeItem(key) { this.values.delete(key) }11 setItem(key, value) {12 if (this.failNext) { this.failNext = false; throw new DOMException('quota', 'QuotaExceededError') }13 const next = new Map(this.values); next.set(key, String(value))14 if ([...next.values()].reduce((n, item) => n + item.length, 0) > this.limit) throw new DOMException('quota', 'QuotaExceededError')15 this.values = next16 }17}18const cryptoStub = { getRandomValues(bytes) { bytes.set([0xde, 0xad, 0xbe, 0xef].slice(0, bytes.length)); return bytes } }19const doc = (title = 'Draft') => ({ state: { type: 'd2', source: 'x', meta: { title } }, detailsSource: '{}', fileSnapshot: null, drafts: { d2: 'x' }, dirty: false })2021test('creates, validates, upserts, touches, loads, and deletes local bodies', () => {22 const storage = new MemoryStorage(); const docs = createLocalDocuments({ storage, cryptoImpl: cryptoStub, now: () => 10 })23 const id = docs.saveLocalDocument(doc())24 assert.equal(id, 'l_deadbeef')25 assert.deepEqual(docs.loadLocalDocument(id), { state: doc().state, detailsSource: '{}', fileSnapshot: null, drafts: { d2: 'x' } })26 assert.equal(storage.getItem(`${LOCAL_DOCUMENT_PREFIX}${id}`).includes('dirty'), false)27 assert.equal(docs.touchRecent({ localId: id }, { title: 'Renamed' }), true)28 assert.equal(docs.loadRecents()[0].title, 'Renamed')29 assert.equal(docs.deleteLocalDocument(id), true)30 assert.equal(docs.loadLocalDocument(id), null)31 assert.deepEqual(docs.loadRecents(), [])32})3334test('keeps at most 50 recent entries, ordering by updated time, and aliases have no body', () => {35 const storage = new MemoryStorage(); let clock = 0; const docs = createLocalDocuments({ storage, cryptoImpl: cryptoStub, now: () => ++clock })36 for (let i = 0; i < 55; i++) docs.upsertRecent({ kind: 'alias', aliasId: `a${i}`, title: `A${i}`, type: 'd2', mode: 'open' })37 docs.upsertRecent({ kind: 'alias', aliasId: 'locked', title: 'secret', type: 'd2', mode: 'locked' })38 assert.equal(docs.loadRecents().length, 50)39 assert.equal(docs.loadRecents()[0].title, null)40 assert.equal(docs.loadRecents()[0].type, '')41 assert.equal(storage.getItem(`${LOCAL_DOCUMENT_PREFIX}locked`), null)42})4344test('ignores malformed index and bodies, and clearRecents preserves write credentials', () => {45 const storage = new MemoryStorage(); storage.setItem(RECENTS_KEY, '{bad'); storage.setItem(`${WRITE_CAPABILITY_PREFIX}a`, 'credential')46 storage.setItem(`${LOCAL_DOCUMENT_PREFIX}l_ffffffff`, JSON.stringify(doc('orphan')))47 const docs = createLocalDocuments({ storage, cryptoImpl: cryptoStub })48 assert.deepEqual(docs.loadRecents(), [])49 assert.equal(docs.loadLocalDocument('l_00000000'), null)50 docs.upsertRecent({ kind: 'alias', aliasId: 'a', title: 'A', mode: 'open', type: 'd2' })51 assert.equal(docs.clearRecents(), true)52 assert.equal(storage.getItem(`${WRITE_CAPABILITY_PREFIX}a`), 'credential')53 assert.equal(storage.getItem(`${LOCAL_DOCUMENT_PREFIX}l_ffffffff`), null)54})5556test('migrates the legacy draft once into a body and index entry', () => {57 const storage = new MemoryStorage(); storage.setItem(LEGACY_DRAFT_KEY, JSON.stringify(doc('Legacy')))58 const docs = createLocalDocuments({ storage, cryptoImpl: cryptoStub, now: () => 3 })59 assert.equal(docs.migrateLegacyDraft(), 'l_deadbeef')60 assert.equal(storage.getItem(LEGACY_DRAFT_KEY), null)61 assert.equal(docs.migrateLegacyDraft(), null)62})6364test('on quota evicts the oldest clean document, excluding current, and retries once', () => {65 const storage = new MemoryStorage(700); let clock = 0; const docs = createLocalDocuments({ storage, cryptoImpl: cryptoStub, now: () => ++clock })66 const first = docs.saveLocalDocument({ ...doc('one'), dirty: true })67 storage.setItem(`${WRITE_CAPABILITY_PREFIX}keep`, 'credential')68 const second = docs.saveLocalDocument({ ...doc('two'), localId: 'l_00000002' })69 assert.ok(first && second)70 storage.failNext = true71 const result = docs.saveLocalDocument({ ...doc('three'), localId: 'l_00000003' }, { currentLocalId: second })72 assert.equal(result, null)73 assert.ok(docs.loadLocalDocument(first))74 assert.equal(storage.getItem(`${WRITE_CAPABILITY_PREFIX}keep`), 'credential')75})