● Add comprehensive test coverage for wallet modules
- Add tests for wallet-store.js (40 tests: MemoryStorage, WalletOutput, WalletTransaction) - Add tests for wallet-sync.js (25 tests: sync engine, events, progress tracking) - Add tests for query.js (45 tests: OutputQuery, TxQuery, TransferQuery, presets) - Add tests for connection-manager.js (29 tests: ConnectionInfo, failover, events) - Add tests for offline.js (29 tests: unsigned/signed tx, key images, output export) - Add tests for multisig.js (39 tests: KEX, signers, accounts, tx sets) - Add tests for persistent-wallet.js (27 tests: lifecycle, balance, outputs, sync) - Update test/all.js to include new test suites - Rename SYNC_UNLOCK_BLOCKS to avoid export conflict with wallet.js
This commit is contained in:
@@ -0,0 +1,419 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* Connection Manager Tests
|
||||
*
|
||||
* Tests for connection-manager.js:
|
||||
* - ConnectionInfo class
|
||||
* - ConnectionManager class
|
||||
* - Connection state tracking
|
||||
*/
|
||||
|
||||
import {
|
||||
ConnectionManager,
|
||||
ConnectionInfo,
|
||||
CONNECTION_STATE,
|
||||
createDaemonConnectionManager,
|
||||
createWalletConnectionManager
|
||||
} from '../src/connection-manager.js';
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
passed++;
|
||||
} catch (e) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` Error: ${e.message}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
async function testAsync(name, fn) {
|
||||
try {
|
||||
await fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
passed++;
|
||||
} catch (e) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` Error: ${e.message}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
function assert(condition, message) {
|
||||
if (!condition) throw new Error(message || 'Assertion failed');
|
||||
}
|
||||
|
||||
function assertEqual(actual, expected, message) {
|
||||
if (actual !== expected) {
|
||||
throw new Error(message || `Expected ${expected}, got ${actual}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('=== Connection Manager Tests ===\n');
|
||||
|
||||
// ============================================================================
|
||||
// Constants Tests
|
||||
// ============================================================================
|
||||
|
||||
console.log('--- Constants ---');
|
||||
|
||||
test('CONNECTION_STATE has correct values', () => {
|
||||
assertEqual(CONNECTION_STATE.DISCONNECTED, 'disconnected');
|
||||
assertEqual(CONNECTION_STATE.CONNECTING, 'connecting');
|
||||
assertEqual(CONNECTION_STATE.CONNECTED, 'connected');
|
||||
assertEqual(CONNECTION_STATE.FAILED, 'failed');
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// ConnectionInfo Tests
|
||||
// ============================================================================
|
||||
|
||||
console.log('\n--- ConnectionInfo ---');
|
||||
|
||||
test('creates with uri', () => {
|
||||
const info = new ConnectionInfo({ uri: 'http://localhost:19081' });
|
||||
assertEqual(info.uri, 'http://localhost:19081');
|
||||
assertEqual(info.state, CONNECTION_STATE.DISCONNECTED);
|
||||
});
|
||||
|
||||
test('creates with url (alias for uri)', () => {
|
||||
const info = new ConnectionInfo({ url: 'http://localhost:19081' });
|
||||
assertEqual(info.uri, 'http://localhost:19081');
|
||||
});
|
||||
|
||||
test('creates with all options', () => {
|
||||
const info = new ConnectionInfo({
|
||||
uri: 'http://seed01.salvium.io:19081',
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
priority: 5,
|
||||
timeout: 60000,
|
||||
retries: 5,
|
||||
retryDelay: 2000
|
||||
});
|
||||
|
||||
assertEqual(info.uri, 'http://seed01.salvium.io:19081');
|
||||
assertEqual(info.username, 'user');
|
||||
assertEqual(info.password, 'pass');
|
||||
assertEqual(info.priority, 5);
|
||||
assertEqual(info.timeout, 60000);
|
||||
assertEqual(info.retries, 5);
|
||||
assertEqual(info.retryDelay, 2000);
|
||||
});
|
||||
|
||||
test('defaults priority to 1', () => {
|
||||
const info = new ConnectionInfo({ uri: 'http://localhost:19081' });
|
||||
assertEqual(info.priority, 1);
|
||||
});
|
||||
|
||||
test('defaults timeout to 30000', () => {
|
||||
const info = new ConnectionInfo({ uri: 'http://localhost:19081' });
|
||||
assertEqual(info.timeout, 30000);
|
||||
});
|
||||
|
||||
test('defaults retries to 3', () => {
|
||||
const info = new ConnectionInfo({ uri: 'http://localhost:19081' });
|
||||
assertEqual(info.retries, 3);
|
||||
});
|
||||
|
||||
test('markFailed updates state and failCount', () => {
|
||||
const info = new ConnectionInfo({ uri: 'http://localhost:19081' });
|
||||
assertEqual(info.failCount, 0);
|
||||
assertEqual(info.state, CONNECTION_STATE.DISCONNECTED);
|
||||
|
||||
info.markFailed(new Error('Connection refused'));
|
||||
|
||||
assertEqual(info.state, CONNECTION_STATE.FAILED);
|
||||
assertEqual(info.failCount, 1);
|
||||
assert(info.lastError !== null);
|
||||
});
|
||||
|
||||
test('markFailed increments failCount', () => {
|
||||
const info = new ConnectionInfo({ uri: 'http://localhost:19081' });
|
||||
|
||||
info.markFailed(new Error('Error 1'));
|
||||
info.markFailed(new Error('Error 2'));
|
||||
info.markFailed(new Error('Error 3'));
|
||||
|
||||
assertEqual(info.failCount, 3);
|
||||
});
|
||||
|
||||
test('markSuccess updates state and resets failCount', () => {
|
||||
const info = new ConnectionInfo({ uri: 'http://localhost:19081' });
|
||||
info.markFailed(new Error('test'));
|
||||
assertEqual(info.failCount, 1);
|
||||
|
||||
info.markSuccess(100);
|
||||
|
||||
assertEqual(info.state, CONNECTION_STATE.CONNECTED);
|
||||
assertEqual(info.failCount, 0);
|
||||
assertEqual(info.lastError, null);
|
||||
});
|
||||
|
||||
test('markSuccess tracks response time', () => {
|
||||
const info = new ConnectionInfo({ uri: 'http://localhost:19081' });
|
||||
|
||||
info.markSuccess(100);
|
||||
assertEqual(info.responseTime, 100);
|
||||
|
||||
// Should use exponential moving average
|
||||
info.markSuccess(200);
|
||||
assert(info.responseTime > 100 && info.responseTime < 200);
|
||||
});
|
||||
|
||||
test('reset clears failure state', () => {
|
||||
const info = new ConnectionInfo({ uri: 'http://localhost:19081' });
|
||||
info.markFailed(new Error('test'));
|
||||
info.markFailed(new Error('test2'));
|
||||
|
||||
info.reset();
|
||||
|
||||
assertEqual(info.state, CONNECTION_STATE.DISCONNECTED);
|
||||
assertEqual(info.failCount, 0);
|
||||
});
|
||||
|
||||
test('toRpcOptions returns correct object', () => {
|
||||
const info = new ConnectionInfo({
|
||||
uri: 'http://localhost:19081',
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
const opts = info.toRpcOptions();
|
||||
|
||||
assertEqual(opts.url, 'http://localhost:19081');
|
||||
assertEqual(opts.username, 'user');
|
||||
assertEqual(opts.password, 'pass');
|
||||
assertEqual(opts.timeout, 5000);
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// ConnectionManager Tests
|
||||
// ============================================================================
|
||||
|
||||
console.log('\n--- ConnectionManager ---');
|
||||
|
||||
test('creates with array of connection configs', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [
|
||||
{ uri: 'http://seed01.salvium.io:19081' },
|
||||
{ uri: 'http://seed02.salvium.io:19081' },
|
||||
{ uri: 'http://seed03.salvium.io:19081' }
|
||||
]
|
||||
});
|
||||
|
||||
assertEqual(manager.connections.length, 3);
|
||||
});
|
||||
|
||||
test('creates with ConnectionInfo objects', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [
|
||||
new ConnectionInfo({ uri: 'http://seed01.salvium.io:19081', priority: 1 }),
|
||||
new ConnectionInfo({ uri: 'http://seed02.salvium.io:19081', priority: 2 })
|
||||
]
|
||||
});
|
||||
|
||||
assertEqual(manager.connections.length, 2);
|
||||
});
|
||||
|
||||
test('sorts connections by priority (lower first)', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [
|
||||
{ uri: 'http://low:19081', priority: 10 },
|
||||
{ uri: 'http://high:19081', priority: 1 },
|
||||
{ uri: 'http://med:19081', priority: 5 }
|
||||
]
|
||||
});
|
||||
|
||||
// Lower priority = higher priority (sorted first)
|
||||
assertEqual(manager.connections[0].uri, 'http://high:19081');
|
||||
assertEqual(manager.connections[1].uri, 'http://med:19081');
|
||||
assertEqual(manager.connections[2].uri, 'http://low:19081');
|
||||
});
|
||||
|
||||
test('addConnection adds new connection', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [{ uri: 'http://seed01.salvium.io:19081' }]
|
||||
});
|
||||
|
||||
manager.addConnection({ uri: 'http://seed02.salvium.io:19081' });
|
||||
|
||||
assertEqual(manager.connections.length, 2);
|
||||
});
|
||||
|
||||
test('removeConnection removes by URI', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [
|
||||
{ uri: 'http://seed01.salvium.io:19081' },
|
||||
{ uri: 'http://seed02.salvium.io:19081' },
|
||||
{ uri: 'http://seed03.salvium.io:19081' }
|
||||
]
|
||||
});
|
||||
|
||||
manager.removeConnection('http://seed02.salvium.io:19081');
|
||||
|
||||
assertEqual(manager.connections.length, 2);
|
||||
assert(!manager.connections.some(c => c.uri === 'http://seed02.salvium.io:19081'));
|
||||
});
|
||||
|
||||
test('getConnections returns all connections', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [
|
||||
{ uri: 'http://seed01.salvium.io:19081' },
|
||||
{ uri: 'http://seed02.salvium.io:19081' }
|
||||
]
|
||||
});
|
||||
|
||||
const connections = manager.getConnections();
|
||||
assertEqual(connections.length, 2);
|
||||
});
|
||||
|
||||
test('getCurrentConnection returns first connection initially', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [
|
||||
{ uri: 'http://seed01.salvium.io:19081', priority: 1 },
|
||||
{ uri: 'http://seed02.salvium.io:19081', priority: 2 }
|
||||
]
|
||||
});
|
||||
|
||||
const current = manager.getCurrentConnection();
|
||||
assertEqual(current.uri, 'http://seed01.salvium.io:19081');
|
||||
});
|
||||
|
||||
test('switchTo changes active connection', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [
|
||||
{ uri: 'http://seed01.salvium.io:19081' },
|
||||
{ uri: 'http://seed02.salvium.io:19081' }
|
||||
]
|
||||
});
|
||||
|
||||
manager.switchTo('http://seed02.salvium.io:19081');
|
||||
const current = manager.getCurrentConnection();
|
||||
|
||||
assertEqual(current.uri, 'http://seed02.salvium.io:19081');
|
||||
});
|
||||
|
||||
test('getBestConnection returns connected connection', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [
|
||||
{ uri: 'http://seed01.salvium.io:19081' },
|
||||
{ uri: 'http://seed02.salvium.io:19081' }
|
||||
]
|
||||
});
|
||||
|
||||
// Mark first as failed, second as connected
|
||||
manager.connections[0].markFailed(new Error('test'));
|
||||
manager.connections[1].markSuccess(100);
|
||||
|
||||
const best = manager.getBestConnection();
|
||||
assertEqual(best.uri, 'http://seed02.salvium.io:19081');
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Event System Tests
|
||||
// ============================================================================
|
||||
|
||||
console.log('\n--- Events ---');
|
||||
|
||||
test('on adds listener', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [{ uri: 'http://localhost:19081' }]
|
||||
});
|
||||
let called = false;
|
||||
|
||||
manager.on('test', () => { called = true; });
|
||||
manager._emit('test');
|
||||
|
||||
assert(called);
|
||||
});
|
||||
|
||||
test('off removes listener', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [{ uri: 'http://localhost:19081' }]
|
||||
});
|
||||
let count = 0;
|
||||
const handler = () => { count++; };
|
||||
|
||||
manager.on('test', handler);
|
||||
manager._emit('test');
|
||||
assertEqual(count, 1);
|
||||
|
||||
manager.off('test', handler);
|
||||
manager._emit('test');
|
||||
assertEqual(count, 1);
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Factory Function Tests
|
||||
// ============================================================================
|
||||
|
||||
console.log('\n--- Factory Functions ---');
|
||||
|
||||
test('createDaemonConnectionManager creates manager for daemon', () => {
|
||||
// First arg is connections array, second is options
|
||||
const manager = createDaemonConnectionManager([
|
||||
{ uri: 'http://localhost:19081' }
|
||||
]);
|
||||
|
||||
assert(manager instanceof ConnectionManager);
|
||||
assertEqual(manager.proxyType, 'daemon');
|
||||
});
|
||||
|
||||
test('createWalletConnectionManager creates manager for wallet', () => {
|
||||
// First arg is connections array, second is options
|
||||
const manager = createWalletConnectionManager([
|
||||
{ uri: 'http://localhost:19083' }
|
||||
]);
|
||||
|
||||
assert(manager instanceof ConnectionManager);
|
||||
assertEqual(manager.proxyType, 'wallet');
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Configuration Tests
|
||||
// ============================================================================
|
||||
|
||||
console.log('\n--- Configuration ---');
|
||||
|
||||
test('autoSwitch defaults to true', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [{ uri: 'http://localhost:19081' }]
|
||||
});
|
||||
assertEqual(manager.autoSwitch, true);
|
||||
});
|
||||
|
||||
test('checkPeriod defaults to 30000', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [{ uri: 'http://localhost:19081' }]
|
||||
});
|
||||
assertEqual(manager.checkPeriod, 30000);
|
||||
});
|
||||
|
||||
test('proxyType defaults to daemon', () => {
|
||||
const manager = new ConnectionManager({
|
||||
connections: [{ uri: 'http://localhost:19081' }]
|
||||
});
|
||||
assertEqual(manager.proxyType, 'daemon');
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Summary
|
||||
// ============================================================================
|
||||
|
||||
console.log('\n--- Summary ---');
|
||||
console.log(`Passed: ${passed}`);
|
||||
console.log(`Failed: ${failed}`);
|
||||
|
||||
if (failed > 0) {
|
||||
console.log('\n✗ Some tests failed!');
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log('\n✓ All connection manager tests passed!');
|
||||
process.exit(0);
|
||||
}
|
||||
Reference in New Issue
Block a user