|
|
@@ -0,0 +1,116 @@
|
|
|
+import { test } from 'node:test'
|
|
|
+import assert from 'node:assert/strict'
|
|
|
+import path from 'node:path'
|
|
|
+import { promises as fs } from 'node:fs'
|
|
|
+import { determineNextState } from '../../src/state-machine/index.js'
|
|
|
+import { stageChapter, rejectFrom } from '../../src/staging/index.js'
|
|
|
+import { gitBookCtx } from '../commands/_helper.js'
|
|
|
+
|
|
|
+// 开关矩阵(PRD #14):自动确认细纲 × 连写。全关=既有回归(router.test.js 全量);
|
|
|
+// 本文件测:单开细纲自动确认的序 6 标志、批次进行中的序 3 明细。全开端到端见 finalize-batch.test.js AC1。
|
|
|
+
|
|
|
+async function reachOutline(ctx) {
|
|
|
+ // sample-book 工作区自带细纲 → 会命中序 3,清掉以到达序 6
|
|
|
+ await fs.rm(path.join(ctx.repoPath, '工作区', '细纲.md'), { force: true })
|
|
|
+}
|
|
|
+
|
|
|
+async function setBookYaml(ctx, key, value) {
|
|
|
+ const p = path.join(ctx.repoPath, 'book.yaml')
|
|
|
+ const src = await fs.readFile(p, 'utf8')
|
|
|
+ const line = `${key}: ${value}`
|
|
|
+ const re = new RegExp(`^${key}:.*$`, 'm')
|
|
|
+ await fs.writeFile(p, re.test(src) ? src.replace(re, line) : `${src}${line}\n`, 'utf8')
|
|
|
+}
|
|
|
+
|
|
|
+async function stage3(ctx) {
|
|
|
+ await fs.mkdir(path.join(ctx.repoPath, '工作区'), { recursive: true })
|
|
|
+ await fs.writeFile(
|
|
|
+ path.join(ctx.repoPath, '工作区', '审稿.md'),
|
|
|
+ '# 第 3 章审稿单\n\n> 完整两审模式。\n> 共 0 个问题:0 阻断。\n',
|
|
|
+ 'utf8'
|
|
|
+ )
|
|
|
+ const r = await stageChapter(ctx, {
|
|
|
+ chapterNum: 3,
|
|
|
+ payload: {
|
|
|
+ frontMatter: {
|
|
|
+ 章号: 3,
|
|
|
+ 标题: '连写3',
|
|
|
+ 卷: 1,
|
|
|
+ 书内时间: '夏月初三',
|
|
|
+ 字数: 100,
|
|
|
+ 章定位: '推进',
|
|
|
+ 钩子: '危机钩-强',
|
|
|
+ 情绪定位: '铺垫',
|
|
|
+ 伏笔: ['推进 伏笔-001'],
|
|
|
+ },
|
|
|
+ body: '第3章正文。',
|
|
|
+ workspaceFiles: [],
|
|
|
+ },
|
|
|
+ })
|
|
|
+ assert.equal(r.ok, true, r.error)
|
|
|
+}
|
|
|
+
|
|
|
+test('开关矩阵:全关 → 序 6 dto.自动确认细纲=false,行为与既有一致', async () => {
|
|
|
+ const { ctx, cleanup } = await gitBookCtx()
|
|
|
+ try {
|
|
|
+ await reachOutline(ctx)
|
|
|
+ const r = await determineNextState(ctx)
|
|
|
+ assert.equal(r.序, 6, JSON.stringify(r))
|
|
|
+ assert.equal(r.dto.自动确认细纲, false)
|
|
|
+ assert.match(r.dto.期望产物, /作者确认/)
|
|
|
+ } finally {
|
|
|
+ await cleanup()
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+test('开关矩阵:自动确认细纲开 → 序 6 dto 标志与提案直接生效指引', async () => {
|
|
|
+ const { ctx, cleanup } = await gitBookCtx()
|
|
|
+ try {
|
|
|
+ await reachOutline(ctx)
|
|
|
+ await setBookYaml(ctx, '自动确认细纲', 'true')
|
|
|
+ const r = await determineNextState(ctx)
|
|
|
+ assert.equal(r.序, 6, JSON.stringify(r))
|
|
|
+ assert.equal(r.dto.自动确认细纲, true)
|
|
|
+ assert.match(r.dto.期望产物, /不再问作者/)
|
|
|
+ } finally {
|
|
|
+ await cleanup()
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+test('批次进行中:序 3 dto.批次 带章清单与「继续下一章」建议;打回后建议重写', async () => {
|
|
|
+ const { ctx, cleanup } = await gitBookCtx()
|
|
|
+ try {
|
|
|
+ await reachOutline(ctx)
|
|
|
+ await stage3(ctx)
|
|
|
+ const r = await determineNextState(ctx)
|
|
|
+ assert.equal(r.序, 3, JSON.stringify(r))
|
|
|
+ assert.equal(r.state, 'resume')
|
|
|
+ assert.equal(r.dto.从哪继续, '待定稿批次续跑')
|
|
|
+ assert.equal(r.dto.批次.章数, 1)
|
|
|
+ assert.deepEqual(r.dto.批次.章, [{ 章号: 3, 标题: '连写3', 状态: '待审收' }])
|
|
|
+ assert.match(r.dto.批次.建议, /继续批内下一章(第 4 章)/)
|
|
|
+
|
|
|
+ const rej = await rejectFrom(ctx.repoPath, 3)
|
|
|
+ assert.equal(rej.ok, true, rej.error)
|
|
|
+ const r2 = await determineNextState(ctx)
|
|
|
+ assert.equal(r2.序, 3)
|
|
|
+ assert.match(r2.dto.批次.建议, /重写打回章(第 3 章)/)
|
|
|
+ } finally {
|
|
|
+ await cleanup()
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+test('批次进行中:停止命中(写满)→ 序 3 建议批量过稿', async () => {
|
|
|
+ const { ctx, cleanup } = await gitBookCtx()
|
|
|
+ try {
|
|
|
+ await reachOutline(ctx)
|
|
|
+ await setBookYaml(ctx, '连写批次大小', '1')
|
|
|
+ await stage3(ctx)
|
|
|
+ const r = await determineNextState(ctx)
|
|
|
+ assert.equal(r.序, 3, JSON.stringify(r))
|
|
|
+ assert.equal(r.dto.批次.停止.stop, true)
|
|
|
+ assert.match(r.dto.批次.建议, /批量过稿/)
|
|
|
+ } finally {
|
|
|
+ await cleanup()
|
|
|
+ }
|
|
|
+})
|