Skip to main content
Glama

mcp-adr-analysis-server

by tosin2013
test-helpers.ts8.57 kB
/** * Enhanced Test Helper Utilities * Provides utilities for different test types with proper resource management */ import { jest } from '@jest/globals'; import { testInfrastructure, withTimeout, withResourceTracking } from './test-infrastructure.js'; import * as fs from 'fs/promises'; import * as path from 'path'; // Test type decorators export function unitTest(name: string, testFn: () => Promise<void>): void { it( name, withTimeout( 'unit', withResourceTracking(async () => { await testFn(); }) ) ); } export function integrationTest(name: string, testFn: () => Promise<void>): void { it( name, withTimeout( 'integration', withResourceTracking(async () => { await testFn(); }) ) ); } export function performanceTest(name: string, testFn: () => Promise<void>): void { it( name, withTimeout( 'performance', withResourceTracking(async () => { // Record initial memory testInfrastructure.recordMemoryUsage(); const startTime = Date.now(); await testFn(); const endTime = Date.now(); // Log performance metrics const duration = endTime - startTime; const memStats = testInfrastructure.getMemoryStats(); console.log( `Performance Test "${name}": ${duration}ms, Memory: ${memStats.current.toFixed(2)}MB` ); }) ) ); } // Resource management helpers export async function createTestFile(content: string, filename?: string): Promise<string> { const tempDir = await testInfrastructure.createTempDir('test-file-'); const filePath = path.join(tempDir, filename || 'test-file.txt'); await fs.writeFile(filePath, content, 'utf8'); return filePath; } export async function createTestDirectory(structure: Record<string, string>): Promise<string> { const tempDir = await testInfrastructure.createTempDir('test-dir-'); for (const [relativePath, content] of Object.entries(structure)) { const fullPath = path.join(tempDir, relativePath); const dirPath = path.dirname(fullPath); // Ensure directory exists await fs.mkdir(dirPath, { recursive: true }); // Write file await fs.writeFile(fullPath, content, 'utf8'); } return tempDir; } export function createMockTimer(delay: number = 100): Promise<void> { return new Promise(resolve => { const timer = setTimeout(resolve, delay); testInfrastructure.trackTimer(timer); }); } export function createMockInterval(callback: () => void, interval: number = 100): NodeJS.Timeout { const intervalId = setInterval(callback, interval); testInfrastructure.trackInterval(intervalId); return intervalId; } // Memory and performance monitoring export function expectMemoryUsage(maxMB: number): void { const memStats = testInfrastructure.getMemoryStats(); expect(memStats.current).toBeLessThan(maxMB); } export function expectNoResourceLeaks(): void { const status = testInfrastructure.getResourceStatus(); expect(status.tempDirs).toBeLessThan(10); expect(status.fileHandles).toBeLessThan(20); expect(status.timers).toBeLessThan(5); expect(status.intervals).toBeLessThan(5); } // Async operation helpers export async function waitForCondition( condition: () => boolean | Promise<boolean>, timeout: number = 5000, interval: number = 100 ): Promise<void> { const startTime = Date.now(); while (Date.now() - startTime < timeout) { if (await condition()) { return; } await new Promise(resolve => setTimeout(resolve, interval)); } throw new Error(`Condition not met within ${timeout}ms`); } export async function waitForAsyncOperations(maxWait: number = 1000): Promise<void> { // Wait for next tick await new Promise(resolve => setImmediate(resolve)); // Wait for any pending promises await new Promise(resolve => setTimeout(resolve, 10)); // Additional wait for complex async operations let waited = 0; while (waited < maxWait) { await new Promise(resolve => setTimeout(resolve, 50)); waited += 50; // Check if we can break early (no pending operations) const status = testInfrastructure.getResourceStatus(); if (status.timers === 0 && status.intervals === 0) { break; } } } // Error simulation helpers export function createTimeoutError(message: string = 'Operation timed out'): Error { const error = new Error(message); error.name = 'TimeoutError'; return error; } export function createResourceError(resource: string, limit: number): Error { const error = new Error(`${resource} limit exceeded: ${limit}`); error.name = 'ResourceError'; return error; } // Test data generators export function generateLargeDataset(size: number): Array<{ id: string; data: string }> { return Array.from({ length: size }, (_, i) => ({ id: `item-${i}`, data: `test-data-${i}`.repeat(10), // Make each item reasonably sized })); } export function generateConcurrentOperations<T>( count: number, operationFactory: (index: number) => Promise<T> ): Promise<T>[] { return Array.from({ length: count }, (_, i) => operationFactory(i)); } // Mock helpers with cleanup export function createMockFunction<T extends (...args: any[]) => any>( implementation?: T ): jest.MockedFunction<T> { const mockFn = jest.fn(implementation) as jest.MockedFunction<T>; // Add cleanup callback testInfrastructure.addCleanupCallback(async () => { mockFn.mockRestore(); }); return mockFn; } export function mockConsoleMethod( method: 'log' | 'warn' | 'error' | 'info' | 'debug' ): jest.MockedFunction<any> { const originalMethod = console[method]; const mockMethod = jest.fn(); (console as any)[method] = mockMethod; testInfrastructure.addCleanupCallback(async () => { (console as any)[method] = originalMethod; }); return mockMethod; } // Performance benchmarking export class PerformanceBenchmark { private startTime: number = 0; private endTime: number = 0; private memoryStart: number = 0; private memoryEnd: number = 0; start(): void { this.startTime = Date.now(); this.memoryStart = process.memoryUsage().heapUsed; testInfrastructure.recordMemoryUsage(); } end(): void { this.endTime = Date.now(); this.memoryEnd = process.memoryUsage().heapUsed; testInfrastructure.recordMemoryUsage(); } getDuration(): number { return this.endTime - this.startTime; } getMemoryDelta(): number { return (this.memoryEnd - this.memoryStart) / 1024 / 1024; // MB } expectDurationLessThan(maxMs: number): void { expect(this.getDuration()).toBeLessThan(maxMs); } expectMemoryDeltaLessThan(maxMB: number): void { expect(Math.abs(this.getMemoryDelta())).toBeLessThan(maxMB); } getReport(): string { return `Duration: ${this.getDuration()}ms, Memory Delta: ${this.getMemoryDelta().toFixed(2)}MB`; } } export function createBenchmark(): PerformanceBenchmark { return new PerformanceBenchmark(); } // Test environment helpers export function isCI(): boolean { return process.env.CI === 'true'; } export function isCoverageRun(): boolean { return process.argv.includes('--coverage') || process.env.NODE_ENV === 'test'; } export function getTestTimeout(testType: 'unit' | 'integration' | 'performance'): number { return testInfrastructure.getTimeoutForTestType(testType); } // Retry helpers for flaky tests export async function retryOperation<T>( operation: () => Promise<T>, maxRetries: number = 3, delay: number = 100 ): Promise<T> { let lastError: Error; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await operation(); } catch (error) { lastError = error as Error; if (attempt === maxRetries) { throw lastError; } // Wait before retry await new Promise(resolve => setTimeout(resolve, delay * attempt)); } } throw lastError!; } // Cleanup verification export function verifyCleanup(): void { const status = testInfrastructure.getResourceStatus(); const memStats = testInfrastructure.getMemoryStats(); // Log status for debugging console.log('Resource Status:', status); console.log('Memory Stats:', memStats); // Verify no major leaks expect(status.tempDirs).toBeLessThan(20); expect(status.fileHandles).toBeLessThan(50); expect(status.timers).toBeLessThan(10); expect(status.intervals).toBeLessThan(10); } // Directory helper for tests export function getCurrentDir(): string { return process.cwd(); } export { testInfrastructure };

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/tosin2013/mcp-adr-analysis-server'

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