import { describe, expect, it } from 'vitest'; import { buildCustomWorldAnchorPackFromIntent, buildCustomWorldCreatorIntentDisplayText, buildCustomWorldCreatorIntentFoundationText, buildPendingClarifications, createEmptyCustomWorldCreatorIntent, evaluateCustomWorldCreatorIntentReadiness, mergeCustomWorldCreatorIntent, normalizeCustomWorldCreatorIntent, } from './customWorldCreatorIntent'; describe('customWorldCreatorIntent', () => { it('builds a readable summary from creator intent cards', () => { const intent = { ...createEmptyCustomWorldCreatorIntent('card'), worldHook: '一个会被灵潮反复改写地形的边境世界。', themeKeywords: ['边境', '灵潮'], toneDirectives: ['紧张', '潮湿'], playerPremise: '玩家是带着旧名单回来的前巡夜人。', coreConflicts: ['旧案名单再次出现'], keyCharacters: [ { id: 'character-1', name: '沈砺', role: '灰炬向导', publicMask: '看起来只是熟路的带路人', hiddenHook: '他一直在追查撤离线失控真相', relationToPlayer: '会先怀疑玩家身份', notes: '', locked: true, }, ], }; const summary = buildCustomWorldCreatorIntentDisplayText(intent); expect(summary).toContain( '世界一句话:一个会被灵潮反复改写地形的边境世界。', ); expect(summary).toContain('主题关键词:边境、灵潮'); expect(summary).toContain('关键角色:沈砺 / 灰炬向导'); }); it('builds six-anchor foundation text from structured creator intent', () => { const intent = { ...createEmptyCustomWorldCreatorIntent('card'), worldHook: '一个会被灵潮反复改写地形的边境世界。', themeKeywords: ['边境', '灵潮'], toneDirectives: ['紧张', '潮湿'], playerPremise: '玩家是带着旧名单回来的前巡夜人。', openingSituation: '返乡第一夜,封锁线外出现了本不该存在的灯火。', coreConflicts: ['旧案名单再次出现'], keyCharacters: [ { id: 'character-1', name: '沈砺', role: '灰炬向导', publicMask: '看起来只是熟路的带路人', hiddenHook: '他一直在追查撤离线失控真相', relationToPlayer: '会先怀疑玩家身份', notes: '', locked: true, }, ], iconicElements: ['会逆向蔓延的潮雾'], }; const foundationText = buildCustomWorldCreatorIntentFoundationText(intent); expect(foundationText).toContain( '世界一句话:一个会被灵潮反复改写地形的边境世界。', ); expect(foundationText).toContain('玩家开局:玩家是带着旧名单回来的前巡夜人。'); expect(foundationText).toContain('主题气质:边境、灵潮 / 紧张、潮湿'); expect(foundationText).toContain('关键关系:沈砺 · 灰炬向导'); expect(foundationText).toContain('标志元素:会逆向蔓延的潮雾'); }); it('builds anchor pack from creator intent and keeps locked ids', () => { const intent = { ...createEmptyCustomWorldCreatorIntent('card'), worldHook: '边境世界', coreConflicts: ['裂潮失控'], keyFactions: [ { id: 'faction-1', name: '巡边司', publicGoal: '维持边境秩序', tension: '正在被旧案拖入裂潮', notes: '', locked: true, }, ], keyLandmarks: [ { id: 'landmark-1', name: '断桥旧哨', purpose: '边境咽喉', mood: '压迫', secret: '封桥旧令来源不明', locked: true, }, ], }; const anchorPack = buildCustomWorldAnchorPackFromIntent(intent); expect(anchorPack?.keyConflictSummaries).toEqual(['裂潮失控']); expect(anchorPack?.keyFactionSummaries[0]).toContain('巡边司'); expect(anchorPack?.lockedAnchorIds).toEqual( expect.arrayContaining(['faction-1', 'landmark-1']), ); }); it('normalizes sparse creator intent payloads', () => { const intent = normalizeCustomWorldCreatorIntent({ sourceMode: 'card', worldHook: '雾海边城', themeKeywords: ['雾海', '旧案'], keyCharacters: [ { name: '梁砺', role: '断桥巡守', }, ], }); expect(intent?.sourceMode).toBe('card'); expect(intent?.keyCharacters[0]?.name).toBe('梁砺'); expect(intent?.keyCharacters[0]?.id).toBeTruthy(); }); it('merges creator intent patches without dropping unrelated anchors', () => { const baseIntent = { ...createEmptyCustomWorldCreatorIntent('freeform'), worldHook: '潮雾会改写地形的列岛世界。', playerPremise: '玩家是失职返乡的守灯人。', }; const merged = mergeCustomWorldCreatorIntent(baseIntent, { coreConflicts: ['守灯会与沉船商盟争夺航道解释权'], toneDirectives: ['冷峻'], }); if (!merged) { throw new Error('expected merged creator intent'); } expect(merged.worldHook).toBe('潮雾会改写地形的列岛世界。'); expect(merged.playerPremise).toBe('玩家是失职返乡的守灯人。'); expect(merged.coreConflicts).toEqual(['守灯会与沉船商盟争夺航道解释权']); expect(merged.toneDirectives).toEqual(['冷峻']); }); it('replaces array anchors when a patch marks explicit rewrite fields', () => { const merged = mergeCustomWorldCreatorIntent( { ...createEmptyCustomWorldCreatorIntent('freeform'), themeKeywords: ['海岛', '旧案'], coreConflicts: ['守灯会与沉船商盟争夺航道解释权'], }, { themeKeywords: ['宫廷', '悬疑'], coreConflicts: ['王庭继承人与旧灯塔盟约对抗'], replaceFields: ['themeKeywords', 'coreConflicts'], }, ); if (!merged) { throw new Error('expected merged creator intent'); } expect(merged.themeKeywords).toEqual(['宫廷', '悬疑']); expect(merged.coreConflicts).toEqual(['王庭继承人与旧灯塔盟约对抗']); }); it('evaluates readiness and limits clarifications to top gaps', () => { const readiness = evaluateCustomWorldCreatorIntentReadiness({ ...createEmptyCustomWorldCreatorIntent('freeform'), worldHook: '一个被潮雾切开的列岛世界。', themeKeywords: ['海岛'], toneDirectives: ['冷峻'], coreConflicts: ['旧灯塔正在被沉船商盟接管'], }); const clarifications = buildPendingClarifications( { ...createEmptyCustomWorldCreatorIntent('freeform'), worldHook: '一个被潮雾切开的列岛世界。', themeKeywords: ['海岛'], toneDirectives: ['冷峻'], coreConflicts: ['旧灯塔正在被沉船商盟接管'], }, readiness, ); expect(readiness.isReady).toBe(false); expect(readiness.completedKeys).toContain('world_hook'); expect(readiness.missingKeys).toContain('player_premise'); expect(clarifications).toHaveLength(3); expect(clarifications[0]?.targetKey).toBe('player_premise'); }); });