/** * ╔══════════════════════════════════════════════════════════════════════════════╗ * ║ AUTONOMOUS SYSTEM INTEGRATION TESTS ║ * ║ ║ * ║ Comprehensive test suite for the autonomous MCP routing system. ║ * ║ Covers: ║ * ║ • PatternMemory persistence ║ * ║ • FailureMemory recording and recovery paths ║ * ║ • DecisionEngine source scoring ║ * ║ • AutonomousAgent routing and learning ║ * ║ • SelfHealing circuit breaker ║ * ║ • API endpoint validation ║ * ╚══════════════════════════════════════════════════════════════════════════════╝ */ import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest'; import { initializeDatabase, getDatabase, getSqlJsDatabase } from '../database/index.js'; import { SqlJsStorageAdapter } from '../mcp/memory/StorageAdapter.js'; // Mock Embeddings to avoid ONNX/Float32Array issues in tests vi.mock('../platform/embeddings/TransformersEmbeddings', () => ({ getTransformersEmbeddings: () => ({ embed: vi.fn().mockResolvedValue(new Array(384).fill(0.1)), // Mock 384-dim vector embedBatch: vi.fn().mockImplementation(async (texts) => texts.map(() => new Array(384).fill(0.1))), initialize: vi.fn().mockResolvedValue(undefined), isInitialized: vi.fn().mockReturnValue(true), }) })); // ═══════════════════════════════════════════════════════════════════════════════ // TEST SETUP // ═══════════════════════════════════════════════════════════════════════════════ // Mock Prisma to avoid DATABASE_URLRequirement vi.mock('../database/prisma', () => ({ prisma: { $connect: vi.fn(), $disconnect: vi.fn(), $queryRaw: vi.fn().mockResolvedValue([]), widgetPermission: { findUnique: vi.fn(), upsert: vi.fn(), findMany: vi.fn(), } }, checkPrismaConnection: vi.fn().mockResolvedValue(false), disconnectPrisma: vi.fn(), default: { $connect: vi.fn(), $disconnect: vi.fn(), } })); let db: any; beforeAll(async () => { // Initialize test database await initializeDatabase(); // Use raw SqlJs database for Memory components that require .exec() db = getSqlJsDatabase(); if (!db) { throw new Error('Failed to initialize SqlJs database for testing'); } // Force clean tables try { db.exec('DROP TABLE IF EXISTS mcp_failure_memory'); db.exec('DROP TABLE IF EXISTS mcp_query_patterns'); } catch (e) { /* ignore */ } }); afterAll(async () => { // Cleanup would go here vi.clearAllMocks(); }); afterEach(() => { vi.clearAllMocks(); }); // ═══════════════════════════════════════════════════════════════════════════════ // PATTERN MEMORY TESTS // ═══════════════════════════════════════════════════════════════════════════════ describe('PatternMemory - Enterprise Persistence', () => { let patternMemory: any; beforeEach(async () => { const { PatternMemory } = await import('../mcp/memory/PatternMemory.js'); patternMemory = new PatternMemory(new SqlJsStorageAdapter(db)); }); it('should record query patterns', async () => { await patternMemory.recordQuery({ widgetId: 'test-widget', queryType: 'agents.list', queryParams: { limit: 10 }, sourceUsed: 'test-source', latencyMs: 50, resultSize: 1024, success: true }); // Pattern should be in cache immediately const similar = await patternMemory.findSimilarQueries('agents.list', { limit: 10 }); const cache = (patternMemory as any).cache; const genSig = (patternMemory as any).generateSignature('agents.list', { limit: 10 }); const debugInfo = { cacheLen: cache.length, firstSig: cache[0]?.querySignature, expectedSig: genSig, matches: cache[0]?.querySignature === genSig, firstItem: cache[0] }; expect(similar.length).toBeGreaterThan(0); expect(similar[0].pattern.widgetId).toBe('test-widget'); }); it('should calculate average latency correctly', async () => { // Record multiple patterns with different latencies for (let i = 0; i < 5; i++) { await patternMemory.recordQuery({ widgetId: 'latency-test', queryType: 'test.query', queryParams: {}, sourceUsed: 'latency-source', latencyMs: (i + 1) * 20, // 20, 40, 60, 80, 100 success: true }); } const avgLatency = await patternMemory.getAverageLatency('latency-source'); expect(avgLatency).toBe(60); // Average of 20, 40, 60, 80, 100 }); it('should calculate success rate correctly', async () => { // Record 4 successful, 1 failed for (let i = 0; i < 5; i++) { await patternMemory.recordQuery({ widgetId: 'success-test', queryType: 'success.query', queryParams: {}, sourceUsed: 'success-source', latencyMs: 50, success: i < 4 // First 4 succeed, last fails }); } const successRate = await patternMemory.getSuccessRate('success-source', 'success.query'); expect(successRate).toBe(0.8); // 4 out of 5 }); it('should provide widget usage patterns', async () => { // Record patterns for a widget using multiple sources await patternMemory.recordQuery({ widgetId: 'multi-source-widget', queryType: 'data.fetch', queryParams: {}, sourceUsed: 'primary-source', latencyMs: 30, success: true }); await patternMemory.recordQuery({ widgetId: 'multi-source-widget', queryType: 'data.fetch', queryParams: {}, sourceUsed: 'primary-source', latencyMs: 40, success: true }); await patternMemory.recordQuery({ widgetId: 'multi-source-widget', queryType: 'data.fetch', queryParams: {}, sourceUsed: 'secondary-source', latencyMs: 100, success: true }); const patterns = await patternMemory.getWidgetPatterns('multi-source-widget'); expect(patterns.widgetId).toBe('multi-source-widget'); expect(patterns.queryCount).toBe(3); expect(patterns.commonSources[0]).toBe('primary-source'); // Most used expect(patterns.successRate).toBe(1.0); }); it('should provide comprehensive statistics', async () => { const stats = await patternMemory.getStatistics(); expect(stats).toHaveProperty('totalPatterns'); expect(stats).toHaveProperty('uniqueWidgets'); expect(stats).toHaveProperty('uniqueSources'); expect(stats).toHaveProperty('avgLatencyMs'); expect(stats).toHaveProperty('successRate'); expect(stats).toHaveProperty('queriesLast24h'); expect(stats).toHaveProperty('queriesLast7d'); expect(stats).toHaveProperty('topSources'); expect(stats).toHaveProperty('topWidgets'); }); }); // ═══════════════════════════════════════════════════════════════════════════════ // FAILURE MEMORY TESTS // ═══════════════════════════════════════════════════════════════════════════════ describe('FailureMemory - Self-Healing Intelligence', () => { let failureMemory: any; beforeEach(async () => { const { FailureMemory } = await import('../mcp/memory/FailureMemory.js'); failureMemory = new FailureMemory(new SqlJsStorageAdapter(db)); }); it('should record failures with context', async () => { const error = new Error('Connection refused'); (error as any).code = 'ECONNREFUSED'; const id = await failureMemory.recordFailure({ sourceName: 'failing-source', error, queryContext: { operation: 'test' } }); expect(id).toBeDefined(); expect(typeof id).toBe('string'); const history = await failureMemory.getFailureHistory('failing-source', 1); expect(history.length).toBe(1); expect(history[0].errorType).toBe('Error'); expect(history[0].errorMessage).toBe('Connection refused'); expect(history[0].errorContext.code).toBe('ECONNREFUSED'); }); it('should track recovery paths', async () => { const error = new Error('Timeout'); // Record failure with recovery await failureMemory.recordFailure({ sourceName: 'recovery-test', error, recoveryAction: 'retry-with-backoff', recoverySuccess: true, recoveryTimeMs: 500 }); await failureMemory.recordFailure({ sourceName: 'recovery-test', error, recoveryAction: 'retry-with-backoff', recoverySuccess: true, recoveryTimeMs: 600 }); await failureMemory.recordFailure({ sourceName: 'recovery-test', error, recoveryAction: 'use-fallback', recoverySuccess: false, recoveryTimeMs: 1000 }); const paths = await failureMemory.getRecoveryPaths('recovery-test', 'Error'); expect(paths.length).toBeGreaterThan(0); // retry-with-backoff should be ranked higher (100% success) const retryPath = paths.find((p: any) => p.action === 'retry-with-backoff'); expect(retryPath).toBeDefined(); expect(retryPath.successRate).toBe(1.0); expect(retryPath.occurrences).toBe(2); }); it('should detect recurring failures', async () => { const error = new Error('Rate limited'); // Record 3 failures within a short time for (let i = 0; i < 3; i++) { await failureMemory.recordFailure({ sourceName: 'recurring-source', error }); } const isRecurring = await failureMemory.isRecurringFailure('recurring-source', 'Error', 60); expect(isRecurring).toBe(true); }); it('should recommend best recovery action', async () => { const error = new Error('DB connection lost'); // Create a history of recoveries for (let i = 0; i < 3; i++) { await failureMemory.recordFailure({ sourceName: 'db-source', error, recoveryAction: 'reconnect', recoverySuccess: true, recoveryTimeMs: 200 }); } await failureMemory.recordFailure({ sourceName: 'db-source', error, recoveryAction: 'restart-pool', recoverySuccess: false, recoveryTimeMs: 5000 }); const best = await failureMemory.getBestRecoveryAction('db-source', 'Error'); expect(best).toBeDefined(); expect(best?.action).toBe('reconnect'); expect(best?.successRate).toBe(1.0); }); it('should provide source health summary', async () => { const summary = await failureMemory.getSourceHealthSummary('db-source'); expect(summary).toHaveProperty('sourceName'); expect(summary).toHaveProperty('totalFailures'); expect(summary).toHaveProperty('uniqueErrorTypes'); expect(summary).toHaveProperty('recoverySuccessRate'); expect(summary).toHaveProperty('isRecurring'); }); }); // ═══════════════════════════════════════════════════════════════════════════════ // DECISION ENGINE TESTS // ═══════════════════════════════════════════════════════════════════════════════ describe('DecisionEngine - AI-Powered Source Selection', () => { let decisionEngine: any; let cognitiveMemory: any; beforeEach(async () => { const { initCognitiveMemory, getCognitiveMemory } = await import('../mcp/memory/CognitiveMemory.js'); const { DecisionEngine } = await import('../mcp/autonomous/DecisionEngine.js'); initCognitiveMemory(db); cognitiveMemory = getCognitiveMemory(); decisionEngine = new DecisionEngine(cognitiveMemory); }); it('should analyze query intent correctly', async () => { const intent = await decisionEngine.analyzeIntent({ type: 'agents.list', priority: 'high', freshness: 'realtime' }); expect(intent.type).toBe('agents.list'); expect(intent.domain).toBe('agents'); expect(intent.operation).toBe('list'); expect(intent.priority).toBe('high'); expect(intent.freshness).toBe('realtime'); }); it('should score sources with reasoning', async () => { const mockSources = [ { name: 'fast-source', type: 'database', capabilities: ['agents.list', 'agents.get'], isHealthy: async () => true, estimatedLatency: 20, costPerQuery: 0 }, { name: 'slow-source', type: 'api', capabilities: ['agents.*'], isHealthy: async () => true, estimatedLatency: 500, costPerQuery: 0.01 } ]; const intent = { type: 'agents.list', domain: 'agents', operation: 'list', params: {}, priority: 'high' as const, freshness: 'normal' as const }; const scores = await decisionEngine.scoreAllSources(mockSources, intent); expect(scores.length).toBe(2); expect(scores[0].source.name).toBe('fast-source'); // Should be ranked first expect(scores[0].score).toBeGreaterThan(scores[1].score); expect(scores[0].reasoning).toBeDefined(); }); it('should make confident decisions', async () => { const mockSources = [ { name: 'primary', type: 'database', capabilities: ['*'], isHealthy: async () => true, estimatedLatency: 50, costPerQuery: 0 } ]; const intent = { type: 'test.query', domain: 'test', operation: 'query', params: {}, priority: 'normal' as const, freshness: 'normal' as const }; const decision = await decisionEngine.decide(mockSources, intent); expect(decision.selectedSource).toBeDefined(); expect(decision.selectedSource.name).toBe('primary'); expect(decision.confidence).toBeGreaterThan(0); expect(decision.confidence).toBeLessThanOrEqual(1); expect(decision.reasoning).toBeDefined(); expect(decision.alternatives).toBeInstanceOf(Array); }); }); // ═══════════════════════════════════════════════════════════════════════════════ // SELF-HEALING ADAPTER TESTS // ═══════════════════════════════════════════════════════════════════════════════ describe('SelfHealingAdapter - Circuit Breaker & Recovery', () => { let selfHealing: any; beforeEach(async () => { const { selfHealing: sh } = await import('../services/SelfHealingAdapter.js'); selfHealing = sh; }); it('should have default healing strategies', () => { // The adapter should initialize with default strategies expect(selfHealing).toBeDefined(); }); it('should attempt healing and return result', async () => { const error = new Error('Test Error'); const selfHealing = (await import('../services/SelfHealingAdapter.js')).selfHealing; // Mock the internal logic if needed, or rely on integration // Since we are testing integration, we expect actual behavior // But heal returns HealingResult object now? const healed = await selfHealing.heal(error, 'test-context', { source: 'test-source' }); expect(healed).toHaveProperty('healed'); expect(healed).toHaveProperty('strategyUsed'); }); it('should prevent recursive healing loops', async () => { const error = new Error('Recursive Error'); const selfHealing = (await import('../services/SelfHealingAdapter.js')).selfHealing; const first = await selfHealing.heal(error, 'recursion-test', { source: 'test-source' }); // If we somehow trigger during healing, it should handle gracefully expect(first).toBeDefined(); }); it('should provide system status', async () => { const selfHealing = (await import('../services/SelfHealingAdapter.js')).selfHealing; const status = selfHealing.getSystemStatus(); expect(status).toHaveProperty('overallHealth'); expect(status).toHaveProperty('services'); expect(status).toHaveProperty('uptime'); expect(['HEALTHY', 'DEGRADED', 'CRITICAL']).toContain(status.overallHealth); }); // it('should update service status', () => { // selfHealing.updateServiceStatus('test-service', 'healthy'); // const status = selfHealing.getSystemStatus(); // expect(status.services).toContainEqual({ name: 'test-service', status: 'healthy' }); // }); // const testService = status.services.find((s: any) => s.name === 'test-service'); // expect(testService).toBeDefined(); // expect(testService?.status).toBe('healthy'); }); // ═══════════════════════════════════════════════════════════════════════════════ // AUTONOMOUS AGENT TESTS // ═══════════════════════════════════════════════════════════════════════════════ describe('AutonomousAgent - Orchestration & Learning', () => { let agent: any; let registry: any; beforeEach(async () => { const { initCognitiveMemory, getCognitiveMemory } = await import('../mcp/memory/CognitiveMemory.js'); const { AutonomousAgent } = await import('../mcp/autonomous/AutonomousAgent.js'); const { getSourceRegistry } = await import('../mcp/SourceRegistry.js'); initCognitiveMemory(db); const memory = getCognitiveMemory(); registry = getSourceRegistry(); // Register a test source registry.registerSource({ name: 'test-agent-source', type: 'test', capabilities: ['test.*'], isHealthy: async () => true, estimatedLatency: 50, costPerQuery: 0, query: async () => ({ result: 'test-data' }) }); agent = new AutonomousAgent(memory, registry); }); it('should route queries to appropriate sources', async () => { const source = await agent.route({ type: 'test.query', domain: 'test', operation: 'query', params: {} }); expect(source).toBeDefined(); expect(source.name).toBe('test-agent-source'); }); it('should execute and learn from queries', async () => { const result = await agent.executeAndLearn( { type: 'test.query', widgetId: 'agent-test-widget' }, async (source: any) => { return { data: 'executed-result', source: source.name }; } ); expect(result).toBeDefined(); expect(result.data).toBeDefined(); expect(result.source).toBeDefined(); expect(result.latencyMs).toBeGreaterThanOrEqual(0); expect(result.cached).toBe(false); }); it('should provide agent statistics', async () => { const stats = await agent.getStats(); expect(stats).toHaveProperty('totalDecisions'); expect(stats).toHaveProperty('averageConfidence'); expect(stats).toHaveProperty('topSources'); }); }); // ═══════════════════════════════════════════════════════════════════════════════ // COGNITIVE MEMORY INTEGRATION TESTS // ═══════════════════════════════════════════════════════════════════════════════ describe('CognitiveMemory - Unified Interface', () => { let cognitiveMemory: any; beforeEach(async () => { const { initCognitiveMemory, getCognitiveMemory } = await import('../mcp/memory/CognitiveMemory.js'); initCognitiveMemory(db); cognitiveMemory = getCognitiveMemory(); }); it('should initialize correctly', () => { expect(cognitiveMemory).toBeDefined(); }); it('should record successful queries', async () => { await cognitiveMemory.recordSuccess({ widgetId: 'cognitive-test', queryType: 'data.fetch', queryParams: { id: 123 }, sourceUsed: 'test-source', latencyMs: 75, resultSize: 2048 }); // Query should be recorded in pattern memory const patterns = await cognitiveMemory.patternMemory.getWidgetPatterns('cognitive-test'); expect(patterns.queryCount).toBeGreaterThan(0); }); it('should record failures with recovery info', async () => { const error = new Error('Test failure'); await cognitiveMemory.recordFailure({ sourceName: 'cognitive-failure-test', error, queryContext: { operation: 'test' }, recoveryAction: 'retry', recoverySuccess: true, recoveryTimeMs: 100 }); const history = await cognitiveMemory.failureMemory.getFailureHistory('cognitive-failure-test'); expect(history.length).toBeGreaterThan(0); }); it('should get source intelligence combining patterns and failures', async () => { const intel = await cognitiveMemory.getSourceIntelligence('test-source'); expect(intel).toHaveProperty('averageLatency'); expect(intel).toHaveProperty('overallSuccessRate'); expect(intel).toHaveProperty('recentFailures'); }); });