/** * In-Memory Health Check Adapter * * Simulates API health check responses for testing purposes. * This adapter allows controlled testing of health check scenarios * without making actual HTTP requests. */ import { HealthCheckQuery, ConnectionStatus, ConnectionHealth, HealthCheckResult, } from '../../../../core/health/ports/HealthCheckQuery'; export interface HealthCheckResponse { healthy: boolean; responseTime: number; error?: string; timestamp: Date; } export class InMemoryHealthCheckAdapter implements HealthCheckQuery { private responses: Map = new Map(); public shouldFail: boolean = false; public failError: string = 'Network error'; private responseTime: number = 50; private health: ConnectionHealth = { status: 'disconnected', lastCheck: null, lastSuccess: null, lastFailure: null, consecutiveFailures: 0, totalRequests: 0, successfulRequests: 0, failedRequests: 0, averageResponseTime: 0, }; /** * Configure the adapter to return a specific response */ configureResponse(endpoint: string, response: HealthCheckResponse): void { this.responses.set(endpoint, response); } /** * Configure the adapter to fail all requests */ setShouldFail(shouldFail: boolean, error?: string): void { this.shouldFail = shouldFail; if (error) { this.failError = error; } } /** * Set the response time for health checks */ setResponseTime(time: number): void { this.responseTime = time; } /** * Perform a health check against an endpoint */ async performHealthCheck(): Promise { // Simulate network delay await new Promise(resolve => setTimeout(resolve, this.responseTime)); if (this.shouldFail) { this.recordFailure(this.failError); return { healthy: false, responseTime: this.responseTime, error: this.failError, timestamp: new Date(), }; } // Default successful response this.recordSuccess(this.responseTime); return { healthy: true, responseTime: this.responseTime, timestamp: new Date(), }; } /** * Get current connection status */ getStatus(): ConnectionStatus { return this.health.status; } /** * Get detailed health information */ getHealth(): ConnectionHealth { return { ...this.health }; } /** * Get reliability percentage */ getReliability(): number { if (this.health.totalRequests === 0) return 0; return (this.health.successfulRequests / this.health.totalRequests) * 100; } /** * Check if API is currently available */ isAvailable(): boolean { return this.health.status === 'connected' || this.health.status === 'degraded'; } /** * Record a successful health check */ private recordSuccess(responseTime: number): void { this.health.totalRequests++; this.health.successfulRequests++; this.health.consecutiveFailures = 0; this.health.lastSuccess = new Date(); this.health.lastCheck = new Date(); // Update average response time const total = this.health.successfulRequests; if (total === 1) { this.health.averageResponseTime = responseTime; } else { this.health.averageResponseTime = ((this.health.averageResponseTime * (total - 1)) + responseTime) / total; } this.updateStatus(); } /** * Record a failed health check */ private recordFailure(error: string): void { this.health.totalRequests++; this.health.failedRequests++; this.health.consecutiveFailures++; this.health.lastFailure = new Date(); this.health.lastCheck = new Date(); this.updateStatus(); } /** * Update connection status based on current metrics */ private updateStatus(): void { const reliability = this.health.totalRequests > 0 ? this.health.successfulRequests / this.health.totalRequests : 0; // More nuanced status determination if (this.health.totalRequests === 0) { // No requests yet - don't assume disconnected this.health.status = 'checking'; } else if (this.health.consecutiveFailures >= 3) { // Multiple consecutive failures indicates real connectivity issue this.health.status = 'disconnected'; } else if (reliability < 0.7 && this.health.totalRequests >= 5) { // Only degrade if we have enough samples and reliability is low this.health.status = 'degraded'; } else if (reliability >= 0.7 || this.health.successfulRequests > 0) { // If we have any successes, we're connected this.health.status = 'connected'; } else { // Default to checking if uncertain this.health.status = 'checking'; } } /** * Clear all configured responses and settings */ clear(): void { this.responses.clear(); this.shouldFail = false; this.failError = 'Network error'; this.responseTime = 50; this.health = { status: 'disconnected', lastCheck: null, lastSuccess: null, lastFailure: null, consecutiveFailures: 0, totalRequests: 0, successfulRequests: 0, failedRequests: 0, averageResponseTime: 0, }; } }