import { readFile } from 'node:fs/promises' import path from 'node:path' import { fileURLToPath } from 'node:url' import test from 'brittle' import b4a from 'b4a' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor async function loadBin(name) { const runtime = await readFile(path.join(__dirname, '../lib/runtime.js'), 'utf8') const body = await readFile(path.join(__dirname, `../src/${name}.js`), 'utf8') return new AsyncFunction( 'ctx', 'argv', `${runtime}\n${body}\nif (typeof run === 'function') return await run(ctx, argv)\n` ) } function mkCtx(vfs = {}) { const logs = [] const errs = [] return { b4a, exitCode: 0, identity: { state: 'locked' }, shellStdin: '', console: { log: (m) => logs.push(String(m)), error: (m) => errs.push(String(m)) }, vfs: { env: {}, ...vfs }, _logs: logs, _errs: errs } } test('hdms unavailable returns nonzero', async (t) => { const run = await loadBin('hdms') const ctx = mkCtx() await run(ctx, ['hdms', 'ls']) t.is(ctx.exitCode, 1) t.ok(ctx._errs.join('\n').includes('unavailable')) }) test('git-pear clone requires url', async (t) => { const run = await loadBin('git-pear') const ctx = mkCtx() await run(ctx, ['git-pear', 'clone']) t.is(ctx.exitCode, 1) t.ok(ctx._errs.join('\n').includes('missing repository URL')) }) test('git-pear clone delegates to git clone', async (t) => { const run = await loadBin('git-pear') const ctx = mkCtx() /** @type {string[][]} */ const seen = [] ctx.runBinCommand = async (argv) => { seen.push(argv.slice()) ctx.exitCode = 0 } await run(ctx, ['git-pear', 'clone', 'git.ssh.surf/org/repo', '/tmp/repo']) t.is(ctx.exitCode, 0) t.is(seen.length, 1) t.is(seen[0][0], 'git') t.is(seen[0][1], 'clone') }) test('oidc-publish unknown subcommand is explicit', async (t) => { const run = await loadBin('oidc-publish') const ctx = mkCtx() await run(ctx, ['oidc-publish', 'unknown']) t.is(ctx.exitCode, 1) t.ok(ctx._errs.join('\n').includes('unknown subcommand')) }) test('crontab -e requires unlocked identity', async (t) => { const run = await loadBin('crontab') const ctx = mkCtx({ async readFile() { return null }, async writeFile() {}, async unlink() {} }) await run(ctx, ['crontab', '-e']) t.is(ctx.exitCode, 1) t.ok(ctx._errs.join('\n').includes('log in')) }) test('ssh command reports unavailable runtime explicitly', async (t) => { const run = await loadBin('ssh') const ctx = mkCtx() await run(ctx, ['ssh', 'localhost']) t.is(ctx.exitCode, 1) t.ok(ctx._errs.join('\n').includes('client unavailable')) }) test('kill -TERM -1 treats -1 as target', async (t) => { const run = await loadBin('kill') const ctx = mkCtx() /** @type {Array<{ target: string | number, signal: string }>} */ const sent = [] ctx.bareOsSendSignal = (target, signal) => { sent.push({ target, signal }) return { ok: true, delivered: false, exists: false, ignored: false } } await run(ctx, ['kill', '-TERM', '-1']) t.is(ctx.exitCode, 1) t.is(sent.length, 1) t.is(String(sent[0].target), '-1') }) test('crontab rejects fd/stdin style install paths explicitly', async (t) => { const run = await loadBin('crontab') const ctx = mkCtx({ async readFile() { return null }, async writeFile() {} }) ctx.identity = { state: 'unlocked' } await run(ctx, ['crontab', '/dev/fd/3']) t.is(ctx.exitCode, 1) t.ok(ctx._errs.join('\n').includes('stdin/fd-based installs')) }) test('test empty integer operand returns status 2', async (t) => { const run = await loadBin('test') const ctx = mkCtx() await run(ctx, ['test', '', '-eq', '1']) t.is(ctx.exitCode, 2) t.ok(ctx._errs.join('\n').includes('integer expression expected')) })