import { describe, it, expect } from 'vitest' import { DatabaseSync } from 'node:sqlite' import { migrate } from '../../src/main/db/migrate' import { AiSessionRepository } from '../../src/main/db/repositories/ai-session.repo' import { InteractiveRepository } from '../../src/main/db/repositories/interactive.repo' import { WizardWritingService } from '../../src/main/services/wizard-writing.service' import { AutoWritingService } from '../../src/main/services/auto-writing.service' import { AiClientService } from '../../src/main/services/ai-client.service' import { DEFAULT_AI_CONFIG, type AutoWritingConfig, type InteractiveFlow } from '../../src/shared/types' const AI_TEST_CONFIG = { ...DEFAULT_AI_CONFIG, timeoutMs: 300_000, maxTokens: 4096 } const CONTEXT = '林远站在演武场中央,众人目光汇聚,等待入门考核开始。' describe('WizardWritingService.startGenerate', () => { const wizard = (db: DatabaseSync) => new WizardWritingService(db, new AiClientService(() => AI_TEST_CONFIG)) const auto = (db: DatabaseSync) => new AutoWritingService(db, new AiClientService(() => AI_TEST_CONFIG)) async function setupWizardFlow(svc: WizardWritingService, sessionId: string): Promise { let flow = svc.start(sessionId) flow = svc.setGoal(flow.id, { chapterGoal: '林远通过入门考核,展现天赋', wordMin: 600, wordMax: 1200 }) flow = svc.setRhythm(flow.id, 'tense') flow = svc.setPov(flow.id) flow = svc.confirmContext(flow.id, { binding: { chapterIds: [], outlineIds: [], settingIds: [], inspirationIds: [], knowledgeIds: [] }, systemPrompt: CONTEXT, contextSummary: '0章·0纲·0设·0感·0知' }) return flow.id } async function continueAfterPartialGenerate( wizSvc: WizardWritingService, autoSvc: AutoWritingService, repo: InteractiveRepository, flowId: string, initial: InteractiveFlow ): Promise { let flow = initial for (let guard = 0; guard < 8 && flow.flowMode !== 'interactive'; guard++) { if (flow.step === 'naming_pause' && flow.namingPending) { flow = await autoSvc.resolveNaming( flowId, flow.namingPending.options[0]!, CONTEXT, () => {} ) continue } const config = repo.getModeConfig(flowId) const planLen = config.scenePlan?.length ?? 0 const idx = config.currentPlanIndex ?? 0 if (idx < planLen) { flow = await autoSvc.generateNext(flowId, CONTEXT, () => {}) continue } if (flow.scenes.length > 0) { return wizSvc.handoffToInteractive(flowId) } break } return flow } it('IT-WIZ-02: startGenerate handoffs to interactive with scenes', async () => { const db = new DatabaseSync(':memory:') migrate(db) const repo = new InteractiveRepository(db) const session = new AiSessionRepository(db).create('t', DEFAULT_AI_CONFIG.model) const wizSvc = wizard(db) const autoSvc = auto(db) const flowId = await setupWizardFlow(wizSvc, session.id) const initial = await wizSvc.startGenerate(flowId, CONTEXT, () => {}) const flow = initial.flowMode === 'interactive' ? initial : await continueAfterPartialGenerate(wizSvc, autoSvc, repo, flowId, initial) expect(flow.flowMode).toBe('interactive') expect(flow.scenes.length).toBeGreaterThanOrEqual(1) db.close() }, 600_000) })