/** * AriaEngine SSTable Builder + Reader 单元测试 */ import { SSTableBuilder } from '../../src/engine/aria/index/sstable_builder'; import { SSTableReader } from '../../src/engine/aria/index/sstable'; import type { SSTableMeta } from '../../src/engine/aria/types'; // =================================================================== // SSTable Builder + Reader // =================================================================== describe('AriaEngine — SSTable Builder + Reader', () => { const makeMeta = (data: Uint8Array): SSTableMeta => ({ id: 1, level: 0, minKey: '', maxKey: '\uffff', blockCount: 1, totalSize: data.byteLength, bloomData: null, }); it('构建单条目 SSTable 并精确读取', () => { const builder = new SSTableBuilder(4096); builder.add('key1', { name: 'Alice', age: 30 }); const { sstableData } = builder.build(); const reader = new SSTableReader(sstableData, makeMeta(sstableData)); const result = reader.get('key1'); expect(result).not.toBeNull(); expect(result!.name).toBe('Alice'); expect(result!.age).toBe(30); }); it('构建多条 SSTable 并全部读取', () => { const builder = new SSTableBuilder(4096); const items: [string, Record][] = []; for (let i = 0; i < 100; i++) { const key = `user-${String(i).padStart(3, '0')}`; const value = { idx: i, name: `User${i}` }; items.push([key, value]); builder.add(key, value); } const { sstableData } = builder.build(); const reader = new SSTableReader(sstableData, makeMeta(sstableData)); for (const [key, value] of items) { const result = reader.get(key); expect(result).not.toBeNull(); expect(result!.idx).toBe(value.idx); } }); it('get — 不存在的 key 返回 null', () => { const builder = new SSTableBuilder(4096); builder.add('a', { v: 1 }); const { sstableData } = builder.build(); const reader = new SSTableReader(sstableData, makeMeta(sstableData)); expect(reader.get('nonexistent')).toBeNull(); }); it('rangeScan — 范围查询', () => { const builder = new SSTableBuilder(4096); for (let i = 0; i < 20; i++) { builder.add(`k-${String(i).padStart(2, '0')}`, { v: i }); } const { sstableData } = builder.build(); const reader = new SSTableReader(sstableData, makeMeta(sstableData)); const results: [string, Record][] = []; reader.rangeScan('k-05', 'k-10', (k, v) => results.push([k, v])); expect(results).toHaveLength(6); expect(results[0][0]).toBe('k-05'); expect(results[results.length - 1][0]).toBe('k-10'); }); it('scanAll — 遍历所有条目', () => { const builder = new SSTableBuilder(4096); const count = 50; for (let i = 0; i < count; i++) { builder.add(`item-${i}`, { idx: i }); } const { sstableData } = builder.build(); const reader = new SSTableReader(sstableData, makeMeta(sstableData)); const items: [string, Record][] = []; reader.scanAll((k, v) => items.push([k, v])); expect(items).toHaveLength(count); }); it('getIndexBlockCount 返回索引块数', () => { const builder = new SSTableBuilder(256); // 小 block size 触发多个 block for (let i = 0; i < 100; i++) { builder.add(`k-${i}`, { data: 'x'.repeat(50) }); } const { sstableData } = builder.build(); const reader = new SSTableReader(sstableData, makeMeta(sstableData)); expect(reader.getIndexBlockCount()).toBeGreaterThanOrEqual(1); }); it('边界 — 空 SSTable 不抛异常', () => { const builder = new SSTableBuilder(4096); const { sstableData } = builder.build(); const reader = new SSTableReader(sstableData, makeMeta(sstableData)); expect(reader.get('any')).toBeNull(); const results: [string, Record][] = []; reader.scanAll((k, v) => results.push([k, v])); expect(results).toHaveLength(0); }); it('带特殊字符的 key', () => { const builder = new SSTableBuilder(4096); builder.add('key-with-dash', { v: 1 }); builder.add('key.with.dot', { v: 2 }); builder.add('key with space', { v: 3 }); const { sstableData } = builder.build(); const reader = new SSTableReader(sstableData, makeMeta(sstableData)); expect(reader.get('key-with-dash')!.v).toBe(1); expect(reader.get('key.with.dot')!.v).toBe(2); expect(reader.get('key with space')!.v).toBe(3); }); it('getEntryCount 返回正确条目数', () => { const builder = new SSTableBuilder(4096); builder.add('a', { v: 1 }); builder.add('b', { v: 2 }); builder.add('c', { v: 3 }); expect(builder.getEntryCount()).toBe(3); }); });