OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-note / test / unit / notebook-rename.test.js

76 lines · javascript ref:
import test from 'brittle'
import fs from 'bare-fs'
import path from 'bare-path'
import os from 'bare-os'
import Corestore from 'corestore'
import Hyperbee from 'hyperbee'
import { DriveManager } from '../../lib/drive-manager.js'
import { NoteManager } from '../../lib/note-manager.js'

function tmpSetup () {
  const dir = path.join(os.tmpdir(), 'hn-rename-' + Date.now() + '-' + Math.random().toString(36).slice(2))
  fs.mkdirSync(dir, { recursive: true })
  const store = new Corestore(dir)
  const registry = new Hyperbee(store.get({ name: 'registry' }), { keyEncoding: 'utf-8', valueEncoding: 'json' })
  const dm = new DriveManager(store, registry)
  return { store, registry, dm, dir }
}

function rmrf (dir) {
  try {
    for (const f of fs.readdirSync(dir)) {
      const fp = path.join(dir, f)
      if (fs.statSync(fp).isDirectory()) rmrf(fp)
      else fs.unlinkSync(fp)
    }
    fs.rmdirSync(dir)
  } catch {}
}

test('renameNotebook — happy path moves metadata + all notes, removes old', async (t) => {
  const { registry, dm, dir } = tmpSetup()
  await registry.ready()
  const nm = new NoteManager(dm)

  await nm.createNotebook('work', { name: 'Work Notes' })
  await nm.createNote('work', { title: 'One', content: '# One' })
  await nm.createNote('work', { title: 'Two', content: '# Two' })
  const before = (await nm.listNotes('work')).length
  t.ok(before >= 2, 'at least the two created notes are present')

  const renamed = await nm.renameNotebook('work', 'tasks')
  t.is(renamed.slug, 'tasks', 'returns new slug (newMeta in scope)')

  t.ok(await nm.getNotebook('tasks'), 'new notebook exists')
  t.absent(await nm.getNotebook('work'), 'old notebook removed')
  const notes = await nm.listNotes('tasks')
  t.is(notes.length, before, 'all notes copied to new notebook (count preserved)')

  clearInterval(nm._keyCacheCleanupInterval)
  rmrf(dir)
})

test('renameNotebook — Phase-2 delete failure leaves the OLD notebook intact (no data loss)', async (t) => {
  const { registry, dm, dir } = tmpSetup()
  await registry.ready()
  const nm = new NoteManager(dm)

  await nm.createNotebook('work', { name: 'Work Notes' })
  await nm.createNote('work', { title: 'One', content: '# One' })
  await nm.createNote('work', { title: 'Two', content: '# Two' })
  const before = (await nm.listNotes('work')).length

  // Force the commit-phase delete to fail AFTER the copy succeeded.
  nm.deleteNotebook = async () => { throw new Error('simulated delete failure') }

  await t.exception(() => nm.renameNotebook('work', 'tasks'), /removing old/, 'rename surfaces the failure')

  // The critical property: the original notebook + its notes are NOT corrupted.
  t.ok(await nm.getNotebook('work'), 'old notebook still exists after failed commit')
  const oldNotes = await nm.listNotes('work')
  t.is(oldNotes.length, before, 'old notes intact — no data loss')

  clearInterval(nm._keyCacheCleanupInterval)
  rmrf(dir)
})