108 lines
3.2 KiB
JavaScript
108 lines
3.2 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-parse.js'), 'utf8'),
|
|
readFile(join(__dirname, '../lib/irc-isupport.js'), 'utf8'),
|
|
readFile(join(__dirname, '../lib/irc-sasl.js'), 'utf8'),
|
|
readFile(join(__dirname, '../lib/irc-state.js'), 'utf8'),
|
|
readFile(join(__dirname, '../lib/irc-config.js'), 'utf8'),
|
|
readFile(join(__dirname, '../lib/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')
|
|
})
|