fix(refactor): remove deprecated Port80Handler and related utilities

- Deleted event-utils.ts which contained deprecated Port80Handler and its subscribers.
- Updated index.ts to remove the export of event-utils.
- Refactored ConnectionManager to extend LifecycleComponent for better resource management.
- Added BinaryHeap implementation for efficient priority queue operations.
- Introduced EnhancedConnectionPool for managing pooled connections with lifecycle management.
- Implemented LifecycleComponent to manage timers and event listeners automatically.
- Added comprehensive tests for BinaryHeap and LifecycleComponent to ensure functionality.
This commit is contained in:
2025-05-31 18:01:09 +00:00
parent 7b81186bb3
commit 829ae0d6a3
13 changed files with 1354 additions and 559 deletions

View File

@ -0,0 +1,206 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import { BinaryHeap } from '../../../ts/core/utils/binary-heap.js';
interface TestItem {
id: string;
priority: number;
value: string;
}
tap.test('should create empty heap', async () => {
const heap = new BinaryHeap<number>((a, b) => a - b);
expect(heap.size).toEqual(0);
expect(heap.isEmpty()).toBeTrue();
expect(heap.peek()).toBeUndefined();
});
tap.test('should insert and extract in correct order', async () => {
const heap = new BinaryHeap<number>((a, b) => a - b);
heap.insert(5);
heap.insert(3);
heap.insert(7);
heap.insert(1);
heap.insert(9);
heap.insert(4);
expect(heap.size).toEqual(6);
// Extract in ascending order
expect(heap.extract()).toEqual(1);
expect(heap.extract()).toEqual(3);
expect(heap.extract()).toEqual(4);
expect(heap.extract()).toEqual(5);
expect(heap.extract()).toEqual(7);
expect(heap.extract()).toEqual(9);
expect(heap.extract()).toBeUndefined();
});
tap.test('should work with custom objects and comparator', async () => {
const heap = new BinaryHeap<TestItem>(
(a, b) => a.priority - b.priority,
(item) => item.id
);
heap.insert({ id: 'a', priority: 5, value: 'five' });
heap.insert({ id: 'b', priority: 2, value: 'two' });
heap.insert({ id: 'c', priority: 8, value: 'eight' });
heap.insert({ id: 'd', priority: 1, value: 'one' });
const first = heap.extract();
expect(first?.priority).toEqual(1);
expect(first?.value).toEqual('one');
const second = heap.extract();
expect(second?.priority).toEqual(2);
expect(second?.value).toEqual('two');
});
tap.test('should support reverse order (max heap)', async () => {
const heap = new BinaryHeap<number>((a, b) => b - a);
heap.insert(5);
heap.insert(3);
heap.insert(7);
heap.insert(1);
heap.insert(9);
// Extract in descending order
expect(heap.extract()).toEqual(9);
expect(heap.extract()).toEqual(7);
expect(heap.extract()).toEqual(5);
});
tap.test('should extract by predicate', async () => {
const heap = new BinaryHeap<TestItem>((a, b) => a.priority - b.priority);
heap.insert({ id: 'a', priority: 5, value: 'five' });
heap.insert({ id: 'b', priority: 2, value: 'two' });
heap.insert({ id: 'c', priority: 8, value: 'eight' });
const extracted = heap.extractIf(item => item.id === 'b');
expect(extracted?.id).toEqual('b');
expect(heap.size).toEqual(2);
// Should not find it again
const notFound = heap.extractIf(item => item.id === 'b');
expect(notFound).toBeUndefined();
});
tap.test('should extract by key', async () => {
const heap = new BinaryHeap<TestItem>(
(a, b) => a.priority - b.priority,
(item) => item.id
);
heap.insert({ id: 'a', priority: 5, value: 'five' });
heap.insert({ id: 'b', priority: 2, value: 'two' });
heap.insert({ id: 'c', priority: 8, value: 'eight' });
expect(heap.hasKey('b')).toBeTrue();
const extracted = heap.extractByKey('b');
expect(extracted?.id).toEqual('b');
expect(heap.size).toEqual(2);
expect(heap.hasKey('b')).toBeFalse();
// Should not find it again
const notFound = heap.extractByKey('b');
expect(notFound).toBeUndefined();
});
tap.test('should throw when using key operations without extractKey', async () => {
const heap = new BinaryHeap<TestItem>((a, b) => a.priority - b.priority);
heap.insert({ id: 'a', priority: 5, value: 'five' });
let error: Error | null = null;
try {
heap.extractByKey('a');
} catch (e: any) {
error = e;
}
expect(error).not.toBeNull();
expect(error?.message).toContain('extractKey function must be provided');
});
tap.test('should handle duplicates correctly', async () => {
const heap = new BinaryHeap<number>((a, b) => a - b);
heap.insert(5);
heap.insert(5);
heap.insert(5);
heap.insert(3);
heap.insert(7);
expect(heap.size).toEqual(5);
expect(heap.extract()).toEqual(3);
expect(heap.extract()).toEqual(5);
expect(heap.extract()).toEqual(5);
expect(heap.extract()).toEqual(5);
expect(heap.extract()).toEqual(7);
});
tap.test('should convert to array without modifying heap', async () => {
const heap = new BinaryHeap<number>((a, b) => a - b);
heap.insert(5);
heap.insert(3);
heap.insert(7);
const array = heap.toArray();
expect(array).toContain(3);
expect(array).toContain(5);
expect(array).toContain(7);
expect(array.length).toEqual(3);
// Heap should still be intact
expect(heap.size).toEqual(3);
expect(heap.extract()).toEqual(3);
});
tap.test('should clear the heap', async () => {
const heap = new BinaryHeap<TestItem>(
(a, b) => a.priority - b.priority,
(item) => item.id
);
heap.insert({ id: 'a', priority: 5, value: 'five' });
heap.insert({ id: 'b', priority: 2, value: 'two' });
expect(heap.size).toEqual(2);
expect(heap.hasKey('a')).toBeTrue();
heap.clear();
expect(heap.size).toEqual(0);
expect(heap.isEmpty()).toBeTrue();
expect(heap.hasKey('a')).toBeFalse();
});
tap.test('should handle complex extraction patterns', async () => {
const heap = new BinaryHeap<number>((a, b) => a - b);
// Insert numbers 1-10 in random order
[8, 3, 5, 9, 1, 7, 4, 10, 2, 6].forEach(n => heap.insert(n));
// Extract some in order
expect(heap.extract()).toEqual(1);
expect(heap.extract()).toEqual(2);
// Insert more
heap.insert(0);
heap.insert(1.5);
// Continue extracting
expect(heap.extract()).toEqual(0);
expect(heap.extract()).toEqual(1.5);
expect(heap.extract()).toEqual(3);
// Verify remaining size (10 - 2 extracted + 2 inserted - 3 extracted = 7)
expect(heap.size).toEqual(7);
});
tap.start();

