/** * v0.8.0 回归套件 —— 插件 priority 语义(G6 宣称与实现一致) * ============================================================================ * 修复前:`PluginManager.register()` 会把插件**按 priority 插入数组的正确位置**, * 但 `install()` 是在 `register()` 内部**立即**调用的 —— 于是 install 与钩子的 * 实际执行顺序仍等于 `config.plugins` 的数组顺序。 * 实测(priority 为 low=1 / high=100 / mid=50,数组顺序 low,high,mid): * 钩子顺序 low→high→mid,只有 `getPlugins()` 是 high,mid,low。 * 而 README / CONTRIBUTING / `constants.ts` 一直宣称"priority 越大越先执行" * —— 文档与实现不符。本套件锁定**让实现符合文档**后的行为(priority 是用户 * 可见的配置项,静默无效比没有这个字段更糟)。 * * 实现要点:Core 在注册前按 priority **稳定**排序(同优先级保持数组顺序), * 因此 `install()` 与由它注册的钩子都按优先级降序执行。 */ import { describe, it, expect } from '@jest/globals'; import { MetonaSqlark } from '../src/core'; import { PluginManager } from '../src/plugin/index'; import type { MetonaPlugin } from '../src/constants'; function makePlugin(name: string, priority: number, log: string[]): MetonaPlugin { return { name, version: '1.0.0', priority, install: (db?: unknown) => { log.push(`install:${name}`); // register(plugin) 不带 db 时(纯 PluginManager 用法)没有 db 实例可挂钩子 if (db) (db as MetonaSqlark).on('afterQuery', () => { log.push(`hook:${name}`); }); }, destroy: () => log.push(`destroy:${name}`), } as MetonaPlugin; } describe('[v0.8.0] 插件 priority', () => { it('install 与钩子都按 priority 降序执行(同优先级保持数组顺序)', async () => { const log: string[] = []; const db = await MetonaSqlark.create({ name: 'prio-order', mode: 'memory', plugins: [makePlugin('low', 1, log), makePlugin('high', 100, log), makePlugin('mid', 50, log)], }); await db.defineTable('t', { id: { type: 'string', primaryKey: true } }); await db.query("INSERT INTO t VALUES ('1')"); expect(log.filter((l) => l.startsWith('install:'))).toEqual([ 'install:high', 'install:mid', 'install:low', ]); expect(log.filter((l) => l.startsWith('hook:'))).toEqual([ 'hook:high', 'hook:mid', 'hook:low', ]); await db.close(); }); it('同优先级保持 config 数组顺序(稳定排序)', async () => { const log: string[] = []; const db = await MetonaSqlark.create({ name: 'prio-stable', mode: 'memory', plugins: [makePlugin('a', 5, log), makePlugin('b', 5, log), makePlugin('c', 5, log)], }); await db.defineTable('t', { id: { type: 'string', primaryKey: true } }); await db.query("INSERT INTO t VALUES ('1')"); expect(log.filter((l) => l.startsWith('install:'))).toEqual(['install:a', 'install:b', 'install:c']); await db.close(); }); it('缺省 priority 视为 0(排在显式正优先级之后)', async () => { const log: string[] = []; const noPriority = { ...makePlugin('none', 0, log) } as MetonaPlugin & { priority?: number }; delete noPriority.priority; const db = await MetonaSqlark.create({ name: 'prio-default', mode: 'memory', plugins: [noPriority, makePlugin('explicit', 10, log)], }); await db.defineTable('t', { id: { type: 'string', primaryKey: true } }); await db.query("INSERT INTO t VALUES ('1')"); expect(log.filter((l) => l.startsWith('install:'))).toEqual(['install:explicit', 'install:none']); await db.close(); }); it('PluginManager.getPlugins() 按 priority 降序(回归护栏)', () => { const log: string[] = []; const pm = new PluginManager(); pm.register(makePlugin('low', 1, log)); pm.register(makePlugin('high', 100, log)); pm.register(makePlugin('mid', 50, log)); expect(pm.getPlugins().map((p) => p.name)).toEqual(['high', 'mid', 'low']); }); });