import { readFile } from 'node:fs/promises' import path from 'node:path' import { fileURLToPath } from 'node:url' import test from 'brittle' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor async function loadBin() { const runtime = await readFile(path.join(__dirname, '../lib/runtime.js'), 'utf8') const body = await readFile(path.join(__dirname, '../src/init-discord.js'), 'utf8') return new AsyncFunction( 'ctx', 'argv', `${runtime}\n${body}\nif (typeof run === 'function') return await run(ctx, argv)\n` ) } function makeCtx(tree) { const env = { USER: 'alice', HOME: '/home/alice' } const logs = [] const errs = [] return { logs, errs, env, exitCode: 0, console: { log: (m) => logs.push(String(m)), error: (m) => errs.push(String(m)) }, b4a: { toString: (b) => Buffer.from(b).toString('utf8'), from: (s) => Buffer.from(String(s)) }, vfs: { env, mkdir: async (p) => { if (!tree[p]) tree[p] = [] }, readFile: async (p) => { if (typeof tree[p] !== 'string') { const e = new Error('ENOENT') e.code = 'ENOENT' throw e } return Buffer.from(tree[p]) }, writeFile: async (p, buf) => { tree[p] = Buffer.from(buf).toString('utf8') } } } } test('init-discord --help', async (t) => { const run = await loadBin() const tree = {} const ctx = makeCtx(tree) await run(ctx, ['init-discord', '--help']) t.is(ctx.exitCode, 0) t.ok(ctx.logs.some((l) => /--token/.test(l))) }) test('init-discord --yes writes env and hello plugin without echoing the token', async (t) => { const run = await loadBin() const tree = {} const ctx = makeCtx(tree) await run(ctx, [ 'init-discord', '--yes', '--token', 'abc.def.secret-token', '--guild', '111', '--whitelist', '222,333' ]) t.is(ctx.exitCode, 0) t.ok(/DISCORD_TOKEN=abc\.def\.secret-token/.test(tree['~/.discord/.env'])) t.ok(/DISCORD_GUILD_ID=111/.test(tree['~/.discord/.env'])) t.ok(/DISCORD_ID_WHITELIST=222,333/.test(tree['~/.discord/.env'])) t.ok(/"name": "hello"/.test(tree['~/.discord/plugins/hello.json'])) t.ok(ctx.logs.some((l) => /hello\.json/.test(l))) t.absent(ctx.logs.some((l) => /secret-token/.test(l)), 'never prints full token') t.is(ctx.env.DISCORD_GUILD_ID, '111') }) test('init-discord --yes refuses to clobber a token without --force', async (t) => { const run = await loadBin() const tree = { '~/.discord/.env': 'DISCORD_TOKEN=old.token.value\n' } const ctx = makeCtx(tree) await run(ctx, ['init-discord', '--yes', '--token', 'new.token.value']) t.is(ctx.exitCode, 1) t.ok(/old\.token\.value/.test(tree['~/.discord/.env'])) await run(ctx, ['init-discord', '--yes', '--force', '--token', 'new.token.value']) t.is(ctx.exitCode, 0) t.ok(/new\.token\.value/.test(tree['~/.discord/.env'])) }) test('init-discord --plugin-only installs hello without requiring a token', async (t) => { const run = await loadBin() const tree = {} const ctx = makeCtx(tree) await run(ctx, ['init-discord', '--plugin-only', '--yes']) t.is(ctx.exitCode, 0) t.ok(/hello/.test(tree['~/.discord/plugins/hello.json'])) t.absent(tree['~/.discord/.env']) })