auto-mode.test.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import { test } from 'node:test'
  2. import assert from 'node:assert/strict'
  3. import path from 'node:path'
  4. import { promises as fs } from 'node:fs'
  5. import { determineNextState } from '../../src/state-machine/index.js'
  6. import { stageChapter, rejectFrom } from '../../src/staging/index.js'
  7. import { gitBookCtx } from '../commands/_helper.js'
  8. // 开关矩阵(PRD #14):自动确认细纲 × 连写。全关=既有回归(router.test.js 全量);
  9. // 本文件测:单开细纲自动确认的序 6 标志、批次进行中的序 3 明细。全开端到端见 finalize-batch.test.js AC1。
  10. async function reachOutline(ctx) {
  11. // sample-book 工作区自带细纲 → 会命中序 3,清掉以到达序 6
  12. await fs.rm(path.join(ctx.repoPath, '工作区', '细纲.md'), { force: true })
  13. }
  14. async function setBookYaml(ctx, key, value) {
  15. const p = path.join(ctx.repoPath, 'book.yaml')
  16. const src = await fs.readFile(p, 'utf8')
  17. const line = `${key}: ${value}`
  18. const re = new RegExp(`^${key}:.*$`, 'm')
  19. await fs.writeFile(p, re.test(src) ? src.replace(re, line) : `${src}${line}\n`, 'utf8')
  20. }
  21. async function stage3(ctx) {
  22. await fs.mkdir(path.join(ctx.repoPath, '工作区'), { recursive: true })
  23. await fs.writeFile(
  24. path.join(ctx.repoPath, '工作区', '审稿.md'),
  25. '# 第 3 章审稿单\n\n> 完整两审模式。\n> 共 0 个问题:0 阻断。\n',
  26. 'utf8'
  27. )
  28. const r = await stageChapter(ctx, {
  29. chapterNum: 3,
  30. payload: {
  31. frontMatter: {
  32. 章号: 3,
  33. 标题: '连写3',
  34. 卷: 1,
  35. 书内时间: '夏月初三',
  36. 字数: 100,
  37. 章定位: '推进',
  38. 钩子: '危机钩-强',
  39. 情绪定位: '铺垫',
  40. 伏笔: ['推进 伏笔-001'],
  41. },
  42. body: '第3章正文。',
  43. workspaceFiles: [],
  44. },
  45. })
  46. assert.equal(r.ok, true, r.error)
  47. }
  48. test('开关矩阵:全关 → 序 6 dto.自动确认细纲=false,行为与既有一致', async () => {
  49. const { ctx, cleanup } = await gitBookCtx()
  50. try {
  51. await reachOutline(ctx)
  52. const r = await determineNextState(ctx)
  53. assert.equal(r.序, 6, JSON.stringify(r))
  54. assert.equal(r.dto.自动确认细纲, false)
  55. assert.match(r.dto.期望产物, /作者确认/)
  56. } finally {
  57. await cleanup()
  58. }
  59. })
  60. test('开关矩阵:自动确认细纲开 → 序 6 dto 标志与提案直接生效指引', async () => {
  61. const { ctx, cleanup } = await gitBookCtx()
  62. try {
  63. await reachOutline(ctx)
  64. await setBookYaml(ctx, '自动确认细纲', 'true')
  65. const r = await determineNextState(ctx)
  66. assert.equal(r.序, 6, JSON.stringify(r))
  67. assert.equal(r.dto.自动确认细纲, true)
  68. assert.match(r.dto.期望产物, /不再问作者/)
  69. } finally {
  70. await cleanup()
  71. }
  72. })
  73. test('批次进行中:序 3 dto.批次 带章清单与「继续下一章」建议;打回后建议重写', async () => {
  74. const { ctx, cleanup } = await gitBookCtx()
  75. try {
  76. await reachOutline(ctx)
  77. await stage3(ctx)
  78. const r = await determineNextState(ctx)
  79. assert.equal(r.序, 3, JSON.stringify(r))
  80. assert.equal(r.state, 'resume')
  81. assert.equal(r.dto.从哪继续, '待定稿批次续跑')
  82. assert.equal(r.dto.批次.章数, 1)
  83. assert.deepEqual(r.dto.批次.章, [{ 章号: 3, 标题: '连写3', 状态: '待审收' }])
  84. assert.match(r.dto.批次.建议, /继续批内下一章(第 4 章)/)
  85. const rej = await rejectFrom(ctx.repoPath, 3)
  86. assert.equal(rej.ok, true, rej.error)
  87. const r2 = await determineNextState(ctx)
  88. assert.equal(r2.序, 3)
  89. assert.match(r2.dto.批次.建议, /重写打回章(第 3 章)/)
  90. } finally {
  91. await cleanup()
  92. }
  93. })
  94. test('批次进行中:停止命中(写满)→ 序 3 建议批量过稿', async () => {
  95. const { ctx, cleanup } = await gitBookCtx()
  96. try {
  97. await reachOutline(ctx)
  98. await setBookYaml(ctx, '连写批次大小', '1')
  99. await stage3(ctx)
  100. const r = await determineNextState(ctx)
  101. assert.equal(r.序, 3, JSON.stringify(r))
  102. assert.equal(r.dto.批次.停止.stop, true)
  103. assert.match(r.dto.批次.建议, /批量过稿/)
  104. } finally {
  105. await cleanup()
  106. }
  107. })