340 líneas
11 KiB
JavaScript
340 líneas
11 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { RingNode } from '../src/ring-node.js';
|
|
import { OracleNode } from '../src/oracle-node.js';
|
|
import chalk from 'chalk';
|
|
|
|
console.log(chalk.blue.bold('🧪 Ring Network Test Suite\n'));
|
|
|
|
class NetworkTester {
|
|
constructor() {
|
|
this.nodes = [];
|
|
this.testResults = [];
|
|
}
|
|
|
|
async delay(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
|
|
log(message, type = 'info') {
|
|
const colors = {
|
|
info: chalk.blue,
|
|
success: chalk.green,
|
|
error: chalk.red,
|
|
warning: chalk.yellow
|
|
};
|
|
console.log(`${colors[type]}${message}`);
|
|
}
|
|
|
|
async runTest(name, testFn) {
|
|
this.log(`\n🔬 Running test: ${name}`, 'info');
|
|
try {
|
|
const startTime = Date.now();
|
|
await testFn();
|
|
const duration = Date.now() - startTime;
|
|
this.log(`✅ Test passed: ${name} (${duration}ms)`, 'success');
|
|
this.testResults.push({ name, status: 'passed', duration });
|
|
} catch (error) {
|
|
this.log(`❌ Test failed: ${name} - ${error.message}`, 'error');
|
|
this.testResults.push({ name, status: 'failed', error: error.message });
|
|
}
|
|
}
|
|
|
|
async setupBasicNetwork() {
|
|
this.log('🏗️ Setting up basic network with 1 Oracle and 2 regular nodes', 'info');
|
|
|
|
// Create Oracle node
|
|
const oracle = new OracleNode({
|
|
id: 'oracle-1',
|
|
port: 8080
|
|
});
|
|
this.nodes.push(oracle);
|
|
|
|
// Create regular nodes
|
|
const node1 = new RingNode({
|
|
id: 'node-1',
|
|
port: 8081
|
|
});
|
|
this.nodes.push(node1);
|
|
|
|
const node2 = new RingNode({
|
|
id: 'node-2',
|
|
port: 8082
|
|
});
|
|
this.nodes.push(node2);
|
|
|
|
// Allow nodes to initialize
|
|
await this.delay(2000);
|
|
|
|
return { oracle, node1, node2 };
|
|
}
|
|
|
|
async testNodeInitialization() {
|
|
const { oracle, node1, node2 } = await this.setupBasicNetwork();
|
|
|
|
// Verify nodes are properly initialized
|
|
if (!oracle.id || !oracle.isOracle) {
|
|
throw new Error('Oracle node not properly initialized');
|
|
}
|
|
|
|
if (!node1.id || node1.isOracle) {
|
|
throw new Error('Regular node 1 not properly initialized');
|
|
}
|
|
|
|
if (!node2.id || node2.isOracle) {
|
|
throw new Error('Regular node 2 not properly initialized');
|
|
}
|
|
|
|
this.log(`Oracle ID: ${oracle.id}`, 'info');
|
|
this.log(`Node 1 ID: ${node1.id}`, 'info');
|
|
this.log(`Node 2 ID: ${node2.id}`, 'info');
|
|
}
|
|
|
|
async testPeerConnections() {
|
|
const { oracle, node1, node2 } = this.nodes.length > 0 ?
|
|
{ oracle: this.nodes[0], node1: this.nodes[1], node2: this.nodes[2] } :
|
|
await this.setupBasicNetwork();
|
|
|
|
// Simulate peer connections
|
|
// In a real test, we would actually establish WebRTC connections
|
|
// For this test, we'll simulate the connection state
|
|
|
|
oracle.knownNodes.set(node1.id, { port: node1.port });
|
|
oracle.knownNodes.set(node2.id, { port: node2.port });
|
|
|
|
node1.knownNodes.set(oracle.id, { port: oracle.port, isOracle: true });
|
|
node1.knownNodes.set(node2.id, { port: node2.port });
|
|
|
|
node2.knownNodes.set(oracle.id, { port: oracle.port, isOracle: true });
|
|
node2.knownNodes.set(node1.id, { port: node1.port });
|
|
|
|
// Update oracle nodes list
|
|
node1.oracleNodes.add(oracle.id);
|
|
node2.oracleNodes.add(oracle.id);
|
|
|
|
this.log(`Oracle knows ${oracle.knownNodes.size} nodes`, 'info');
|
|
this.log(`Node 1 knows ${node1.knownNodes.size} nodes`, 'info');
|
|
this.log(`Node 2 knows ${node2.knownNodes.size} nodes`, 'info');
|
|
}
|
|
|
|
async testRingTopology() {
|
|
const { oracle, node1, node2 } = this.nodes.length > 0 ?
|
|
{ oracle: this.nodes[0], node1: this.nodes[1], node2: this.nodes[2] } :
|
|
await this.setupBasicNetwork();
|
|
|
|
// Simulate ring topology setup
|
|
// Oracle -> Node1 -> Node2 -> Oracle (inner ring)
|
|
oracle.rings.inner.right = node1.id;
|
|
oracle.rings.inner.left = node2.id;
|
|
|
|
node1.rings.inner.left = oracle.id;
|
|
node1.rings.inner.right = node2.id;
|
|
|
|
node2.rings.inner.left = node1.id;
|
|
node2.rings.inner.right = oracle.id;
|
|
|
|
// Oracle -> Node2 -> Node1 -> Oracle (outer ring - reversed)
|
|
oracle.rings.outer.right = node2.id;
|
|
oracle.rings.outer.left = node1.id;
|
|
|
|
node1.rings.outer.left = node2.id;
|
|
node1.rings.outer.right = oracle.id;
|
|
|
|
node2.rings.outer.left = oracle.id;
|
|
node2.rings.outer.right = node1.id;
|
|
|
|
// Verify ring integrity
|
|
const oracleHasValidRings = oracle.rings.inner.left && oracle.rings.inner.right &&
|
|
oracle.rings.outer.left && oracle.rings.outer.right;
|
|
|
|
if (!oracleHasValidRings) {
|
|
throw new Error('Oracle rings not properly configured');
|
|
}
|
|
|
|
this.log('Ring topology established successfully', 'success');
|
|
}
|
|
|
|
async testOracleServices() {
|
|
const oracle = this.nodes[0];
|
|
|
|
if (!oracle.isOracle) {
|
|
throw new Error('First node is not an Oracle');
|
|
}
|
|
|
|
// Test network analysis
|
|
const analysis = oracle.analyzeNetwork();
|
|
if (!analysis.networkSize || !analysis.ringTopology) {
|
|
throw new Error('Network analysis failed');
|
|
}
|
|
this.log(`Network analysis: ${analysis.networkSize} nodes`, 'info');
|
|
|
|
// Test data storage
|
|
const storeResult = oracle.handleDataStorage({
|
|
operation: 'set',
|
|
key: 'test-key',
|
|
value: 'test-value'
|
|
});
|
|
|
|
if (!storeResult.success) {
|
|
throw new Error('Data storage (set) failed');
|
|
}
|
|
|
|
const getResult = oracle.handleDataStorage({
|
|
operation: 'get',
|
|
key: 'test-key'
|
|
});
|
|
|
|
if (!getResult.success || getResult.value !== 'test-value') {
|
|
throw new Error('Data storage (get) failed');
|
|
}
|
|
|
|
this.log('Oracle data storage working correctly', 'success');
|
|
|
|
// Test consensus
|
|
const proposalId = 'test-proposal-' + Date.now();
|
|
const consensusResult = oracle.handleConsensus({
|
|
proposalId,
|
|
proposal: 'Test proposal for network upgrade'
|
|
});
|
|
|
|
if (!consensusResult.success) {
|
|
throw new Error('Consensus proposal creation failed');
|
|
}
|
|
|
|
this.log('Oracle consensus mechanism working', 'success');
|
|
|
|
// Test health check
|
|
const health = oracle.performHealthCheck();
|
|
if (!health.checks) {
|
|
throw new Error('Health check failed');
|
|
}
|
|
|
|
this.log(`Health check: ${health.healthy ? 'Healthy' : 'Issues detected'}`,
|
|
health.healthy ? 'success' : 'warning');
|
|
}
|
|
|
|
async testMessageRouting() {
|
|
// This test simulates message routing through the ring
|
|
// In a real implementation, this would test actual WebRTC communication
|
|
|
|
const oracle = this.nodes[0];
|
|
let messageReceived = false;
|
|
|
|
// Set up message handler
|
|
oracle.on('ringMessage', ({ from, payload }) => {
|
|
if (payload.type === 'test-message') {
|
|
messageReceived = true;
|
|
}
|
|
});
|
|
|
|
// Simulate sending a message
|
|
oracle.emit('ringMessage', {
|
|
from: 'test-sender',
|
|
payload: { type: 'test-message', content: 'Hello Ring!' }
|
|
});
|
|
|
|
await this.delay(100);
|
|
|
|
if (!messageReceived) {
|
|
throw new Error('Message routing failed');
|
|
}
|
|
|
|
this.log('Message routing test passed', 'success');
|
|
}
|
|
|
|
async testNetworkResilience() {
|
|
const { oracle, node1, node2 } = this.nodes.length > 0 ?
|
|
{ oracle: this.nodes[0], node1: this.nodes[1], node2: this.nodes[2] } :
|
|
await this.setupBasicNetwork();
|
|
|
|
// Simulate node disconnection
|
|
const originalPeerCount = oracle.knownNodes.size;
|
|
|
|
// Remove node2 from network
|
|
oracle.handlePeerDisconnection(node2.id);
|
|
node1.handlePeerDisconnection(node2.id);
|
|
|
|
if (oracle.knownNodes.has(node2.id)) {
|
|
throw new Error('Node not properly removed from network');
|
|
}
|
|
|
|
// Verify network adapted to disconnection
|
|
const newPeerCount = oracle.knownNodes.size;
|
|
if (newPeerCount >= originalPeerCount) {
|
|
throw new Error('Network did not adapt to peer disconnection');
|
|
}
|
|
|
|
this.log(`Network adapted to disconnection: ${originalPeerCount} -> ${newPeerCount} nodes`, 'success');
|
|
}
|
|
|
|
async cleanup() {
|
|
this.log('\n🧹 Cleaning up test environment', 'info');
|
|
|
|
for (const node of this.nodes) {
|
|
try {
|
|
await node.destroy();
|
|
} catch (error) {
|
|
this.log(`Warning: Error cleaning up node ${node.id}: ${error.message}`, 'warning');
|
|
}
|
|
}
|
|
|
|
this.nodes = [];
|
|
}
|
|
|
|
printResults() {
|
|
this.log('\n📊 Test Results Summary:', 'info');
|
|
|
|
const passed = this.testResults.filter(r => r.status === 'passed').length;
|
|
const failed = this.testResults.filter(r => r.status === 'failed').length;
|
|
|
|
this.log(`✅ Passed: ${passed}`, 'success');
|
|
this.log(`❌ Failed: ${failed}`, failed > 0 ? 'error' : 'info');
|
|
|
|
if (failed > 0) {
|
|
this.log('\nFailed tests:', 'error');
|
|
this.testResults
|
|
.filter(r => r.status === 'failed')
|
|
.forEach(r => {
|
|
this.log(` - ${r.name}: ${r.error}`, 'error');
|
|
});
|
|
}
|
|
|
|
const totalDuration = this.testResults.reduce((sum, r) => sum + (r.duration || 0), 0);
|
|
this.log(`\nTotal test duration: ${totalDuration}ms`, 'info');
|
|
|
|
return failed === 0;
|
|
}
|
|
|
|
async runAllTests() {
|
|
try {
|
|
await this.runTest('Node Initialization', () => this.testNodeInitialization());
|
|
await this.runTest('Peer Connections', () => this.testPeerConnections());
|
|
await this.runTest('Ring Topology', () => this.testRingTopology());
|
|
await this.runTest('Oracle Services', () => this.testOracleServices());
|
|
await this.runTest('Message Routing', () => this.testMessageRouting());
|
|
await this.runTest('Network Resilience', () => this.testNetworkResilience());
|
|
} finally {
|
|
await this.cleanup();
|
|
}
|
|
|
|
return this.printResults();
|
|
}
|
|
}
|
|
|
|
// Run tests if this file is executed directly
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
const tester = new NetworkTester();
|
|
|
|
tester.runAllTests()
|
|
.then(success => {
|
|
console.log(chalk.blue.bold('\n🏁 Test suite completed'));
|
|
process.exit(success ? 0 : 1);
|
|
})
|
|
.catch(error => {
|
|
console.error(chalk.red.bold(`\n💥 Test suite crashed: ${error.message}`));
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
export { NetworkTester };
|