Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
63 lines
2.8 KiB
JavaScript
63 lines
2.8 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { EventEmitter } = require('node:events');
|
|
const { startPtyJob } = require('./ptyExec.cjs');
|
|
const { buildLiveShellProbe } = require('./liveShellProbe.cjs');
|
|
const { buildWrappedCommand } = require('./ptyExecHelpers.cjs');
|
|
|
|
for (const background of [true, false]) {
|
|
test(`paced ${background ? 'background' : 'silent foreground'} delivery does not consume startup time`, async (t) => {
|
|
t.mock.timers.enable({ apis: ['setTimeout'] });
|
|
const pty = new EventEmitter();
|
|
const writes = [];
|
|
pty.write = (data) => writes.push(String(data));
|
|
const command = `echo ${'x'.repeat(background ? 150000 : 12000)}`;
|
|
const job = startPtyJob(pty, command, {
|
|
shellKind: 'posix', probeLiveShell: true,
|
|
timeoutMs: background ? 3600000 : 500,
|
|
maxBufferedChars: background ? 1024 : 0,
|
|
});
|
|
const advance = (ms) => {
|
|
for (let elapsed = 0; elapsed < ms; elapsed += 30) t.mock.timers.tick(30);
|
|
};
|
|
try {
|
|
const probeLength = 2 + buildLiveShellProbe(job.marker).length;
|
|
while (writes.join('').length < probeLength && !writes.includes('\x03')) advance(30);
|
|
assert.ok(!writes.includes('\x03'), 'probe delivery was interrupted');
|
|
pty.emit('data', `${job.marker}_P:sh\n${job.marker}_Q`);
|
|
// The background case crosses the old probe timer; the foreground
|
|
// case has no echo to refresh its inactivity timer during delivery.
|
|
advance(background ? 32010 : 1200);
|
|
assert.ok(!writes.includes('\x03'), 'wrapper interrupted before delivery completed');
|
|
const totalLength = probeLength + 2 + buildWrappedCommand(command, 'posix', job.marker, true).length;
|
|
while (writes.join('').length < totalLength && !writes.includes('\x03')) advance(30);
|
|
assert.ok(!writes.includes('\x03'), 'delivery did not finish');
|
|
pty.emit('data', `${job.marker}_S\nOK\n${job.marker}_E:0\n`);
|
|
assert.equal((await job.resultPromise).exitCode, 0);
|
|
} finally {
|
|
pty.emit('close');
|
|
t.mock.timers.reset();
|
|
}
|
|
});
|
|
}
|
|
|
|
test('completed delivery still has a bounded wait for a missing probe reply', async (t) => {
|
|
t.mock.timers.enable({ apis: ['setTimeout'] });
|
|
const pty = new EventEmitter();
|
|
const writes = [];
|
|
pty.write = (data) => writes.push(String(data));
|
|
const job = startPtyJob(pty, 'echo never', {
|
|
shellKind: 'posix', probeLiveShell: true, timeoutMs: 500,
|
|
});
|
|
const length = 2 + buildLiveShellProbe(job.marker).length;
|
|
while (writes.join('').length < length) t.mock.timers.tick(30);
|
|
// Unrelated output must not keep a never-started command alive forever.
|
|
for (let i = 0; i < 6; i++) {
|
|
pty.emit('data', 'unrelated output\n');
|
|
t.mock.timers.tick(100);
|
|
}
|
|
const result = await job.resultPromise;
|
|
assert.match(result.error, /Command startup timed out/);
|
|
assert.ok(writes.includes('\x03'));
|
|
});
|