Files
stack/packages/discord/tests/processing-stop.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

55 lines
3.6 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync, writeFileSync, symlinkSync, unlinkSync } from 'node:fs';
import { join } from 'node:path';
import { randomUUID } from 'node:crypto';
import { makeRoot } from './helpers.mjs';
import { ensureJournal, requestStop, stopRequested, stopPath } from '../src/journal.mjs';
const api = await import('../src/journal.mjs');
function fixture() { const dir = join(makeRoot(), 'journal'); ensureJournal(dir); return dir; }
test('resume clears only its own generation-tagged Discord STOP and keeps manual or newer brakes', () => {
assert.equal(typeof api.requestProcessingStop, 'function');
const dir = fixture(), generation = randomUUID();
api.requestProcessingStop(dir, generation); const first = readFileSync(stopPath(dir));
api.requestProcessingStop(dir, generation); assert.deepEqual(readFileSync(stopPath(dir)), first);
assert.equal(api.clearProcessingStop(dir, randomUUID()), false);
assert.equal(api.clearProcessingStop(dir, generation), true); assert.equal(stopRequested(dir), false);
api.requestProcessingStop(dir, generation); requestStop(dir, 'cli stop');
const mixed = readFileSync(stopPath(dir));
assert.equal(api.clearProcessingStop(dir, generation), false); assert.deepEqual(readFileSync(stopPath(dir)), mixed);
});
test('unreadable, malformed and symlink STOP never get removed or followed by processing recovery', () => {
assert.equal(typeof api.requestProcessingStop, 'function');
const dir = fixture(), generation = randomUUID();
writeFileSync(stopPath(dir), 'unreadable operator brake\n', { mode: 0o600 });
assert.equal(api.clearProcessingStop(dir, generation), false);
unlinkSync(stopPath(dir)); const outside = join(dir, 'outside');
writeFileSync(outside, 'untouched', { mode: 0o600 }); symlinkSync(outside, stopPath(dir));
assert.equal(api.clearProcessingStop(dir, generation), false);
assert.throws(() => api.requestProcessingStop(dir, generation));
assert.equal(readFileSync(outside, 'utf8'), 'untouched');
});
test('coordinator halt writes the Discord brake and resume clears only its own stopped runtime', async () => {
const { initializeControl } = await import('../../processing-control/src/state.mjs');
const { registerRuntime, updateRuntime } = await import('../../processing-control/src/runtimes.mjs');
const { createProcessing } = await import('../../processing-control/src/coordinator.mjs');
const { discordProcessingHooks } = await import('../src/processing.mjs');
const root = makeRoot(); initializeControl(root);
const dir = join(root, 'discord', 'test-seat'); ensureJournal(dir);
const processing = createProcessing({ dataRoot: root, ...discordProcessingHooks(root) });
const first = registerRuntime(root, { kind: 'discord', business: 'instance', run: 'test-seat' });
updateRuntime(root, first.id, { phase: 'ended' });
const halted = await processing.halt({ business: 'demo', human: 'jason' });
assert.equal(halted.quiescent, true); assert.equal(stopRequested(dir), true);
await processing.resume({ business: 'demo', human: 'jason', generation: halted.generation });
assert.equal(stopRequested(dir), false);
const second = registerRuntime(root, { kind: 'discord', business: 'instance', run: 'test-seat' });
updateRuntime(root, second.id, { phase: 'ended' });
const again = await processing.halt({ business: 'demo', human: 'jason' });
requestStop(dir, 'cli stop'); const bytes = readFileSync(stopPath(dir));
await processing.resume({ business: 'demo', human: 'jason', generation: again.generation });
assert.deepEqual(readFileSync(stopPath(dir)), bytes);
await processing.close();
});