#!/usr/bin/env bare
/**
* P2P Connectivity Test Suite
*
* Run on any machine to verify Hyperswarm connectivity:
* bare test/p2p-connectivity.js [tabertron-server-key]
*
* Tests:
* 1. Hyperswarm initialization (can we create a swarm?)
* 2. DHT bootstrap (can we reach the DHT network?)
* 3. Topic announcement (can we announce + discover?)
* 4. Peer connection (can we connect to a specific server?)
* 5. git-remote-punch (does the git transport work?)
*/
import Hyperswarm from 'hyperswarm'
import crypto from 'hypercore-crypto'
import { spawn } from 'bare-subprocess'
import os from 'bare-os'
import process from 'bare-process'
const SERVER_KEY = process.argv[2] || 'f580547b1fdba52331b0ed9483fc2cbe50fb3fb3cc76c904b580b7b7ee6f3d15'
const REPO = process.argv[3] || 'hyper-overlay'
const TIMEOUT = 15000
let passed = 0
let failed = 0
// Catch uncaught errors from streams — prevent crash
process.on('uncaughtException', (err) => {
console.log(` [uncaught] ${err.code || err.message}`)
})
function ok (name) { passed++; console.log(` ✓ ${name}`) }
function fail (name, reason) { failed++; console.log(` ✗ ${name}: ${reason}`) }
async function withTimeout (promise, ms, label) {
return Promise.race([
promise,
new Promise((_, reject) => setTimeout(() => reject(new Error(`timeout after ${ms}ms`)), ms))
])
}
console.log('P2P Connectivity Tests')
console.log('======================')
console.log(`Host: ${os.hostname()}`)
console.log(`Server key: ${SERVER_KEY.slice(0, 8)}...`)
console.log(`Test repo: ${REPO}`)
console.log('')
// Test 1: Hyperswarm initialization
console.log('1. Hyperswarm initialization')
let swarm
try {
swarm = new Hyperswarm()
ok('Created Hyperswarm instance')
} catch (err) {
fail('Hyperswarm creation', err.message)
process.exit(1)
}
// Test 2: DHT bootstrap
console.log('2. DHT bootstrap')
try {
// The swarm's DHT bootstraps on first operation
const topic = crypto.randomBytes(32)
const discovery = swarm.join(topic, { server: false, client: true })
await withTimeout(swarm.flush(), 10000, 'DHT flush')
discovery.destroy()
ok('DHT bootstrap + flush succeeded')
} catch (err) {
fail('DHT bootstrap', err.message)
}
// Test 3: Self-connection test (announce + discover same topic)
console.log('3. Self-announce test')
try {
const swarm2 = new Hyperswarm()
const topic = crypto.randomBytes(32)
const serverReady = new Promise((resolve) => {
swarm2.on('connection', (conn) => {
conn.destroy()
resolve()
})
})
swarm2.join(topic, { server: true, client: false })
await swarm2.flush()
swarm.join(topic, { server: false, client: true })
await withTimeout(serverReady, TIMEOUT, 'self-connection')
ok('Self-connection works (announce + discover)')
await swarm2.destroy()
} catch (err) {
fail('Self-connection', err.message)
}
// Test 4: Connect to remote server topic
console.log('4. Remote server connection')
try {
const keyBuf = Buffer.from(SERVER_KEY, 'hex')
const topic = crypto.hash(Buffer.concat([keyBuf, Buffer.from(REPO, 'utf-8')])).subarray(0, 32)
const swarm3 = new Hyperswarm()
swarm3.on('error', () => {}) // prevent uncaught
swarm3.join(topic, { server: false, client: true })
const result = await withTimeout(
new Promise((resolve, reject) => {
swarm3.on('connection', (conn, info) => {
const peerKey = info.publicKey.toString('hex')
conn.on('error', () => {}) // prevent uncaught ECONNRESET
resolve({ conn, peerKey })
})
}),
TIMEOUT,
'remote connection'
)
const shortKey = result.peerKey.slice(0, 12)
const isSelf = result.peerKey === swarm3.keyPair.publicKey.toString('hex')
if (isSelf) {
fail('Remote connection', `connected to SELF (${shortKey}...) — local pear-git serving same topic?`)
} else {
ok(`Connected to peer ${shortKey}... (not self)`)
}
result.conn.on('error', () => {})
result.conn.destroy()
await swarm3.destroy()
} catch (err) {
fail('Remote connection', err.message)
}
// Test 5: git ls-remote via punch://
console.log('5. git ls-remote via punch://')
try {
const url = `punch://${SERVER_KEY}/${REPO}`
const result = await withTimeout(
new Promise((resolve, reject) => {
const child = spawn('git', ['ls-remote', url, 'refs/heads/release'], {
stdio: ['ignore', 'pipe', 'pipe']
})
let stdout = ''
let stderr = ''
child.stdout.on('data', (d) => { stdout += d.toString() })
child.stderr.on('data', (d) => { stderr += d.toString() })
child.on('exit', (code) => {
if (code === 0) resolve(stdout.trim())
else reject(new Error(`exit ${code}: ${stderr.trim().split('\n').pop()}`))
})
child.on('error', reject)
}),
30000,
'git ls-remote'
)
const ref = result.split('\t')[0]
ok(`git ls-remote returned: ${ref.slice(0, 12)}...`)
} catch (err) {
fail('git ls-remote', err.message)
}
// Cleanup
await swarm.destroy()
// Summary
console.log('')
console.log(`Results: ${passed} passed, ${failed} failed`)
process.exit(failed > 0 ? 1 : 0)