128 lines
4.6 KiB
TypeScript
128 lines
4.6 KiB
TypeScript
/**
|
||
* AriaEngine LZ4 压缩 + LSM Merge Iterator 单元测试
|
||
* 注:LZ4 为简化演示实现(默认 compression:false),测试聚焦于「不卡死」
|
||
*/
|
||
import { compressLZ4, decompressLZ4 } from '../../src/engine/aria/compression/lz4';
|
||
import { MergeIterator, ArrayEntrySource } from '../../src/engine/aria/index/merge_iterator';
|
||
|
||
// ===================================================================
|
||
// LZ4 压缩 — 安全烟雾测试(不卡死)
|
||
// ===================================================================
|
||
describe('AriaEngine — LZ4 Compression', () => {
|
||
it('短于 4 字节时原样返回', () => {
|
||
const input = new Uint8Array([1, 2]);
|
||
const compressed = compressLZ4(input);
|
||
expect(compressed).toBe(input);
|
||
});
|
||
|
||
it('简单文本压缩不抛出异常且产生输出', () => {
|
||
const input = new TextEncoder().encode('hello world hello world hello world');
|
||
const compressed = compressLZ4(input);
|
||
expect(compressed).toBeInstanceOf(Uint8Array);
|
||
expect(compressed.byteLength).toBeGreaterThan(0);
|
||
});
|
||
|
||
it('重复数据有压缩效果', () => {
|
||
const pattern = 'ABCD';
|
||
const repeated = pattern.repeat(100);
|
||
const input = new TextEncoder().encode(repeated);
|
||
const compressed = compressLZ4(input);
|
||
expect(compressed.byteLength).toBeLessThan(input.byteLength);
|
||
});
|
||
|
||
it('随机不可压缩数据不卡死', () => {
|
||
const input = new Uint8Array(256);
|
||
for (let i = 0; i < 256; i++) input[i] = Math.floor(Math.random() * 256);
|
||
const compressed = compressLZ4(input);
|
||
expect(compressed).toBeInstanceOf(Uint8Array);
|
||
expect(compressed.byteLength).toBeGreaterThan(0);
|
||
});
|
||
|
||
it('长文本压缩不卡死', () => {
|
||
const input = new TextEncoder().encode('The quick brown fox jumps over the lazy dog. '.repeat(10));
|
||
const compressed = compressLZ4(input);
|
||
expect(compressed).toBeTruthy();
|
||
expect(compressed.byteLength).toBeGreaterThan(0);
|
||
});
|
||
|
||
it('多种长度输入均不卡死', () => {
|
||
for (const size of [10, 50, 100, 200, 500]) {
|
||
const input = new Uint8Array(size);
|
||
for (let i = 0; i < size; i++) input[i] = i % 256;
|
||
const compressed = compressLZ4(input);
|
||
expect(compressed.byteLength).toBeLessThanOrEqual(input.byteLength + 16);
|
||
}
|
||
});
|
||
|
||
it('解压不抛出异常', () => {
|
||
const input = new TextEncoder().encode('test data for decompression smoke test');
|
||
const compressed = compressLZ4(input);
|
||
expect(() => decompressLZ4(compressed, input.byteLength)).not.toThrow();
|
||
});
|
||
});
|
||
|
||
// ===================================================================
|
||
// MergeIterator — 归并迭代器单元测试
|
||
// ===================================================================
|
||
describe('AriaEngine — MergeIterator', () => {
|
||
it('单数据源归并', () => {
|
||
const mi = new MergeIterator();
|
||
mi.addSource(new ArrayEntrySource([
|
||
['a', { v: 1 }],
|
||
['b', { v: 2 }],
|
||
['c', { v: 3 }],
|
||
]));
|
||
const result = mi.drain();
|
||
expect(result).toHaveLength(3);
|
||
expect(result.map(([k]) => k)).toEqual(['a', 'b', 'c']);
|
||
});
|
||
|
||
it('多数据源归并去重(保留最新)', () => {
|
||
const mi = new MergeIterator();
|
||
mi.addSource(new ArrayEntrySource([['a', { v: 'new' }], ['c', { v: 3 }]]));
|
||
mi.addSource(new ArrayEntrySource([['a', { v: 'old' }], ['b', { v: 2 }]]));
|
||
const result = mi.drain();
|
||
expect(result).toHaveLength(3);
|
||
expect(result[0][0]).toBe('a');
|
||
expect(result[0][1].v).toBe('new');
|
||
expect(result[1][0]).toBe('b');
|
||
expect(result[2][0]).toBe('c');
|
||
});
|
||
|
||
it('空数据源归并', () => {
|
||
const mi = new MergeIterator();
|
||
mi.addSource(new ArrayEntrySource([]));
|
||
const result = mi.drain();
|
||
expect(result).toHaveLength(0);
|
||
});
|
||
|
||
it('大数量归并', () => {
|
||
const mi = new MergeIterator();
|
||
for (let s = 0; s < 5; s++) {
|
||
const entries: [string, Record<string, unknown>][] = [];
|
||
for (let i = 0; i < 100; i++) {
|
||
entries.push([`src${s}-key-${String(i).padStart(3, '0')}`, { src: s, idx: i }]);
|
||
}
|
||
mi.addSource(new ArrayEntrySource(entries));
|
||
}
|
||
const result = mi.drain();
|
||
// 5 sources × 100 unique keys = 500 total (keys are unique per source)
|
||
expect(result).toHaveLength(500);
|
||
});
|
||
|
||
it('ArrayEntrySource — 迭代器用完返回 null', () => {
|
||
const src = new ArrayEntrySource([['k', { v: 1 }]]);
|
||
expect(src.next()).not.toBeNull();
|
||
expect(src.next()).toBeNull();
|
||
expect(src.next()).toBeNull();
|
||
});
|
||
|
||
it('ArrayEntrySource — reset 重置', () => {
|
||
const src = new ArrayEntrySource([['k1', { v: 1 }], ['k2', { v: 2 }]]);
|
||
src.next();
|
||
src.reset();
|
||
const val = src.next();
|
||
expect(val![0]).toBe('k1');
|
||
});
|
||
});
|