cli-spawn-smoke.test.js 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  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 { fileURLToPath } from 'node:url'
  7. import { execFile } from 'node:child_process'
  8. import { promisify } from 'node:util'
  9. import { tempV6, inlineFixture } from '../migrate/_v6.js'
  10. const exec = promisify(execFile)
  11. const __dirname = path.dirname(fileURLToPath(import.meta.url))
  12. const BIN = path.join(__dirname, '../../bin/webnovel-writer.js')
  13. const fixtureRoot = path.join(__dirname, '../fixtures/sample-book')
  14. /**
  15. * G-6:M6/M7 新命令的 bin spawn 冒烟——参数解析、exitCode、错误人话(不带栈)。
  16. * 全走真子进程;finally 的 rm 兼作 cache.close 探针(db 未关 Windows 会 EBUSY/EPERM)。
  17. */
  18. test('bin spawn 冒烟:export 单章(书仓库直启)+ 坏参数人话退出', async () => {
  19. const repo = await fs.mkdtemp(path.join(os.tmpdir(), 'wnw-spawn-book-'))
  20. await fs.cp(fixtureRoot, repo, { recursive: true })
  21. const run = (args) => exec(process.execPath, [BIN, ...args], { cwd: repo, encoding: 'utf8' })
  22. try {
  23. const r = await run(['export', '1'])
  24. assert.match(r.stdout, /第0001章-开局\.txt/)
  25. const exported = await fs.readFile(path.join(repo, '工作区', '导出', '第0001章-开局.txt'), 'utf8')
  26. assert.ok(exported.length > 0)
  27. assert.ok(!exported.startsWith('---'), '导出应剥 front matter')
  28. const err = await run(['export', 'abc']).then(
  29. () => null,
  30. (e) => e
  31. )
  32. assert.ok(err, '坏参数应非零退出')
  33. assert.equal(err.code, 1)
  34. assert.match(err.stderr, /用法/)
  35. assert.ok(!/\n\s+at /.test(err.stderr), `错误不带栈:${err.stderr}`)
  36. } finally {
  37. await fs.rm(repo, { recursive: true, force: true })
  38. }
  39. })
  40. test('bin spawn 冒烟:migrate v6 项目(工作目录)+ 缺参数人话退出', async () => {
  41. const workdir = await fs.mkdtemp(path.join(os.tmpdir(), 'wnw-spawn-workdir-'))
  42. await fs.mkdir(path.join(workdir, '.webnovel'), { recursive: true })
  43. const v6 = await tempV6(inlineFixture)
  44. const run = (args) => exec(process.execPath, [BIN, ...args], { cwd: workdir, encoding: 'utf8' })
  45. try {
  46. const r = await run(['migrate', v6.v6Path])
  47. assert.match(r.stdout, /迁移报告/)
  48. await fs.access(path.join(workdir, '剑碎虚空', 'book.yaml'))
  49. await fs.access(path.join(workdir, '剑碎虚空', '工作区', '迁移报告.md'))
  50. const err = await run(['migrate']).then(
  51. () => null,
  52. (e) => e
  53. )
  54. assert.ok(err, '缺参数应非零退出')
  55. assert.equal(err.code, 1)
  56. assert.match(err.stderr, /用法/)
  57. assert.ok(!/\n\s+at /.test(err.stderr), `错误不带栈:${err.stderr}`)
  58. } finally {
  59. await fs.rm(workdir, { recursive: true, force: true })
  60. await v6.cleanup()
  61. }
  62. })