test(P0): v0.8.0 验证基座与工程门禁根治

工作流 C-1 / C-3 前半 + 测试代码类型检查。

【故障注入基座】新增 tests/helpers/storage-harness.ts + faulty-backend.ts
- TransactionalFileStore:忠实 OPFS 提交语义(close 才可见)+ 字节级故障注入
  (failNextWrite/Append/Delete、truncateAppendTo 撕裂写、crashPending 真崩溃)
- 删除旧 opfs-mock:读返回内部引用、keepExistingData:false 不截断、close 空实现
  导致"提交前可见"等真实缺陷无法被测出(31 个测试文件迁移至新 harness)
- 删除 aria-opfs-backend 内的第三份重复 mock(含从未被断言使用的 writeCalls 死代码
  与 entry.content.subarray 恒等分支)
- FaultyBackend:包装任意 IStorageBackend 注入故障;crash() 明确区别于 close()
  (后者是优雅停机,会刷完写队列 —— 这正是此前所有"崩溃恢复"测试的真相)
- 16 条基座自测证明注入真的生效(含 close 不能当崩溃的对照组)

【覆盖率口径】jest.config.cjs
- 移除 '!src/**/index.ts'(该 glob 把 AriaEngine 主实现等 15 个实现文件整体
  排除出统计,与 v0.2.6 曾承认过的问题同源),改为只排除纯类型声明文件并附理由
- 新增 coverageThreshold 门禁(此前完全不存在)
- 真实基线:语句 90.66% / 分支 82.94% / 函数 94.36% / 行 93.43%
- 修正 testMatch 使 tests/helpers 下的测试可被发现

【测试代码类型检查】tsconfig.test.json + npm run typecheck:tests
- 修复 103 个测试代码类型错误(此前 babel 剥离类型 + tsconfig 排除 tests,全部隐藏)
- 新增 tests/helpers/assertions.ts:nonNull/decode/rows/object/engineMethod/expectCode
  以断言收窄替代 as any
- 消除 21 个 lint warning(含 v043-hardening 中定义后从未调用的 mockOPFS 死代码)
- parser.test.ts 12 处 toBeDefined() 空断言升级为结构断言(并新增 AND/OR 优先级用例,
  当前红灯,对应总账第 11 项,将在工作流 A 修复)

【版本契约】新增 tests/version-contract.test.ts
- 校验 src VERSION / package.json / dist 三者一致,替代两处硬编码版本字面量

