_helper.js 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import path from 'node:path'
  2. import os from 'node:os'
  3. import { fileURLToPath } from 'node:url'
  4. import { mkdtemp, mkdir, writeFile, rm, cp } from 'node:fs/promises'
  5. import { execFile } from 'node:child_process'
  6. import { promisify } from 'node:util'
  7. import { CacheManager } from '../../src/cache/index.js'
  8. const execFileAsync = promisify(execFile)
  9. const __dirname = path.dirname(fileURLToPath(import.meta.url))
  10. // 共享的示例书仓库(只读,命令测试默认喂它)
  11. export const fixtureRoot = path.join(__dirname, '../fixtures/sample-book')
  12. /**
  13. * 用共享 sample-book fixture 建命令运行上下文。
  14. * @returns {Promise<{ctx: {repoPath, cache}, cleanup: () => Promise<void>}>}
  15. */
  16. export function fixtureCtx() {
  17. return repoCtx(fixtureRoot, null)
  18. }
  19. /**
  20. * 建命令运行上下文。
  21. * @param {string|null} repoPath 现成书仓库路径(与 files 二选一)
  22. * @param {object|null} files {相对路径: 内容} → 在临时目录现造一个书仓库
  23. */
  24. export async function repoCtx(repoPath, files) {
  25. let tmpRepo = null
  26. if (files) {
  27. tmpRepo = await mkdtemp(path.join(os.tmpdir(), 'wnw-cmd-repo-'))
  28. for (const [rel, content] of Object.entries(files)) {
  29. const full = path.join(tmpRepo, rel)
  30. await mkdir(path.dirname(full), { recursive: true })
  31. await writeFile(full, content, 'utf8')
  32. }
  33. repoPath = tmpRepo
  34. }
  35. // db 放独立临时目录,绝不写进书仓库
  36. const dbDir = await mkdtemp(path.join(os.tmpdir(), 'wnw-cmd-db-'))
  37. const cache = new CacheManager(path.join(dbDir, 'index.db'))
  38. await cache.ensureReady(repoPath)
  39. return {
  40. ctx: { repoPath, cache },
  41. cleanup: async () => {
  42. await cache.close()
  43. await rm(dbDir, { recursive: true, force: true })
  44. if (tmpRepo) await rm(tmpRepo, { recursive: true, force: true })
  45. },
  46. }
  47. }
  48. /**
  49. * 把 sample-book fixture 整体拷到临时目录(可写),建 ctx。
  50. * 用于会写入书仓库的流程(备料写本章写作材料、定稿写定稿/git),避免污染 committed fixture。
  51. */
  52. export async function tempBookCtx() {
  53. const tmpRepo = await mkdtemp(path.join(os.tmpdir(), 'wnw-book-'))
  54. await cp(fixtureRoot, tmpRepo, { recursive: true })
  55. const dbDir = await mkdtemp(path.join(os.tmpdir(), 'wnw-book-db-'))
  56. const cache = new CacheManager(path.join(dbDir, 'index.db'))
  57. await cache.ensureReady(tmpRepo)
  58. return {
  59. ctx: { repoPath: tmpRepo, cache },
  60. cleanup: async () => {
  61. await cache.close()
  62. await rm(dbDir, { recursive: true, force: true })
  63. await rm(tmpRepo, { recursive: true, force: true })
  64. },
  65. }
  66. }
  67. /**
  68. * 同 tempBookCtx,但额外 git init + 首提交(定稿/git 流程测试用)。
  69. */
  70. export async function gitBookCtx() {
  71. const { ctx, cleanup } = await tempBookCtx()
  72. const git = (args) => execFileAsync('git', args, { cwd: ctx.repoPath })
  73. await git(['init', '-q'])
  74. await git(['config', 'user.email', 't@example.com'])
  75. await git(['config', 'user.name', 'test'])
  76. await git(['add', '-A'])
  77. await git(['commit', '-q', '-m', 'init'])
  78. return { ctx, cleanup }
  79. }