Skip to main content
Glama

Prompt Auto-Optimizer MCP

by sloth-wq
memory-leak-detector.test.ts13.1 kB
/** * Comprehensive Test Suite for Memory Leak Detection System */ import { MemoryLeakDetector, MemoryLeakIntegration, ComponentMemoryTracker } from './memory-leak-detector'; describe('MemoryLeakDetector', () => { let detector: MemoryLeakDetector; beforeEach(() => { detector = new MemoryLeakDetector({ heapGrowthRate: 5, // Lower threshold for testing maxHeapSize: 100, maxObjectCount: 1000, monitoringWindow: 10000, snapshotInterval: 5000, memoryIncreaseThreshold: 25, }); }); afterEach(() => { detector.shutdown(); }); describe('Component Registration', () => { it('should register components for monitoring', () => { const tracker = detector.registerComponent('test-component', { maxObjectCount: 500, }); expect(tracker.name).toBe('test-component'); expect(tracker.objectCount).toBe(0); expect(tracker.thresholds.maxObjectCount).toBe(500); }); it('should emit componentRegistered event', (done) => { detector.on('componentRegistered', ({ name, tracker }) => { expect(name).toBe('test-component'); expect(tracker).toBeDefined(); done(); }); detector.registerComponent('test-component'); }); }); describe('Object Tracking', () => { let tracker: ComponentMemoryTracker; beforeEach(() => { tracker = detector.registerComponent('test-component'); }); it('should track object allocation', () => { const testObject = { data: 'test' }; detector.trackObjectAllocation('test-component', testObject, 100); expect(tracker.objectCount).toBe(1); expect(tracker.memoryUsage).toBe(100); expect(tracker.weakRefs.size).toBe(1); }); it('should track object deallocation', () => { detector.trackObjectAllocation('test-component', { data: 'test' }, 100); detector.trackObjectDeallocation('test-component', 50); expect(tracker.objectCount).toBe(0); expect(tracker.memoryUsage).toBe(50); }); it('should not allow negative object counts', () => { detector.trackObjectDeallocation('test-component', 100); expect(tracker.objectCount).toBe(0); expect(tracker.memoryUsage).toBe(0); }); }); describe('Memory Leak Detection', () => { it('should detect object accumulation leaks', async () => { const tracker = detector.registerComponent('leak-component', { maxObjectCount: 5, }); // Allocate more objects than threshold for (let i = 0; i < 10; i++) { detector.trackObjectAllocation('leak-component', { id: i }); } const detections = await detector.detectMemoryLeaks(); const objectLeak = detections.find(d => d.leakType === 'object_accumulation'); expect(objectLeak).toBeDefined(); expect(objectLeak?.component).toBe('leak-component'); expect(objectLeak?.severity).toBe('high'); }); it('should detect weak reference buildup', async () => { const tracker = detector.registerComponent('weakref-component', { maxObjectCount: 10, }); // Create many weak references for (let i = 0; i < 20; i++) { detector.trackObjectAllocation('weakref-component', { id: i }); } const detections = await detector.detectMemoryLeaks(); const weakRefLeak = detections.find(d => d.leakType === 'weak_ref_buildup'); expect(weakRefLeak).toBeDefined(); expect(weakRefLeak?.component).toBe('weakref-component'); }); it('should emit memoryLeakDetected events', (done) => { detector.on('memoryLeakDetected', (detection) => { expect(detection.component).toBeDefined(); expect(detection.leakType).toBeDefined(); expect(detection.severity).toBeDefined(); done(); }); detector.registerComponent('event-component', { maxObjectCount: 1, }); // Trigger leak detection detector.trackObjectAllocation('event-component', { data: 'test' }); detector.trackObjectAllocation('event-component', { data: 'test2' }); detector.detectMemoryLeaks(); }); }); describe('Heap Snapshots', () => { it('should create heap snapshots', async () => { const snapshot = await detector.createHeapSnapshot(); expect(snapshot).toBeDefined(); expect(snapshot.timestamp).toBeDefined(); expect(snapshot.memoryUsage).toBeDefined(); }); it('should compare heap snapshots', async () => { const snapshot1 = await detector.createHeapSnapshot(); // Allocate some memory Array.from({ length: 100 }, () => Buffer.alloc(1024)); const snapshot2 = await detector.createHeapSnapshot(); const comparison = await detector.compareHeapSnapshots(snapshot1, snapshot2); expect(comparison.sizeIncrement).toBeGreaterThan(0); expect(comparison.added).toBeGreaterThanOrEqual(0); }); }); describe('Cleanup Operations', () => { it('should force cleanup and remove dead weak references', async () => { const tracker = detector.registerComponent('cleanup-component'); // Create objects and let them go out of scope (() => { for (let i = 0; i < 10; i++) { detector.trackObjectAllocation('cleanup-component', { id: i }); } })(); // Force garbage collection if available if (global.gc) { global.gc(); } const result = await detector.forceCleanup(); expect(result.cleaned).toBeGreaterThanOrEqual(0); }); it('should emit cleanup events', (done) => { detector.on('forceCleanup', ({ cleaned, memoryFreed }) => { expect(typeof cleaned).toBe('number'); expect(typeof memoryFreed).toBe('number'); done(); }); detector.forceCleanup(); }); }); describe('Memory Pressure Simulation', () => { it('should simulate memory pressure', async () => { const config = { enabled: true, targetMemoryMB: 10, duration: 1000, escalationSteps: 2, }; const events: string[] = []; detector.on('memoryPressureStart', () => events.push('start')); detector.on('memoryPressureStep', () => events.push('step')); detector.on('memoryPressureEnd', () => events.push('end')); await detector.simulateMemoryPressure(config); expect(events).toContain('start'); expect(events).toContain('step'); expect(events).toContain('end'); }); it('should not simulate when disabled', async () => { const config = { enabled: false, targetMemoryMB: 10, duration: 1000, escalationSteps: 2, }; let eventCount = 0; detector.on('memoryPressureStart', () => eventCount++); await detector.simulateMemoryPressure(config); expect(eventCount).toBe(0); }); }); describe('Statistics and Reporting', () => { it('should provide comprehensive statistics', () => { const tracker = detector.registerComponent('stats-component'); detector.trackObjectAllocation('stats-component', { data: 'test' }, 100); const stats = detector.getStatistics(); expect(stats.detections).toBeDefined(); expect(stats.components).toHaveLength(6); // 5 default + 1 test component expect(stats.recentDetections).toBeDefined(); expect(stats.memoryTrend).toBeDefined(); const testComponent = stats.components.find(c => c.name === 'stats-component'); expect(testComponent?.objectCount).toBe(1); expect(testComponent?.memoryUsage).toBe(100); }); it('should track detection history', async () => { const tracker = detector.registerComponent('history-component', { maxObjectCount: 1, }); // Trigger multiple detections detector.trackObjectAllocation('history-component', { id: 1 }); detector.trackObjectAllocation('history-component', { id: 2 }); await detector.detectMemoryLeaks(); const stats = detector.getStatistics(); expect(stats.detections.totalDetections).toBeGreaterThan(0); }); }); describe('Auto-Fix Functionality', () => { it('should apply auto-fixes for heap growth', async () => { let autoFixApplied = false; detector.on('autoFixApplied', () => { autoFixApplied = true; }); // Simulate heap growth detection with auto-fix const detection = { timestamp: Date.now(), component: 'system', leakType: 'heap_growth' as const, severity: 'high' as const, currentUsage: 1000000, growthRate: 15, recommendation: 'Force garbage collection', autoFixAvailable: true, }; await (detector as any).applyAutoFix(detection); // Check if global.gc was called (if available) if (global.gc) { expect(autoFixApplied).toBe(true); } }); }); }); describe('MemoryLeakIntegration', () => { beforeEach(() => { MemoryLeakIntegration.initialize(); }); afterEach(() => { MemoryLeakIntegration.shutdown(); }); describe('Integration Hooks', () => { it('should track cache operations', () => { const detector = MemoryLeakIntegration.getDetector(); expect(detector).toBeDefined(); // These should not throw MemoryLeakIntegration.trackCacheOperation('set', 'test-key', 100); MemoryLeakIntegration.trackCacheOperation('get', 'test-key'); MemoryLeakIntegration.trackCacheOperation('delete', 'test-key', 100); }); it('should track Pareto frontier operations', () => { MemoryLeakIntegration.trackParetoOperation('add', 'candidate-1', 200); MemoryLeakIntegration.trackParetoOperation('remove', 'candidate-1', 200); }); it('should track LLM process operations', () => { MemoryLeakIntegration.trackLLMProcess('spawn', 'process-1', 1024); MemoryLeakIntegration.trackLLMProcess('exit', 'process-1', 1024); }); it('should handle operations when detector is not initialized', () => { MemoryLeakIntegration.shutdown(); // These should not throw even when detector is null expect(() => { MemoryLeakIntegration.trackCacheOperation('set', 'test-key'); MemoryLeakIntegration.trackParetoOperation('add', 'candidate-1'); MemoryLeakIntegration.trackLLMProcess('spawn', 'process-1'); }).not.toThrow(); }); }); describe('Lifecycle Management', () => { it('should initialize detector only once', () => { const detector1 = MemoryLeakIntegration.initialize(); const detector2 = MemoryLeakIntegration.initialize(); expect(detector1).toBe(detector2); }); it('should properly shutdown detector', () => { const detector = MemoryLeakIntegration.getDetector(); expect(detector).toBeDefined(); MemoryLeakIntegration.shutdown(); expect(MemoryLeakIntegration.getDetector()).toBeNull(); }); }); }); describe('Memory Leak Stress Tests', () => { let detector: MemoryLeakDetector; beforeEach(() => { detector = new MemoryLeakDetector({ heapGrowthRate: 1, maxObjectCount: 100, monitoringWindow: 5000, }); }); afterEach(() => { detector.shutdown(); }); it('should handle rapid object allocation/deallocation', () => { const tracker = detector.registerComponent('stress-component'); // Rapidly allocate and deallocate objects for (let i = 0; i < 1000; i++) { detector.trackObjectAllocation('stress-component', { id: i }); if (i % 2 === 0) { detector.trackObjectDeallocation('stress-component'); } } // Should not crash or corrupt state expect(tracker.objectCount).toBeGreaterThanOrEqual(0); expect(tracker.memoryUsage).toBeGreaterThanOrEqual(0); }); it('should handle concurrent operations', async () => { const tracker = detector.registerComponent('concurrent-component'); // Simulate concurrent operations const promises = Array.from({ length: 10 }, async (_, i) => { for (let j = 0; j < 100; j++) { detector.trackObjectAllocation('concurrent-component', { id: `${i}-${j}` }); if (j % 10 === 0) { await detector.detectMemoryLeaks(); } } }); await Promise.all(promises); // Verify final state is consistent expect(tracker.objectCount).toBe(1000); expect(tracker.weakRefs.size).toBe(1000); }); it('should maintain performance under load', async () => { const tracker = detector.registerComponent('performance-component'); const startTime = performance.now(); // Allocate many objects for (let i = 0; i < 10000; i++) { detector.trackObjectAllocation('performance-component', { id: i }); } // Run detection await detector.detectMemoryLeaks(); const duration = performance.now() - startTime; // Should complete within reasonable time (adjust threshold as needed) expect(duration).toBeLessThan(5000); // 5 seconds }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sloth-wq/prompt-auto-optimizer-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server