// D2 (P0-3): the dev Console's start-up revive (bus-revive.mjs), on fakes. The real-host paths // (a SIGKILLed host, a live host, a live writer lock) are in packages/cli/tests/org-revive.test.mjs. import { test } from 'node:test'; import assert from 'node:assert/strict'; import { reviveBuses, revivePending } from '../src/bus-revive.mjs'; import { reviveStartOptions } from '../../cli/src/org-bus.mjs'; const cliError = (message, extra = {}) => Object.assign(new Error(message), { name: 'CliError', exitCode: 3 }, extra); const orgs = ids => async () => ids.map(id => ({ id, state: 'active' })); function run({ ids = ['alpha'], status, revive, trail, env = {} }) { const calls = [], lines = [], trailed = []; const done = reviveBuses({ ctx: {}, listOrgs: orgs(ids), busStatus: async (ctx, id) => status(id), busRevive: async (ctx, id, opts) => { calls.push([id, opts]); return revive(id); }, trail: trail ?? (e => trailed.push(e)), log: line => lines.push(line), env, }); return { done, calls, lines, trailed }; } test('the start-up revive asks busRevive for pm:false, so no chief launches without a press', async () => { const r = run({ status: () => { throw cliError('stale bus host lock'); }, revive: () => ({ pid: 77 }) }); assert.deepEqual(await r.done, [{ id: 'alpha', outcome: 'revived', pid: 77 }]); assert.deepEqual(r.calls, [['alpha', { pm: false }]]); }); test('reviveStartOptions keeps the recorded options, never wider, and pm only when both allow it', () => { assert.deepEqual(reviveStartOptions({ devLocalOwner: true, pm: true, noLaunch: false }, { pm: false }), { detach: true, devLocalOwner: true, pm: false, noLaunch: false }); assert.deepEqual(reviveStartOptions({ devLocalOwner: true, pm: true, noLaunch: false }), { detach: true, devLocalOwner: true, pm: true, noLaunch: false }); assert.deepEqual(reviveStartOptions({ pm: false, noLaunch: true }, { pm: true }), { detach: true, devLocalOwner: false, pm: false, noLaunch: true }); // Anything but a literal true is false: a malformed record widens nothing. assert.deepEqual(reviveStartOptions({ devLocalOwner: 'yes', pm: 1 }), { detach: true, devLocalOwner: false, pm: false, noLaunch: false }); assert.deepEqual(reviveStartOptions(null), { detach: true, devLocalOwner: false, pm: false, noLaunch: false }); }); test('a dropped --pm is logged with the way back', async () => { const r = run({ status: () => { throw cliError('stale bus host lock'); }, revive: () => Object.defineProperty({ pid: 9 }, 'pmDropped', { value: true }) }); await r.done; assert.ok(r.lines.some(l => l === 'bus revive: alpha: the dead host was started with --pm; the chief was not relaunched. mosaic bus stop alpha; mosaic bus start alpha --pm brings it back'), r.lines.join('\n')); }); test('running, not-answering, stopped and unreadable buses are left alone and never trailed', async () => { const states = { a: { healthy: true, pid: 1 }, b: { healthy: false, pid: 2 }, c: { healthy: false, pid: null } }; const r = run({ ids: ['a', 'b', 'c', 'd'], status: id => { if (id === 'd') throw new Error('EACCES /secret/path'); return states[id]; }, revive: () => assert.fail('revived') }); assert.deepEqual(await r.done, [{ id: 'a', outcome: 'running' }, { id: 'b', outcome: 'not-answering' }, { id: 'c', outcome: 'stopped' }, { id: 'd', outcome: 'uncertain' }]); assert.deepEqual(r.trailed, []); assert.ok(!r.lines.join('\n').includes('/secret/path')); }); test('a trail that cannot be written skips the revive', async () => { const r = run({ status: () => { throw cliError('stale bus host lock'); }, revive: () => assert.fail('revived without a trail line'), trail: () => { throw new Error('disk full'); } }); assert.deepEqual(await r.done, [{ id: 'alpha', outcome: 'trail-unavailable' }]); assert.deepEqual(r.calls, []); }); test('a start that fails after the dead state was cleared is "failed", not "refused"', async () => { const r = run({ status: () => { throw cliError('stale bus host lock'); }, revive: () => { throw cliError('bus failed to start; see bus-host/host.log in the runtime directory of organization alpha', { recovered: { pid: 4 } }); } }); assert.deepEqual(await r.done, [{ id: 'alpha', outcome: 'failed' }]); assert.deepEqual(r.trailed.map(e => [e.outcome, e.recovered]), [['requested', undefined], ['failed', true]]); assert.match(r.lines.at(-1), /^bus revive: alpha: dead bus host cleared, but the bus did not start \(bus failed to start;/); // A non-CliError shows its code, never its text. const q = run({ status: () => { throw cliError('stale bus host lock'); }, revive: () => { throw Object.assign(new Error('EIO /home/someone/x'), { code: 'org-locked' }); } }); assert.deepEqual(await q.done, [{ id: 'alpha', outcome: 'refused' }]); assert.equal(q.lines.at(-1), 'bus revive: alpha: refused (unexpected error org-locked); left for mosaic bus status alpha'); }); test('revivePending holds each organization until its own revive ends', async () => { let release; const gate = new Promise(resolve => { release = resolve; }); const r = run({ ids: ['alpha', 'beta'], status: id => { if (id === 'alpha') throw cliError('stale bus host lock'); return { healthy: true, pid: 3 }; }, revive: async () => { await gate; return { pid: 5 }; } }); await new Promise(resolve => setImmediate(resolve)); const alpha = revivePending('alpha'), beta = revivePending('beta'); assert.ok(alpha instanceof Promise); assert.ok(beta instanceof Promise); assert.equal(revivePending('gamma'), null); let settled = false; alpha.then(() => { settled = true; }); await new Promise(resolve => setImmediate(resolve)); assert.equal(settled, false); release(); await r.done; assert.equal(settled, true); assert.equal(revivePending('alpha'), null); assert.equal(revivePending('beta'), null); }); test('a Console that runs inside an agent run revives nothing and reads no organization', async () => { for (const marker of ['MOSAIC_AGENT_RUN', 'CLAUDECODE', 'CODEX_THREAD_ID']) { let listed = false; const lines = []; const results = await reviveBuses({ ctx: {}, listOrgs: async () => { listed = true; return []; }, busStatus: () => assert.fail('read'), busRevive: () => assert.fail('revived'), trail: () => assert.fail('trailed'), log: line => lines.push(line), env: { [marker]: '1' } }); assert.deepEqual(results, []); assert.equal(listed, false); assert.deepEqual(lines, ['bus revive: this Console runs inside an agent run; no bus was checked']); } });