| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 |
- import { afterAll, describe, expect, it } from 'vitest'
- import * as fs from 'node:fs'
- import * as path from 'node:path'
- import * as os from 'node:os'
- import { createNovelTools, type NovelToolsDeps } from '../src/novel-tools'
- import { APPROVE_LABEL, REJECT_LABEL, type AskRequest, type AskResponse } from '@webnovel/core'
- const roots: string[] = []
- function mkWs(): string {
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'webnovel-settle-'))
- roots.push(dir)
- return dir
- }
- afterAll(() => { for (const r of roots) { try { fs.rmSync(r, { recursive: true, force: true, maxRetries: 10, retryDelay: 200 }) } catch { /* Windows 句柄延迟释放 */ } } })
- const AGENT = { agent: { id: 'agent-1', session: { append: () => {} } } }
- const CONCEPT = {
- 状态: '已确认',
- 核心创意: 'x', 题材与目标读者: 'x', 主角核心欲望: 'x', 主要冲突: 'x',
- 核心看点: 'x', 差异化方向: 'x', 明确不要什么: 'x',
- }
- async function setup(ask?: NovelToolsDeps['askFn']) {
- const ws = mkWs()
- const tools = createNovelTools({
- workspaceRoot: () => ws,
- bookRootOfBookId: () => path.join(ws, '审批书'),
- askFn: ask,
- })
- const call = async (name: string, args: Record<string, unknown>) => {
- const tool = tools.find((t) => t.name === name)!
- return (await tool.execute(args, AGENT)) as Record<string, unknown> & { ok: boolean }
- }
- const created = await call('novel_create_book', { bookName: '审批书', concept: CONCEPT }) as { bookId: string }
- return { call, bookId: created.bookId }
- }
- const KEY = { 卷: 1, 章: 1, 章名: '第一章', summary: 'x' }
- describe('定稿沉淀:作者审批不可由模型自证(裁决 11/14)', () => {
- it('未注入 askFn:拒绝沉淀,不落任何文件', async () => {
- const { call, bookId } = await setup(undefined)
- const r = await call('novel_settle_chapter', { bookId, ...KEY })
- expect(r.ok).toBe(false)
- expect(String(r.reason)).toContain('裁决通道')
- })
- it('作者驳回:拒绝沉淀', async () => {
- const { call, bookId } = await setup(async () => ({ answers: [{ id: '定稿入档', selected: [REJECT_LABEL] }] }))
- const r = await call('novel_settle_chapter', { bookId, ...KEY })
- expect(r.ok).toBe(false)
- expect(String(r.reason)).toContain('未获作者批准')
- })
- it('作者批准:进入沉淀(无定稿包则由 archiveChapter fail-closed)', async () => {
- let seen: AskRequest | undefined
- const { call, bookId } = await setup(async (req): Promise<AskResponse> => {
- seen = req
- return { answers: [{ id: '定稿入档', selected: [APPROVE_LABEL] }] }
- })
- const r = await call('novel_settle_chapter', { bookId, ...KEY })
- expect(seen, 'askFn 应被调用').toBeDefined()
- expect(seen!.questions[0]?.id).toBe('定稿入档')
- expect(r.ok).toBe(false)
- expect(String(r.reason)).not.toContain('裁决通道')
- })
- it('模型无法用参数绕过:裁决记录 已不是入参', async () => {
- const { call, bookId } = await setup(undefined)
- const tools = createNovelTools({ workspaceRoot: () => '', bookRootOfBookId: () => undefined })
- const settle = tools.find((t) => t.name === 'novel_settle_chapter')!
- const required = (settle.parameters as { required: string[] }).required
- expect(required).not.toContain('裁决记录')
- const r = await call('novel_settle_chapter', { bookId, ...KEY, 裁决记录: '作者说可以' })
- expect(r.ok).toBe(false)
- })
- })
|