Files
stack/packages/cli/tests/processing-cli.test.mjs
rocko 4bdc7e0e86 processing: inspectable per-run stop seam for lifecycle fire
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.
2026-10-21 22:42:12 -05:00

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/);
});