fix(worker): marked-command survives tail-exec; kill script excludes itself
This commit is contained in:
@@ -22,14 +22,54 @@ describe('in-box process-group marker + kill helpers', () => {
|
||||
expect(argv[2]).toBe('-lc');
|
||||
const script = argv[3] ?? '';
|
||||
expect(script).toContain('# spoon-turn-abc');
|
||||
expect(script.split('\n').at(-1)).toBe("'codex' 'exec' '--json' 'hi'");
|
||||
expect(script).toContain("'codex' 'exec' '--json' 'hi'");
|
||||
});
|
||||
|
||||
test('buildMarkedCommand keeps a trailing exit after the CLI so bash survives tail-exec while propagating the exit code (C1 guard)', async () => {
|
||||
const { buildMarkedCommand } = await loadDocker();
|
||||
const script = buildMarkedCommand('spoon-turn-abc', ['codex', 'run'])[3] ?? '';
|
||||
// The CLI must NOT be the final simple command (bash would tail-exec it,
|
||||
// replacing the marker-carrying bash). The exit code must still propagate.
|
||||
expect(script).toContain("'codex' 'run'\nrc=$?\nexit \"$rc\"");
|
||||
expect(script.split('\n').at(-1)).toBe('exit "$rc"');
|
||||
expect(script.split('\n').at(-1)).not.toBe("'codex' 'run'");
|
||||
});
|
||||
|
||||
test('buildMarkedCommand rejects a marker that could break out of the comment', async () => {
|
||||
const { buildMarkedCommand } = await loadDocker();
|
||||
expect(() => buildMarkedCommand('bad\nmarker', ['codex'])).toThrow();
|
||||
});
|
||||
|
||||
test('buildKillScript TERM-then-KILLs every group matching the marker', async () => {
|
||||
const { buildKillScript } = await loadDocker();
|
||||
const script = buildKillScript('spoon-turn-abc');
|
||||
expect(script).toContain("pgrep -f 'spoon-turn-abc'");
|
||||
expect(script).toContain('kill -TERM -"$pid"');
|
||||
expect(script).toContain('kill -KILL -"$pid"');
|
||||
expect(script).toContain('kill -TERM -"$pgid"');
|
||||
expect(script).toContain('kill -KILL -"$pgid"');
|
||||
});
|
||||
|
||||
test('buildKillScript excludes its own process group so it does not kill itself (C2 guard)', async () => {
|
||||
const { buildKillScript } = await loadDocker();
|
||||
const script = buildKillScript('spoon-turn-abc');
|
||||
// Computes its own pgid and skips any match sharing it (the kill script's
|
||||
// own bash + its command-substitution subshells all carry the marker).
|
||||
expect(script).toContain('self_pgid=$(ps -o pgid= -p $$ | tr -d \' \')');
|
||||
expect(script).toContain('[ "$pgid" = "$self_pgid" ] && continue');
|
||||
});
|
||||
|
||||
test('buildKillScript escalates TERM -> KILL with a sleep 2 in between (order matters)', async () => {
|
||||
const { buildKillScript } = await loadDocker();
|
||||
const script = buildKillScript('spoon-turn-abc');
|
||||
const termIdx = script.indexOf('kill -TERM -"$pgid"');
|
||||
const sleepIdx = script.indexOf('\nsleep 2\n');
|
||||
const killIdx = script.indexOf('kill -KILL -"$pgid"');
|
||||
expect(termIdx).toBeGreaterThanOrEqual(0);
|
||||
expect(sleepIdx).toBeGreaterThan(termIdx);
|
||||
expect(killIdx).toBeGreaterThan(sleepIdx);
|
||||
});
|
||||
|
||||
test('buildKillScript rejects a marker that could break out of the pgrep pattern', async () => {
|
||||
const { buildKillScript } = await loadDocker();
|
||||
expect(() => buildKillScript('bad\nmarker')).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user