"use strict"; /** * Integration Tests - Comprehensive tests for agentic coordination * * Tests: * - Multi-agent coordination * - Failover scenarios * - Load distribution * - Performance benchmarks */ Object.defineProperty(exports, "__esModule", { value: true }); const agent_coordinator_1 = require("./agent-coordinator"); const regional_agent_1 = require("./regional-agent"); const swarm_manager_1 = require("./swarm-manager"); const coordination_protocol_1 = require("./coordination-protocol"); /** * Test utilities */ class TestUtils { static async sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } static generateRandomVector(dimensions) { return Array.from({ length: dimensions }, () => Math.random()); } static async measureLatency(fn) { const start = Date.now(); const result = await fn(); const latency = Date.now() - start; return { result, latency }; } } /** * Test Suite 1: Agent Coordinator Tests */ describe('AgentCoordinator', () => { let coordinator; beforeEach(() => { const config = { maxAgentsPerRegion: 10, healthCheckInterval: 5000, taskTimeout: 10000, retryBackoffBase: 100, retryBackoffMax: 5000, loadBalancingStrategy: 'round-robin', failoverThreshold: 3, enableClaudeFlowHooks: false, // Disable for testing }; coordinator = new agent_coordinator_1.AgentCoordinator(config); }); afterEach(async () => { await coordinator.shutdown(); }); test('should register agents successfully', async () => { const registration = { agentId: 'test-agent-1', region: 'us-east', endpoint: 'https://us-east.ruvector.io/agent/test-agent-1', capabilities: ['query', 'index'], capacity: 1000, registeredAt: Date.now(), }; await coordinator.registerAgent(registration); const status = coordinator.getStatus(); expect(status.totalAgents).toBe(1); expect(status.regionDistribution['us-east']).toBe(1); }); test('should distribute tasks using round-robin', async () => { // Register multiple agents for (let i = 0; i < 3; i++) { await coordinator.registerAgent({ agentId: `agent-${i}`, region: 'us-east', endpoint: `https://us-east.ruvector.io/agent/agent-${i}`, capabilities: ['query'], capacity: 1000, registeredAt: Date.now(), }); } // Submit tasks const taskIds = []; for (let i = 0; i < 6; i++) { const taskId = await coordinator.submitTask({ type: 'query', payload: { query: `test-query-${i}` }, priority: 1, maxRetries: 3, }); taskIds.push(taskId); } expect(taskIds.length).toBe(6); await TestUtils.sleep(1000); const status = coordinator.getStatus(); expect(status.queuedTasks + status.activeTasks).toBeGreaterThan(0); }); test('should handle agent failures with circuit breaker', async () => { const registration = { agentId: 'failing-agent', region: 'us-west', endpoint: 'https://us-west.ruvector.io/agent/failing-agent', capabilities: ['query'], capacity: 1000, registeredAt: Date.now(), }; await coordinator.registerAgent(registration); // Simulate agent going unhealthy coordinator.updateAgentMetrics({ agentId: 'failing-agent', region: 'us-west', cpuUsage: 95, memoryUsage: 95, activeStreams: 1000, queryLatency: 5000, timestamp: Date.now(), healthy: false, }); const status = coordinator.getStatus(); expect(status.healthyAgents).toBe(0); }); test('should enforce max agents per region', async () => { const config = { maxAgentsPerRegion: 2, healthCheckInterval: 5000, taskTimeout: 10000, retryBackoffBase: 100, retryBackoffMax: 5000, loadBalancingStrategy: 'round-robin', failoverThreshold: 3, enableClaudeFlowHooks: false, }; const limitedCoordinator = new agent_coordinator_1.AgentCoordinator(config); // Register agents await limitedCoordinator.registerAgent({ agentId: 'agent-1', region: 'eu-west', endpoint: 'https://eu-west.ruvector.io/agent/agent-1', capabilities: ['query'], capacity: 1000, registeredAt: Date.now(), }); await limitedCoordinator.registerAgent({ agentId: 'agent-2', region: 'eu-west', endpoint: 'https://eu-west.ruvector.io/agent/agent-2', capabilities: ['query'], capacity: 1000, registeredAt: Date.now(), }); // Third agent should fail await expect(limitedCoordinator.registerAgent({ agentId: 'agent-3', region: 'eu-west', endpoint: 'https://eu-west.ruvector.io/agent/agent-3', capabilities: ['query'], capacity: 1000, registeredAt: Date.now(), })).rejects.toThrow('has reached max agent capacity'); await limitedCoordinator.shutdown(); }); }); /** * Test Suite 2: Regional Agent Tests */ describe('RegionalAgent', () => { let agent; beforeEach(() => { const config = { agentId: 'test-agent-us-east-1', region: 'us-east', coordinatorEndpoint: 'coordinator.ruvector.io', localStoragePath: '/tmp/test-agent', maxConcurrentStreams: 100, metricsReportInterval: 5000, syncInterval: 2000, enableClaudeFlowHooks: false, vectorDimensions: 768, capabilities: ['query', 'index', 'sync'], }; agent = new regional_agent_1.RegionalAgent(config); }); afterEach(async () => { await agent.shutdown(); }); test('should process query successfully', async () => { // Index some vectors await agent.indexVectors([ { id: 'vec-1', vector: TestUtils.generateRandomVector(768), metadata: { category: 'test' }, }, { id: 'vec-2', vector: TestUtils.generateRandomVector(768), metadata: { category: 'test' }, }, ]); // Query const result = await agent.processQuery({ id: 'query-1', vector: TestUtils.generateRandomVector(768), topK: 2, timeout: 5000, }); expect(result.matches.length).toBeGreaterThan(0); expect(result.region).toBe('us-east'); expect(result.latency).toBeGreaterThan(0); }); test('should validate query dimensions', async () => { await expect(agent.processQuery({ id: 'query-invalid', vector: TestUtils.generateRandomVector(512), // Wrong dimension topK: 10, timeout: 5000, })).rejects.toThrow('Invalid vector dimensions'); }); test('should apply filters in query', async () => { // Index vectors with different metadata await agent.indexVectors([ { id: 'vec-1', vector: TestUtils.generateRandomVector(768), metadata: { category: 'A', type: 'test' }, }, { id: 'vec-2', vector: TestUtils.generateRandomVector(768), metadata: { category: 'B', type: 'test' }, }, { id: 'vec-3', vector: TestUtils.generateRandomVector(768), metadata: { category: 'A', type: 'prod' }, }, ]); // Query with filter const result = await agent.processQuery({ id: 'query-filtered', vector: TestUtils.generateRandomVector(768), topK: 10, filters: { category: 'A' }, timeout: 5000, }); // Should only return vectors with category 'A' expect(result.matches.length).toBeGreaterThan(0); }); test('should enforce rate limiting', async () => { // Try to exceed max concurrent streams const promises = []; for (let i = 0; i < 150; i++) { promises.push(agent.processQuery({ id: `query-${i}`, vector: TestUtils.generateRandomVector(768), topK: 5, timeout: 5000, }).catch(err => err)); } const results = await Promise.all(promises); const rateLimitErrors = results.filter(r => r instanceof Error && r.message.includes('Rate limit')); expect(rateLimitErrors.length).toBeGreaterThan(0); }); test('should handle sync payloads from other regions', async () => { const syncPayload = { type: 'index', data: [ { id: 'sync-vec-1', vector: TestUtils.generateRandomVector(768), metadata: { synced: true }, }, ], timestamp: Date.now(), sourceRegion: 'us-west', }; await agent.handleSyncPayload(syncPayload); const status = agent.getStatus(); expect(status.indexSize).toBeGreaterThan(0); }); }); /** * Test Suite 3: Swarm Manager Tests */ describe('SwarmManager', () => { let coordinator; let swarmManager; beforeEach(() => { const coordinatorConfig = { maxAgentsPerRegion: 10, healthCheckInterval: 5000, taskTimeout: 10000, retryBackoffBase: 100, retryBackoffMax: 5000, loadBalancingStrategy: 'adaptive', failoverThreshold: 3, enableClaudeFlowHooks: false, }; coordinator = new agent_coordinator_1.AgentCoordinator(coordinatorConfig); const swarmConfig = { topology: 'mesh', minAgentsPerRegion: 1, maxAgentsPerRegion: 5, scaleUpThreshold: 80, scaleDownThreshold: 20, scaleUpCooldown: 30000, scaleDownCooldown: 60000, healthCheckInterval: 5000, enableAutoScaling: true, enableClaudeFlowHooks: false, regions: ['us-east', 'us-west', 'eu-west'], }; swarmManager = new swarm_manager_1.SwarmManager(swarmConfig, coordinator); }); afterEach(async () => { await swarmManager.shutdown(); await coordinator.shutdown(); }); test('should spawn initial agents for all regions', async () => { await TestUtils.sleep(1000); // Wait for initialization const status = swarmManager.getStatus(); expect(status.totalAgents).toBeGreaterThanOrEqual(3); // At least 1 per region expect(Object.keys(status.metrics.regionMetrics).length).toBe(3); }); test('should spawn additional agents in specific region', async () => { const initialStatus = swarmManager.getStatus(); const initialCount = initialStatus.totalAgents; await swarmManager.spawnAgent('us-east'); const newStatus = swarmManager.getStatus(); expect(newStatus.totalAgents).toBe(initialCount + 1); }); test('should calculate swarm metrics correctly', async () => { await TestUtils.sleep(1000); const metrics = swarmManager.calculateSwarmMetrics(); expect(metrics.totalAgents).toBeGreaterThan(0); expect(metrics.regionMetrics).toBeDefined(); expect(Object.keys(metrics.regionMetrics).length).toBe(3); for (const region of ['us-east', 'us-west', 'eu-west']) { expect(metrics.regionMetrics[region]).toBeDefined(); expect(metrics.regionMetrics[region].agentCount).toBeGreaterThan(0); } }); test('should despawn agent and redistribute tasks', async () => { await TestUtils.sleep(1000); const status = swarmManager.getStatus(); const agentIds = Object.keys(status.metrics.regionMetrics); if (agentIds.length > 0) { const initialCount = status.totalAgents; // Get first agent ID from any region const regionMetrics = Object.values(status.metrics.regionMetrics); const firstRegion = regionMetrics[0]; // We'll need to track spawned agents to despawn them // For now, just verify the mechanism works expect(initialCount).toBeGreaterThan(0); } }); }); /** * Test Suite 4: Coordination Protocol Tests */ describe('CoordinationProtocol', () => { let protocol1; let protocol2; beforeEach(() => { const config1 = { nodeId: 'node-1', heartbeatInterval: 2000, messageTimeout: 5000, consensusTimeout: 10000, maxMessageQueueSize: 1000, enableClaudeFlowHooks: false, pubSubTopics: ['sync', 'metrics', 'alerts'], }; const config2 = { nodeId: 'node-2', heartbeatInterval: 2000, messageTimeout: 5000, consensusTimeout: 10000, maxMessageQueueSize: 1000, enableClaudeFlowHooks: false, pubSubTopics: ['sync', 'metrics', 'alerts'], }; protocol1 = new coordination_protocol_1.CoordinationProtocol(config1); protocol2 = new coordination_protocol_1.CoordinationProtocol(config2); // Connect protocols protocol1.registerNode('node-2'); protocol2.registerNode('node-1'); // Set up message forwarding protocol1.on('message:transmit', (message) => { if (message.to === 'node-2' || !message.to) { protocol2.receiveMessage(message); } }); protocol2.on('message:transmit', (message) => { if (message.to === 'node-1' || !message.to) { protocol1.receiveMessage(message); } }); }); afterEach(async () => { await protocol1.shutdown(); await protocol2.shutdown(); }); test('should send and receive messages between nodes', async () => { let receivedMessage = false; protocol2.on('request:received', (message) => { receivedMessage = true; expect(message.from).toBe('node-1'); }); await protocol1.sendMessage('node-2', 'request', { test: 'data' }); await TestUtils.sleep(100); expect(receivedMessage).toBe(true); }); test('should handle request-response pattern', async () => { protocol2.on('request:received', async (message) => { await protocol2.sendResponse(message.id, message.from, { status: 'ok', data: 'response', }); }); const response = await protocol1.sendMessage('node-2', 'request', { query: 'test' }, { expectResponse: true }); expect(response.status).toBe('ok'); }); test('should broadcast messages to all nodes', async () => { let received = false; protocol2.on('broadcast:received', (message) => { received = true; expect(message.type).toBe('broadcast'); }); await protocol1.broadcastMessage('broadcast', { event: 'test' }); await TestUtils.sleep(100); expect(received).toBe(true); }); test('should handle consensus proposals', async () => { // Node 2 auto-approves proposals protocol2.on('consensus:proposed', async (proposal) => { // Auto-approve handled internally in test setup }); const approved = await protocol1.proposeConsensus('schema_change', { change: 'add_field' }, 1 // Only need 1 vote (from proposer) ); expect(approved).toBe(true); }); test('should handle pub/sub topics', async () => { let receivedMessage = false; // Subscribe node 2 to 'sync' topic protocol2.subscribe('sync', 'node-2'); protocol2.on('topic:message', (data) => { if (data.topicName === 'sync') { receivedMessage = true; expect(data.message.payload.data).toBe('sync-data'); } }); // Publish to topic await protocol1.publishToTopic('sync', { data: 'sync-data' }); await TestUtils.sleep(100); expect(receivedMessage).toBe(true); }); test('should detect unhealthy nodes', async () => { let unhealthyDetected = false; protocol1.on('node:unhealthy', (data) => { unhealthyDetected = true; expect(data.nodeId).toBe('node-2'); }); // Stop node 2 heartbeat await protocol2.shutdown(); // Wait for health check to detect await TestUtils.sleep(7000); expect(unhealthyDetected).toBe(true); }); }); /** * Test Suite 5: Performance Benchmarks */ describe('Performance Benchmarks', () => { test('should handle high query throughput', async () => { const config = { agentId: 'perf-agent', region: 'us-east', coordinatorEndpoint: 'coordinator.ruvector.io', localStoragePath: '/tmp/perf-agent', maxConcurrentStreams: 1000, metricsReportInterval: 30000, syncInterval: 5000, enableClaudeFlowHooks: false, vectorDimensions: 768, capabilities: ['query'], }; const agent = new regional_agent_1.RegionalAgent(config); // Index vectors const vectors = Array.from({ length: 10000 }, (_, i) => ({ id: `vec-${i}`, vector: TestUtils.generateRandomVector(768), metadata: { index: i }, })); await agent.indexVectors(vectors); // Run queries const queryCount = 1000; const queries = []; const startTime = Date.now(); for (let i = 0; i < queryCount; i++) { queries.push(agent.processQuery({ id: `perf-query-${i}`, vector: TestUtils.generateRandomVector(768), topK: 10, timeout: 5000, }).catch(() => null) // Ignore rate limit errors ); } const results = await Promise.all(queries); const successfulQueries = results.filter(r => r !== null); const totalTime = Date.now() - startTime; const qps = (successfulQueries.length / totalTime) * 1000; console.log(`\nPerformance Benchmark:`); console.log(`Total queries: ${queryCount}`); console.log(`Successful: ${successfulQueries.length}`); console.log(`Time: ${totalTime}ms`); console.log(`QPS: ${qps.toFixed(2)}`); expect(successfulQueries.length).toBeGreaterThan(0); expect(qps).toBeGreaterThan(1); // At least 1 QPS await agent.shutdown(); }); test('should scale agents based on load', async () => { const coordinatorConfig = { maxAgentsPerRegion: 10, healthCheckInterval: 5000, taskTimeout: 10000, retryBackoffBase: 100, retryBackoffMax: 5000, loadBalancingStrategy: 'adaptive', failoverThreshold: 3, enableClaudeFlowHooks: false, }; const coordinator = new agent_coordinator_1.AgentCoordinator(coordinatorConfig); const swarmConfig = { topology: 'mesh', minAgentsPerRegion: 1, maxAgentsPerRegion: 5, scaleUpThreshold: 70, scaleDownThreshold: 30, scaleUpCooldown: 1000, // Short cooldown for testing scaleDownCooldown: 2000, healthCheckInterval: 1000, enableAutoScaling: true, enableClaudeFlowHooks: false, regions: ['us-east'], }; const swarmManager = new swarm_manager_1.SwarmManager(swarmConfig, coordinator); await TestUtils.sleep(1000); const initialCount = swarmManager.getStatus().totalAgents; // Spawn additional agents to simulate scale-up await swarmManager.spawnAgent('us-east'); await swarmManager.spawnAgent('us-east'); await TestUtils.sleep(500); const scaledCount = swarmManager.getStatus().totalAgents; expect(scaledCount).toBeGreaterThan(initialCount); await swarmManager.shutdown(); await coordinator.shutdown(); }, 15000); }); /** * Test Suite 6: Failover Scenarios */ describe('Failover Scenarios', () => { test('should handle agent failure and task redistribution', async () => { const coordinatorConfig = { maxAgentsPerRegion: 10, healthCheckInterval: 1000, taskTimeout: 5000, retryBackoffBase: 100, retryBackoffMax: 2000, loadBalancingStrategy: 'round-robin', failoverThreshold: 2, enableClaudeFlowHooks: false, }; const coordinator = new agent_coordinator_1.AgentCoordinator(coordinatorConfig); // Register two agents await coordinator.registerAgent({ agentId: 'agent-1', region: 'us-east', endpoint: 'https://us-east.ruvector.io/agent/agent-1', capabilities: ['query'], capacity: 1000, registeredAt: Date.now(), }); await coordinator.registerAgent({ agentId: 'agent-2', region: 'us-east', endpoint: 'https://us-east.ruvector.io/agent/agent-2', capabilities: ['query'], capacity: 1000, registeredAt: Date.now(), }); // Submit tasks await coordinator.submitTask({ type: 'query', payload: { query: 'test' }, priority: 1, maxRetries: 3, }); // Simulate agent-1 failure coordinator.updateAgentMetrics({ agentId: 'agent-1', region: 'us-east', cpuUsage: 100, memoryUsage: 100, activeStreams: 1000, queryLatency: 10000, timestamp: Date.now(), healthy: false, }); await TestUtils.sleep(2000); const status = coordinator.getStatus(); expect(status.healthyAgents).toBe(1); // Only agent-2 healthy await coordinator.shutdown(); }); test('should handle network partition in coordination protocol', async () => { const protocol1 = new coordination_protocol_1.CoordinationProtocol({ nodeId: 'node-1', heartbeatInterval: 1000, messageTimeout: 5000, consensusTimeout: 10000, maxMessageQueueSize: 1000, enableClaudeFlowHooks: false, pubSubTopics: [], }); const protocol2 = new coordination_protocol_1.CoordinationProtocol({ nodeId: 'node-2', heartbeatInterval: 1000, messageTimeout: 5000, consensusTimeout: 10000, maxMessageQueueSize: 1000, enableClaudeFlowHooks: false, pubSubTopics: [], }); protocol1.registerNode('node-2'); protocol2.registerNode('node-1'); // Set up message forwarding let networkPartitioned = false; protocol1.on('message:transmit', (message) => { if (!networkPartitioned && message.to === 'node-2') { protocol2.receiveMessage(message); } }); // Normal communication await protocol1.sendMessage('node-2', 'request', { test: 'data' }); await TestUtils.sleep(100); // Simulate network partition networkPartitioned = true; let unhealthyDetected = false; protocol1.on('node:unhealthy', (data) => { if (data.nodeId === 'node-2') { unhealthyDetected = true; } }); // Wait for health check to detect partition await TestUtils.sleep(4000); expect(unhealthyDetected).toBe(true); await protocol1.shutdown(); await protocol2.shutdown(); }, 10000); }); console.log('\n=== Integration Tests ==='); console.log('Run with: npm test'); console.log('Tests include:'); console.log(' - Agent Coordinator: Registration, load balancing, failover'); console.log(' - Regional Agent: Query processing, indexing, rate limiting'); console.log(' - Swarm Manager: Auto-scaling, health monitoring, metrics'); console.log(' - Coordination Protocol: Messaging, consensus, pub/sub'); console.log(' - Performance: High throughput, latency benchmarks'); console.log(' - Failover: Agent failure, network partition, recovery'); //# sourceMappingURL=integration-tests.js.map