【CI 门禁】.gitea/workflows/ci.yml
- lint 去掉 continue-on-error(此前永远不让 CI 变红)
- 新增 tests 类型检查、--coverage 覆盖率门禁、dist 与源码同步校验
- 版本 0.7.4 升至 0.8.0
This commit is contained in:
thzxx
2026-09-14 21:03:06 +08:00
parent c8b59bd16f
commit 0dba1abf2a
65 changed files with 1533 additions and 454 deletions
+2 -3
View File
@@ -8,11 +8,10 @@ import { WAL, type WALStore } from '../../src/engine/aria/wal/log';
import { WALRecordType } from '../../src/engine/aria/types';
import { BufferPool } from '../../src/engine/aria/buffer/pool';
import { MemoryBackend } from '../../src/engine/aria/store/backend';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
describe('AriaEngine — Bloom + WAL + BufferPool', () => {
// ---- BloomFilter 完整测试 ----
+2 -2
View File
@@ -5,9 +5,9 @@ import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { MemoryBackend } from '../../src/engine/aria/store/backend';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
describe('AriaEngine — 批量扩展测试', () => {
let engine: AriaEngine;
+2 -2
View File
@@ -98,8 +98,8 @@ describe('AriaEngine — EvictionManager', () => {
}
it('access 更新 LRU', async () => {
let evicted = -1;
const em = new EvictionManager(3, async (p) => { evicted = p.pageId; });
let _evicted = -1;
const em = new EvictionManager(3, async (p) => { _evicted = p.pageId; });
const p = makePage(1);
em.add(p);
em.access(p);
+5 -5
View File
@@ -10,9 +10,9 @@
import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
/** 构造小缓存 + 小 MemTable 阈值的引擎,快速产生多个 SSTable */
function createSmallCacheEngine(bufferPoolPages = 2) {
@@ -61,7 +61,7 @@ describe('AriaEngine SSTable 缓存内存上限', () => {
for (let round = 0; round < 5; round++) {
const rows = await engine.find('users', { table: 'users', where: { age: 25 } });
expect(rows.length).toBe(10);
expect(lsm.cacheSize).toBeLessThanOrEqual(lsm.cacheLimitBytes);
expect(lsm.getCacheSize()).toBeLessThanOrEqual(lsm.getCacheLimit());
}
await engine.close();
@@ -82,7 +82,7 @@ describe('AriaEngine SSTable 缓存内存上限', () => {
expect(all.length).toBe(300);
const lsm = (engine as any).lsm;
expect(lsm.cacheSize).toBeLessThanOrEqual(lsm.cacheLimitBytes);
expect(lsm.getCacheSize()).toBeLessThanOrEqual(lsm.getCacheLimit());
await engine.close();
});
@@ -173,7 +173,7 @@ describe('AriaEngine SSTable 缓存内存上限', () => {
for (let batch = 0; batch < 10; batch++) {
await engine.insert('users', makeRows(30).map((r, i) => ({ ...r, id: `b${batch}_u${i}` })));
const lsm = (engine as any).lsm;
expect(lsm.cacheSize).toBeLessThanOrEqual(lsm.cacheLimitBytes);
expect(lsm.getCacheSize()).toBeLessThanOrEqual(lsm.getCacheLimit());
}
const all = await engine.find('users', { table: 'users' });
+2 -2
View File
@@ -9,9 +9,9 @@
import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
let idbCounter = 0;
function uniqueDB(): string {
+3 -3
View File
@@ -5,9 +5,9 @@
import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
describe('AriaEngine — 扩展边缘测试', () => {
let engine: AriaEngine;
@@ -69,7 +69,7 @@ describe('AriaEngine — 扩展边缘测试', () => {
});
it('多列 ORDER BY', async () => {
const rows = await engine.find('users', { table: 'users', orderBy: [{ column: 'age', direction: 'asc' }, { column: 'score', direction: 'desc' }] });
const rows = await engine.find('users', { table: 'users', orderBy: [{ column: 'age', direction: 'asc' }, { column: 'score', direction: 'desc' }] }) as Array<{ age: number }>;
expect(rows[0].age).toBeLessThanOrEqual(rows[4].age);
});
+10 -9
View File
@@ -15,9 +15,10 @@ import { EncryptedBackend } from '../../src/engine/aria/store/encrypted_backend'
import { MemoryBackend } from '../../src/engine/aria/store/backend';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
import { decode as decodeBytes } from '../helpers/assertions';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
let idbCounter = 0;
function uniqueDB(): string {
@@ -48,11 +49,11 @@ describe('AriaEngine — EncryptedBackend 单元', () => {
await backend.write('k1', payload);
const back = await backend.read('k1');
expect(back).not.toBeNull();
expect(new TextDecoder().decode(back)).toBe('hello encrypted world');
expect(decodeBytes(back)).toBe('hello encrypted world');
// 底层是密文(非明文)
const raw = await inner.read('k1');
const rawStr = new TextDecoder().decode(raw);
const rawStr = decodeBytes(raw, 'ciphertext');
expect(rawStr).not.toContain('hello encrypted world');
await backend.close();
@@ -65,8 +66,8 @@ describe('AriaEngine — EncryptedBackend 单元', () => {
const enc = (s: string) => new TextEncoder().encode(s).buffer;
await backend.writeMany({ a: enc('AAA'), b: enc('BBB'), c: enc('CCC') });
expect(new TextDecoder().decode(await backend.read('a'))).toBe('AAA');
expect(new TextDecoder().decode(await backend.read('c'))).toBe('CCC');
expect(decodeBytes(await backend.read('a'))).toBe('AAA');
expect(decodeBytes(await backend.read('c'))).toBe('CCC');
await backend.deleteMany(['a', 'c']);
expect(await backend.exists('a')).toBe(false);
@@ -94,8 +95,8 @@ describe('AriaEngine — EncryptedBackend 单元', () => {
expect((rawX as ArrayBuffer).byteLength).toBe((rawY as ArrayBuffer).byteLength);
// 但解密一致
expect(new TextDecoder().decode(await backend.read('x'))).toBe('same plaintext');
expect(new TextDecoder().decode(await backend.read('y'))).toBe('same plaintext');
expect(decodeBytes(await backend.read('x'))).toBe('same plaintext');
expect(decodeBytes(await backend.read('y'))).toBe('same plaintext');
await backend.close();
});
@@ -115,7 +116,7 @@ describe('AriaEngine — EncryptedBackend 单元', () => {
const backend2 = new EncryptedBackend(inner, 'pw');
await backend2.open('enc-unit-4');
await backend2.write('k2', new TextEncoder().encode('v2').buffer);
expect(new TextDecoder().decode(await backend2.read('k2'))).toBe('v2');
expect(decodeBytes(await backend2.read('k2'))).toBe('v2');
// 换密码被拒(keymeta 与旧密码绑定)——注意:MemoryBackend close 清空 store
// 因此在 close 前验证(backend2 仍持有 inner
+2 -2
View File
@@ -4,9 +4,9 @@
import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
describe('AriaEngine — 补充测试', () => {
let engine: AriaEngine;
+2 -2
View File
@@ -7,9 +7,9 @@ import { createSchema } from '../../src/table/schema';
import { MemoryBackend } from '../../src/engine/aria/store/backend';
import { checkFieldType } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
describe('AriaEngine — 最终扩展测试', () => {
let engine: AriaEngine;
+2 -2
View File
@@ -8,14 +8,14 @@
*/
import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
let counter = 0;
function uniqueDB(): string {
return `idxrace-${Date.now()}-${++counter}-${Math.random().toString(36).slice(2, 6)}`;
}
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
describe('AriaEngine — 二级索引完整性(P0 回归)', () => {
it('5 万行写入:索引查询与主表一致(修复前丢 106~771 条)', async () => {
+2 -2
View File
@@ -5,9 +5,9 @@
import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
describe('AriaEngine — 二级索引查询', () => {
let engine: AriaEngine;
+1 -1
View File
@@ -275,7 +275,7 @@ describe('MetonaSqlark + diskEngine: kv(高层 API', () => {
});
// 事务回滚
await expect(db.transaction(async (trx) => {
await expect(db.transaction(async (trx: { table: (n: string) => any }) => {
await trx.table('users').insert({ id: '1' });
throw new Error('boom');
})).rejects.toThrow('boom');
+2 -2
View File
@@ -12,9 +12,9 @@ import { AriaEngine } from '../../src/engine/aria/index';
import { DatabaseLock, lockName } from '../../src/engine/aria/locks';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
let idbCounter = 0;
function uniqueDB(): string {
+6 -3
View File
@@ -7,9 +7,10 @@ import { createSchema } from '../../src/table/schema';
import { QueryExecutor } from '../../src/query/executor';
import { parse } from '../../src/sql/parser';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
import { object } from '../helpers/assertions';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
describe('AriaEngine — ANALYZE/VACUUM/REINDEX + EXPLAIN', () => {
let engine: AriaEngine;
@@ -78,7 +79,9 @@ describe('AriaEngine — ANALYZE/VACUUM/REINDEX + EXPLAIN', () => {
const executor = new QueryExecutor(engine);
const selectStmt = parse('SELECT * FROM users WHERE id = \'1\'');
const explainStmt: any = { type: 'EXPLAIN', query: selectStmt };
const plan = await executor.execute(explainStmt);
const plan = object<{ type: string; table: string; usingIndex?: unknown; actualTimeMs: number }>(
await executor.execute(explainStmt),
);
expect(plan.type).toBe('SELECT');
expect(plan.table).toBe('users');
expect(plan.usingIndex).toBeDefined();
+2 -2
View File
@@ -9,7 +9,7 @@
import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { SharedMemoryBackend } from '../../src/engine/kvstore/shared_memory_medium';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
let counter = 0;
function uniqueDB(): string {
@@ -56,7 +56,7 @@ async function reopen(dbName: string, config: Record<string, unknown>): Promise<
beforeEach(() => {
SharedMemoryBackend.clearRegistry();
installOPFSMock(new Map());
resetOPFSMock();
});
describe('生产矩阵审计 — 后端 × 核心功能', () => {
+2 -2
View File
@@ -5,9 +5,9 @@
import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
describe('AriaEngine — MVCC 事务 + Savepoint', () => {
let engine: AriaEngine;
+24 -78
View File
@@ -10,82 +10,28 @@
* 6. open 清理崩溃残留临时文件(.crswap/.tmp
* 7. writeMany/deleteMany 语义
*/
// ===================================================================
// v0.8.0: 删除本文件内的第三份 OPFS mock —— 改用共享的 storage-harness。
//
// 原实现的问题(见 PLAN-v0.7.5.md 工作流 C-1):
// - 与 tests/helpers/opfs-mock.ts 重复(两份都在测同一件事,语义各自漂移);
// - `close()` 是空函数、写入立即可见 → 无法表达"提交前崩溃"
// - 定义了 `writeCalls` 记录但**从未被任何断言使用**(死代码);
// - `entry.content.subarray ? entry.content : entry.content` 是恒等表达式(无意义分支)。
//
// 共享 harness 提供:真实提交语义(close 才可见)、读返回副本、
// keepExistingData:false 截断、字节级故障注入、真崩溃模拟。
// ===================================================================
import { OPFSBackend } from '../../src/engine/aria/store/opfs_backend';
// ===================================================================
// 真实语义 OPFS mock:记录文件内容、支持 keepExistingData+position
// ===================================================================
interface MockFile {
content: ArrayBuffer;
writeCalls: { position?: number; data: ArrayBuffer; keepExistingData?: boolean }[];
}
function createOPFSMock() {
const files = new Map<string, MockFile>();
const getFileHandle = async (name: string, opts?: { create?: boolean }) => {
if (!files.has(name)) {
if (!opts?.create) throw new Error(`NotFoundError: ${name}`);
files.set(name, { content: new ArrayBuffer(0), writeCalls: [] });
}
const entry = files.get(name)!;
return {
getFile: async () => ({ size: entry.content.byteLength, arrayBuffer: async () => entry.content }),
createWritable: async (wOpts?: { keepExistingData?: boolean }) => {
const w: {
write: (arg: ArrayBuffer | { type: string; position: number; data: ArrayBuffer }) => Promise<void>;
close: () => Promise<void>;
} = {
write: async (arg) => {
const keepExisting = wOpts?.keepExistingData ?? false;
const isChunk = typeof arg !== 'object' || !('type' in (arg as object)) || (arg as { type?: string }).type === undefined
? { data: arg as ArrayBuffer, position: keepExisting ? entry.content.byteLength : 0 }
: { data: (arg as { data: ArrayBuffer }).data, position: (arg as { position: number }).position };
entry.writeCalls.push({ position: isChunk.position, data: isChunk.data, keepExistingData: keepExisting });
const merged = new Uint8Array(isChunk.position + isChunk.data.byteLength);
if (keepExisting || isChunk.position > 0) {
merged.set(new Uint8Array(entry.content.subarray ? entry.content : entry.content), 0);
}
merged.set(new Uint8Array(isChunk.data), isChunk.position);
entry.content = merged.buffer;
},
close: async () => { /* no-op */ },
};
return w;
},
};
};
const dir = {
getFileHandle,
entries: async function* () {
for (const [name] of files) yield [name];
},
removeEntry: async (name: string) => {
files.delete(name);
},
};
Object.defineProperty(globalThis, 'navigator', {
value: {
storage: {
getDirectory: async () => ({
getDirectoryHandle: async (_name: string, _opts?: unknown) => dir,
}),
},
},
configurable: true,
writable: true,
});
return { files, dir };
}
import { resetOPFSMock } from '../helpers/storage-harness';
import { decode as decodeBytes } from '../helpers/assertions';
const enc = (s: string) => new Uint8Array(new TextEncoder().encode(s)).buffer;
describe('AriaEngine — OPFSBackend v2', () => {
beforeEach(() => {
createOPFSMock();
resetOPFSMock();
});
it('read/write/exists/delete 基本语义', async () => {
@@ -95,7 +41,7 @@ describe('AriaEngine — OPFSBackend v2', () => {
await backend.write('k1', enc('hello'));
expect(await backend.exists('k1')).toBe(true);
expect(new TextDecoder().decode(await backend.read('k1'))).toBe('hello');
expect(decodeBytes(await backend.read('k1'))).toBe('hello');
expect(await backend.read('missing')).toBeNull();
await backend.delete('k1');
@@ -111,7 +57,7 @@ describe('AriaEngine — OPFSBackend v2', () => {
await backend.append('wal', enc('BBB'));
await backend.append('wal', enc('CCC'));
const all = await backend.read('wal');
expect(new TextDecoder().decode(all)).toBe('AAABBBCCC');
expect(decodeBytes(all)).toBe('AAABBBCCC');
await backend.close();
});
@@ -120,7 +66,7 @@ describe('AriaEngine — OPFSBackend v2', () => {
await backend.open('opfs-test-3');
await backend.write('f', enc('OLD-CONTENT'));
await backend.write('f', enc('NEW'));
expect(new TextDecoder().decode(await backend.read('f'))).toBe('NEW');
expect(decodeBytes(await backend.read('f'))).toBe('NEW');
await backend.close();
});
@@ -132,7 +78,7 @@ describe('AriaEngine — OPFSBackend v2', () => {
const p2 = backend.append('log', enc('B'));
const p3 = backend.append('log', enc('C'));
await Promise.all([p1, p2, p3]);
expect(new TextDecoder().decode(await backend.read('log'))).toBe('ABC');
expect(decodeBytes(await backend.read('log'))).toBe('ABC');
await backend.close();
});
@@ -157,7 +103,7 @@ describe('AriaEngine — OPFSBackend v2', () => {
await expect(origWrite('boom', enc('x'))).rejects.toThrow('Injected write failure');
// 队列链恢复:后续写成功
await backend.write('ok', enc('fine'));
expect(new TextDecoder().decode(await backend.read('ok'))).toBe('fine');
expect(decodeBytes(await backend.read('ok'))).toBe('fine');
await backend.close();
});
@@ -189,7 +135,7 @@ describe('AriaEngine — OPFSBackend v2', () => {
expect(keys).toEqual(['data']);
expect(keys.some((k) => k.endsWith('.crswap') || k.endsWith('.tmp'))).toBe(false);
// 正常数据不受影响
expect(new TextDecoder().decode(await backend2.read('data'))).toBe('real');
expect(decodeBytes(await backend2.read('data'))).toBe('real');
await backend2.close();
});
@@ -197,8 +143,8 @@ describe('AriaEngine — OPFSBackend v2', () => {
const backend = new OPFSBackend();
await backend.open('opfs-test-8');
await backend.writeMany({ a: enc('AAA'), b: enc('BBB') });
expect(new TextDecoder().decode(await backend.read('a'))).toBe('AAA');
expect(new TextDecoder().decode(await backend.read('b'))).toBe('BBB');
expect(decodeBytes(await backend.read('a'))).toBe('AAA');
expect(decodeBytes(await backend.read('b'))).toBe('BBB');
await backend.deleteMany(['a']);
expect(await backend.exists('a')).toBe(false);
expect(await backend.exists('b')).toBe(true);
+9 -8
View File
@@ -20,7 +20,8 @@ import { PAGE_SIZE } from '../../src/engine/aria/types';
// ===================================================================
// OPFS mock(共享工具)
// ===================================================================
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
import { decode as decodeBytes } from '../helpers/assertions';
const SCHEMA = () => createSchema('users', {
id: { type: 'string', primaryKey: true },
@@ -47,7 +48,7 @@ describe('AriaEngine — PageSSTableStore 单元', () => {
const back = await store.load(1, pageIds, data.byteLength);
expect(back).not.toBeNull();
expect(new TextDecoder().decode(back)).toBe('hello page store');
expect(decodeBytes(back)).toBe('hello page store');
// 页面已落盘(backend 有 pg_ 键)
expect(await backend.exists('pg_1')).toBe(true);
@@ -130,7 +131,7 @@ describe('AriaEngine — PageSSTableStore 单元', () => {
// ===================================================================
describe('AriaEngine — 页面化 SSTable 集成(OPFS', () => {
it('写入 → flush → close → reopen 数据完整(页面模式默认启用)', async () => {
const files = installOPFSMock(new Map());
resetOPFSMock();
const engine = new AriaEngine({
storageBackend: 'opfs',
memtableSizeThreshold: 64 * 1024 * 1024,
@@ -173,7 +174,7 @@ describe('AriaEngine — 页面化 SSTable 集成(OPFS', () => {
});
it('多级 compaction 后页面化数据仍完整', async () => {
const files = installOPFSMock(new Map());
resetOPFSMock();
const engine = new AriaEngine({
storageBackend: 'opfs',
memtableSizeThreshold: 16 * 1024,
@@ -204,7 +205,7 @@ describe('AriaEngine — 页面化 SSTable 集成(OPFS', () => {
});
it('页面损坏(篡改 pg_ 文件)→ 打开自愈清理,其余数据可读', async () => {
const files = installOPFSMock(new Map());
resetOPFSMock();
const engine = new AriaEngine({
storageBackend: 'opfs',
memtableSizeThreshold: 64 * 1024 * 1024,
@@ -245,7 +246,7 @@ describe('AriaEngine — 页面化 SSTable 集成(OPFS', () => {
});
it('页面模式与非页面模式混合兼容(pageIds 缺失 → 整 value 读取)', async () => {
const { files } = installOPFSMock(new Map());
const opfs = resetOPFSMock();
// 阶段 1:非页面模式写入(pageStorage: false → 整 value SSTable
const engine = new AriaEngine({
storageBackend: 'opfs',
@@ -259,7 +260,7 @@ describe('AriaEngine — 页面化 SSTable 集成(OPFS', () => {
await (engine as any).lsm.flush();
await engine.close();
const backendKeys1 = Array.from(files.keys());
const backendKeys1 = await opfs.listKeys();
expect(backendKeys1.some((k) => k.startsWith('sst_'))).toBe(true);
// 阶段 2:页面模式打开(默认 opfs → 启用),读旧数据 + 写新数据
@@ -285,7 +286,7 @@ describe('AriaEngine — 页面化 SSTable 集成(OPFS', () => {
});
it('页面化 + 加密 + 压缩组合:往返完整', async () => {
const files = installOPFSMock(new Map());
resetOPFSMock();
const engine = new AriaEngine({
storageBackend: 'opfs',
compression: true,
+4 -2
View File
@@ -14,7 +14,7 @@
import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { SharedMemoryBackend } from '../../src/engine/kvstore/shared_memory_medium';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
let counter = 0;
function uniqueDB(): string {
@@ -30,7 +30,7 @@ const SCHEMA = () => createSchema('big', {
beforeEach(() => {
SharedMemoryBackend.clearRegistry();
installOPFSMock(new Map());
resetOPFSMock();
});
describe('AriaEngine — 生产负载验证', () => {
@@ -317,6 +317,7 @@ describe('AriaEngine — 生产负载验证', () => {
// 修复后本机 ~12.5s。CIdebian runner 慢 2~3 倍、重型套件串行)下
// 健康耗时约 30~80s;护栏放宽到 240s —— 仍能拦截性能悬崖回归(353s >> 240s),
// 不误报健康慢环境。
// eslint-disable-next-line no-console -- 性能护栏需要输出实测耗时
console.log(`10万行 kv 插入耗时: ${insertMs}ms`);
expect(insertMs).toBeLessThan(240000);
expect(await engine.count('big')).toBe(TOTAL);
@@ -364,6 +365,7 @@ describe('AriaEngine — 生产负载验证', () => {
}
const insertMs = Date.now() - t0;
// 同上:CI 慢环境护栏放宽(本机 ~25s;悬崖回归仍会被拦截)
// eslint-disable-next-line no-console -- 性能护栏需要输出实测耗时
console.log(`10万行 opfs 插入耗时: ${insertMs}ms`);
expect(insertMs).toBeLessThan(300000);
expect(await engine.count('big')).toBe(TOTAL);
+9 -9
View File
@@ -11,9 +11,9 @@
*/
import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
let idbCounter = 0;
function uniqueDB(): string {
@@ -65,20 +65,20 @@ describe('AriaEngine — repair 自愈增强', () => {
it('清理 OPFS 残留临时文件(.crswap/.tmp', async () => {
// 用 OPFS mock 后端验证 cleanupStaleFiles 被调用
const { files, dir } = installOPFSMock(new Map());
const opfs = resetOPFSMock();
const engine = new AriaEngine({ storageBackend: 'opfs', checkpointInterval: 100000 });
await engine.open('repair-opfs-1', 1);
await engine.createTable(SCHEMA());
await engine.insert('items', [{ id: 'a', val: 1, tag: 'x' }]);
// 制造残留
await dir.getFileHandle('junk.crswap', { create: true });
await dir.getFileHandle('junk2.tmp', { create: true });
expect(Array.from(files.keys()).some((k) => k.endsWith('.crswap'))).toBe(true);
// 制造残留(模拟 createWritable 中断留下的临时文件)
await opfs.createFile('junk.crswap');
await opfs.createFile('junk2.tmp');
expect((await opfs.listKeys()).some((k) => k.endsWith('.crswap'))).toBe(true);
await (engine as any).repair();
const after = Array.from(files.keys());
const after = await opfs.listKeys();
expect(after.some((k) => k.endsWith('.crswap'))).toBe(false);
expect(after.some((k) => k.endsWith('.tmp'))).toBe(false);
expect(await engine.count('items')).toBe(1);
@@ -193,7 +193,7 @@ describe('AriaEngine — 随机操作压力 + 模拟崩溃', () => {
});
it('随机操作 + 页面化 + 模拟崩溃 → 重开验证', async () => {
const files = installOPFSMock(new Map());
resetOPFSMock();
const dbName = `rand-opfs-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
const engine = new AriaEngine({
storageBackend: 'opfs',
+2 -2
View File
@@ -14,9 +14,9 @@ import { MemoryBackend } from '../../src/engine/aria/store/backend';
import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
let idbCounter = 0;
function uniqueDB(): string {
+2 -2
View File
@@ -9,9 +9,9 @@ import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { MetonaSqlark } from '../../src/core';
import { installOPFSMock } from '../helpers/opfs-mock';
import { resetOPFSMock } from '../helpers/storage-harness';
beforeEach(() => { installOPFSMock(new Map()); });
beforeEach(() => { resetOPFSMock(); });
// ===================================================================
// AriaEngine 引擎级测试 (Memory Backend)
+1 -1
View File
@@ -24,7 +24,7 @@ function uniqueDB(): string {
}
const enc = (s: string) => new TextEncoder().encode(s).buffer as ArrayBuffer;
const dec = (b: ArrayBuffer | null) => (b ? new TextDecoder().decode(b) : null);
const dec = (b: ArrayBuffer | null | undefined) => (b ? new TextDecoder().decode(b) : null);
beforeEach(() => {
SharedMemoryBackend.clearRegistry();