OverviewFilesCommitsIssuesDocsReleasesPeers

hypermosh-shell / test / tunnel.test.mjs

80 lines · javascript ref:
// hypermosh tunnel tests.
//   1) loopback: UDP echo round-trips through the hyperdht tunnel (no mosh needed)
//   2) real mosh: a mosh session runs end-to-end through the tunnel (if mosh present)
import DHT from 'hyperdht'
import dgram from 'dgram'
import b4a from 'b4a'
import { spawn, execSync } from 'child_process'
import { fileURLToPath } from 'url'
import path from 'path'
import { serve, connect } from '../lib/relay.mjs'

const __dirname = path.dirname(fileURLToPath(import.meta.url))
let failures = 0

async function testLoopback () {
  console.log('\n[1] loopback: UDP echo through hyperdht tunnel')
  const echo = dgram.createSocket('udp4')
  echo.on('message', (msg, r) => echo.send(b4a.concat([b4a.from('echo:'), msg]), r.port, r.address))
  await new Promise((res) => echo.bind(0, '127.0.0.1', res))

  const keyPair = DHT.keyPair()
  const s = await serve({ keyPair, udpPort: echo.address().port })
  const c = await connect({ remoteKey: keyPair.publicKey })

  const probe = dgram.createSocket('udp4')
  const got = new Promise((resolve, reject) => {
    const t = setTimeout(() => reject(new Error('timeout')), 45000) // cold self-hairpin DHT connect can be slow on one machine
    probe.on('message', (m) => { clearTimeout(t); resolve(b4a.toString(m)) })
  })
  await new Promise((res) => probe.bind(0, '127.0.0.1', res))
  const payload = 'hello-' + Math.floor(process.uptime() * 1000)
  probe.send(b4a.from(payload), c.localPort, '127.0.0.1')

  try {
    const reply = await got
    if (reply === 'echo:' + payload) console.log('    ✅ PASS — round-trip ok')
    else { console.log('    ❌ FAIL — got', reply); failures++ }
  } catch (e) { console.log('    ❌ FAIL —', e.message); failures++ }
  finally { probe.close(); echo.close(); await c.close(); await s.close() }
}

async function testRealMosh () {
  let hasMosh = true
  try { execSync('command -v mosh-server && command -v mosh-client && command -v python3', { stdio: 'ignore' }) } catch { hasMosh = false }
  if (!hasMosh) { console.log('\n[2] real mosh: SKIPPED (mosh/python3 not installed)'); return }

  console.log('\n[2] real mosh: session through the hyperdht tunnel')
  const ms = spawn('mosh-server', ['new', '-i', '127.0.0.1', '--', 'bash', '-lc', 'echo HYPERMOSH_OK; sleep 8'],
    { env: { ...process.env, LC_ALL: 'C.UTF-8', LANG: 'C.UTF-8' } })
  let sout = ''
  let conn
  try {
    conn = await new Promise((resolve, reject) => {
      const t = setTimeout(() => reject(new Error('no MOSH CONNECT')), 10000)
      const scan = (d) => { sout += d; const m = sout.match(/MOSH CONNECT (\d+) (\S+)/); if (m) { clearTimeout(t); resolve({ port: +m[1], key: m[2] }) } }
      ms.stdout.on('data', scan); ms.stderr.on('data', scan)
    })
  } catch (e) { console.log('    ❌ FAIL —', e.message); failures++; try { ms.kill() } catch {}; return }

  const keyPair = DHT.keyPair()
  const s = await serve({ keyPair, udpPort: conn.port })
  const c = await connect({ remoteKey: keyPair.publicKey })

  const drv = spawn('python3', [path.join(__dirname, 'pty-mosh.py'), conn.key, '127.0.0.1', String(c.localPort), '12'], { env: process.env })
  let cap = ''
  drv.stdout.on('data', (d) => { cap += d })
  await new Promise((res) => { const t = setTimeout(() => { try { drv.kill() } catch {}; res() }, 20000); drv.on('exit', () => { clearTimeout(t); res() }) })

  const clean = cap.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '')
  if (clean.includes('HYPERMOSH_OK')) console.log('    ✅ PASS — mosh session traversed the tunnel')
  else { console.log('    ❌ FAIL — marker not seen'); failures++ }

  await c.close(); await s.close(); try { ms.kill() } catch {}
}

await testLoopback()
await testRealMosh()
console.log(failures === 0 ? '\n✅ all tests passed' : '\n❌ ' + failures + ' test(s) failed')
process.exit(failures === 0 ? 0 : 1)