Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
669
vendor/ruvector/npm/packages/agentic-integration/integration-tests.js
vendored
Normal file
669
vendor/ruvector/npm/packages/agentic-integration/integration-tests.js
vendored
Normal file
@@ -0,0 +1,669 @@
|
||||
"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
|
||||
Reference in New Issue
Block a user