Unify halt and fire cancellation by verified run identity with durable first-owner receipts and shared deadlines. Integrate the instance control with the current host, library and tester-socket paths. Focused nine-file evidence is 62 passing tests; broad suites, mutants and review remain pending.
56 lines
3.1 KiB
JavaScript
56 lines
3.1 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtempSync, rmSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { tmpdir } from 'node:os';
|
|
import { Store } from '../../bus/src/store.mjs';
|
|
import { Broker } from '../../bus/src/broker.mjs';
|
|
import { createProcessing } from '../../processing-control/src/coordinator.mjs';
|
|
import { main } from '../src/cli.mjs';
|
|
import { io, BUSINESSES } from './helpers.mjs';
|
|
function setup(t) {
|
|
const dataRoot = mkdtempSync(join(tmpdir(), 'processing-cli-')), calls = [];
|
|
const processing = createProcessing({ dataRoot });
|
|
const store = new Store(dataRoot);
|
|
const b = new Broker({ store, businesses: BUSINESSES, processing });
|
|
const human = b.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true });
|
|
const deps = { system: { dataRoot }, transport: business => async (verb, args = {}) => {
|
|
assert.equal(business, 'demo'); calls.push(verb); return b.request(human, { verb, args });
|
|
} };
|
|
t.after(async () => { await processing.close(); store.close(); rmSync(dataRoot, { recursive: true, force: true }); });
|
|
return { dataRoot, deps, calls };
|
|
}
|
|
|
|
test('human halt/status/resume uses management transport without loading any channel', async t => {
|
|
const f = setup(t), x = io();
|
|
await main(['halt', '--business', 'demo'], x, f.deps);
|
|
assert.match(x.out.text, /processing halted/);
|
|
assert.ok(x.out.text.includes(f.dataRoot), 'the affected instance root is named');
|
|
const status = io();
|
|
await main(['processing', 'status', '--business', 'demo', '--json'], status, f.deps);
|
|
assert.equal(JSON.parse(status.out.text).halted, true);
|
|
const resumed = io(); await main(['resume', '--business', 'demo'], resumed, f.deps);
|
|
assert.match(resumed.out.text, /processing resumed/);
|
|
assert.deepEqual(f.calls, ['processing.halt', 'processing.status', 'processing.status', 'processing.resume']);
|
|
});
|
|
|
|
for (const verb of ['halt', 'resume']) test(`${verb} refuses inside an agent before transport`, async t => {
|
|
const f = setup(t), x = io({ MOSAIC_RUN_ID: 'managed-run' });
|
|
await assert.rejects(main([verb, '--business', 'demo'], x, f.deps), e => e.exitCode === 3);
|
|
assert.deepEqual(f.calls, []);
|
|
});
|
|
|
|
test('unknown and partially stopped outcomes are never reported as a successful halt', async t => {
|
|
const f = setup(t);
|
|
const unknown = io();
|
|
await assert.rejects(main(['halt', '--business', 'demo'], unknown, {
|
|
...f.deps, transport: () => async () => { throw Object.assign(new Error('outcome-unknown'), { code: 'outcome-unknown' }); },
|
|
}), e => e.exitCode === 1 && /status/.test(e.message));
|
|
assert.doesNotMatch(unknown.out.text, /processing halted/);
|
|
const partial = io();
|
|
await assert.rejects(main(['halt', '--business', 'demo'], partial, {
|
|
...f.deps, transport: () => async () => ({ version: 1, generation: '11111111-1111-4111-8111-111111111111', operation: '11111111-1111-4111-8111-111111111111', business: 'demo', human: 'jason', at: '2026-10-20T00:00:00.000Z', recovered: false, halted: true, quiescent: false, remaining: 1, stopped: 0 }),
|
|
}), e => e.exitCode === 1 && /1.*unconfirmed/.test(e.message));
|
|
assert.doesNotMatch(partial.out.text, /all.*stopped/);
|
|
});
|