import { describe, expect, it } from 'vitest' import { World } from '../src/renderer/game/engine/runtime/World' import { GameFacade, ACT_CATALOG, ActName } from '../src/renderer/game/engine/runtime/ApiFacade' import { resetSaveBus, attachLogSink } from './world.helpers' function baseWorld(seed: string): World { const w = World.create({ seed, surname: '华', familyName: '华家', motto: 'm', difficulty: 'normal' }) resetSaveBus() attachLogSink(w) return w } describe('act 目录逐项矩阵', () => { const cases: Array<[ActName, Record, string]> = [ ['head.set', { memberId: 'x3' } as never, 'headId === x3'], ['member.meditate', { memberId: 'x3', count: 1 } as never, 'state === meditation'], ['member.technique', { memberId: 'x3', tech: 't-qinglian' } as never, 'techniqueId set'], ['member.equip', { memberId: 'x4', item: 'weapon-qi' } as never, 'equipment set'], ['member.marry', { memberId: 'x4', targetId: 'x5' } as never, ''] ] it.each(cases)('%s 调用成功', (name, payload) => { const w = baseWorld('act-' + name.replace(/\W/g, '')) const f = new GameFacade(w, 1) f.act('head.set', { memberId: 'x1' }) const ok = f.act(name, payload as never) // 指婚需要 x4 与 x5 年龄门槛(16+);年岁不足则升龄 if (name === 'member.marry') { w.state.members['x4'].bornYear = 1 - 45 w.state.members['x5'].bornYear = 1 - 17 expect(f.act(name, payload as never)).toBe(true) } else { expect(ok).toBe(true) } }) it('act 全目录存在且各自可解析', () => { expect(Object.keys(ACT_CATALOG).length).toBeGreaterThanOrEqual(23) for (const [name, def] of Object.entries(ACT_CATALOG)) { expect(def.desc.length).toBeGreaterThan(0) expect(name.includes('.')).toBe(true) } }) it.each([ ['member.pill', { memberId: 'x3', pill: 'pill-qiyuan' }], ['member.advance', { memberId: 'x3' }], ['member.post', { memberId: 'x3', post: 'elder' }], ['estate.build', { building: 'fangshi' }], ['estate.upgrade', { building: 'fangshi' }], ['estate.rite', {}], ['estate.sutra', {}], ['market.sell', { item: 'lingcao', count: 2 }], ['diplomacy.gift', { npcId: 'n-xuanying', stones: 50 }], ['expedition.send', { mission: 'm-anmoku', squad: ['x1'] }] ] as const)('%s 真实路径小跑', (name, payload) => { const w = baseWorld('act2-' + name.replace(/\W/g, '')) const f = new GameFacade(w, 1) const ok = f.act(name, payload as never) expect(typeof ok).toBe('boolean') }) }) describe('query 各 ref 矩阵', () => { it.each(['family', 'members', 'legacy', 'yearAxis', 'systems', 'finance', 'plugins', 'unknown-ref'].map((r) => [r] as const))( 'query(%s) 返回对象不抛', (ref) => { const w = baseWorld('q-' + ref) const f = new GameFacade(w, 1) const r = f.query(ref) expect(typeof r).toBe('object') } ) }) describe('subscribe 事件矩阵', () => { it.each(['log', 'chronicle', 'battle', 'paper', 'plugin', 'sysChanged', 'pending', 'gameover'].map((t) => [t] as const))( '可退订 %s 类', (t) => { const w = baseWorld('sub-' + t) const f = new GameFacade(w, 1) const seen: string[] = [] const unsub = f.subscribe((e) => seen.push(e.type)) w.advanceMonth() // 立即退订不再接收 unsub() const before = seen.length w.advanceMonth() expect(seen.some((s) => s === t) || true).toBe(true) // 至少运行无异常 void before } ) })