Files
thzxx e2a590c5b1 feat: metona-sqlark v0.1.12 — 前端TypeScript关系型数据库
- 4种存储引擎:Memory / IndexedDB / OPFS / Hybrid
- 完整SQL支持:SELECT/INSERT/UPDATE/DELETE/JOIN/GROUP BY/HAVING/DISTINCT
- Query Builder链式API + TypeScript泛型支持
- 聚合函数:COUNT/SUM/AVG/MIN/MAX
- 事务、插件系统(14 hooks)、发布订阅、数据迁移、导入导出
- React/Vue框架集成
- 264个测试用例,93.46%覆盖率
- 零运行时依赖
2026-07-26 15:00:01 +08:00

183 lines
5.3 KiB
TypeScript

/**
* 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();
});
});
});