/** * PluginManager 完整测试 */ import { PluginManager } from '../../src/plugin/index'; import type { MetonaPlugin } from '../../src/constants'; describe('PluginManager', () => { let pm: PluginManager; beforeEach(() => { pm = new PluginManager(); }); // ---- 注册/卸载 ---- describe('注册和卸载', () => { it('注册插件', () => { const plugin: MetonaPlugin = { name: 'test-plugin', version: '1.0.0', install: jest.fn(), destroy: jest.fn(), }; pm.register(plugin); expect(pm.getPlugins()).toHaveLength(1); expect(plugin.install).toHaveBeenCalled(); }); it('按优先级排序', () => { const p1: MetonaPlugin = { name: 'low', version: '1.0', priority: 1, install: jest.fn(), destroy: jest.fn() }; const p2: MetonaPlugin = { name: 'high', version: '1.0', priority: 10, install: jest.fn(), destroy: jest.fn() }; const p3: MetonaPlugin = { name: 'mid', version: '1.0', priority: 5, install: jest.fn(), destroy: jest.fn() }; pm.register(p1); pm.register(p2); pm.register(p3); const plugins = pm.getPlugins(); expect(plugins[0].name).toBe('high'); expect(plugins[1].name).toBe('mid'); expect(plugins[2].name).toBe('low'); }); it('卸载插件调用 destroy', () => { const destroyFn = jest.fn(); const plugin: MetonaPlugin = { name: 'test', version: '1.0', install: jest.fn(), destroy: destroyFn, }; pm.register(plugin); pm.unregister('test'); expect(destroyFn).toHaveBeenCalled(); expect(pm.getPlugins()).toHaveLength(0); }); it('卸载不存在的插件不报错', () => { expect(() => pm.unregister('nonexistent')).not.toThrow(); }); it('getPlugins 返回副本', () => { const plugin: MetonaPlugin = { name: 'test', version: '1.0', install: jest.fn(), destroy: jest.fn(), }; pm.register(plugin); const plugins = pm.getPlugins(); plugins.push({ name: 'external', version: '1.0', install: jest.fn(), destroy: jest.fn() }); expect(pm.getPlugins()).toHaveLength(1); }); }); // ---- 钩子 ---- describe('钩子系统', () => { it('注册和触发钩子', async () => { const callback = jest.fn(); pm.on('beforeInsert', callback); await pm.trigger('beforeInsert', { id: '1' }); expect(callback).toHaveBeenCalledWith({ id: '1' }); }); it('多个回调', async () => { const cb1 = jest.fn(); const cb2 = jest.fn(); pm.on('afterQuery', cb1); pm.on('afterQuery', cb2); await pm.trigger('afterQuery', 'SELECT *', []); expect(cb1).toHaveBeenCalled(); expect(cb2).toHaveBeenCalled(); }); it('卸载钩子', async () => { const cb = jest.fn(); pm.on('beforeUpdate', cb); pm.off('beforeUpdate', cb); await pm.trigger('beforeUpdate'); expect(cb).not.toHaveBeenCalled(); }); it('触发未注册的钩子不报错', async () => { await expect(pm.trigger('beforeInsert')).resolves.toBeUndefined(); }); it('卸载不存在的回调不报错', () => { expect(() => pm.off('beforeInsert', jest.fn())).not.toThrow(); }); it('异步回调', async () => { const cb = jest.fn().mockResolvedValue(undefined); pm.on('beforeQuery', cb); await pm.trigger('beforeQuery'); expect(cb).toHaveBeenCalled(); }); }); // ---- 生命周期钩子覆盖 ---- describe('全部钩子类型', () => { const hooks = [ 'beforeCreateTable', 'afterCreateTable', 'beforeDropTable', 'afterDropTable', 'beforeInsert', 'afterInsert', 'beforeUpdate', 'afterUpdate', 'beforeDelete', 'afterDelete', 'beforeQuery', 'afterQuery', 'beforeTransaction', 'afterTransaction', ] as const; hooks.forEach((hook) => { it(`钩子 ${hook} 可注册和触发`, async () => { const cb = jest.fn(); pm.on(hook, cb); await pm.trigger(hook); expect(cb).toHaveBeenCalledTimes(1); }); }); }); // ---- destroy ---- describe('销毁', () => { it('destroy 调用所有插件的 destroy', () => { const d1 = jest.fn(); const d2 = jest.fn(); pm.register({ name: 'p1', version: '1.0', install: jest.fn(), destroy: d1 }); pm.register({ name: 'p2', version: '1.0', install: jest.fn(), destroy: d2 }); pm.destroy(); expect(d1).toHaveBeenCalled(); expect(d2).toHaveBeenCalled(); expect(pm.getPlugins()).toHaveLength(0); }); it('destroy 清空钩子', async () => { const cb = jest.fn(); pm.on('beforeInsert', cb); pm.destroy(); await pm.trigger('beforeInsert'); expect(cb).not.toHaveBeenCalled(); }); it('插件 destroy 出错不影响销毁流程', () => { const badPlugin: MetonaPlugin = { name: 'bad', version: '1.0', install: jest.fn(), destroy: () => { throw new Error('destroy failed'); }, }; const goodPlugin: MetonaPlugin = { name: 'good', version: '1.0', install: jest.fn(), destroy: jest.fn(), }; pm.register(badPlugin); pm.register(goodPlugin); expect(() => pm.destroy()).not.toThrow(); expect(goodPlugin.destroy).toHaveBeenCalled(); }); }); });