chinese-path.test.js 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. import { test } from 'node:test'
  2. import assert from 'node:assert/strict'
  3. import { promises as fs } from 'node:fs'
  4. import path from 'node:path'
  5. import os from 'node:os'
  6. import { CacheManager } from '../../src/cache/index.js'
  7. import { ChapterReader } from '../../src/storage/adapters/ChapterReader.js'
  8. // 守护不变量:中文目录名/文件名/中文内容在任何平台都必须 UTF-8 正确往返过我们的全栈
  9. // (重建缓存 + 读取)。Windows 是一等公民(story-repo-spec §2.2),但 Linux CI 也跑,
  10. // 双平台都覆盖,不依赖系统 locale。
  11. test('中文路径全链路(重建缓存 + 读取,跨平台)', async (t) => {
  12. const tmpDir = path.join(os.tmpdir(), '测试书仓库', `test-${Date.now()}`)
  13. try {
  14. // 构建含中文目录/文件的 fixture
  15. await fs.mkdir(path.join(tmpDir, '定稿', '正文'), { recursive: true })
  16. await fs.mkdir(path.join(tmpDir, '.cache'), { recursive: true })
  17. const testChapter = `---
  18. 章号: 1
  19. 标题: 测试章节
  20. 卷: 1
  21. 字数: 100
  22. 章定位: 推进
  23. ---
  24. 这是测试正文。`
  25. await fs.writeFile(
  26. path.join(tmpDir, '定稿', '正文', '0001-测试章节.md'),
  27. testChapter,
  28. 'utf8'
  29. )
  30. await fs.writeFile(
  31. path.join(tmpDir, 'book.yaml'),
  32. 'spec_version: "7.0"\n书名: 测试\n类型: 玄幻\n每章目标字数: 3000\n卷规模: 40',
  33. 'utf8'
  34. )
  35. // 重建缓存
  36. const cache = new CacheManager(path.join(tmpDir, '.cache', 'index.db'))
  37. await cache.ensureReady(tmpDir)
  38. // 读取接口
  39. const reader = new ChapterReader(tmpDir, cache)
  40. const result = await reader.readFrontMatter(1)
  41. assert.equal(result.ok, true, `读取失败:${result.error}`)
  42. // 缓存返回英文字段名(title),源文件解析返回中文字段名(标题)
  43. assert.equal(result.data.title || result.data.标题, '测试章节')
  44. // 显式关闭数据库连接(避免 Windows 文件锁导致清理失败)
  45. await cache.close()
  46. } finally {
  47. // 清理
  48. try {
  49. await fs.rm(tmpDir, { recursive: true, force: true })
  50. } catch (err) {
  51. // Windows 文件锁可能导致删除失败,忽略
  52. }
  53. }
  54. })