Files
smartshell/test/test.spawn.ts

150 lines
4.7 KiB
TypeScript

import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as smartshell from '../ts/index.js';
tap.test('execSpawn should execute commands with args array (shell:false)', async () => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// Test basic command with args
const result = await testSmartshell.execSpawn('echo', ['Hello', 'World']);
expect(result.exitCode).toEqual(0);
expect(result.stdout).toContain('Hello World');
});
tap.test('execSpawn should handle command not found errors', async () => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
let errorThrown = false;
try {
await testSmartshell.execSpawn('nonexistentcommand123', ['arg1']);
} catch (error) {
errorThrown = true;
expect(error.code).toEqual('ENOENT');
}
expect(errorThrown).toBeTrue();
});
tap.test('execSpawn should properly escape arguments', async () => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// Test that shell metacharacters are treated as literals
const result = await testSmartshell.execSpawn('echo', ['$HOME', '&&', 'ls']);
expect(result.exitCode).toEqual(0);
// Should output literal strings, not expanded/executed
expect(result.stdout).toContain('$HOME && ls');
});
tap.test('execSpawn streaming should work', async () => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
const streaming = await testSmartshell.execSpawnStreaming('echo', ['Streaming test']);
const result = await streaming.finalPromise;
expect(result.exitCode).toEqual(0);
expect(result.stdout).toContain('Streaming test');
});
tap.test('execSpawn interactive control should work', async (tools) => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
const interactive = await testSmartshell.execSpawnInteractiveControl('cat', []);
await interactive.sendLine('Input line');
interactive.endInput();
const result = await interactive.finalPromise;
expect(result.exitCode).toEqual(0);
expect(result.stdout).toContain('Input line');
});
tap.test('execSpawn should capture stderr', async () => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// ls on non-existent directory should produce stderr
const result = await testSmartshell.execSpawn('ls', ['/nonexistent/directory/path']);
expect(result.exitCode).not.toEqual(0);
expect(result.stderr).toBeTruthy();
expect(result.stderr).toContain('No such file or directory');
});
tap.test('execSpawn with timeout should terminate process', async () => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
const start = Date.now();
const result = await testSmartshell.execSpawn('sleep', ['10'], { timeout: 100 });
const duration = Date.now() - start;
// Process should be terminated by timeout
expect(duration).toBeLessThan(500);
expect(result.exitCode).not.toEqual(0);
expect(result.signal).toBeTruthy(); // Should have been killed by signal
});
tap.test('execSpawn with maxBuffer should truncate output', async () => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// Generate large output
const result = await testSmartshell.execSpawn('bash', ['-c', 'for i in {1..1000}; do echo "Line $i with some padding text to make it longer"; done'], {
maxBuffer: 1024, // Very small buffer
});
expect(result.exitCode).toEqual(0);
expect(result.stdout).toContain('[Output truncated - exceeded maxBuffer]');
});
tap.test('execSpawn with onData callback should stream data', async (tools) => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
let dataReceived = '';
const result = await testSmartshell.execSpawn('echo', ['Test data'], {
onData: (chunk) => {
dataReceived += chunk.toString();
}
});
expect(result.exitCode).toEqual(0);
expect(dataReceived).toContain('Test data');
});
tap.test('execSpawn with signal should report signal in result', async () => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
const streaming = await testSmartshell.execSpawnStreaming('sleep', ['10']);
// Send SIGTERM after a short delay
setTimeout(() => streaming.terminate(), 100);
const result = await streaming.finalPromise;
expect(result.exitCode).not.toEqual(0);
expect(result.signal).toEqual('SIGTERM');
});
export default tap.start();