Files
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

37 lines
2.7 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { initializeControl, transition } from '../src/state.mjs';
import { sandboxProblem } from '../../seat/src/sandbox.mjs';
const skip = sandboxProblem() ?? false;
test('the actual nested read-only control mount refuses unlink, replacement and parent removal', { skip }, t => {
const root = mkdtempSync(join(tmpdir(), 'processing-mount-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
initializeControl(root); transition(root, { business: 'demo', human: 'jason', halted: true });
const file = join(root, 'control', 'processing.json'), bytes = readFileSync(file), inode = statSync(file).ino;
writeFileSync(join(root, 'replacement'), 'off replacement');
const r = spawnSync('bwrap', ['--unshare-user', '--unshare-pid', '--die-with-parent',
'--ro-bind', '/usr', '/usr', '--symlink', 'usr/bin', '/bin', '--symlink', 'usr/lib', '/lib',
'--symlink', 'usr/lib64', '/lib64', '--proc', '/proc', '--dev', '/dev',
'--bind', root, '/fixture', '--ro-bind', join(root, 'control'), '/fixture/control',
'/bin/sh', '-c', 'rm /fixture/control/processing.json && exit 10; echo unlink-refused; mv /fixture/replacement /fixture/control/processing.json && exit 11; echo replace-refused; rm -rf /fixture/control && exit 12; echo parent-refused'],
{ encoding: 'utf8', timeout: 5000 });
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, /unlink-refused/); assert.match(r.stdout, /replace-refused/); assert.match(r.stdout, /parent-refused/);
assert.deepEqual(readFileSync(file), bytes); assert.equal(statSync(file).ino, inode);
});
test('the image entrypoint refuses halted and stale generations using the shared reader', t => {
const root = mkdtempSync(join(tmpdir(), 'processing-entry-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
const initial = initializeControl(root), entry = new URL('../src/entrypoint.mjs', import.meta.url).pathname;
const run = generation => spawnSync(process.execPath, [entry, root], { env: { ...process.env, MOSAIC_PROCESSING_GENERATION: generation }, encoding: 'utf8' });
assert.equal(run(initial.generation).status, 0);
const halt = transition(root, { business: 'demo', human: 'jason', halted: true });
assert.equal(run(initial.generation).status, 3);
transition(root, { business: 'demo', human: 'jason', halted: false, generation: halt.generation });
const stale = run(initial.generation); assert.equal(stale.status, 3); assert.match(stale.stderr, /processing-generation-changed/);
});