UNPKG

@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
/** * 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(); }); }); });