Bound the halt-abort expectation, check Discord refusal before malformed journal reads, and isolate HTTP origin and valid oversized-body guards. These test changes are committed for the row 191 gate; execution awaits the coordinated slot.
75 lines
4.6 KiB
JavaScript
75 lines
4.6 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { spawn } from 'node:child_process';
|
|
import { once } from 'node:events';
|
|
import { mkdtempSync, rmSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { initializeControl, transition } from '../src/state.mjs';
|
|
const api = await import('../src/effects.mjs').catch(e => { if (e.code === 'ERR_MODULE_NOT_FOUND') return {}; throw e; });
|
|
function fixture(t) { const root = mkdtempSync(join(tmpdir(), 'processing-effect-')); initializeControl(root); t.after(() => rmSync(root, { recursive: true, force: true })); return root; }
|
|
const actor = { business: 'demo', human: 'jason', halted: true };
|
|
test('halt aborts an active local request and prevents a subsequent effect after halt/resume', { timeout: 4000 }, async t => {
|
|
assert.equal(typeof api.withProcessing, 'function');
|
|
const root = fixture(t); let started;
|
|
const ready = new Promise(resolve => { started = resolve; });
|
|
const work = api.withProcessing(root, async scope => {
|
|
await new Promise(resolve => { scope.signal.addEventListener('abort', resolve, { once: true }); started(); });
|
|
scope.guard();
|
|
});
|
|
await ready; const halted = transition(root, actor);
|
|
transition(root, { ...actor, halted: false, generation: halted.generation });
|
|
await assert.rejects(work, e => e.code === 'processing-generation-changed');
|
|
});
|
|
test('processing scopes isolate roots and cancel only the registered child group', async t => {
|
|
assert.equal(typeof api.withProcessing, 'function');
|
|
const a = fixture(t), b = fixture(t);
|
|
let admitted;
|
|
const ready = new Promise(resolve => { admitted = resolve; });
|
|
const task = api.withProcessing(a, async scope => {
|
|
const child = spawn(process.execPath, ['-e', "process.on('SIGTERM',()=>process.exit(0));console.log('ready');setInterval(()=>{},1000)"], { detached: true, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
const exit = once(child, 'exit'); t.after(() => { try { process.kill(-child.pid, 'SIGKILL'); } catch {} });
|
|
scope.track(child); await once(child.stdout, 'data'); admitted();
|
|
const outcome = await exit; assert.deepEqual(outcome, [0, null]); scope.guard();
|
|
});
|
|
await ready; transition(b, actor);
|
|
await api.withProcessing(a, async scope => scope.guard());
|
|
transition(a, actor);
|
|
await assert.rejects(task, e => e.code === 'processing-halted');
|
|
});
|
|
test('halted scopes never invoke their body and unscoped human work has no processing context', async t => {
|
|
assert.equal(typeof api.withProcessing, 'function');
|
|
const root = fixture(t); transition(root, actor); let called = false;
|
|
await assert.rejects(api.withProcessing(root, async () => { called = true; }), e => e.code === 'processing-halted');
|
|
assert.equal(called, false); assert.equal(api.currentProcessing(), undefined);
|
|
});
|
|
|
|
test('host git is stopped promptly by the active scope rather than waiting for its long deadline', async t => {
|
|
const { mkdirSync, writeFileSync } = await import('node:fs');
|
|
const { runGit, gitEnv } = await import('../../seat/src/checkout.mjs');
|
|
const root = fixture(t), bin = join(root, 'bin'); mkdirSync(bin);
|
|
writeFileSync(join(bin, 'git'), `#!${process.execPath}\nprocess.on('SIGTERM',()=>process.exit(0));setInterval(()=>{},1000);\n`, { mode: 0o755 });
|
|
const begin = Date.now();
|
|
const task = api.withProcessing(root, async () => runGit(['status'], { ...gitEnv(), PATH: bin + ':' + process.env.PATH }, { timeoutMs: 10000, graceMs: 100 }));
|
|
await new Promise(resolve => setTimeout(resolve, 100)); transition(root, actor);
|
|
await assert.rejects(task, e => e.code === 'processing-halted');
|
|
assert.ok(Date.now() - begin < 2000, 'the halt cancels the actual git process');
|
|
});
|
|
|
|
test('a child leader exiting does not erase an unverified surviving group receipt', async t => {
|
|
const { listRuntimes } = await import('../src/runtimes.mjs');
|
|
const root = fixture(t);
|
|
let child;
|
|
await api.withProcessing(root, async scope => {
|
|
const script = `const {spawn}=require('node:child_process');spawn(process.execPath,['-e','setInterval(()=>{},1000)'],{stdio:'ignore'});console.log('ready');setTimeout(()=>process.exit(0),80);`;
|
|
child = spawn(process.execPath, ['-e', script], { detached: true, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
t.after(() => { try { process.kill(-child.pid, 'SIGKILL'); } catch {} });
|
|
const closed = once(child, 'close');
|
|
scope.track(child); await once(child.stdout, 'data'); await closed;
|
|
});
|
|
const records = listRuntimes(root);
|
|
assert.equal(records.length, 1, 'unknown group remains discoverable to the independent halt sweep');
|
|
assert.equal(records[0].pid, child.pid);
|
|
assert.equal(records[0].group, true);
|
|
});
|