ledger.spec.ts 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. import { afterAll, describe, expect, it } from 'vitest'
  2. import * as fs from 'node:fs'
  3. import * as os from 'node:os'
  4. import * as path from 'node:path'
  5. import {
  6. LEDGER_NAMES,
  7. MEMORY_KINDS,
  8. paths,
  9. queryLedger,
  10. queryMemory,
  11. seedMinDesign,
  12. writeFileAtomic,
  13. } from '../src/index'
  14. const roots: string[] = []
  15. function mkBook(): string {
  16. const root = fs.mkdtempSync(path.join(os.tmpdir(), 'webnovel-ledger-query-'))
  17. roots.push(root)
  18. seedMinDesign(root)
  19. for (const name of LEDGER_NAMES) {
  20. writeFileAtomic(root, paths.账本(name), `# ${name}\n`)
  21. }
  22. return root
  23. }
  24. afterAll(() => { for (const root of roots) { try { fs.rmSync(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 200 }) } catch { /* Windows 句柄延迟释放,%TEMP% 残留无害 */ } } })
  25. describe('故事账本查询', () => {
  26. it('查询五类账本并支持分类/状态筛选', () => {
  27. const root = mkBook()
  28. writeFileAtomic(root, paths.账本('故事线'), '# 故事线\n\n## 主线启动\n状态:进行中\n计划来源:卷纲#1\n')
  29. writeFileAtomic(root, paths.账本('人物弧线'), '# 人物弧线\n\n## 主角警觉\n状态:已兑现\n计划来源:卷纲#2\n')
  30. writeFileAtomic(root, paths.账本('承诺'), '# 承诺\n\n## 查明异响\n状态:进行中\n计划来源:卷纲#3\n')
  31. writeFileAtomic(root, paths.账本('线索'), '# 线索\n\n## 纸鹤\n状态:已埋\n计划来源:卷纲#4\n埋设点:第1章\n预期兑现区间:第8-12章\n')
  32. writeFileAtomic(root, paths.账本('时间线'), '# 时间线\n\n## 城门异响\n事件:发现异常\n章号:第0001章\n')
  33. const all = queryLedger(root)
  34. expect(all.ok).toBe(true)
  35. if (!all.ok) return
  36. expect(all.files).toEqual(LEDGER_NAMES.map((name) => paths.账本(name)))
  37. expect(all.entries.map((entry) => entry.名称)).toEqual(['主线启动', '主角警觉', '查明异响', '纸鹤', '城门异响'])
  38. const active = queryLedger(root, { 状态: '进行中' })
  39. expect(active.ok).toBe(true)
  40. if (active.ok) expect(active.entries.map((entry) => entry.名称)).toEqual(['主线启动', '查明异响'])
  41. const timeline = queryLedger(root, { 分类: '时间线' })
  42. expect(timeline.ok).toBe(true)
  43. if (timeline.ok) expect(timeline.entries[0]?.字段['事件']).toBe('发现异常')
  44. })
  45. it('旧格式记忆部分文件在时按条目读并标旧;全无记忆才报缺失', () => {
  46. const root = mkBook()
  47. for (const 类 of MEMORY_KINDS) {
  48. if (类 === '灵感') continue
  49. writeFileAtomic(root, paths.记忆(类), `# ${类}\n\n## 条目-${类}\n类:${类}\n状态:已确认\n`)
  50. }
  51. const partial = queryMemory(root, { 类: '文风' })
  52. expect(partial.ok).toBe(true)
  53. if (partial.ok) {
  54. expect(partial.entries.map((entry) => entry.名称)).toEqual(['条目-文风'])
  55. expect(partial.entries[0]!.格式).toBe('旧')
  56. }
  57. // 新格式一事一文件:条目文件带 类 字段,不标旧
  58. writeFileAtomic(root, paths.本书记忆条目('克制叙述'), '---\n名称: 克制叙述\n类: 文风\n状态: 已确认\n来源: x\n---\n短句。\n')
  59. const mixed = queryMemory(root, { 类: '文风' })
  60. expect(mixed.ok).toBe(true)
  61. if (mixed.ok) expect(mixed.entries.map((entry) => entry.名称).sort()).toEqual(['克制叙述', '条目-文风'])
  62. if (mixed.ok) expect(mixed.entries.find((entry) => entry.名称 === '克制叙述')?.格式).toBeUndefined()
  63. })
  64. })