integration tests
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* 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<string, HealthCheckResponse> = 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<HealthCheckResult> {
|
||||
// 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;
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user