summaryrefslogtreecommitdiff
path: root/tst/exec.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'tst/exec.test.ts')
-rw-r--r--tst/exec.test.ts110
1 files changed, 110 insertions, 0 deletions
diff --git a/tst/exec.test.ts b/tst/exec.test.ts
new file mode 100644
index 0000000..1022ec7
--- /dev/null
+++ b/tst/exec.test.ts
@@ -0,0 +1,110 @@
+import { EventEmitter } from 'node:events';
+import type { Command } from '../lib/index';
+import { CollectingTrace, TestTraceable } from './test_utils';
+
+jest.mock('node:child_process', () => ({
+ exec: jest.fn(),
+}));
+
+const getExecMock = async () => {
+ const mod = await import('node:child_process');
+ return (mod as any).exec as jest.Mock;
+};
+
+describe('process/exec (getStdout/getStdoutMany)', () => {
+ let execMock: jest.Mock;
+
+ beforeEach(async () => {
+ jest.resetModules();
+ execMock = await getExecMock();
+ execMock.mockReset();
+ });
+
+ test('getStdout executes command and returns stdout', async () => {
+ const { getStdout } = await import('../lib/index');
+
+ const proc: any = new EventEmitter();
+ proc.stdout = new EventEmitter();
+ proc.stderr = new EventEmitter();
+
+ execMock.mockReturnValue(proc);
+
+ const trace = new CollectingTrace<any>();
+ const cmd = TestTraceable.of<Command, any>(['echo', 'hi'], trace);
+
+ const p = getStdout(cmd, { env: { FOO: 'bar' }, streamTraceable: ['stdout'] });
+
+ proc.stdout.emit('data', Buffer.from('hello'));
+ proc.emit('exit', 0);
+
+ const res = await p;
+ expect(res.right().get()).toBe('hello');
+
+ expect(execMock).toHaveBeenCalledTimes(1);
+ expect(execMock.mock.calls[0][0]).toBe('echo hi');
+ expect(execMock.mock.calls[0][1]).toEqual(
+ expect.objectContaining({ env: expect.objectContaining({ FOO: 'bar' }) }),
+ );
+
+ const flattened = trace.events.flatMap((e) => e);
+ expect(flattened).toEqual(expect.arrayContaining(['hello']));
+ });
+
+ test('getStdout returns left on non-zero exit', async () => {
+ const { getStdout } = await import('../lib/index');
+
+ const proc: any = new EventEmitter();
+ proc.stdout = new EventEmitter();
+ proc.stderr = new EventEmitter();
+ execMock.mockReturnValue(proc);
+
+ const cmd = TestTraceable.of<Command, any>('false', new CollectingTrace<any>());
+ const p = getStdout(cmd);
+
+ proc.emit('exit', 2);
+ const res = await p;
+ expect(res.left().present()).toBe(true);
+ expect(res.left().get().message).toMatch(/non-zero/);
+ });
+
+ test('getStdoutMany runs commands sequentially', async () => {
+ const { getStdoutMany } = await import('../lib/index');
+
+ const makeProc = (out: string) => {
+ const proc: any = new EventEmitter();
+ proc.stdout = new EventEmitter();
+ proc.stderr = new EventEmitter();
+ return proc;
+ };
+
+ execMock
+ .mockImplementationOnce(() => {
+ const proc = makeProc('a');
+ queueMicrotask(() => {
+ proc.stdout.emit('data', Buffer.from('a'));
+ proc.emit('exit', 0);
+ });
+ return proc;
+ })
+ .mockImplementationOnce(() => {
+ const proc = makeProc('b');
+ queueMicrotask(() => {
+ proc.stdout.emit('data', Buffer.from('b'));
+ proc.emit('exit', 0);
+ });
+ return proc;
+ });
+
+ const cmds = TestTraceable.of<Array<Command>, any>(
+ [
+ ['echo', 'a'],
+ ['echo', 'b'],
+ ],
+ new CollectingTrace<any>(),
+ );
+
+ const res = await getStdoutMany(cmds);
+ expect(res.right().get()).toEqual(['a', 'b']);
+ expect(execMock).toHaveBeenCalledTimes(2);
+ });
+});