mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
- Add Disposable, BufferConfig, MemoryMetrics, CleanupRegistration types - Create src/config/buffer-limits.ts with consolidated buffer size constants - Create src/config/map-limits.ts with Map size limits to prevent unbounded growth - Implement BufferAccumulator utility with configurable trim and onTrim callback - Implement LRUMap with automatic eviction and O(1) operations - Implement CleanupManager for unified resource cleanup with isStopped guard - Add comprehensive tests for all new utilities This lays the foundation for memory leak prevention and performance improvements. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
194 lines
5.6 KiB
TypeScript
194 lines
5.6 KiB
TypeScript
/**
|
|
* Tests for BufferAccumulator utility.
|
|
*
|
|
* Port: N/A (unit tests, no server)
|
|
*/
|
|
|
|
import { BufferAccumulator } from '../../src/utils/buffer-accumulator.js';
|
|
|
|
describe('BufferAccumulator', () => {
|
|
describe('basic operations', () => {
|
|
it('should start empty', () => {
|
|
const buffer = new BufferAccumulator(1000, 800);
|
|
expect(buffer.value).toBe('');
|
|
expect(buffer.length).toBe(0);
|
|
expect(buffer.isEmpty).toBe(true);
|
|
});
|
|
|
|
it('should append data', () => {
|
|
const buffer = new BufferAccumulator(1000, 800);
|
|
buffer.append('hello');
|
|
buffer.append(' ');
|
|
buffer.append('world');
|
|
|
|
expect(buffer.value).toBe('hello world');
|
|
expect(buffer.length).toBe(11);
|
|
expect(buffer.isEmpty).toBe(false);
|
|
});
|
|
|
|
it('should ignore empty appends', () => {
|
|
const buffer = new BufferAccumulator(1000, 800);
|
|
buffer.append('hello');
|
|
buffer.append('');
|
|
buffer.append(null as unknown as string);
|
|
buffer.append(undefined as unknown as string);
|
|
|
|
expect(buffer.value).toBe('hello');
|
|
expect(buffer.length).toBe(5);
|
|
});
|
|
|
|
it('should clear the buffer', () => {
|
|
const buffer = new BufferAccumulator(1000, 800);
|
|
buffer.append('hello world');
|
|
buffer.clear();
|
|
|
|
expect(buffer.value).toBe('');
|
|
expect(buffer.length).toBe(0);
|
|
expect(buffer.isEmpty).toBe(true);
|
|
});
|
|
|
|
it('should set buffer to specific value', () => {
|
|
const buffer = new BufferAccumulator(1000, 800);
|
|
buffer.append('hello');
|
|
buffer.set('goodbye');
|
|
|
|
expect(buffer.value).toBe('goodbye');
|
|
expect(buffer.length).toBe(7);
|
|
});
|
|
|
|
it('should set to empty value', () => {
|
|
const buffer = new BufferAccumulator(1000, 800);
|
|
buffer.append('hello');
|
|
buffer.set('');
|
|
|
|
expect(buffer.value).toBe('');
|
|
expect(buffer.length).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('automatic trimming', () => {
|
|
it('should trim when maxSize is exceeded', () => {
|
|
const buffer = new BufferAccumulator(100, 50);
|
|
|
|
// Fill buffer beyond max
|
|
buffer.append('a'.repeat(60));
|
|
buffer.append('b'.repeat(60)); // Total 120, exceeds 100
|
|
|
|
// Should have trimmed to 50
|
|
expect(buffer.length).toBe(50);
|
|
// Should keep most recent data (all 'b's)
|
|
expect(buffer.value).toBe('b'.repeat(50));
|
|
});
|
|
|
|
it('should call onTrim callback when trimming', () => {
|
|
let trimmedBytes = 0;
|
|
const buffer = new BufferAccumulator({
|
|
maxSize: 100,
|
|
trimSize: 50,
|
|
onTrim: (bytes) => { trimmedBytes = bytes; },
|
|
});
|
|
|
|
// Fill buffer beyond max
|
|
buffer.append('a'.repeat(60));
|
|
buffer.append('b'.repeat(60)); // Total 120, trims to 50
|
|
|
|
expect(trimmedBytes).toBe(70); // 120 - 50 = 70
|
|
});
|
|
|
|
it('should not call onTrim when no trimming needed', () => {
|
|
let trimCalled = false;
|
|
const buffer = new BufferAccumulator({
|
|
maxSize: 100,
|
|
trimSize: 50,
|
|
onTrim: () => { trimCalled = true; },
|
|
});
|
|
|
|
buffer.append('a'.repeat(50)); // Under max
|
|
|
|
expect(trimCalled).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('tail and search operations', () => {
|
|
it('should get tail of buffer', () => {
|
|
const buffer = new BufferAccumulator(1000, 800);
|
|
buffer.append('hello world');
|
|
|
|
expect(buffer.tail(5)).toBe('world');
|
|
expect(buffer.tail(6)).toBe(' world');
|
|
expect(buffer.tail(100)).toBe('hello world'); // Returns all if n > length
|
|
});
|
|
|
|
it('should check endsWith', () => {
|
|
const buffer = new BufferAccumulator(1000, 800);
|
|
buffer.append('hello world');
|
|
|
|
expect(buffer.endsWith('world')).toBe(true);
|
|
expect(buffer.endsWith('hello')).toBe(false);
|
|
expect(buffer.endsWith('')).toBe(true);
|
|
expect(buffer.endsWith('a very long string')).toBe(false);
|
|
});
|
|
|
|
it('should search with contains (string)', () => {
|
|
const buffer = new BufferAccumulator(1000, 800);
|
|
buffer.append('hello world, how are you?');
|
|
|
|
expect(buffer.contains('world')).toBe(true);
|
|
expect(buffer.contains('foo')).toBe(false);
|
|
expect(buffer.contains('you', 10)).toBe(true);
|
|
expect(buffer.contains('hello', 10)).toBe(false);
|
|
});
|
|
|
|
it('should search with contains (regex)', () => {
|
|
const buffer = new BufferAccumulator(1000, 800);
|
|
buffer.append('hello world 123');
|
|
|
|
expect(buffer.contains(/\d+/)).toBe(true);
|
|
expect(buffer.contains(/foo/)).toBe(false);
|
|
expect(buffer.contains(/world/)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('constructor overloads', () => {
|
|
it('should accept simple number parameters', () => {
|
|
const buffer = new BufferAccumulator(100, 50);
|
|
buffer.append('a'.repeat(120));
|
|
expect(buffer.length).toBe(50);
|
|
});
|
|
|
|
it('should accept BufferConfig object', () => {
|
|
const buffer = new BufferAccumulator({
|
|
maxSize: 100,
|
|
trimSize: 50,
|
|
});
|
|
buffer.append('a'.repeat(120));
|
|
expect(buffer.length).toBe(50);
|
|
});
|
|
});
|
|
|
|
describe('chunk consolidation', () => {
|
|
it('should consolidate chunks on value access', () => {
|
|
const buffer = new BufferAccumulator(1000, 800);
|
|
buffer.append('a');
|
|
buffer.append('b');
|
|
buffer.append('c');
|
|
|
|
// First access consolidates
|
|
const value1 = buffer.value;
|
|
expect(value1).toBe('abc');
|
|
|
|
// Second access should return same value efficiently
|
|
const value2 = buffer.value;
|
|
expect(value2).toBe('abc');
|
|
});
|
|
|
|
it('should handle single chunk efficiently', () => {
|
|
const buffer = new BufferAccumulator(1000, 800);
|
|
buffer.append('hello world');
|
|
|
|
// Single chunk doesn't need joining
|
|
expect(buffer.value).toBe('hello world');
|
|
});
|
|
});
|
|
});
|