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'));
|
||
|
|
});
|