import { describe, expect, it } from 'vitest' import { World } from '../src/renderer/game/engine/runtime/World' import { modParse } from '../src/renderer/game/engine/runtime/modSchema' import { modToPlugin, modPreflight } from '../src/renderer/game/engine/runtime/modManager' import { examplePlugin } from '../src/renderer/game/engine/runtime/demo-plugins' import { registerPluginFactory } from '../src/renderer/game/engine/runtime/World' import { engineFromSnapshot } from '../src/renderer/game/engine/GameEngine' function mk(seed: string): World { return World.create({ seed, surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' }) } const TEXT = (id: string, extra: Record = {}) => JSON.stringify({ id, name: id, version: '1.0.0', schema: 1, author: 'tester', data: { ...extra } }) describe('矩阵:MOD schema 组合', () => { it.each([ ['空数据', {}], ['纯事件', { events: [{ id: 'ev-a1', name: 'x', category: 'daily', weight: 2, text: 't', options: [{ label: 'a', eff: {} }] }] }], ['纯模板', { npcs: [{ id: 'n-t22', name: 't22氏', region: 'r', style: 's', desc: 'd', leaderRealm: 'foundation', initialPower: 150, powerGrowth: [3, 8] }] }], ['纯词库', { worldgen: { fams: ['电'], regions: ['电谷'] } }], ['纯丹方', { pills: [{ output: 'p-t1', name: 'x', danfangLevel: 1, stones: 10, lingcao: 5, beastcore: 0, desc: '' }] }], ['纯灾因', { calamities: [{ name: '试灾', family: { lingcao: 0.9 }, effect: { beastcore: -0.1 } }] }], ['全混合', { events: [{ id: 'ev-mix1', name: 'x', category: 'daily', weight: 1, text: 't', options: [{ label: 'a', eff: {} }] }], npcs: [{ id: 'n-mix1', name: '混氏', region: 'r', style: 's', desc: 'd', leaderRealm: 'qi', initialPower: 100, powerGrowth: [2, 6] }], worldgen: { fams: ['混'] }, pills: [{ output: 'p-mix1', name: 'x', danfangLevel: 1, stones: 5, lingcao: 3, beastcore: 0, desc: '' }], calamities: [{ name: '混灾', family: {}, effect: {} }] }] ])('MOD %s:安装→清单/导出/回滚闭环', (label, data) => { const w = mk(`mbl-${label}`) const pr = modParse(TEXT(`mk-${label.replace(/[^\w-]/g, 'm')}`, data)) if (!pr.ok) return expect(pr.ok).toBe(true) expect(w.installPlugin(modToPlugin(pr.pack)).ok).toBe(true) expect(w.pluginList().some((p) => p.id === `mod-mk-${label.replace(/[^\w-]/g, 'm')}`)).toBe(true) const bundle = w.exportModBundle() expect(bundle).toContain(`mk-${label.replace(/[^\w-]/g, 'm')}`) expect(w.removePlugin(`mod-mk-${label.replace(/[^\w-]/g, 'm')}`).ok).toBe(true) expect(w.pluginList().some((p) => p.id === `mod-mk-${label.replace(/[^\w-]/g, 'm')}`)).toBe(false) }) it.each([ ['重装同包', 'dup-1'], ['重装同包2', 'dup-2'], ['重装同包3', 'dup-3'] ])('%s:重复安装被拒(幂等不炸)', (label, id) => { const w = mk(`di-${label}`) const pr = modParse(TEXT(id)) if (!pr.ok) return expect(pr.ok).toBe(true) const plug = modToPlugin(pr.pack) expect(w.installPlugin(plug).ok).toBe(true) expect(w.installPlugin(plug).ok).toBe(false) }) it.each([ ['合法', 'ev-safe-x', 'ev-other-name', true], ['撞核心', 'ev-legacypass', 'ev-x2', false], ['撞动态', 'ev-raid-x', 'ev-x3', false] ])('preflight %s:冲突矩阵', (_label, evId, uniqId, expectOk) => { const w = mk(`pf-${_label}`) const pr = modParse(TEXT(`m-${_label.replace(/[^\w-]/g, 'x')}`, { events: [{ id: evId, name: uniqId, category: 'daily', weight: 1, text: 't', options: [{ label: 'a', eff: {} }] }] })) if (!pr.ok) return expect(pr.ok).toBe(true) const r = modPreflight(w, pr.pack) expect(r.ok).toBe(expectOk) }) it.each(Array.from({ length: 10 }, (_, i) => [`seed-${i}`]))('持久化往返 %s:装→快照→重载→卸载', (seed) => { const w = mk(`pp-${seed}`) const pr = modParse(TEXT(`pp-${seed}`)) if (!pr.ok) return expect(pr.ok).toBe(true) w.installPlugin(modToPlugin(pr.pack)) const snap = JSON.parse(JSON.stringify(w.state)) // 注册表工厂注册后 engineFromSnapshot(构造期读档真实重装) registerPluginFactory(`mod-pp-${seed}`, () => modToPlugin(pr.pack as never) as never) const e3 = engineFromSnapshot(snap as never) expect(e3.world.pluginList().some((p) => p.id === `mod-pp-${seed}`)).toBe(true) }) }) describe('矩阵:插件启停/能力卡', () => { it.each([ ['cap-1', 'demo-peaks', 'demo-guardian'], ['cap-2', 'demo-peaks', 'demo-guardian'], ['cap-3', 'demo-peaks', 'demo-guardian'] ] as const)('%s:能力卡随启停联动', (seed, pid, capId) => { const w = mk(`cg-${seed}`) w.installPlugin(examplePlugin) expect(w.sysEnabled(capId)).toBe(true) const r = w.setPluginEnabled(pid, false) if (r.ok) expect(w.sysEnabled(capId)).toBe(false) else expect(w.sysEnabled(capId)).toBe(true) // 已装即启用 }) it.each([['k6'], ['k7'], ['k8']])('系统卡开关 %s:启停往返合法', (seed) => { const w = mk(`ks-${seed}`) expect(w.sysEnabled('production')).toBe(true) const first = w.toggleSystem('production') // 返回切换后状态(false) expect(w.sysEnabled('production')).toBe(first) const second = w.toggleSystem('production') expect(w.sysEnabled('production')).toBe(second) expect(first).not.toBe(second) }) })