import { describe, expect, it } from 'vitest'; import { type CustomWorldProfile,WorldType } from '../../types'; import { buildKnowledgeGraph } from './knowledgeGraph'; const profile: CustomWorldProfile = { id: 'world-1', settingText: '裂潮边城', name: '裂潮边城', subtitle: '旧案回响', summary: '一座在裂潮和旧案之间摇摇欲坠的边城。', tone: '紧张、克制、暗流涌动', playerGoal: '查清封桥旧令', templateWorldType: WorldType.WUXIA, majorFactions: ['巡边司'], coreConflicts: ['封桥旧案再起'], attributeSchema: { id: 'schema', worldId: 'world-1', schemaVersion: 1, generatedFrom: { worldType: WorldType.CUSTOM, worldName: '裂潮边城', settingSummary: '裂潮边城', tone: '紧张、克制、暗流涌动', conflictCore: '封桥旧案再起', }, slots: [], }, playableNpcs: [], storyNpcs: [ { id: 'npc-1', name: '梁砺', title: '断桥巡守', role: '巡守', description: '守着断桥与旧哨火的巡守。', backstory: '旧案爆发时,他是最后一个封桥的人。', personality: '警觉直接,不喜欢绕弯。', motivation: '不想让旧案再次借裂潮翻上来。', combatStyle: '长兵先压,再卡住路口。', initialAffinity: 6, relationshipHooks: ['封桥', '旧哨火'], tags: ['巡守', '断桥'], backstoryReveal: { publicSummary: '他只承认自己还在守桥。', chapters: [ { id: 'surface', title: '表层来意', affinityRequired: 0, teaser: '桥还不能放开。', content: '他总先谈桥和路。', contextSnippet: '桥还不能放开。', }, ], }, skills: [], initialItems: [], narrativeProfile: { publicMask: '他只承认自己还在守桥。', firstContactMask: '先别问旧案,桥口还不能放开。', visibleLine: '他把全部注意力都压在桥口和来路上。', hiddenLine: '他真正盯着的是封桥旧令背后的名字。', contradiction: '嘴上只说守桥,提到名单时却会明显收紧语气。', debtOrBurden: '封桥那夜的后果还压在他身上。', taboo: '封桥令', immediatePressure: '裂潮再逼桥口,他不敢轻易放人过去。', relatedThreadIds: ['thread-1'], relatedScarIds: ['scar-1'], reactionHooks: ['名单', '旧哨火'], }, }, ], items: [], landmarks: [], themePack: null, storyGraph: { visibleThreads: [ { id: 'thread-1', title: '封桥旧案', visibility: 'visible', summary: '封桥旧案再次被人提起。', conflictType: '调查', stakes: '边城安稳', involvedFactionIds: [], involvedActorIds: ['npc-1'], relatedLocationIds: ['bridge'], }, ], hiddenThreads: [], scars: [ { id: 'scar-1', title: '断桥旧痕', pastEvent: '封桥夜留下的旧痕。', publicResidue: '桥柱上还留着旧封痕。', hiddenTruth: '封桥令另有来头。', relatedActorIds: ['npc-1'], relatedLocationIds: ['bridge'], }, ], motifs: [], }, knowledgeFacts: null, threadContracts: null, }; describe('buildKnowledgeGraph', () => { it('builds actor facts from narrative profile and backstory chapters', () => { const facts = buildKnowledgeGraph(profile); expect(facts.some((fact) => fact.title.includes('公开面'))).toBe(true); expect(facts.some((fact) => fact.title.includes('禁区') && fact.visibility === 'forbidden')).toBe(true); expect(facts.some((fact) => fact.title === '表层来意')).toBe(true); }); });