import { expect, tap } from '@git.zone/tstest/tapbundle'; import { SmartProxy } from '../ts/index.js'; tap.test('cleanup queue bug - verify queue processing handles more than batch size', async () => { console.log('\n=== Cleanup Queue Bug Test ==='); console.log('Purpose: Verify that the cleanup queue correctly processes all connections'); console.log('even when there are more than the batch size (100)'); // Create proxy const proxy = new SmartProxy({ routes: [{ name: 'test-route', match: { ports: 8588 }, action: { type: 'forward', target: { host: 'localhost', port: 9996 } } }], enableDetailedLogging: false, }); await proxy.start(); console.log('āœ“ Proxy started on port 8588'); // Access connection manager const cm = (proxy as any).connectionManager; // Create mock connection records console.log('\n--- Creating 150 mock connections ---'); const mockConnections: any[] = []; for (let i = 0; i < 150; i++) { // Create mock socket objects with necessary methods const mockIncoming = { destroyed: true, writable: false, remoteAddress: '127.0.0.1', removeAllListeners: () => {}, destroy: () => {}, end: () => {}, on: () => {}, once: () => {}, emit: () => {}, pause: () => {}, resume: () => {} }; const mockOutgoing = { destroyed: true, writable: false, removeAllListeners: () => {}, destroy: () => {}, end: () => {}, on: () => {}, once: () => {}, emit: () => {} }; const mockRecord = { id: `mock-${i}`, incoming: mockIncoming, outgoing: mockOutgoing, connectionClosed: false, incomingStartTime: Date.now(), lastActivity: Date.now(), remoteIP: '127.0.0.1', remotePort: 10000 + i, localPort: 8588, bytesReceived: 100, bytesSent: 100, incomingTerminationReason: null, cleanupTimer: null }; // Add to connection records cm.connectionRecords.set(mockRecord.id, mockRecord); mockConnections.push(mockRecord); } console.log(`Created ${cm.getConnectionCount()} mock connections`); expect(cm.getConnectionCount()).toEqual(150); // Queue all connections for cleanup console.log('\n--- Queueing all connections for cleanup ---'); // The cleanup queue processes immediately when it reaches batch size (100) // So after queueing 150, the first 100 will be processed immediately for (const conn of mockConnections) { cm.initiateCleanupOnce(conn, 'test_cleanup'); } // After queueing 150, the first 100 should have been processed immediately // leaving 50 in the queue console.log(`Cleanup queue size after queueing: ${cm.cleanupQueue.size}`); console.log(`Active connections after initial batch: ${cm.getConnectionCount()}`); // The first 100 should have been cleaned up immediately expect(cm.cleanupQueue.size).toEqual(50); expect(cm.getConnectionCount()).toEqual(50); // Wait for remaining cleanup to complete console.log('\n--- Waiting for remaining cleanup batches to process ---'); // The remaining 50 connections should be cleaned up in the next batch let waitTime = 0; let lastCount = cm.getConnectionCount(); while (cm.getConnectionCount() > 0 || cm.cleanupQueue.size > 0) { await new Promise(resolve => setTimeout(resolve, 100)); waitTime += 100; const currentCount = cm.getConnectionCount(); if (currentCount !== lastCount) { console.log(`Active connections: ${currentCount}, Queue size: ${cm.cleanupQueue.size}`); lastCount = currentCount; } if (waitTime > 5000) { console.log('Timeout waiting for cleanup to complete'); break; } } console.log(`All cleanup completed in ${waitTime}ms`); // Check final state const finalCount = cm.getConnectionCount(); console.log(`\nFinal connection count: ${finalCount}`); console.log(`Final cleanup queue size: ${cm.cleanupQueue.size}`); // All connections should be cleaned up expect(finalCount).toEqual(0); expect(cm.cleanupQueue.size).toEqual(0); // Verify termination stats - all 150 should have been terminated const stats = cm.getTerminationStats(); console.log('Termination stats:', stats); expect(stats.incoming.test_cleanup).toEqual(150); // Cleanup console.log('\n--- Stopping proxy ---'); await proxy.stop(); console.log('\nāœ“ Test complete: Cleanup queue now correctly processes all connections'); }); tap.start();