Files
bare-operating-system/packages/bare-os-coreutils/test/irc-client.test.mjs
T
2026-08-18 18:11:34 -04:00

108 lines
3.3 KiB
JavaScript

import test from 'brittle'
import { readFile } from 'node:fs/promises'
import { EventEmitter } from 'node:events'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = dirname(fileURLToPath(import.meta.url))
async function load() {
const parts = await Promise.all([
readFile(join(__dirname, '../lib/irc/irc-parse.js'), 'utf8'),
readFile(join(__dirname, '../lib/irc/irc-isupport.js'), 'utf8'),
readFile(join(__dirname, '../lib/irc/irc-sasl.js'), 'utf8'),
readFile(join(__dirname, '../lib/irc/irc-state.js'), 'utf8'),
readFile(join(__dirname, '../lib/irc/irc-config.js'), 'utf8'),
readFile(join(__dirname, '../lib/irc/irc-client.js'), 'utf8')
])
const hook = {}
new Function(
'exports',
parts.join('\n') +
'\nexports.bareIrcCreateClient = bareIrcCreateClient;\nexports.bareIrcSaslPlainChunks = bareIrcSaslPlainChunks;\nexports.bareIrcParseIsupport = bareIrcParseIsupport;\n'
)(hook)
return hook
}
function fakeSocket() {
const s = new EventEmitter()
s.writes = []
s.write = function (x) {
s.writes.push(String(x))
return true
}
s.end = function () {
s.emit('close')
}
return s
}
test('CAP LS 302 then NICK USER on attach', async (t) => {
const h = await load()
const sock = fakeSocket()
const c = h.bareIrcCreateClient({ nick: 'tester' })
c.attach(sock)
const blob = sock.writes.join('')
t.ok(/CAP LS :?302/.test(blob))
t.ok(/NICK :?tester/.test(blob))
t.ok(blob.includes('USER '))
})
test('PING answers PONG; 001 assigns nick and CAP REQ', async (t) => {
const h = await load()
const sock = fakeSocket()
const c = h.bareIrcCreateClient({ nick: 'alice' })
let registered = ''
c.on('registered', (p) => {
registered = p.nick
})
c.attach(sock)
sock.emit('data', 'PING :irc.example.com\r\n')
t.ok(/PONG\s+:?irc\.example\.com/.test(sock.writes.join('')))
sock.emit(
'data',
':irc.example.com CAP * LS :sasl multi-prefix echo-message\r\n'
)
t.ok(sock.writes.join('').includes('CAP REQ'))
sock.emit(
'data',
':irc.example.com CAP * ACK :sasl multi-prefix echo-message\r\n'
)
sock.emit('data', ':irc.example.com CAP * ACK :sasl\r\n')
sock.emit('data', ':irc.example.com 001 alice :Welcome to the network\r\n')
t.is(registered, 'alice')
t.is(c.state.nick, 'alice')
})
test('PRIVMSG lands in channel buffer', async (t) => {
const h = await load()
const sock = fakeSocket()
const c = h.bareIrcCreateClient({ nick: 'alice' })
c.attach(sock)
sock.emit('data', ':bob!b@h PRIVMSG #chan :hello there\r\n')
const buf = Object.values(c.state.buffers).find((b) => b.kind === 'channel')
t.ok(buf)
t.is(buf.name, '#chan')
t.ok(buf.lines.some((l) => l.text === 'hello there' && l.from === 'bob'))
})
test('SASL PLAIN payload is null-separated base64', async (t) => {
const h = await load()
const chunks = h.bareIrcSaslPlainChunks('user', 'pass')
t.ok(chunks.length >= 1)
const raw = Buffer.from(chunks.join(''), 'base64').toString('utf8')
t.is(raw, '\0user\0pass')
})
test('ISUPPORT tokens parse', async (t) => {
const h = await load()
const tok = h.bareIrcParseIsupport([
'alice',
'CHANTYPES=#&',
'NICKLEN=16',
'are supported by this server'
])
t.is(tok.CHANTYPES, '#&')
t.is(tok.NICKLEN, '16')
})