View File

@ -0,0 +1,252 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import { LifecycleComponent } from '../../../ts/core/utils/lifecycle-component.js';
import { EventEmitter } from 'events';
// Test implementation of LifecycleComponent
class TestComponent extends LifecycleComponent {
public timerCallCount = 0;
public intervalCallCount = 0;
public cleanupCalled = false;
public testEmitter = new EventEmitter();
public listenerCallCount = 0;
constructor() {
super();
this.setupTimers();
this.setupListeners();
}
private setupTimers() {
// Set up a timeout
this.setTimeout(() => {
this.timerCallCount++;
}, 100);
// Set up an interval
this.setInterval(() => {
this.intervalCallCount++;
}, 50);
}
private setupListeners() {
this.addEventListener(this.testEmitter, 'test-event', () => {
this.listenerCallCount++;
});
}
protected async onCleanup(): Promise<void> {
this.cleanupCalled = true;
}
// Expose protected methods for testing
public testSetTimeout(handler: Function, timeout: number): NodeJS.Timeout {
return this.setTimeout(handler, timeout);
}
public testSetInterval(handler: Function, interval: number): NodeJS.Timeout {
return this.setInterval(handler, interval);
}
public testClearTimeout(timer: NodeJS.Timeout): void {
return this.clearTimeout(timer);
}
public testClearInterval(timer: NodeJS.Timeout): void {
return this.clearInterval(timer);
}
public testAddEventListener(target: any, event: string, handler: Function): void {
return this.addEventListener(target, event, handler);
}
public testIsShuttingDown(): boolean {
return this.isShuttingDownState();
}
}
tap.test('should manage timers properly', async () => {
const component = new TestComponent();
// Wait for timers to fire
await new Promise(resolve => setTimeout(resolve, 200));
expect(component.timerCallCount).toEqual(1);
expect(component.intervalCallCount).toBeGreaterThan(2);
await component.cleanup();
});
tap.test('should manage event listeners properly', async () => {
const component = new TestComponent();
// Emit events
component.testEmitter.emit('test-event');
component.testEmitter.emit('test-event');
expect(component.listenerCallCount).toEqual(2);
// Cleanup and verify listeners are removed
await component.cleanup();
component.testEmitter.emit('test-event');
expect(component.listenerCallCount).toEqual(2); // Should not increase
});
tap.test('should prevent timer execution after cleanup', async () => {
const component = new TestComponent();
let laterCallCount = 0;
component.testSetTimeout(() => {
laterCallCount++;
}, 100);
// Cleanup immediately
await component.cleanup();
// Wait for timer that would have fired
await new Promise(resolve => setTimeout(resolve, 150));
expect(laterCallCount).toEqual(0);
});
tap.test('should handle child components', async () => {
class ParentComponent extends LifecycleComponent {
public child: TestComponent;
constructor() {
super();
this.child = new TestComponent();
this.registerChildComponent(this.child);
}
}
const parent = new ParentComponent();
// Wait for child timers
await new Promise(resolve => setTimeout(resolve, 100));
expect(parent.child.timerCallCount).toEqual(1);
// Cleanup parent should cleanup child
await parent.cleanup();
expect(parent.child.cleanupCalled).toBeTrue();
expect(parent.child.testIsShuttingDown()).toBeTrue();
});
tap.test('should handle multiple cleanup calls gracefully', async () => {
const component = new TestComponent();
// Call cleanup multiple times
const promises = [
component.cleanup(),
component.cleanup(),
component.cleanup()
];
await Promise.all(promises);
// Should only clean up once
expect(component.cleanupCalled).toBeTrue();
});
tap.test('should clear specific timers', async () => {
const component = new TestComponent();
let callCount = 0;
const timer = component.testSetTimeout(() => {
callCount++;
}, 100);
// Clear the timer
component.testClearTimeout(timer);
// Wait and verify it didn't fire
await new Promise(resolve => setTimeout(resolve, 150));
expect(callCount).toEqual(0);
await component.cleanup();
});
tap.test('should clear specific intervals', async () => {
const component = new TestComponent();
let callCount = 0;
const interval = component.testSetInterval(() => {
callCount++;
}, 50);
// Let it run a bit
await new Promise(resolve => setTimeout(resolve, 120));
const countBeforeClear = callCount;
expect(countBeforeClear).toBeGreaterThan(1);
// Clear the interval
component.testClearInterval(interval);
// Wait and verify it stopped
await new Promise(resolve => setTimeout(resolve, 100));
expect(callCount).toEqual(countBeforeClear);
await component.cleanup();
});
tap.test('should handle once event listeners', async () => {
const component = new TestComponent();
const emitter = new EventEmitter();
let callCount = 0;
const handler = () => {
callCount++;
};
component.testAddEventListener(emitter, 'once-event', handler, { once: true });
// Check listener count before emit
const beforeCount = emitter.listenerCount('once-event');
expect(beforeCount).toEqual(1);
// Emit once - the listener should fire and auto-remove
emitter.emit('once-event');
expect(callCount).toEqual(1);
// Check listener was auto-removed
const afterCount = emitter.listenerCount('once-event');
expect(afterCount).toEqual(0);
// Emit again - should not increase count
emitter.emit('once-event');
expect(callCount).toEqual(1);
await component.cleanup();
});
tap.test('should not create timers when shutting down', async () => {
const component = new TestComponent();
// Start cleanup
const cleanupPromise = component.cleanup();
// Try to create timers during shutdown
let timerFired = false;
let intervalFired = false;
component.testSetTimeout(() => {
timerFired = true;
}, 10);
component.testSetInterval(() => {
intervalFired = true;
}, 10);
await cleanupPromise;
await new Promise(resolve => setTimeout(resolve, 50));
expect(timerFired).toBeFalse();
expect(intervalFired).toBeFalse();
});
tap.start();