@clduab11/gemini-flow
Version:
Revolutionary AI agent swarm coordination platform with Google Services integration, multimedia processing, and production-ready monitoring. Features 8 Google AI services, quantum computing capabilities, and enterprise-grade security.
979 lines (866 loc) • 27.9 kB
text/typescript
/**
* Unit Tests for Error Handling and Monitoring
*
* Comprehensive test suite for the ErrorHandlingMonitoring class,
* covering error handling, metrics collection, alerting, health checks,
* distributed tracing, and SLO management.
*/
import {
describe,
it,
expect,
beforeEach,
afterEach,
jest,
} from "@jest/globals";
import { EventEmitter } from "events";
import { ErrorHandlingMonitoring } from "../infrastructure/error-handling-monitoring.js";
import { Logger } from "../../../utils/logger.js";
// Mock dependencies
jest.mock("../../../utils/logger.js");
describe("ErrorHandlingMonitoring", () => {
let errorHandling: ErrorHandlingMonitoring;
let mockConfig: any;
let mockLogger: jest.Mocked<Logger>;
beforeEach(() => {
// Setup mock configuration
mockConfig = {
environment: "test",
errorHandling: {
retryAttempts: 3,
circuitBreaker: {
failureThreshold: 5,
timeout: 30000,
monitoringPeriod: 60000,
},
escalation: {
enabled: true,
levels: ["warn", "error", "critical"],
},
},
metrics: {
collection: {
interval: 5000,
batchSize: 100,
},
retention: 86400,
aggregation: {
enabled: true,
window: 60,
},
},
alerting: {
enabled: true,
channels: ["email", "slack"],
rateLimit: {
maxAlerts: 10,
window: 300,
},
escalation: {
timeout: 300,
levels: 3,
},
},
healthChecks: {
interval: 30000,
timeout: 5000,
checks: [
{ name: "database", type: "liveness", endpoint: "/health/db" },
{ name: "cache", type: "readiness", endpoint: "/health/cache" },
],
},
tracing: {
enabled: true,
sampleRate: 0.1,
maxSpans: 1000,
},
slo: {
enabled: true,
targets: {
availability: 99.9,
latency: 100,
errorRate: 0.1,
},
},
};
// Setup mock logger
mockLogger = {
info: jest.fn(),
debug: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
} as any;
(Logger as jest.MockedClass<typeof Logger>).mockReturnValue(mockLogger);
errorHandling = new ErrorHandlingMonitoring(mockConfig);
});
afterEach(() => {
jest.clearAllMocks();
});
describe("Initialization", () => {
it("should initialize successfully with valid configuration", async () => {
// Arrange
const initSpy = jest.spyOn(errorHandling, "initialize");
// Act
await errorHandling.initialize();
// Assert
expect(initSpy).toHaveBeenCalledTimes(1);
expect(mockLogger.info).toHaveBeenCalledWith(
"Initializing Error Handling and Monitoring System",
);
});
it("should start monitoring services after initialization", async () => {
// Act
await errorHandling.initialize();
// Assert
// Verify that monitoring services are started
expect(mockLogger.info).toHaveBeenCalledWith(
"Initializing Error Handling and Monitoring System",
);
});
it("should handle initialization failure gracefully", async () => {
// Arrange - Create invalid configuration
const invalidConfig = { ...mockConfig };
delete invalidConfig.metrics;
const invalidErrorHandling = new ErrorHandlingMonitoring(invalidConfig);
// Act & Assert
await expect(invalidErrorHandling.initialize()).rejects.toThrow();
});
});
describe("Error Handling", () => {
beforeEach(async () => {
await errorHandling.initialize();
});
it("should handle errors with comprehensive context", async () => {
// Arrange
const testError = new Error("Test error message");
const errorContext = {
source: "test-service",
operation: "data-processing",
component: "processor",
severity: "error" as const,
category: "business" as const,
retryable: true,
transient: false,
};
// Act
const result = await errorHandling.handleError(testError, errorContext);
// Assert
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.handled).toBe(true);
expect(result.data.recovery).toBeDefined();
expect(mockLogger.error).toHaveBeenCalledWith(
"Handling error with context",
expect.objectContaining({
source: "test-service",
operation: "data-processing",
component: "processor",
severity: "error",
category: "business",
message: "Test error message",
}),
);
});
it("should execute recovery strategies", async () => {
// Arrange
const testError = new Error("Recoverable error");
const errorContext = {
source: "api-service",
operation: "user-request",
component: "controller",
severity: "warn" as const,
category: "network" as const,
retryable: true,
recovery: {
type: "retry" as const,
maxAttempts: 3,
backoffStrategy: "exponential" as const,
timeout: 5000,
},
};
// Act
const result = await errorHandling.handleError(testError, errorContext);
// Assert
expect(result.success).toBe(true);
expect(result.data.recovery).toBeDefined();
expect(result.data.recovery.recovered).toBe(true);
});
it("should assess error impact correctly", async () => {
// Arrange
const criticalError = new Error("Critical system failure");
const errorContext = {
source: "database-service",
operation: "connection",
component: "connection-pool",
severity: "fatal" as const,
category: "system" as const,
impact: {
scope: "system" as const,
affectedUsers: 1000,
affectedServices: ["user-service", "order-service"],
businessImpact: "critical" as const,
dataIntegrity: false,
securityImplications: true,
complianceRisk: true,
},
};
// Act
const result = await errorHandling.handleError(
criticalError,
errorContext,
);
// Assert
expect(result.success).toBe(true);
expect(result.data.handled).toBe(true);
});
it("should handle non-retryable errors appropriately", async () => {
// Arrange
const validationError = new Error("Invalid input parameters");
const errorContext = {
source: "validation-service",
operation: "input-validation",
component: "validator",
severity: "error" as const,
category: "validation" as const,
retryable: false,
recovery: {
type: "none" as const,
},
};
// Act
const result = await errorHandling.handleError(
validationError,
errorContext,
);
// Assert
expect(result.success).toBe(true);
expect(result.data.recovery.recovered).toBe(true); // Should still handle gracefully
});
});
describe("Metrics Collection", () => {
beforeEach(async () => {
await errorHandling.initialize();
});
it("should record metrics with contextual information", async () => {
// Arrange
const metricName = "api.response_time";
const metricValue = 150;
const metricType = "histogram";
const tags = { endpoint: "/api/users", method: "GET", status: "200" };
// Act
const result = await errorHandling.recordMetric(
metricName,
metricValue,
metricType,
tags,
);
// Assert
expect(result.success).toBe(true);
});
it("should handle different metric types", async () => {
// Arrange
const metrics = [
{ name: "requests.count", value: 1, type: "counter" as const },
{ name: "memory.usage", value: 75.5, type: "gauge" as const },
{ name: "processing.time", value: 250, type: "timer" as const },
{ name: "errors.rate", value: 0.02, type: "meter" as const },
];
// Act
const results = await Promise.all(
metrics.map((metric) =>
errorHandling.recordMetric(metric.name, metric.value, metric.type),
),
);
// Assert
expect(results.every((result) => result.success)).toBe(true);
});
it("should check performance thresholds", async () => {
// Arrange
const highLatencyMetric = "api.latency";
const highLatencyValue = 2000; // Above typical threshold
const tags = { service: "payment-service" };
// Act
const result = await errorHandling.recordMetric(
highLatencyMetric,
highLatencyValue,
"gauge",
tags,
);
// Assert
expect(result.success).toBe(true);
// Should potentially trigger threshold checks
});
it("should batch metric recordings efficiently", async () => {
// Arrange
const batchMetrics = Array.from({ length: 50 }, (_, i) => ({
name: `batch.metric.${i}`,
value: Math.random() * 100,
type: "gauge" as const,
tags: { batch: "test", index: i.toString() },
}));
// Act
const startTime = Date.now();
const results = await Promise.all(
batchMetrics.map((metric) =>
errorHandling.recordMetric(
metric.name,
metric.value,
metric.type,
metric.tags,
),
),
);
const endTime = Date.now();
// Assert
expect(results.every((result) => result.success)).toBe(true);
expect(endTime - startTime).toBeLessThan(1000); // Should complete quickly
});
});
describe("Alerting System", () => {
beforeEach(async () => {
await errorHandling.initialize();
});
it("should create alerts with comprehensive information", async () => {
// Arrange
const alertTitle = "High Error Rate Detected";
const alertDescription =
"Error rate has exceeded 5% for the past 5 minutes";
const severity = "critical";
const alertSource = {
component: "payment-service",
service: "payment",
environment: "production",
region: "us-east-1",
metadata: { version: "1.2.3" },
};
const conditions = [
{
metric: "error_rate",
operator: ">",
threshold: 0.05,
actual: 0.12,
duration: 300,
},
];
// Act
const result = await errorHandling.createAlert(
alertTitle,
alertDescription,
severity as any,
alertSource,
conditions,
);
// Assert
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.title).toBe(alertTitle);
expect(result.data.severity).toBe(severity);
expect(result.data.conditions).toEqual(conditions);
expect(mockLogger.info).toHaveBeenCalledWith(
"Creating alert",
expect.objectContaining({
severity,
title: alertTitle,
source: "payment-service",
}),
);
});
it("should route alerts to appropriate notification channels", async () => {
// Arrange
const criticalAlert = {
title: "Service Down",
description: "Payment service is completely unavailable",
severity: "critical" as const,
source: {
component: "payment-service",
service: "payment",
environment: "production",
region: "us-east-1",
metadata: {},
},
conditions: [
{
metric: "availability",
operator: "<",
threshold: 0.5,
actual: 0,
duration: 60,
},
],
channels: [
{
type: "pagerduty" as const,
target: "critical-alerts",
priority: 1,
rateLimit: {
enabled: false,
maxEvents: 0,
window: 0,
burstSize: 0,
},
},
],
};
// Act
const result = await errorHandling.createAlert(
criticalAlert.title,
criticalAlert.description,
criticalAlert.severity,
criticalAlert.source,
criticalAlert.conditions,
criticalAlert.channels,
);
// Assert
expect(result.success).toBe(true);
expect(result.data.channels).toBeDefined();
expect(result.data.channels.length).toBeGreaterThan(0);
});
it("should respect rate limiting for alerts", async () => {
// Arrange
const rapidAlerts = Array.from({ length: 20 }, (_, i) => ({
title: `Rapid Alert ${i}`,
description: `Test alert ${i}`,
severity: "warn" as const,
source: {
component: "test-service",
service: "test",
environment: "test",
region: "local",
metadata: {},
},
conditions: [
{
metric: "test_metric",
operator: ">",
threshold: 1,
actual: 2,
duration: 1,
},
],
}));
// Act
const results = await Promise.all(
rapidAlerts.map((alert) =>
errorHandling.createAlert(
alert.title,
alert.description,
alert.severity,
alert.source,
alert.conditions,
),
),
);
// Assert
const successfulAlerts = results.filter((result) => result.success);
expect(successfulAlerts.length).toBeGreaterThan(0);
// Should respect rate limiting - not all alerts may be processed
});
});
describe("Health Monitoring", () => {
beforeEach(async () => {
await errorHandling.initialize();
});
it("should perform comprehensive health checks", async () => {
// Act
const result = await errorHandling.performHealthCheck();
// Assert
expect(result.success).toBe(true);
expect(Array.isArray(result.data)).toBe(true);
if (Array.isArray(result.data)) {
expect(result.data.length).toBeGreaterThan(0);
const firstHealthStatus = result.data[0];
expect(firstHealthStatus).toHaveProperty("component");
expect(firstHealthStatus).toHaveProperty("status");
expect(firstHealthStatus).toHaveProperty("score");
expect(firstHealthStatus).toHaveProperty("timestamp");
expect(firstHealthStatus).toHaveProperty("checks");
expect(firstHealthStatus).toHaveProperty("dependencies");
}
});
it("should check specific component health", async () => {
// Arrange
const componentName = "database";
// Act
const result = await errorHandling.performHealthCheck(componentName);
// Assert
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
if (!Array.isArray(result.data)) {
expect(result.data.component).toBe(componentName);
expect(["healthy", "degraded", "unhealthy", "unknown"]).toContain(
result.data.status,
);
expect(typeof result.data.score).toBe("number");
expect(result.data.score).toBeGreaterThanOrEqual(0);
expect(result.data.score).toBeLessThanOrEqual(100);
}
});
it("should track dependency health", async () => {
// Act
const result = await errorHandling.performHealthCheck();
// Assert
expect(result.success).toBe(true);
if (Array.isArray(result.data) && result.data.length > 0) {
const healthStatus = result.data[0];
expect(Array.isArray(healthStatus.dependencies)).toBe(true);
}
});
it("should provide health recommendations", async () => {
// Act
const result = await errorHandling.performHealthCheck();
// Assert
expect(result.success).toBe(true);
if (Array.isArray(result.data) && result.data.length > 0) {
const healthStatus = result.data[0];
expect(Array.isArray(healthStatus.recommendations)).toBe(true);
}
});
});
describe("Distributed Tracing", () => {
beforeEach(async () => {
await errorHandling.initialize();
});
it("should record trace spans with proper context", async () => {
// Arrange
const operationName = "database.query";
const duration = 150;
const status = "ok";
const tags = {
"db.statement": "SELECT * FROM users WHERE id = ?",
"db.type": "postgresql",
"http.method": "GET",
};
// Act
const result = await errorHandling.recordTrace(
operationName,
duration,
status,
tags,
);
// Assert
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.operationName).toBe(operationName);
expect(result.data.duration).toBe(duration);
expect(result.data.status).toBe(status);
expect(result.data.tags).toMatchObject(tags);
expect(result.data.traceId).toBeDefined();
expect(result.data.spanId).toBeDefined();
});
it("should create child spans with parent relationships", async () => {
// Arrange
const parentSpan = await errorHandling.recordTrace(
"parent.operation",
200,
);
const parentSpanId = parentSpan.data.spanId;
const childOperationName = "child.operation";
const childDuration = 50;
// Act
const result = await errorHandling.recordTrace(
childOperationName,
childDuration,
"ok",
{},
parentSpanId,
);
// Assert
expect(result.success).toBe(true);
expect(result.data.parentSpanId).toBe(parentSpanId);
expect(result.data.references).toBeDefined();
expect(result.data.references.length).toBeGreaterThan(0);
});
it("should handle error traces", async () => {
// Arrange
const operationName = "api.request";
const duration = 500;
const status = "error";
const tags = {
error: "true",
"error.kind": "validation_error",
"http.status_code": "400",
};
// Act
const result = await errorHandling.recordTrace(
operationName,
duration,
status,
tags,
);
// Assert
expect(result.success).toBe(true);
expect(result.data.status).toBe(status);
expect(result.data.tags.error).toBe("true");
});
});
describe("Service Level Objectives (SLO)", () => {
beforeEach(async () => {
await errorHandling.initialize();
});
it("should create and manage SLOs", async () => {
// Arrange
const slo = {
name: "API Availability",
type: "availability" as const,
target: 99.9,
unit: "percent",
measurement: {
query:
'sum(rate(http_requests_total{status!~"5.."})) / sum(rate(http_requests_total))',
datasource: "prometheus",
aggregation: "avg" as const,
filters: { service: "api" },
},
timeWindow: {
type: "rolling" as const,
duration: "30d",
},
alerting: {
burnRateAlerts: [
{
shortWindow: "5m",
longWindow: "1h",
burnRate: 14.4,
severity: "critical" as const,
},
],
errorBudgetAlerts: [
{
threshold: 50,
severity: "warn" as const,
lookback: "7d",
},
],
},
};
// Act
const result = await errorHandling.createSLO(slo);
// Assert
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.sloId).toBeDefined();
expect(mockLogger.info).toHaveBeenCalledWith(
"SLO created",
expect.objectContaining({
name: slo.name,
type: slo.type,
}),
);
});
it("should handle different SLO types", async () => {
// Arrange
const sloTypes = [
{
name: "Request Latency",
type: "latency" as const,
target: 100,
unit: "milliseconds",
},
{
name: "Error Rate",
type: "error_rate" as const,
target: 0.1,
unit: "percent",
},
{
name: "Throughput",
type: "throughput" as const,
target: 1000,
unit: "requests_per_second",
},
];
// Act
const results = await Promise.all(
sloTypes.map((sloType) =>
errorHandling.createSLO({
...sloType,
measurement: {
query: "test_query",
datasource: "prometheus",
aggregation: "avg" as const,
filters: {},
},
timeWindow: {
type: "rolling" as const,
duration: "1d",
},
alerting: {
burnRateAlerts: [],
errorBudgetAlerts: [],
},
}),
),
);
// Assert
expect(results.every((result) => result.success)).toBe(true);
});
});
describe("System Metrics and Performance", () => {
beforeEach(async () => {
await errorHandling.initialize();
});
it("should provide comprehensive system metrics", async () => {
// Arrange - Generate some activity
await errorHandling.handleError(new Error("Test error"), {
source: "test",
});
await errorHandling.recordMetric("test.metric", 100);
await errorHandling.performHealthCheck();
// Act
const result = await errorHandling.getSystemMetrics();
// Assert
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.performance).toBeDefined();
expect(result.data.health).toBeDefined();
expect(result.data.alerts).toBeDefined();
expect(result.data.sloStatus).toBeDefined();
expect(result.data.circuitBreakers).toBeDefined();
// Check performance metrics structure
expect(typeof result.data.performance.errors_handled).toBe("number");
expect(typeof result.data.performance.errors_recovered).toBe("number");
expect(typeof result.data.performance.alerts_generated).toBe("number");
expect(typeof result.data.performance.health_checks_performed).toBe(
"number",
);
});
it("should track performance over time", async () => {
// Arrange - Create multiple operations
const operations = [
() => errorHandling.recordMetric("op1", 50),
() => errorHandling.recordMetric("op2", 75),
() => errorHandling.recordMetric("op3", 100),
() => errorHandling.performHealthCheck(),
() =>
errorHandling.handleError(new Error("Op error"), {
source: "operations",
}),
];
// Act
await Promise.all(operations.map((op) => op()));
const result = await errorHandling.getSystemMetrics();
// Assert
expect(result.success).toBe(true);
expect(result.data.performance.errors_handled).toBeGreaterThan(0);
expect(result.data.performance.health_checks_performed).toBeGreaterThan(
0,
);
});
it("should handle high-frequency operations efficiently", async () => {
// Arrange
const highFrequencyOps = Array.from(
{ length: 100 },
(_, i) => () =>
errorHandling.recordMetric(`metric_${i}`, Math.random() * 100),
);
// Act
const startTime = Date.now();
await Promise.all(highFrequencyOps.map((op) => op()));
const endTime = Date.now();
const metricsResult = await errorHandling.getSystemMetrics();
// Assert
expect(endTime - startTime).toBeLessThan(2000); // Should handle 100 ops in under 2 seconds
expect(metricsResult.success).toBe(true);
});
});
describe("Error Recovery and Circuit Breaker", () => {
beforeEach(async () => {
await errorHandling.initialize();
});
it("should implement circuit breaker pattern", async () => {
// Arrange - Create repeated failures to trigger circuit breaker
const failingError = new Error("Repeated failure");
const errorContext = {
source: "failing-service",
operation: "critical-operation",
component: "processor",
category: "system" as const,
retryable: true,
};
// Act - Generate multiple failures
const results = await Promise.all(
Array.from({ length: 10 }, () =>
errorHandling.handleError(failingError, errorContext),
),
);
// Assert
expect(results.every((result) => result.success)).toBe(true);
// Circuit breaker should eventually be triggered
});
it("should recover from circuit breaker state", async () => {
// Arrange - First trigger circuit breaker
const initialError = new Error("Service failure");
const errorContext = {
source: "recovery-service",
component: "api",
category: "network" as const,
};
// Trigger multiple failures
await Promise.all(
Array.from({ length: 5 }, () =>
errorHandling.handleError(initialError, errorContext),
),
);
// Wait for recovery period (simulated)
await new Promise((resolve) => setTimeout(resolve, 100));
// Act - Try successful operation
const successfulError = new Error("Temporary issue");
const result = await errorHandling.handleError(successfulError, {
...errorContext,
recovery: { type: "retry" as const, maxAttempts: 1 },
});
// Assert
expect(result.success).toBe(true);
expect(result.data.recovery.recovered).toBe(true);
});
});
describe("Integration and Event Handling", () => {
beforeEach(async () => {
await errorHandling.initialize();
});
it("should emit events for system activities", async () => {
// Arrange
const systemInitializedSpy = jest.fn();
const errorHandledSpy = jest.fn();
const alertCreatedSpy = jest.fn();
errorHandling.on("system:initialized", systemInitializedSpy);
errorHandling.on("error:handled", errorHandledSpy);
errorHandling.on("alert:created", alertCreatedSpy);
// Act
await errorHandling.handleError(new Error("Event test"), {
source: "event-test",
});
await errorHandling.createAlert(
"Test Alert",
"Test Description",
"info",
{
component: "test",
service: "test",
environment: "test",
region: "local",
metadata: {},
},
[],
);
// Assert
expect(errorHandledSpy).toHaveBeenCalled();
expect(alertCreatedSpy).toHaveBeenCalled();
});
it("should handle event listener errors gracefully", async () => {
// Arrange
const faultyListener = jest.fn(() => {
throw new Error("Listener error");
});
errorHandling.on("error:handled", faultyListener);
// Act
const result = await errorHandling.handleError(
new Error("Trigger event"),
{ source: "event-test" },
);
// Assert
expect(result.success).toBe(true); // Should not fail due to listener error
expect(faultyListener).toHaveBeenCalled();
});
});
});