release: v0.5.1 — 存储后端生产级硬化(CRC-32/全库加密/WAL分片/页面化存储/多标签页锁/e2e)+ 深度审查修复(假实现接线/死代码清理)
CI / test (18.x) (push) Successful in 10m10s
CI / test (20.x) (push) Successful in 10m10s
CI / test (22.x) (push) Successful in 10m6s
CI / e2e (push) Successful in 9m51s
CI / test (24.x) (push) Successful in 10m28s

This commit is contained in:
thzxx
2026-08-10 12:07:00 +08:00
parent cff98b0903
commit 334067d89e
88 changed files with 15713 additions and 10626 deletions
+140
View File
@@ -0,0 +1,140 @@
/**
* v0.5.1 — CRUD 生命周期钩子真实接线测试
*
* 此前 beforeInsert/afterInsert/beforeUpdate/afterUpdate/beforeDelete/afterDelete
* 六个钩子虽在 HookName 类型与文档中宣称("14 种钩子"),但从未被任何路径触发。
* 本次验证 Table API 与 SQL query 两条路径均真实触发全部 14 个钩子。
*/
import { MetonaSqlark } from '../src/core';
import 'fake-indexeddb/auto';
async function createDb(name: string): Promise<MetonaSqlark> {
const db = new MetonaSqlark({ name: `hooks-${name}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`, mode: 'memory' });
await db.init();
await db.defineTable('users', {
id: { type: 'string', primaryKey: true },
name: { type: 'string' },
age: { type: 'number' },
});
return db;
}
describe('v0.5.1 — CRUD hooks 真实接线', () => {
it('Table.insert / insertMany 触发 beforeInsert + afterInsert', async () => {
const db = await createDb('table-insert');
const events: string[] = [];
db.on('beforeInsert', (rows) => { events.push(`before:${(rows as unknown[]).length}`); });
db.on('afterInsert', (rows, pks) => { events.push(`after:${(rows as unknown[]).length}:${(pks as string[]).length}`); });
await db.table('users').insert({ id: '1', name: 'A' });
await db.table('users').insertMany([{ id: '2', name: 'B' }, { id: '3', name: 'C' }]);
expect(events).toEqual([
'before:1', 'after:1:1',
'before:2', 'after:2:2',
]);
await db.close();
});
it('Table.update().execute() 触发 beforeUpdate + afterUpdate(含影响行数)', async () => {
const db = await createDb('table-update');
await db.table('users').insertMany([{ id: '1', name: 'A', age: 1 }, { id: '2', name: 'B', age: 2 }]);
const events: string[] = [];
db.on('beforeUpdate', (query, updates) => { events.push(`before:${(updates as Record<string, unknown>).name}`); });
db.on('afterUpdate', (_query, _updates, count) => { events.push(`after:${count}`); });
await db.table('users').update({ name: 'X' }).where({ age: { $gt: 1 } }).execute();
expect(events).toEqual(['before:X', 'after:1']);
await db.close();
});
it('Table.delete().execute() 触发 beforeDelete + afterDelete(含影响行数)', async () => {
const db = await createDb('table-delete');
await db.table('users').insertMany([{ id: '1', name: 'A' }, { id: '2', name: 'B' }]);
const events: string[] = [];
db.on('beforeDelete', (query) => { events.push(`before:${(query as { where: Record<string, unknown> }).where.id}`); });
db.on('afterDelete', (_query, count) => { events.push(`after:${count}`); });
await db.table('users').delete().where({ id: '1' }).execute();
expect(events).toEqual(['before:1', 'after:1']);
await db.close();
});
it('SQL query 路径触发全部 CRUD hooksINSERT/UPDATE/DELETE', async () => {
const db = await createDb('sql-crud');
await db.query("INSERT INTO users VALUES ('1', 'Alice', 30)");
const events: string[] = [];
db.on('beforeInsert', () => events.push('beforeInsert'));
db.on('afterInsert', () => events.push('afterInsert'));
db.on('beforeUpdate', () => events.push('beforeUpdate'));
db.on('afterUpdate', () => events.push('afterUpdate'));
db.on('beforeDelete', () => events.push('beforeDelete'));
db.on('afterDelete', () => events.push('afterDelete'));
await db.query("INSERT INTO users VALUES ('2', 'Bob', 25)");
await db.query("UPDATE users SET age = 26 WHERE id = '2'");
await db.query("DELETE FROM users WHERE id = '1'");
expect(events).toEqual([
'beforeInsert', 'afterInsert',
'beforeUpdate', 'afterUpdate',
'beforeDelete', 'afterDelete',
]);
await db.close();
});
it('SQL INSERT 多行语句按列名映射触发 afterInsert', async () => {
const db = await createDb('sql-insert-cols');
const seen: unknown[] = [];
db.on('afterInsert', (_rows, pks) => seen.push(pks));
await db.query("INSERT INTO users (id, name) VALUES ('9', 'Zed')");
expect(seen).toEqual([['9']]);
await db.close();
});
it('全部 14 个钩子均可注册并真实触发', async () => {
const db = await createDb('all-hooks');
const fired = new Set<string>();
const hooks = [
'beforeCreateTable', 'afterCreateTable',
'beforeDropTable', 'afterDropTable',
'beforeInsert', 'afterInsert',
'beforeUpdate', 'afterUpdate',
'beforeDelete', 'afterDelete',
'beforeQuery', 'afterQuery',
'beforeTransaction', 'afterTransaction',
] as const;
for (const h of hooks) db.on(h, () => fired.add(h));
await db.defineTable('t2', { id: { type: 'string', primaryKey: true } }); // createTable hooks
await db.table('t2').insert({ id: '1' }); // insert hooks
await db.table('t2').update({}).where({ id: '1' }).execute(); // update hooks
await db.table('t2').delete().where({ id: '1' }).execute(); // delete hooks
await db.query('SELECT * FROM t2'); // query hooks
await db.transaction(async () => { /* noop */ }); // transaction hooks
await db.dropTable('t2'); // dropTable hooks
for (const h of hooks) {
expect(fired.has(h)).toBe(true);
if (!fired.has(h)) throw new Error(`hook ${h} 应被触发`);
}
await db.close();
});
it('事务路径不触发 CRUD hooksTable 无 hooks 回调)', async () => {
const db = await createDb('tx-hooks');
let fired = false;
db.on('beforeInsert', () => { fired = true; });
await db.transaction(async (trx) => {
await trx.table('users').insert({ id: '1', name: 'Tx' });
});
expect(fired).toBe(false);
await db.close();
});
});