docs(G6): 宣称与实现一致性收口 —— priority 真正生效、MVCC/backup/引擎数/打包 全部对齐
独立核验(12 条宣称逐条对源码验证)发现 5 处**硬伤**与 2 处**数字过期**, 本提交按"能改代码就让宣称成立、改不动就如实描述"的原则全部收口。 让实现符合文档(2 处): 1. **插件 priority 此前不生效** — `register()` 虽按 priority 插入数组,但 `install()` 在 register 内**立即**执行,因此 install 与钩子顺序 = config 数组 顺序(实测 priority low=1/high=100/mid=50 时钩子按 low→high→mid 触发, 只有 `getPlugins()` 是 high,mid,low)。而 README/CONTRIBUTING/constants 一直宣称"越大越先执行"。 现在 Core 注册前按 priority **稳定降序**排序(同优先级保持数组顺序), install 与钩子都按优先级执行 → 宣称成立。新增 `tests/v080-plugin-priority.test.ts` 锁定 install 顺序、钩子顺序、稳定性、缺省值。 2. **连接池静态方法不在类型系统里** — `MetonaSqlark.connect/disconnect/ disconnectAll/getActiveConnections` 由 connection-manager 用 `as unknown as Record<string, unknown>` 注入,README 的连接池表格在 TypeScript 下全部 TS2339。现在在类上声明为可选静态成员,注入处去掉断言。 如实描述(3 处): 3. **MVCC 快照隔离**(README 三处 + 实现对照)— `snapshotLsn` / `prevVersion` 只写不读,事务读走 `txnSnapshot`+LSM,commit 即清理版本链,并发 `beginTransaction` 抛 `TX_ACTIVE`。改为"快照回滚(事务串行,非 MVCC 隔离)", 并在 README 架构图与维护语句表里同步措辞。 4. **"存储引擎(5 种)"** — 实际是 4 种模式 + 3 种后端,引擎类只有 4 个 (Memory / KVStore / Hybrid / Aria),OPFS 是后端而非引擎。标题与条目已改写, 并写明"`disk`/`hybrid` 恒用 KVStore"。 5. **`diskEngine` 生效范围** — 仅 `mode:'aria'` 生效;`constants.ts` 的注释 此前写成"仅 mode='disk'|'hybrid' 时生效"(正好写反),已改正;README 配置表、 快速开始示例与 Aria 示例同步标注。 数字口径统一(可复现): - 测试 1872(90 套件)+ 14 e2e,另 4 个重型套件在独立 CI job 串行运行; - 覆盖率 语句 90.43% / 分支 82.21% / 函数 94.27% / 行 93.44%; - README 明确写出**产出这些数字的完整命令**(与 CI 常规 job 一致), 并要求改动覆盖范围/阈值时同步更新表格(G5)。 - CHANGELOG 0.8.0 条目与 site 首页/文档页同步。 另修 **CONTRIBUTING 的钩子契约**:明确写出"返回值被忽略(不能取消/改写)、 就地改参数在 Table API 生效、抛异常可取消、SQL 路径的 beforeInsert 收到副本" —— 此前只写 "allow intercepting",容易被理解为返回值可改变行为。 验证:全量 90 套件 / 1872 测试通过(+4 重型套件);覆盖率四项均高于阈值; typecheck(src+tests)、lint、build 零错误零告警;e2e 14 项通过;dist 已重建。
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user