Files
stack/packages/webui/tests/service.test.mjs
T
jason.woltjeandClaude Opus 5.5 1a8ba58737 webui: the unit script takes --send (#1576)
Row 83. scripts/webui-service.sh render and install take --send, only
with the full preview set; otherwise the script refuses before writing
anything. With --send the rendered ExecStart ends in --send; renders
without it are byte-identical to the base. status says whether the
installed unit sends. The script header, install message, README and
TOOLS.md carry the decision 102 start wording: Jason or Mos starts and
restarts the Console, no other agent.

Dewey's round 1, candidate manifest f764cd9e, base d13b9300. Approved
by Darkwing (27721) and Filbert (27722). Sage's gate green 04:07:30Z to
04:10:47Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:10:55 -05:00

366 lines
20 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { spawn } from 'node:child_process';
import { createServer } from 'node:http';
import { once } from 'node:events';
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
// scripts/webui-service.sh (row 75, #1569). Nothing here starts a unit or
// reaches the real systemd: install and uninstall run with --no-reload or a
// stand-in systemctl on PATH, and XDG_CONFIG_HOME points into a temp dir.
// Row 83 (#1576) adds --send, taken only with the preview set.
const REPO = fileURLToPath(new URL('../../..', import.meta.url)).replace(/\/$/, '');
const SCRIPT = join(REPO, 'scripts', 'webui-service.sh');
const NODE_DIR = dirname(process.execPath);
const PREVIEW = ['--public-host', 'console.example.com', '--auth-header', 'X-authentik-uid', '--allow-user', 'jason'];
const PREVIEW_FLAGS = '--public-host console.example.com --auth-header X-authentik-uid --allow-user jason';
const SEND_REFUSAL = /^webui-service: --send needs the preview mode \(--public-host, --auth-header and --allow-user\)$/m;
const TEMPLATE = readFileSync(join(REPO, 'packages', 'webui', 'systemd', 'mosaic-webui.service.in'), 'utf8');
// The unit as the base script renders it (row 75): the template with its four
// placeholders filled in, nothing else. Row 83 adds --send and must leave
// every unit rendered without it byte for byte as this.
function filled(flags) {
const base = '/usr/local/bin:/usr/bin:/bin';
const path = `:${base}:`.includes(`:${NODE_DIR}:`) ? base : `${NODE_DIR}:${base}`;
return TEMPLATE.replaceAll('@REPO@', REPO).replaceAll('@PATH@', path).replaceAll('@NODE@', process.execPath).replaceAll('@FLAGS@', flags);
}
// A loopback port with nothing listening, so status's /healthz request
// reaches no real Console.
async function closedPort() {
const server = createServer();
server.listen(0, '127.0.0.1');
await once(server, 'listening');
const { port } = server.address();
server.close();
await once(server, 'close');
return port;
}
function tmp(t) {
const dir = mkdtempSync(join(tmpdir(), 'webui-service-'));
t.after(() => rmSync(dir, { recursive: true, force: true }));
return dir;
}
// A stand-in systemctl that prints `lines` for list-units and logs every call.
function standIn(dir, lines = '') {
const bin = join(dir, 'bin');
mkdirSync(bin);
writeFileSync(join(dir, 'list-units.txt'), lines);
writeFileSync(join(bin, 'systemctl'), `#!/bin/sh
echo "$*" >> "${dir}/calls.txt"
case "$*" in
*list-units*) cat "${dir}/list-units.txt" ;;
*is-enabled*) echo enabled ;;
*is-active*) echo active ;;
esac
exit 0
`);
chmodSync(join(bin, 'systemctl'), 0o755);
return bin;
}
// Async, so an in-process server can answer the script's /healthz request.
async function run(dir, args, bin = null) {
const env = { PATH: `${bin ? `${bin}:` : ''}${NODE_DIR}:/usr/bin:/bin`, HOME: dir, XDG_CONFIG_HOME: join(dir, 'config') };
const child = spawn(SCRIPT, args, { env });
let stdout = '', stderr = '';
child.stdout.on('data', (d) => { stdout += d; });
child.stderr.on('data', (d) => { stderr += d; });
const [status] = await once(child, 'close');
return { status, stdout, stderr };
}
const execStart = (unit) => unit.split('\n').find((l) => l.startsWith('ExecStart='));
test('render prints the unit without the preview flags', async (t) => {
const dir = tmp(t);
const r = await run(dir, ['render']);
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, new RegExp(`^WorkingDirectory=${REPO.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'm'));
assert.match(r.stdout, /^Environment=PATH=\S*\/usr\/local\/bin:\/usr\/bin:\/bin$/m);
assert.equal(execStart(r.stdout), `ExecStart=${process.execPath} ${REPO}/packages/webui/src/cli.mjs serve --port 7332`);
for (const line of ['Restart=on-failure', 'RestartPreventExitStatus=2', 'NoNewPrivileges=yes', 'WantedBy=default.target']) {
assert.ok(r.stdout.split('\n').includes(line), line);
}
assert.doesNotMatch(r.stdout, /@[A-Z]+@/, 'every placeholder is filled');
});
test('render carries every serve flag, the preview set included', async (t) => {
const dir = tmp(t);
const r = await run(dir, ['render', '--port', '7340', '--business', 'mosaic.dev_1', '--board', 'http://127.0.0.1:7331', ...PREVIEW]);
assert.equal(r.status, 0, r.stderr);
assert.equal(execStart(r.stdout), `ExecStart=${process.execPath} ${REPO}/packages/webui/src/cli.mjs serve --port 7340 --business mosaic.dev_1 --board http://127.0.0.1:7331 --public-host console.example.com --auth-header X-authentik-uid --allow-user jason`);
});
test('a partial preview set is refused', async (t) => {
const dir = tmp(t);
for (const args of [PREVIEW.slice(0, 2), PREVIEW.slice(2, 6), [...PREVIEW.slice(0, 2), ...PREVIEW.slice(4, 6)]]) {
const r = await run(dir, ['render', ...args]);
assert.equal(r.status, 1, args.join(' '));
assert.equal(r.stdout, '');
assert.match(r.stderr, /all three or none/);
}
});
test('a value the unit cannot carry is refused, for every flag', async (t) => {
const dir = tmp(t);
const set = (flag, value) => {
const preview = PREVIEW.slice();
const i = preview.indexOf(flag);
if (i >= 0) { preview[i + 1] = value; return preview; }
return [flag, value];
};
for (const flag of ['--business', '--board', '--public-host', '--auth-header', '--allow-user']) {
for (const bad of [' x', '%n', '\n', '$HOME', '"', '\\', '|', '&']) {
const value = flag === '--board' ? `http://127.0.0.1:7331${bad}` : `a${bad}b`;
const r = await run(dir, ['render', ...set(flag, value)]);
assert.equal(r.status, 1, `${flag} ${JSON.stringify(value)}`);
assert.equal(r.stdout, '', `${flag} ${JSON.stringify(value)}`);
}
}
for (const port of ['0', '65536', '7a', '', '-1', '999999']) {
const r = await run(dir, ['render', '--port', port]);
assert.equal(r.status, 1, `port ${JSON.stringify(port)}`);
}
for (const args of [['--business', 'Mosaic'], ['--business', '-x'], ['--business', 'a'.repeat(65)], ['--board', 'https://127.0.0.1:7331'], ['--board', '127.0.0.1:7331'], set('--public-host', 'console.example.com:443'), set('--public-host', 'https://console.example.com'), set('--auth-header', 'X_uid')]) {
const r = await run(dir, ['render', ...args]);
assert.equal(r.status, 1, args.join(' '));
}
});
test('the rendered unit names none of the human proof agent markers and sets no agent variable', async (t) => {
const dir = tmp(t);
const source = readFileSync(join(REPO, 'packages', 'bus', 'src', 'human.mjs'), 'utf8');
const markers = [...source.match(/const MARKERS = \[([^\]]*)\]/)[1].matchAll(/'([A-Z_]+)'/g)].map((m) => m[1]);
assert.ok(markers.length >= 7 && markers.includes('MOSAIC_BUS_CAP') && markers.includes('CLAUDECODE'), markers.join(' '));
for (const args of [[], [...PREVIEW, '--business', 'mosaic'], [...PREVIEW, '--send']]) {
const r = await run(dir, ['render', ...args]);
assert.equal(r.status, 0, r.stderr);
for (const marker of markers) assert.ok(!r.stdout.includes(marker), marker);
const envLines = r.stdout.split('\n').filter((l) => /^\s*(Environment|EnvironmentFile|PassEnvironment|UnsetEnvironment)\s*=/.test(l));
assert.deepEqual(envLines.map((l) => l.split('=')[1]), ['PATH']);
assert.doesNotMatch(r.stdout, /^[^#]*\b(MOSAIC_|CLAUDE|CODEX|PI_)/m);
}
});
test('install into --dir with --no-reload writes the file whole, then reports unchanged', async (t) => {
const dir = tmp(t);
const unitDir = join(dir, 'units');
const bin = standIn(dir);
const rendered = await run(dir, ['render', '--business', 'mosaic']);
const r = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--business', 'mosaic'], bin);
assert.equal(r.status, 0, r.stderr);
const target = join(unitDir, 'mosaic-webui.service');
assert.match(r.stdout, new RegExp(`^written: ${target}$`, 'm'));
assert.match(r.stdout, /systemctl --user enable --now mosaic-webui/);
assert.equal(readFileSync(target, 'utf8'), rendered.stdout);
assert.equal(statSync(target).mode & 0o777, 0o644);
assert.deepEqual(readdirSync(unitDir), ['mosaic-webui.service'], 'no temp file left behind');
const again = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--business', 'mosaic'], bin);
assert.equal(again.status, 0, again.stderr);
assert.match(again.stdout, new RegExp(`^unchanged: ${target}$`, 'm'));
assert.ok(!existsSync(join(dir, 'calls.txt')), 'no systemctl call with --no-reload');
// A refused value writes nothing and leaves the installed unit alone.
const bad = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--business', 'a b'], bin);
assert.equal(bad.status, 1);
assert.equal(readFileSync(target, 'utf8'), rendered.stdout);
assert.deepEqual(readdirSync(unitDir), ['mosaic-webui.service']);
const fresh = join(dir, 'never');
assert.equal((await run(dir, ['install', '--dir', fresh, '--no-reload', '--port', '0'], bin)).status, 1);
assert.ok(!existsSync(fresh), 'a refusal creates no directory');
});
test('install without --no-reload runs daemon-reload and nothing that starts the unit', async (t) => {
const dir = tmp(t);
const bin = standIn(dir);
const r = await run(dir, ['install', '--dir', join(dir, 'units')], bin);
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, /^daemon-reload done$/m);
assert.equal(readFileSync(join(dir, 'calls.txt'), 'utf8'), '--user daemon-reload\n');
});
test('uninstall refuses while the unit is active, and removes it once it is not', async (t) => {
const dir = tmp(t);
const unitDir = join(dir, 'units');
const target = join(unitDir, 'mosaic-webui.service');
for (const state of ['active', 'activating', 'deactivating']) {
const bin = standIn(mkdtempSync(join(dir, 'si-')), `mosaic-webui.service loaded ${state} running Mosaic Console\n`);
assert.equal((await run(dir, ['install', '--dir', unitDir, '--no-reload'])).status, 0);
const r = await run(dir, ['uninstall', '--dir', unitDir], bin);
assert.equal(r.status, 1, state);
assert.match(r.stderr, /refusing: mosaic-webui\.service is still running/);
assert.ok(existsSync(target), `still installed while ${state}`);
}
const bin = standIn(mkdtempSync(join(dir, 'si-')), 'mosaic-webui.service loaded inactive dead Mosaic Console\n');
const r = await run(dir, ['uninstall', '--dir', unitDir], bin);
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, new RegExp(`^removed: ${target}$`, 'm'));
assert.match(r.stdout, /^daemon-reload done$/m);
assert.ok(!existsSync(target));
const absent = await run(dir, ['uninstall', '--dir', unitDir, '--no-reload'], bin);
assert.equal(absent.status, 0);
assert.match(absent.stdout, /^absent: /m);
});
test('status prints the unit state and GET /healthz on the installed port', async (t) => {
const dir = tmp(t);
const unitDir = join(dir, 'units');
const bin = standIn(dir);
const server = createServer((req, res) => {
if (req.url === '/healthz') { res.writeHead(200, { 'content-type': 'application/json' }); res.end('{"ok":true}\n'); return; }
res.writeHead(404); res.end();
});
server.listen(0, '127.0.0.1');
await once(server, 'listening');
t.after(() => server.close());
const { port } = server.address();
assert.equal((await run(dir, ['install', '--dir', unitDir, '--no-reload', '--port', String(port)])).status, 0);
const r = await run(dir, ['status', '--dir', unitDir], bin);
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, /^ {2}enabled: enabled$/m);
assert.match(r.stdout, /^ {2}active: {2}active$/m);
assert.match(r.stdout, new RegExp(`^healthz: http://127\\.0\\.0\\.1:${port}/healthz 200 \\{"ok":true\\}$`, 'm'));
server.close();
await once(server, 'close');
const down = await run(dir, ['status', '--dir', unitDir], bin);
assert.equal(down.status, 0, down.stderr);
assert.match(down.stdout, /^healthz: \S+ unreachable \(ECONNREFUSED\)$/m);
const none = await run(dir, ['status', '--dir', join(dir, 'empty')], bin);
assert.match(none.stdout, /^healthz: not checked; no unit at /m);
});
test('usage: an unknown verb or flag, and start or enable, exit 4', async (t) => {
const dir = tmp(t);
for (const args of [[], ['start'], ['stop'], ['enable'], ['restart'], ['render', '--bogus'], ['render', '--port'], ['render', '--dir', dir], ['render', '--no-reload'], ['uninstall', '--port', '7332'], ['status', '--business', 'mosaic'], ['status', '--no-reload'], ['render', '--send', 'yes', ...PREVIEW]]) {
const r = await run(dir, args);
assert.equal(r.status, 4, args.join(' '));
assert.match(r.stderr, /^usage: scripts\/webui-service\.sh/);
}
});
test('without --send, render is the template with its placeholders filled, byte for byte', async (t) => {
const dir = tmp(t);
for (const [args, flags] of [
[[], '--port 7332'],
[['--port', '7340'], '--port 7340'],
[['--business', 'mosaic'], '--port 7332 --business mosaic'],
[PREVIEW, `--port 7332 ${PREVIEW_FLAGS}`],
[['--port', '7340', '--business', 'mosaic.dev_1', '--board', 'http://127.0.0.1:7331', ...PREVIEW], `--port 7340 --business mosaic.dev_1 --board http://127.0.0.1:7331 ${PREVIEW_FLAGS}`],
]) {
const r = await run(dir, ['render', ...args]);
assert.equal(r.status, 0, r.stderr);
assert.equal(r.stdout, filled(flags), args.join(' '));
}
});
test('render --send with the preview set ends ExecStart with --send and changes nothing else', async (t) => {
const dir = tmp(t);
const extra = ['--port', '7340', '--business', 'mosaic'];
for (const args of [[...PREVIEW, '--send'], ['--send', ...PREVIEW], [...PREVIEW.slice(0, 2), '--send', ...PREVIEW.slice(2)], ['--send', ...extra, ...PREVIEW]]) {
const plain = args.filter((a) => a !== '--send');
const flags = `${plain.includes('--port') ? '--port 7340 --business mosaic' : '--port 7332'} ${PREVIEW_FLAGS}`;
const r = await run(dir, ['render', ...args]);
assert.equal(r.status, 0, r.stderr);
assert.equal(r.stdout, filled(`${flags} --send`), args.join(' '));
assert.ok(execStart(r.stdout).endsWith(' --allow-user jason --send'), execStart(r.stdout));
const without = await run(dir, ['render', ...plain]);
assert.equal(without.status, 0, without.stderr);
const a = r.stdout.split('\n'), b = without.stdout.split('\n');
assert.equal(a.length, b.length);
assert.deepEqual(a.map((l, i) => (l === b[i] ? null : [l, b[i]])).filter(Boolean), [[`${execStart(without.stdout)} --send`, execStart(without.stdout)]]);
}
});
test('--send without the whole preview set is refused and writes nothing', async (t) => {
const dir = tmp(t);
const pairs = [PREVIEW.slice(0, 2), PREVIEW.slice(2, 4), PREVIEW.slice(4, 6)];
const partial = [[], ['--port', '7340'], ['--business', 'mosaic'], ...pairs, [...pairs[0], ...pairs[1]], [...pairs[0], ...pairs[2]], [...pairs[1], ...pairs[2]]];
for (const args of partial) {
const r = await run(dir, ['render', '--send', ...args]);
assert.equal(r.status, 1, args.join(' '));
assert.equal(r.stdout, '', args.join(' '));
assert.match(r.stderr, SEND_REFUSAL, args.join(' '));
}
const bin = standIn(dir);
const fresh = join(dir, 'never');
for (const args of partial) {
const r = await run(dir, ['install', '--dir', fresh, '--no-reload', ...args, '--send'], bin);
assert.equal(r.status, 1, args.join(' '));
assert.match(r.stderr, SEND_REFUSAL, args.join(' '));
}
assert.ok(!existsSync(fresh), 'a refusal creates no directory');
const unitDir = join(dir, 'units');
const target = join(unitDir, 'mosaic-webui.service');
assert.equal((await run(dir, ['install', '--dir', unitDir, '--no-reload', ...PREVIEW], bin)).status, 0);
const before = readFileSync(target, 'utf8');
const r = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--send', ...PREVIEW.slice(0, 4)], bin);
assert.equal(r.status, 1);
assert.equal(readFileSync(target, 'utf8'), before, 'the installed unit is left alone');
assert.deepEqual(readdirSync(unitDir), ['mosaic-webui.service']);
assert.ok(!existsSync(join(dir, 'calls.txt')), 'no systemctl call');
});
test('install --send into --dir with --no-reload writes the sending unit, and status says whether it sends', async (t) => {
const dir = tmp(t);
const unitDir = join(dir, 'units');
const target = join(unitDir, 'mosaic-webui.service');
const port = await closedPort();
const flags = `--port ${port} ${PREVIEW_FLAGS}`;
const r = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--port', String(port), ...PREVIEW, '--send']);
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, new RegExp(`^written: ${target}$`, 'm'));
// Lead decision 102: Jason or Mos starts it, no other agent.
assert.match(r.stdout, /^this script starts nothing: .* Jason\nstarts and restarts it, or Mos under lead decision 102 .*\ntrail as Jason\); no other agent does:$/m);
assert.match(r.stdout, new RegExp(`^serve: \\S+ ${REPO.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/packages/webui/src/cli\\.mjs serve ${flags} --send$`, 'm'));
assert.equal(readFileSync(target, 'utf8'), filled(`${flags} --send`));
assert.equal(statSync(target).mode & 0o777, 0o644);
assert.deepEqual(readdirSync(unitDir), ['mosaic-webui.service']);
const bin = standIn(dir);
const sending = await run(dir, ['status', '--dir', unitDir], bin);
assert.equal(sending.status, 0, sending.stderr);
assert.match(sending.stdout, /^send: on \(--send; human messages to roles\)$/m);
assert.match(sending.stdout, /^healthz: \S+ unreachable \(ECONNREFUSED\)$/m);
const again = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--port', String(port), '--send', ...PREVIEW], bin);
assert.match(again.stdout, new RegExp(`^unchanged: ${target}$`, 'm'));
const readOnly = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--port', String(port), ...PREVIEW], bin);
assert.match(readOnly.stdout, new RegExp(`^written: ${target}$`, 'm'));
assert.equal(readFileSync(target, 'utf8'), filled(flags));
const off = await run(dir, ['status', '--dir', unitDir], bin);
assert.match(off.stdout, /^send: off \(read-only\)$/m);
assert.doesNotMatch(off.stdout, /^send: on/m);
// The only systemctl calls are status's reads; install ran none.
assert.deepEqual(readFileSync(join(dir, 'calls.txt'), 'utf8').split('\n').filter(Boolean).filter((c) => !/^--user (is-enabled|is-active|show) mosaic-webui\.service/.test(c)), []);
const none = await run(dir, ['status', '--dir', join(dir, 'empty')], bin);
assert.doesNotMatch(none.stdout, /^send:/m, 'no unit, no send line');
// serve takes --send as an allowed user, so ExecStart can end with
// `--allow-user --send` on a unit that doesn't send.
const oddUser = [...PREVIEW.slice(0, 4), '--allow-user', '--send'];
for (const [args, ending, state] of [[oddUser, '--allow-user --send', 'off'], [[...oddUser, '--send'], '--allow-user --send --send', 'on']]) {
const r = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--port', String(port), ...args]);
assert.equal(r.status, 0, r.stderr);
assert.ok(execStart(readFileSync(target, 'utf8')).endsWith(` ${ending}`), args.join(' '));
assert.match((await run(dir, ['status', '--dir', unitDir], bin)).stdout, new RegExp(`^send: ${state} `, 'm'), args.join(' '));
}
});
test('uninstall and status take no --send', async (t) => {
const dir = tmp(t);
const bin = standIn(dir);
const unitDir = join(dir, 'units');
assert.equal((await run(dir, ['install', '--dir', unitDir, '--no-reload', ...PREVIEW, '--send'])).status, 0);
for (const args of [['uninstall', '--dir', unitDir, '--send'], ['uninstall', '--dir', unitDir, '--no-reload', '--send'], ['status', '--dir', unitDir, '--send']]) {
const r = await run(dir, args, bin);
assert.equal(r.status, 4, args.join(' '));
assert.match(r.stderr, /^usage: scripts\/webui-service\.sh/);
assert.equal(r.stdout, '', args.join(' '));
}
assert.ok(existsSync(join(unitDir, 'mosaic-webui.service')), 'still installed');
assert.ok(!existsSync(join(dir, 'calls.txt')), 'no systemctl call');
});