release: v0.5.1 — 存储后端生产级硬化(CRC-32/全库加密/WAL分片/页面化存储/多标签页锁/e2e)+ 深度审查修复(假实现接线/死代码清理)
This commit is contained in:
+198
-216
@@ -1,216 +1,198 @@
|
||||
/**
|
||||
* AriaEngine Bloom Filter + MemTable 单元测试
|
||||
*/
|
||||
import { BloomFilter } from '../../src/engine/aria/index/bloom';
|
||||
import { MemTable } from '../../src/engine/aria/index/memtable';
|
||||
|
||||
// ===================================================================
|
||||
// BloomFilter
|
||||
// ===================================================================
|
||||
describe('AriaEngine — BloomFilter', () => {
|
||||
it('插入后 mayContain 返回 true', () => {
|
||||
const bf = new BloomFilter(100);
|
||||
bf.insert('hello');
|
||||
expect(bf.mayContain('hello')).toBe(true);
|
||||
});
|
||||
|
||||
it('未插入的 key mayContain 返回 false', () => {
|
||||
const bf = new BloomFilter(100);
|
||||
bf.insert('hello');
|
||||
expect(bf.mayContain('world')).toBe(false);
|
||||
});
|
||||
|
||||
it('批量插入后所有 key 都判定存在', () => {
|
||||
const bf = new BloomFilter(500);
|
||||
const keys: string[] = [];
|
||||
for (let i = 0; i < 200; i++) {
|
||||
const k = `key-${i}`;
|
||||
keys.push(k);
|
||||
bf.insert(k);
|
||||
}
|
||||
for (const k of keys) {
|
||||
expect(bf.mayContain(k)).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('False positive 率可控', () => {
|
||||
const n = 500;
|
||||
const bf = new BloomFilter(n, 10);
|
||||
for (let i = 0; i < n; i++) {
|
||||
bf.insert(`present-${i}`);
|
||||
}
|
||||
let fp = 0;
|
||||
for (let i = 0; i < 500; i++) {
|
||||
if (bf.mayContain(`absent-${i}`)) fp++;
|
||||
}
|
||||
expect(fp).toBeLessThan(25);
|
||||
});
|
||||
|
||||
it('getBitSize 返回正确位数', () => {
|
||||
const bf = new BloomFilter(100, 10);
|
||||
expect(bf.getBitSize()).toBeGreaterThanOrEqual(64);
|
||||
});
|
||||
|
||||
it('getInsertedCount 追踪插入数', () => {
|
||||
const bf = new BloomFilter(100);
|
||||
bf.insert('a');
|
||||
bf.insert('b');
|
||||
bf.insert('c');
|
||||
expect(bf.getInsertedCount()).toBe(3);
|
||||
});
|
||||
|
||||
it('getHashCount 返回哈希函数数量', () => {
|
||||
const bf = new BloomFilter(1000, 10);
|
||||
expect(bf.getHashCount()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('serialize + fromData 往返', () => {
|
||||
const bf1 = new BloomFilter(100);
|
||||
bf1.insert('a');
|
||||
bf1.insert('b');
|
||||
const data = bf1.serialize();
|
||||
|
||||
const bf2 = BloomFilter.fromData(data, bf1.getHashCount());
|
||||
expect(bf2.mayContain('a')).toBe(true);
|
||||
expect(bf2.mayContain('b')).toBe(true);
|
||||
expect(bf2.mayContain('c')).toBe(false);
|
||||
});
|
||||
|
||||
it('空过滤器 mayContain 返回 false', () => {
|
||||
const bf = new BloomFilter(100);
|
||||
expect(bf.mayContain('anything')).toBe(false);
|
||||
});
|
||||
|
||||
it('最少 1 个哈希函数', () => {
|
||||
const bf = new BloomFilter(10, 1);
|
||||
expect(bf.getHashCount()).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
// ===================================================================
|
||||
// MemTable
|
||||
// ===================================================================
|
||||
describe('AriaEngine — MemTable', () => {
|
||||
let mt: MemTable;
|
||||
|
||||
beforeEach(() => {
|
||||
mt = new MemTable(4 * 1024 * 1024);
|
||||
});
|
||||
|
||||
it('put + get 往返', () => {
|
||||
mt.put('key1', { name: 'Alice', age: 30 });
|
||||
const val = mt.get('key1');
|
||||
expect(val).not.toBeNull();
|
||||
expect(val!.name).toBe('Alice');
|
||||
});
|
||||
|
||||
it('get — 不存在的 key 返回 null', () => {
|
||||
expect(mt.get('nonexistent')).toBeNull();
|
||||
});
|
||||
|
||||
it('put 更新已存在的 key', () => {
|
||||
mt.put('k', { v: 1 });
|
||||
mt.put('k', { v: 2 });
|
||||
expect(mt.get('k')!.v).toBe(2);
|
||||
});
|
||||
|
||||
it('delete 删除成功', () => {
|
||||
mt.put('k', { v: 1 });
|
||||
expect(mt.delete('k')).toBe(true);
|
||||
expect(mt.get('k')).toBeNull();
|
||||
});
|
||||
|
||||
it('delete — 不存在的 key 返回 false', () => {
|
||||
expect(mt.delete('ghost')).toBe(false);
|
||||
});
|
||||
|
||||
it('getAllEntries 返回所有条目(有序)', () => {
|
||||
mt.put('c', { v: 3 });
|
||||
mt.put('a', { v: 1 });
|
||||
mt.put('b', { v: 2 });
|
||||
const entries = mt.getAllEntries();
|
||||
expect(entries).toHaveLength(3);
|
||||
expect(entries[0][0]).toBe('a');
|
||||
expect(entries[1][0]).toBe('b');
|
||||
expect(entries[2][0]).toBe('c');
|
||||
});
|
||||
|
||||
it('rangeScan — 范围查询', () => {
|
||||
mt.put('a', { v: 1 });
|
||||
mt.put('b', { v: 2 });
|
||||
mt.put('c', { v: 3 });
|
||||
mt.put('d', { v: 4 });
|
||||
const results = mt.rangeScan('b', 'c');
|
||||
expect(results).toHaveLength(2);
|
||||
expect(results[0][0]).toBe('b');
|
||||
expect(results[1][0]).toBe('c');
|
||||
});
|
||||
|
||||
it('rangeScan — 空结果', () => {
|
||||
mt.put('a', { v: 1 });
|
||||
const results = mt.rangeScan('z', 'zz');
|
||||
expect(results).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('getEntryCount 正确计数', () => {
|
||||
expect(mt.getEntryCount()).toBe(0);
|
||||
mt.put('a', { v: 1 });
|
||||
mt.put('b', { v: 2 });
|
||||
expect(mt.getEntryCount()).toBe(2);
|
||||
mt.delete('a');
|
||||
expect(mt.getEntryCount()).toBe(1);
|
||||
});
|
||||
|
||||
it('contains 检查存在性', () => {
|
||||
mt.put('x', { v: 1 });
|
||||
expect(mt.contains('x')).toBe(true);
|
||||
expect(mt.contains('y')).toBe(false);
|
||||
});
|
||||
|
||||
it('shouldFlush — 未达阈值返回 false', () => {
|
||||
expect(mt.shouldFlush()).toBe(false);
|
||||
});
|
||||
|
||||
it('clear 清空所有数据', () => {
|
||||
mt.put('a', { v: 1 });
|
||||
mt.put('b', { v: 2 });
|
||||
mt.clear();
|
||||
expect(mt.getEntryCount()).toBe(0);
|
||||
expect(mt.get('a')).toBeNull();
|
||||
});
|
||||
|
||||
it('getEstimatedSize 返回合理估计值', () => {
|
||||
expect(mt.getEstimatedSize()).toBe(0);
|
||||
mt.put('hello', { name: 'world', count: 42 });
|
||||
expect(mt.getEstimatedSize()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('大量数据插入保持有序', () => {
|
||||
const count = 100;
|
||||
for (let i = count - 1; i >= 0; i--) {
|
||||
mt.put(`key-${String(i).padStart(3, '0')}`, { idx: i });
|
||||
}
|
||||
const entries = mt.getAllEntries();
|
||||
expect(entries).toHaveLength(count);
|
||||
for (let i = 0; i < count; i++) {
|
||||
expect(entries[i][1].idx).toBe(i);
|
||||
}
|
||||
});
|
||||
|
||||
it('删除后重新插入', () => {
|
||||
mt.put('k', { v: 1 });
|
||||
mt.delete('k');
|
||||
mt.put('k', { v: 2 });
|
||||
expect(mt.get('k')!.v).toBe(2);
|
||||
});
|
||||
|
||||
it('范围扫描包含边界', () => {
|
||||
mt.put('aa', { v: 1 });
|
||||
mt.put('ab', { v: 2 });
|
||||
mt.put('ac', { v: 3 });
|
||||
const results = mt.rangeScan('aa', 'ab');
|
||||
expect(results).toHaveLength(2);
|
||||
expect(results[0][0]).toBe('aa');
|
||||
expect(results[1][0]).toBe('ab');
|
||||
});
|
||||
});
|
||||
/**
|
||||
* AriaEngine Bloom Filter + MemTable 单元测试
|
||||
*/
|
||||
import { BloomFilter } from '../../src/engine/aria/index/bloom';
|
||||
import { MemTable } from '../../src/engine/aria/index/memtable';
|
||||
|
||||
// ===================================================================
|
||||
// BloomFilter
|
||||
// ===================================================================
|
||||
describe('AriaEngine — BloomFilter', () => {
|
||||
it('插入后 mayContain 返回 true', () => {
|
||||
const bf = new BloomFilter(100);
|
||||
bf.insert('hello');
|
||||
expect(bf.mayContain('hello')).toBe(true);
|
||||
});
|
||||
|
||||
it('未插入的 key mayContain 返回 false', () => {
|
||||
const bf = new BloomFilter(100);
|
||||
bf.insert('hello');
|
||||
expect(bf.mayContain('world')).toBe(false);
|
||||
});
|
||||
|
||||
it('批量插入后所有 key 都判定存在', () => {
|
||||
const bf = new BloomFilter(500);
|
||||
const keys: string[] = [];
|
||||
for (let i = 0; i < 200; i++) {
|
||||
const k = `key-${i}`;
|
||||
keys.push(k);
|
||||
bf.insert(k);
|
||||
}
|
||||
for (const k of keys) {
|
||||
expect(bf.mayContain(k)).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('False positive 率可控', () => {
|
||||
const n = 500;
|
||||
const bf = new BloomFilter(n, 10);
|
||||
for (let i = 0; i < n; i++) {
|
||||
bf.insert(`present-${i}`);
|
||||
}
|
||||
let fp = 0;
|
||||
for (let i = 0; i < 500; i++) {
|
||||
if (bf.mayContain(`absent-${i}`)) fp++;
|
||||
}
|
||||
expect(fp).toBeLessThan(25);
|
||||
});
|
||||
|
||||
it('getHashCount 返回哈希函数数量', () => {
|
||||
const bf = new BloomFilter(1000, 10);
|
||||
expect(bf.getHashCount()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('serialize + fromData 往返', () => {
|
||||
const bf1 = new BloomFilter(100);
|
||||
bf1.insert('a');
|
||||
bf1.insert('b');
|
||||
const data = bf1.serialize();
|
||||
|
||||
const bf2 = BloomFilter.fromData(data, bf1.getHashCount());
|
||||
expect(bf2.mayContain('a')).toBe(true);
|
||||
expect(bf2.mayContain('b')).toBe(true);
|
||||
expect(bf2.mayContain('c')).toBe(false);
|
||||
});
|
||||
|
||||
it('空过滤器 mayContain 返回 false', () => {
|
||||
const bf = new BloomFilter(100);
|
||||
expect(bf.mayContain('anything')).toBe(false);
|
||||
});
|
||||
|
||||
it('最少 1 个哈希函数', () => {
|
||||
const bf = new BloomFilter(10, 1);
|
||||
expect(bf.getHashCount()).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
// ===================================================================
|
||||
// MemTable
|
||||
// ===================================================================
|
||||
describe('AriaEngine — MemTable', () => {
|
||||
let mt: MemTable;
|
||||
|
||||
beforeEach(() => {
|
||||
mt = new MemTable(4 * 1024 * 1024);
|
||||
});
|
||||
|
||||
it('put + get 往返', () => {
|
||||
mt.put('key1', { name: 'Alice', age: 30 });
|
||||
const val = mt.get('key1');
|
||||
expect(val).not.toBeNull();
|
||||
expect(val!.name).toBe('Alice');
|
||||
});
|
||||
|
||||
it('get — 不存在的 key 返回 null', () => {
|
||||
expect(mt.get('nonexistent')).toBeNull();
|
||||
});
|
||||
|
||||
it('put 更新已存在的 key', () => {
|
||||
mt.put('k', { v: 1 });
|
||||
mt.put('k', { v: 2 });
|
||||
expect(mt.get('k')!.v).toBe(2);
|
||||
});
|
||||
|
||||
it('delete 删除成功', () => {
|
||||
mt.put('k', { v: 1 });
|
||||
expect(mt.delete('k')).toBe(true);
|
||||
expect(mt.get('k')).toBeNull();
|
||||
});
|
||||
|
||||
it('delete — 不存在的 key 返回 false', () => {
|
||||
expect(mt.delete('ghost')).toBe(false);
|
||||
});
|
||||
|
||||
it('getAllEntries 返回所有条目(有序)', () => {
|
||||
mt.put('c', { v: 3 });
|
||||
mt.put('a', { v: 1 });
|
||||
mt.put('b', { v: 2 });
|
||||
const entries = mt.getAllEntries();
|
||||
expect(entries).toHaveLength(3);
|
||||
expect(entries[0][0]).toBe('a');
|
||||
expect(entries[1][0]).toBe('b');
|
||||
expect(entries[2][0]).toBe('c');
|
||||
});
|
||||
|
||||
it('rangeScan — 范围查询', () => {
|
||||
mt.put('a', { v: 1 });
|
||||
mt.put('b', { v: 2 });
|
||||
mt.put('c', { v: 3 });
|
||||
mt.put('d', { v: 4 });
|
||||
const results = mt.rangeScan('b', 'c');
|
||||
expect(results).toHaveLength(2);
|
||||
expect(results[0][0]).toBe('b');
|
||||
expect(results[1][0]).toBe('c');
|
||||
});
|
||||
|
||||
it('rangeScan — 空结果', () => {
|
||||
mt.put('a', { v: 1 });
|
||||
const results = mt.rangeScan('z', 'zz');
|
||||
expect(results).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('getEntryCount 正确计数', () => {
|
||||
expect(mt.getEntryCount()).toBe(0);
|
||||
mt.put('a', { v: 1 });
|
||||
mt.put('b', { v: 2 });
|
||||
expect(mt.getEntryCount()).toBe(2);
|
||||
mt.delete('a');
|
||||
expect(mt.getEntryCount()).toBe(1);
|
||||
});
|
||||
|
||||
|
||||
it('shouldFlush — 未达阈值返回 false', () => {
|
||||
expect(mt.shouldFlush()).toBe(false);
|
||||
});
|
||||
|
||||
it('clear 清空所有数据', () => {
|
||||
mt.put('a', { v: 1 });
|
||||
mt.put('b', { v: 2 });
|
||||
mt.clear();
|
||||
expect(mt.getEntryCount()).toBe(0);
|
||||
expect(mt.get('a')).toBeNull();
|
||||
});
|
||||
|
||||
it('getEstimatedSize 返回合理估计值', () => {
|
||||
expect(mt.getEstimatedSize()).toBe(0);
|
||||
mt.put('hello', { name: 'world', count: 42 });
|
||||
expect(mt.getEstimatedSize()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('大量数据插入保持有序', () => {
|
||||
const count = 100;
|
||||
for (let i = count - 1; i >= 0; i--) {
|
||||
mt.put(`key-${String(i).padStart(3, '0')}`, { idx: i });
|
||||
}
|
||||
const entries = mt.getAllEntries();
|
||||
expect(entries).toHaveLength(count);
|
||||
for (let i = 0; i < count; i++) {
|
||||
expect(entries[i][1].idx).toBe(i);
|
||||
}
|
||||
});
|
||||
|
||||
it('删除后重新插入', () => {
|
||||
mt.put('k', { v: 1 });
|
||||
mt.delete('k');
|
||||
mt.put('k', { v: 2 });
|
||||
expect(mt.get('k')!.v).toBe(2);
|
||||
});
|
||||
|
||||
it('范围扫描包含边界', () => {
|
||||
mt.put('aa', { v: 1 });
|
||||
mt.put('ab', { v: 2 });
|
||||
mt.put('ac', { v: 3 });
|
||||
const results = mt.rangeScan('aa', 'ab');
|
||||
expect(results).toHaveLength(2);
|
||||
expect(results[0][0]).toBe('aa');
|
||||
expect(results[1][0]).toBe('ab');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user