OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-social / hyper-wiki / test / wiki.test.js

227 lines · javascript ref:
import test from 'brittle'
import { Wiki } from '../lib/wiki.js'
import { tmpdir } from 'os'
import { join } from 'path'
import { randomBytes } from 'crypto'

function tmpStorage () {
  return join(tmpdir(), 'hyper-wiki-test-' + randomBytes(8).toString('hex'))
}

test('Wiki - create and read page', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  const meta = await wiki.create('hello-world', '# Hello World\n\nThis is a test page.', {
    title: 'Hello World',
    author: 'alice'
  })

  t.is(meta.title, 'Hello World')
  t.is(meta.author, 'alice')
  t.is(meta.version, 1)
  t.ok(meta.created > 0)

  const page = await wiki.read('hello-world')
  t.ok(page)
  t.is(page.slug, 'hello-world')
  t.is(page.content, '# Hello World\n\nThis is a test page.')
  t.is(page.title, 'Hello World')
  t.is(page.version, 1)

  await wiki.close()
})

test('Wiki - read nonexistent returns null', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  const page = await wiki.read('nonexistent')
  t.is(page, null)

  await wiki.close()
})

test('Wiki - update page increments version', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  await wiki.create('test', 'Version 1', { title: 'Test Page' })
  const meta = await wiki.update('test', 'Version 2')

  t.is(meta.version, 2)
  t.is(meta.title, 'Test Page') // title preserved

  const page = await wiki.read('test')
  t.is(page.content, 'Version 2')
  t.is(page.version, 2)

  await wiki.close()
})

test('Wiki - delete page', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  await wiki.create('to-delete', 'content')
  t.ok(await wiki.exists('to-delete'))

  await wiki.remove('to-delete')
  t.absent(await wiki.exists('to-delete'))
  t.is(await wiki.read('to-delete'), null)

  await wiki.close()
})

test('Wiki - list pages', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  await wiki.create('page-a', 'Content A', { title: 'Page A' })
  await wiki.create('page-b', 'Content B', { title: 'Page B' })
  await wiki.create('page-c', 'Content C', { title: 'Page C' })

  const pages = await wiki.list()
  t.is(pages.length, 3)

  // Check sorted by updated desc (page-c is most recent)
  t.is(pages[0].slug, 'page-c')
  t.is(pages[0].title, 'Page C')

  await wiki.close()
})

test('Wiki - list empty returns empty array', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  const pages = await wiki.list()
  t.alike(pages, [])

  await wiki.close()
})

test('Wiki - exists', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  t.absent(await wiki.exists('nope'))
  await wiki.create('yep', 'exists')
  t.ok(await wiki.exists('yep'))

  await wiki.close()
})

test('Wiki - search by content', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  await wiki.create('hypercore-guide', '# Hypercore\n\nAppend-only log for P2P', { title: 'Hypercore Guide' })
  await wiki.create('hyperbee-guide', '# Hyperbee\n\nDatabase on top of Hypercore', { title: 'Hyperbee Guide' })
  await wiki.create('unrelated', '# Cooking\n\nHow to make pasta', { title: 'Cooking' })

  const results = await wiki.search('hypercore')
  t.ok(results.length >= 2, 'finds pages mentioning hypercore')
  t.ok(results.some(r => r.slug === 'hypercore-guide'))
  t.ok(results.some(r => r.slug === 'hyperbee-guide'))

  await wiki.close()
})

test('Wiki - search by title', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  await wiki.create('setup', 'Install instructions', { title: 'Setup Guide' })
  await wiki.create('faq', 'Common questions', { title: 'FAQ' })

  const results = await wiki.search('setup')
  t.ok(results.length >= 1)
  t.ok(results.some(r => r.slug === 'setup'))

  await wiki.close()
})

test('Wiki - search no results', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  await wiki.create('page1', 'some content')
  const results = await wiki.search('nonexistent-term-xyz')
  t.is(results.length, 0)

  await wiki.close()
})

test('Wiki - multiple updates preserve created time', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  const v1 = await wiki.create('evolving', 'v1')
  const created = v1.created

  await new Promise(r => setTimeout(r, 10))
  const v2 = await wiki.update('evolving', 'v2')

  t.is(v2.created, created, 'created time unchanged')
  t.ok(v2.updated >= created, 'updated time is newer')
  t.is(v2.version, 2)

  await wiki.close()
})

test('Wiki - unicode content', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  await wiki.create('unicode', '# 日本語ページ\n\n🎉 こんにちは世界', {
    title: '日本語テスト',
    author: 'ユーザー'
  })

  const page = await wiki.read('unicode')
  t.is(page.title, '日本語テスト')
  t.ok(page.content.includes('🎉'))
  t.ok(page.content.includes('こんにちは'))

  await wiki.close()
})

test('Wiki - large content', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  const content = '# Big Page\n\n' + 'Lorem ipsum dolor sit amet. '.repeat(1000)
  await wiki.create('big', content, { title: 'Big Page' })

  const page = await wiki.read('big')
  t.is(page.content, content)

  await wiki.close()
})

test('Wiki - tags preserved across updates', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  await wiki.create('tagged', 'content', {
    title: 'Tagged Page',
    tags: ['p2p', 'hypercore']
  })

  await wiki.update('tagged', 'updated content')
  const page = await wiki.read('tagged')
  t.alike(page.tags, ['p2p', 'hypercore'], 'tags preserved')

  await wiki.close()
})

test('Wiki - has discoveryKey', async (t) => {
  const wiki = new Wiki(tmpStorage())
  await wiki.ready()

  t.ok(wiki.key, 'has key')

  await wiki.close()
})