/** * OPFSEngine 测试 — mock FileSystemDirectoryHandle,验证逻辑不卡死 * 注:jsdom 无 OPFS API,全部使用 mock 模拟 */ import { OPFSEngine } from '../../src/engine/opfs'; import { createSchema } from '../../src/table/schema'; // Mock navigator.storage.getDirectory + FileSystemDirectoryHandle function mockOPFS() { const files = new Map(); const dirMock = { getDirectoryHandle: async (_name: string, _opts?: any) => dirMock as any, getFileHandle: async (name: string, opts?: any) => { if (opts?.create) { return { createWritable: async () => ({ write: async (d: string) => { files.set(name, d); }, close: async () => {} }) }; } if (!files.has(name)) throw new Error('Not found'); return { getFile: async () => ({ text: async () => files.get(name)!, arrayBuffer: async () => new ArrayBuffer(0) }) }; }, removeEntry: async (name: string) => { files.delete(name); }, }; // 为 entries() 添加可迭代接口 (dirMock as any).entries = () => ({ [Symbol.asyncIterator]: async function* () { for (const [k] of files) yield [k]; }, }); // jsdom 环境下 navigator 可能已存在,直接设置 storage 属性 const nav = (globalThis as any).navigator || {}; nav.storage = { getDirectory: async () => dirMock }; (globalThis as any).navigator = nav; return { files }; } const userSchema = createSchema('users', { id: { type: 'string', primaryKey: true }, name: { type: 'string', required: true }, age: { type: 'number', default: 0 }, }); describe('OPFSEngine', () => { let engine: OPFSEngine; let mocks: ReturnType; beforeEach(async () => { mocks = mockOPFS(); engine = new OPFSEngine(); await engine.open('test-opfs', 1); }); afterEach(async () => { await engine.close(); }); // ---- 生命周期 ---- it('打开后 isOpen 返回 true', () => { expect(engine.isOpen()).toBe(true); }); it('关闭后 isOpen 返回 false', async () => { await engine.close(); expect(engine.isOpen()).toBe(false); }); // ---- 表管理 ---- it('创建表后 hasTable 返回 true', async () => { await engine.createTable(userSchema); expect(await engine.hasTable('users')).toBe(true); }); it('获取表名列表', async () => { await engine.createTable(userSchema); const names = await engine.getTableNames(); expect(names).toContain('users'); }); it('删除表后 hasTable 返回 false', async () => { await engine.createTable(userSchema); await engine.dropTable('users'); expect(await engine.hasTable('users')).toBe(false); }); // ---- CRUD ---- it('插入后立即可查询', async () => { await engine.createTable(userSchema); await engine.insert('users', [ { id: '1', name: 'Alice', age: 30 }, { id: '2', name: 'Bob', age: 25 }, ]); const rows = await engine.find('users', { table: 'users' }); expect(rows).toHaveLength(2); }); it('count 返回正确行数', async () => { await engine.createTable(userSchema); await engine.insert('users', [{ id: '1', name: 'Alice' }]); expect(await engine.count('users')).toBe(1); }); it('更新后数据变化', async () => { await engine.createTable(userSchema); await engine.insert('users', [{ id: '1', name: 'Alice', age: 30 }]); await engine.update('users', { table: 'users', where: { id: '1' } }, { age: 31 }); const rows = await engine.find('users', { table: 'users', where: { id: '1' } }); expect(rows[0].age).toBe(31); }); it('删除后 count 减少', async () => { await engine.createTable(userSchema); await engine.insert('users', [{ id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }]); await engine.delete('users', { table: 'users', where: { id: '1' } }); expect(await engine.count('users')).toBe(1); }); // ---- 数据持久化(模拟重启) ---- it('close + reopen 后数据恢复', async () => { // 写入数据 await engine.createTable(userSchema); await engine.insert('users', [ { id: '1', name: 'Alice', age: 30 }, { id: '2', name: 'Bob', age: 25 }, ]); const beforeRows = await engine.find('users', { table: 'users' }); expect(beforeRows).toHaveLength(2); // 关闭后重新打开(模拟重启) await engine.close(); engine = new OPFSEngine(); await engine.open('test-opfs', 1); // 数据应恢复 const afterRows = await engine.find('users', { table: 'users' }); expect(afterRows).toHaveLength(2); expect(afterRows.map((r: any) => r.name).sort()).toEqual(['Alice', 'Bob']); }); // ---- 事务 ---- it('事务 commit 后数据持久化', async () => { await engine.createTable(userSchema); await engine.beginTransaction(); await engine.insert('users', [{ id: '1', name: 'Alice' }]); await engine.commitTransaction(); expect(await engine.count('users')).toBe(1); }); it('事务 rollback 后数据恢复', async () => { await engine.createTable(userSchema); await engine.insert('users', [{ id: '1', name: 'Alice' }]); await engine.beginTransaction(); await engine.insert('users', [{ id: '2', name: 'Bob' }]); await engine.rollbackTransaction(); expect(await engine.count('users')).toBe(1); }); });