import { describe, expect, it } from 'vitest' import { World } from '../src/renderer/game/engine/runtime/World' import { GameFacade } from '../src/renderer/game/engine/runtime/ApiFacade' import { installCoreSystems, buildClock } from '../src/renderer/game/engine/runtime/clocks' import { emptyClock } from '../src/renderer/game/engine/runtime/clocks' import { CORE_PLUGINS } from '../src/renderer/game/engine/runtime/plugin-bootstrap' import { examplePlugin, brokenPlugin } from './helpers/examplePlugin' import { stateFingerprint, longRun } from './fingerprint.helper' import { findEvent } from '../src/renderer/game/engine/runtime/Systems/events' 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('PluginCore 插件协议', () => { it('内置三插件常驻且受保护', () => { const w = baseWorld('pl-a') const list = w.pluginList() expect(list.length).toBe(3) for (const p of list) { expect(p.installed).toBe(true) expect(p.enabled).toBe(true) expect(p.protected).toBe(true) } const ids = list.map((p) => p.id) expect(ids).toContain('core-systems') expect(ids).toContain('core-data') expect(ids).toContain('core-events') }) it('核心插件不可卸载(保护)', () => { const w = baseWorld('pl-b') const r = w.removePlugin('core-systems') expect(r.ok).toBe(false) }) it('依赖缺失拒绝安装;依赖满足则放行', () => { const w = baseWorld('pl-c') expect(w.installPlugin(brokenPlugin).ok).toBe(false) expect(w.installPlugin(examplePlugin).ok).toBe(true) expect(w.pluginList().length).toBe(4) }) it('示例安装后:消息池注入生效 + 山神庇佑加值;卸载后两清', () => { const w = baseWorld('pl-d') w.installPlugin(examplePlugin) expect(w.eventPoolIds()).toContain('demo-peaks') const x5 = w.state.members['x5'] x5.realm = { major: 'qi', minor: 1 } const p0 = x5.realmProgress // 推进到跨年触发年首庇佑 for (let i = 0; i < 13; i++) w.advanceMonth() expect(x5.realmProgress).toBeGreaterThan(p0) // 事件注入可被解析(findEvent 命中 demo 池 = 注入生效;确定性校验不赌概率) expect(findEvent('ev-demo-guardian', w)).toBeTruthy() // 卸载 expect(w.removePlugin('demo-peaks').ok).toBe(true) expect(w.eventPoolIds()).not.toContain('demo-peaks') expect(w.pluginList().length).toBe(3) }) it('remove 后时钟钩子计数恢复(真摘钩)', () => { const w = baseWorld('pl-e') const before = w.clock.subscriptionCount() w.installPlugin(examplePlugin) const mounted = w.clock.subscriptionCount() expect(mounted).toBe(before + 1) w.removePlugin('demo-peaks') expect(w.clock.subscriptionCount()).toBe(before) }) it('安装序确定性:同版本两次世界插件列表一致', () => { const a = baseWorld('pl-f') const b = baseWorld('pl-f') expect(JSON.stringify(a.pluginList())).toBe(JSON.stringify(b.pluginList())) }) it('默认管线金钟罩不受插件层影响', () => { expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('9531969e') expect(stateFingerprint(longRun('bell-seed-2').state)).toBe('f2ecc3e1') expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('aeefac96') }) it('facade 插件查询与 about.plugins', () => { const w = baseWorld('pl-g') const f = new GameFacade(w, 1) const pl = f.query('plugins') as { list: { id: string }[] } expect(pl.list.length).toBe(3) expect(f.about().plugins).toBe(3) }) it('订阅可捕获 plugin 生命周期事件', () => { const w = baseWorld('pl-h') const f = new GameFacade(w, 1) const events: string[] = [] const unsub = f.subscribe((e) => { if (e.type === 'plugin') events.push((e as { action: string }).action) }) w.installPlugin(examplePlugin) w.removePlugin('demo-peaks') expect(events).toContain('install') expect(events).toContain('remove') unsub() }) it('buildClock 兼容路径仍完整注册(测试/工具用)', () => { const clock = buildClock() expect(clock.subscriptionCount()).toBeGreaterThanOrEqual(10) const empty = emptyClock() expect(empty.subscriptionCount()).toBe(0) void installCoreSystems }) })