Files
p2ns/test-scripts/core-swarm-handlers.test.js
T
Raven Scott b1a02e2e84 feat(experimental)!: multi-master one ledger (Option B)
Remove the last layer of soft centralization: a lone `--master` on empty
storage could still implicitly genesis a separate Autopass and split the
network on the same TOPIC_SEED. Genesis is now explicit (`--master
--genesis`); secondary masters pair via invite into the same dnsPass as
joiners, with a shared network manifest, split-brain diagnostics, and
writer-aware quorum.

- Genesis vs secondary master boot paths; auto-adopt manifest on upgrade
- Masters without dnsPass accept invites; masters with pass ignore them
- NETWORK_MANIFEST_FILE, P2NS_GENESIS, MASTER_LOAD_DOMAINS, MASTER_INVITE_ONLY
- core.status networkId; admin diagnostics; docs and multi-master tests

BREAKING: operators must run one genesis per network; additional masters
use `node p2ns.js --master` (not `--genesis`) on empty storage.
2026-05-29 05:34:50 -04:00

235 lines
8.1 KiB
JavaScript

#!/usr/bin/env node
/**
* Unit tests for native core swarm RPC handlers.
*/
const assert = require('assert');
const { createCoreSwarmHandlers } = require('../includes/core/core-swarm-handlers');
const { METHODS, INVITE_STATUS } = require('../includes/core/core-rpc-contract');
function testMasterPendingPassAcceptsInvite() {
let pairedPass = null;
const shared = { base: { key: Buffer.alloc(32, 1), writable: true, update: async () => {} }, ready: async () => {} };
const localState = { consecutiveInviteFailures: 0, masterPendingPass: true };
const handlers = createCoreSwarmHandlers({
Autopass: {
pair: () => ({ finished: async () => shared })
},
store: {},
core: { writable: true, update: async () => {} },
state: localState,
isMaster: true,
getDnsPass: () => pairedPass,
setDnsPass: (p) => { pairedPass = p; },
connectedPeers: new Set(['genesis']),
peerChannels: new Map(),
failedInvitePeers: new Set(),
pendingInviteAcks: new Map(),
pendingInviteRequestHandlers: new Set(),
isProcessingInvite: () => false,
acquireInviteLock: async () => {},
releaseInviteLock: () => {},
withTimeout: (p) => p,
getCurrentInvitePromise: () => null,
setCurrentInvitePromise: () => {},
getCurrentPairOperation: () => null,
setCurrentPairOperation: () => {},
setupDomainsWatcher: () => {},
shouldLoadDomains: () => false,
listDomains: async () => [],
doAutoVotes: () => {},
setupListeners: () => {},
coreRpc: { inviteAck: () => true },
channelManager: { getChannelInfo: () => null, waitForBidirectionalOpen: async () => true },
CORE_DOMAIN: 'p2ns.core'
});
return handlers.processInviteWire('genesis', 'wire').then(() => {
assert.strictEqual(pairedPass, shared);
assert.strictEqual(localState.masterPendingPass, false);
});
}
function testMasterWithPassIgnoresInvite() {
let setCalled = false;
const handlers = createCoreSwarmHandlers({
Autopass: { pair: () => { throw new Error('should not pair'); } },
store: {},
core: { writable: true, update: async () => {} },
state: { consecutiveInviteFailures: 0 },
isMaster: true,
getDnsPass: () => ({}),
setDnsPass: () => { setCalled = true; },
connectedPeers: new Set(),
peerChannels: new Map(),
failedInvitePeers: new Set(),
pendingInviteAcks: new Map(),
pendingInviteRequestHandlers: new Set(),
isProcessingInvite: () => false,
acquireInviteLock: async () => {},
releaseInviteLock: () => {},
withTimeout: (p) => p,
getCurrentInvitePromise: () => null,
setCurrentInvitePromise: () => {},
getCurrentPairOperation: () => null,
setCurrentPairOperation: () => {},
setupDomainsWatcher: () => {},
shouldLoadDomains: () => false,
listDomains: async () => [],
doAutoVotes: () => {},
setupListeners: () => {},
coreRpc: { inviteAck: () => true },
channelManager: { getChannelInfo: () => null, waitForBidirectionalOpen: async () => true },
CORE_DOMAIN: 'p2ns.core'
});
return handlers.processInviteWire('peer1', 'wire').then(() => {
assert.strictEqual(setCalled, false);
});
}
function testInviteAckClearsPending() {
const pendingInviteAcks = new Map();
pendingInviteAcks.set('peer1', { timeout: null, retryCount: 1, inviteId: 'abc123' });
const handlers = createCoreSwarmHandlers({
Autopass: {},
store: {},
core: { writable: true, update: async () => {} },
state: { consecutiveInviteFailures: 0 },
isMaster: true,
getDnsPass: () => ({}),
setDnsPass: () => {},
connectedPeers: new Set(['peer1']),
peerChannels: new Map(),
failedInvitePeers: new Set(),
pendingInviteAcks,
pendingInviteRequestHandlers: new Set(),
isProcessingInvite: () => false,
acquireInviteLock: async () => {},
releaseInviteLock: () => {},
withTimeout: (p) => p,
getCurrentInvitePromise: () => null,
setCurrentInvitePromise: () => {},
getCurrentPairOperation: () => null,
setCurrentPairOperation: () => {},
setupDomainsWatcher: () => {},
listDomains: async () => [],
doAutoVotes: () => {},
setupListeners: () => {},
coreRpc: { inviteAck: () => true },
channelManager: { getChannelInfo: () => null, waitForBidirectionalOpen: async () => true },
CORE_DOMAIN: 'p2ns.core'
});
handlers.onInviteAck({ inviteId: 'abc123' }, { peerId: 'peer1' });
assert.strictEqual(pendingInviteAcks.has('peer1'), false);
}
function testInviteAckIgnoresStaleId() {
const pendingInviteAcks = new Map();
pendingInviteAcks.set('peer1', { inviteId: 'expected' });
const handlers = createCoreSwarmHandlers({
Autopass: {},
store: {},
core: { writable: true, update: async () => {} },
state: { consecutiveInviteFailures: 0 },
isMaster: true,
getDnsPass: () => ({}),
setDnsPass: () => {},
connectedPeers: new Set(['peer1']),
peerChannels: new Map(),
failedInvitePeers: new Set(),
pendingInviteAcks,
pendingInviteRequestHandlers: new Set(),
isProcessingInvite: () => false,
acquireInviteLock: async () => {},
releaseInviteLock: () => {},
withTimeout: (p) => p,
getCurrentInvitePromise: () => null,
setCurrentInvitePromise: () => {},
getCurrentPairOperation: () => null,
setCurrentPairOperation: () => {},
setupDomainsWatcher: () => {},
listDomains: async () => [],
doAutoVotes: () => {},
setupListeners: () => {},
coreRpc: { inviteAck: () => true },
channelManager: { getChannelInfo: () => null, waitForBidirectionalOpen: async () => true },
CORE_DOMAIN: 'p2ns.core'
});
handlers.onInviteAck({ inviteId: 'wrong' }, { peerId: 'peer1' });
assert.strictEqual(pendingInviteAcks.has('peer1'), true);
}
function testNoLegacyAdapterInP2ns() {
const fs = require('fs');
const src = fs.readFileSync(require('path').join(__dirname, '../p2ns.js'), 'utf8');
assert.ok(!src.includes('processCoreRequestMessage'), 'p2ns.js must not contain processCoreRequestMessage');
assert.ok(!src.includes("registerPluginChannel(CORE_DOMAIN, 'invite'"), 'invite channel registration removed');
assert.ok(!src.includes('registerPluginChannel'), 'registerPluginChannel removed — use registerPluginProtocol');
}
function testMethodsIncludeDeliver() {
assert.strictEqual(METHODS.INVITE_DELIVER, 'invite.deliver');
assert.strictEqual(METHODS.CORE_STATUS, 'core.status');
assert.ok(INVITE_STATUS.OK);
}
function testCoreStatusMaster() {
const handlers = createCoreSwarmHandlers({
Autopass: {},
store: {},
core: { writable: true, update: async () => {} },
state: { consecutiveInviteFailures: 0, pendingInviteRequests: new Map() },
isMaster: true,
getDnsPass: () => ({}),
setDnsPass: () => {},
connectedPeers: new Set(['peer1', 'peer2']),
peerChannels: new Map(),
failedInvitePeers: new Set(),
pendingInviteAcks: new Map(),
pendingInviteRequestHandlers: new Set(),
isProcessingInvite: () => false,
acquireInviteLock: async () => {},
releaseInviteLock: () => {},
withTimeout: (p) => p,
getCurrentInvitePromise: () => null,
setCurrentInvitePromise: () => {},
getCurrentPairOperation: () => null,
setCurrentPairOperation: () => {},
setupDomainsWatcher: () => {},
listDomains: async () => [],
doAutoVotes: () => {},
setupListeners: () => {},
coreRpc: {},
channelManager: { getChannelInfo: () => null, waitForBidirectionalOpen: async () => true },
CORE_DOMAIN: 'p2ns.core'
});
return handlers.onCoreStatus().then((status) => {
assert.strictEqual(status.nodeType, 'master');
assert.strictEqual(status.dnsPassInitialized, true);
assert.strictEqual(status.canProvideInvite, true);
assert.strictEqual(status.connectedPeers, 2);
assert.ok('networkId' in status);
assert.ok('masterPendingPass' in status);
});
}
testMasterPendingPassAcceptsInvite();
testMasterWithPassIgnoresInvite();
testInviteAckClearsPending();
testInviteAckIgnoresStaleId();
testNoLegacyAdapterInP2ns();
testMethodsIncludeDeliver();
testCoreStatusMaster().then(() => {
console.log('core-swarm-handlers.test: ok');
}).catch((err) => {
console.error(err);
process.exit(1);
});