Skip to main content
Glama
cleanup-integration.test.ts7.53 kB
/** * Integration test for test cleanup utilities * Demonstrates proper usage of EventEmitter cleanup, singleton management, and memory monitoring */ import { describe, it, expect } from 'vitest'; import { EventEmitter } from 'events'; import { withTestCleanup, createTestEventEmitter, createTestService, registerTestSingleton, waitFor, createMockFunction, assertMemoryUsage } from './test-helpers.js'; import { performTestCleanup, registerEventEmitter, registerCleanupFunction, checkMemoryLeaks, getMemoryUsage } from './test-cleanup.js'; // Mock service that extends EventEmitter class MockService extends EventEmitter { private isActive = true; private timers: NodeJS.Timeout[] = []; constructor(public name: string) { super(); this.setMaxListeners(30); // Higher limit for tests } start(): void { this.emit('started', this.name); // Simulate some background work const timer = setInterval(() => { if (this.isActive) { this.emit('heartbeat', Date.now()); } }, 100); this.timers.push(timer); } stop(): void { this.isActive = false; this.timers.forEach(timer => clearInterval(timer)); this.timers = []; this.emit('stopped', this.name); } cleanup(): void { this.stop(); this.removeAllListeners(); } } // Singleton service for testing class SingletonService { private static instance: SingletonService | null = null; private data: string[] = []; static getInstance(): SingletonService { if (!SingletonService.instance) { SingletonService.instance = new SingletonService(); } return SingletonService.instance; } addData(item: string): void { this.data.push(item); } getData(): string[] { return [...this.data]; } reset(): void { this.data = []; SingletonService.instance = null; } } describe('Test Cleanup Integration', () => { // Apply automatic cleanup withTestCleanup('cleanup-integration'); describe('EventEmitter Cleanup', () => { it('should automatically clean up EventEmitters', async () => { const emitter1 = createTestEventEmitter('test-emitter-1'); const emitter2 = createTestEventEmitter('test-emitter-2'); let event1Count = 0; let event2Count = 0; emitter1.on('test', () => event1Count++); emitter2.on('test', () => event2Count++); // Emit some events emitter1.emit('test'); emitter2.emit('test'); expect(event1Count).toBe(1); expect(event2Count).toBe(1); // Cleanup will happen automatically in afterEach // We can also manually trigger it for testing await performTestCleanup(); // After cleanup, listeners should be removed emitter1.emit('test'); emitter2.emit('test'); // Counts should not increase expect(event1Count).toBe(1); expect(event2Count).toBe(1); }); it('should handle services with EventEmitter cleanup', async () => { const service = createTestService( 'mock-service', () => new MockService('test-service'), 'cleanup' ); let startedCount = 0; let heartbeatCount = 0; let stoppedCount = 0; service.on('started', () => startedCount++); service.on('heartbeat', () => heartbeatCount++); service.on('stopped', () => stoppedCount++); service.start(); expect(startedCount).toBe(1); // Wait for some heartbeats await waitFor(() => heartbeatCount > 2, 1000); expect(heartbeatCount).toBeGreaterThan(2); // Cleanup will happen automatically await performTestCleanup(); expect(stoppedCount).toBe(1); }); }); describe('Singleton Management', () => { it('should reset singletons between tests', () => { const singleton1 = SingletonService.getInstance(); singleton1.addData('test-data-1'); registerTestSingleton('SingletonService', SingletonService.getInstance, 'reset'); expect(singleton1.getData()).toEqual(['test-data-1']); // After cleanup, singleton should be reset // This will be tested in the next test }); it('should have clean singleton state', () => { // This test should see a fresh singleton due to cleanup const singleton2 = SingletonService.getInstance(); expect(singleton2.getData()).toEqual([]); singleton2.addData('test-data-2'); expect(singleton2.getData()).toEqual(['test-data-2']); }); }); describe('Memory Management', () => { it('should monitor memory usage', () => { const initialMemory = getMemoryUsage(); expect(initialMemory.heapUsed).toBeGreaterThan(0); expect(initialMemory.formatted.heapUsed).toMatch(/\d+(\.\d+)? MB/); // Create some objects to use memory const largeArray = new Array(10000).fill('test-data'); expect(largeArray.length).toBe(10000); const afterMemory = getMemoryUsage(); expect(afterMemory.heapUsed).toBeGreaterThanOrEqual(initialMemory.heapUsed); }); it('should detect memory leaks', async () => { // Create some EventEmitters without registering them for cleanup const unregisteredEmitter = new EventEmitter(); registerEventEmitter(unregisteredEmitter, 'unregistered-test'); // Create a cleanup function without executing it registerCleanupFunction('test-cleanup', () => { // This won't be executed, simulating a leak }); const leakCheck = checkMemoryLeaks(); // Should detect the unregistered resources expect(leakCheck.hasLeaks).toBe(true); expect(leakCheck.warnings.length).toBeGreaterThan(0); // Clean up manually to avoid affecting other tests await performTestCleanup(); }); it('should assert memory usage limits', () => { // This should pass with reasonable memory usage assertMemoryUsage(1000); // 1GB limit // This would fail if memory usage was too high expect(() => assertMemoryUsage(1)).toThrow('Memory usage too high'); }); }); describe('Mock Functions', () => { it('should create and cleanup mock functions', () => { const mockFn = createMockFunction('test-mock', (x: number) => x * 2); const result1 = mockFn(5); const result2 = mockFn(10); expect(result1).toBe(10); expect(result2).toBe(20); // Mock function tracks calls internally // Cleanup will happen automatically }); }); describe('Async Operations', () => { it('should handle async cleanup properly', async () => { let cleanupExecuted = false; registerCleanupFunction('async-cleanup', async () => { await new Promise(resolve => setTimeout(resolve, 10)); cleanupExecuted = true; }); await performTestCleanup(); expect(cleanupExecuted).toBe(true); }); it('should wait for conditions with timeout', async () => { let counter = 0; const timer = setInterval(() => counter++, 10); try { await waitFor(() => counter >= 5, 1000); expect(counter).toBeGreaterThanOrEqual(5); } finally { clearInterval(timer); } }); it('should timeout when condition is not met', async () => { await expect( waitFor(() => false, 100) // Will never be true ).rejects.toThrow('Condition not met within 100ms'); }); }); });

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/freshtechbro/vibe-coder-mcp'

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