books-commands.test.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import { test } from 'node:test'
  2. import assert from 'node:assert/strict'
  3. import os from 'node:os'
  4. import path from 'node:path'
  5. import { promises as fs } from 'node:fs'
  6. import { run as listBooks } from '../../src/commands/list-books.js'
  7. import { run as switchBook } from '../../src/commands/switch-book.js'
  8. import { run as sessionContext } from '../../src/commands/session-context.js'
  9. import { assembleSessionContext, readBooksRegistry } from '../../src/session/index.js'
  10. async function tmpWorkdir(books = null) {
  11. const root = await fs.mkdtemp(path.join(os.tmpdir(), 'wnw-bkcmd-'))
  12. if (books) {
  13. await fs.mkdir(path.join(root, '.webnovel'), { recursive: true })
  14. await fs.writeFile(
  15. path.join(root, '.webnovel', 'books.jsonl'),
  16. books.map((b) => JSON.stringify(b)).join('\n') + '\n',
  17. 'utf8'
  18. )
  19. }
  20. return { root, ctx: { workdir: root }, cleanup: () => fs.rm(root, { recursive: true, force: true }) }
  21. }
  22. test('list-books:列书单,当前书带标记', async () => {
  23. const { ctx, cleanup } = await tmpWorkdir([
  24. { 书名: '星海', 目录: '星海', 当前: true, 最后打开: '2026-07-01' },
  25. { 书名: '剑起青云', 目录: '剑起青云', 当前: false },
  26. ])
  27. try {
  28. const r = await listBooks([], {}, ctx)
  29. assert.equal(r.ok, true)
  30. assert.ok(r.output.includes('《星海》') && r.output.includes('《剑起青云》'))
  31. assert.ok(r.output.includes('2026-07-01'))
  32. const cur = r.output.split('\n').find((l) => l.startsWith('→'))
  33. assert.ok(cur.includes('星海'), '当前书行应带 → 标记')
  34. } finally {
  35. await cleanup()
  36. }
  37. })
  38. test('list-books:空书单 → 建书指引', async () => {
  39. const { root, ctx, cleanup } = await tmpWorkdir(null)
  40. try {
  41. await fs.mkdir(path.join(root, '.webnovel'), { recursive: true })
  42. const r = await listBooks([], {}, ctx)
  43. assert.equal(r.ok, true)
  44. assert.ok(r.output.includes('建书'))
  45. } finally {
  46. await cleanup()
  47. }
  48. })
  49. test('switch-book:换书改当前标记;缺参数人话报错', async () => {
  50. const { root, ctx, cleanup } = await tmpWorkdir([
  51. { 书名: '星海', 目录: '星海', 当前: true },
  52. { 书名: '剑起青云', 目录: '剑起青云', 当前: false },
  53. ])
  54. try {
  55. const bad = await switchBook([], {}, ctx)
  56. assert.equal(bad.ok, false)
  57. assert.ok(bad.error.includes('用法'))
  58. const r = await switchBook(['剑起青云'], {}, ctx)
  59. assert.equal(r.ok, true)
  60. assert.ok(r.output.includes('剑起青云'))
  61. const reg = await readBooksRegistry(root)
  62. assert.equal(reg.books.find((b) => b.当前).书名, '剑起青云')
  63. } finally {
  64. await cleanup()
  65. }
  66. })
  67. test('session-context:输出与 assembleSessionContext 逐字一致(两宿主入口等价),并刷新最后打开', async () => {
  68. const { root, ctx, cleanup } = await tmpWorkdir([
  69. { 书名: '星海', 目录: '星海', 当前: true, 最后打开: '2000-01-01' },
  70. ])
  71. try {
  72. const expected = (await assembleSessionContext(root)).text
  73. const r = await sessionContext([], {}, ctx)
  74. assert.equal(r.ok, true)
  75. assert.equal(r.output, expected)
  76. const reg = await readBooksRegistry(root)
  77. assert.notEqual(reg.books[0].最后打开, '2000-01-01')
  78. } finally {
  79. await cleanup()
  80. }
  81. })