1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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);
});
});
|