concurrently 10.0.0 → 10.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/flow-control/flow-controller.d.ts +13 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -14
- package/dist/bin/index.spec.js +0 -368
- package/dist/bin/normalize-cli-command.spec.d.ts +0 -1
- package/dist/bin/normalize-cli-command.spec.js +0 -36
- package/dist/lib/__fixtures__/create-mock-instance.d.ts +0 -2
- package/dist/lib/__fixtures__/create-mock-instance.js +0 -5
- package/dist/lib/__fixtures__/fake-command.d.ts +0 -6
- package/dist/lib/__fixtures__/fake-command.js +0 -37
- package/dist/lib/assert.spec.d.ts +0 -1
- package/dist/lib/assert.spec.js +0 -41
- package/dist/lib/command-parser/expand-arguments.spec.d.ts +0 -1
- package/dist/lib/command-parser/expand-arguments.spec.js +0 -57
- package/dist/lib/command-parser/expand-shortcut.spec.d.ts +0 -1
- package/dist/lib/command-parser/expand-shortcut.spec.js +0 -36
- package/dist/lib/command-parser/expand-wildcard.spec.d.ts +0 -1
- package/dist/lib/command-parser/expand-wildcard.spec.js +0 -288
- package/dist/lib/command.spec.d.ts +0 -1
- package/dist/lib/command.spec.js +0 -369
- package/dist/lib/completion-listener.spec.d.ts +0 -1
- package/dist/lib/completion-listener.spec.js +0 -229
- package/dist/lib/concurrently.spec.d.ts +0 -1
- package/dist/lib/concurrently.spec.js +0 -320
- package/dist/lib/date-format.spec.d.ts +0 -1
- package/dist/lib/date-format.spec.js +0 -480
- package/dist/lib/flow-control/input-handler.spec.d.ts +0 -1
- package/dist/lib/flow-control/input-handler.spec.js +0 -116
- package/dist/lib/flow-control/kill-on-signal.spec.d.ts +0 -1
- package/dist/lib/flow-control/kill-on-signal.spec.js +0 -63
- package/dist/lib/flow-control/kill-others.spec.d.ts +0 -1
- package/dist/lib/flow-control/kill-others.spec.js +0 -98
- package/dist/lib/flow-control/log-error.spec.d.ts +0 -1
- package/dist/lib/flow-control/log-error.spec.js +0 -33
- package/dist/lib/flow-control/log-exit.spec.d.ts +0 -1
- package/dist/lib/flow-control/log-exit.spec.js +0 -24
- package/dist/lib/flow-control/log-output.spec.d.ts +0 -1
- package/dist/lib/flow-control/log-output.spec.js +0 -33
- package/dist/lib/flow-control/log-timings.spec.d.ts +0 -1
- package/dist/lib/flow-control/log-timings.spec.js +0 -89
- package/dist/lib/flow-control/logger-padding.spec.d.ts +0 -1
- package/dist/lib/flow-control/logger-padding.spec.js +0 -60
- package/dist/lib/flow-control/output-error-handler.spec.d.ts +0 -1
- package/dist/lib/flow-control/output-error-handler.spec.js +0 -41
- package/dist/lib/flow-control/restart-process.spec.d.ts +0 -1
- package/dist/lib/flow-control/restart-process.spec.js +0 -127
- package/dist/lib/flow-control/teardown.spec.d.ts +0 -1
- package/dist/lib/flow-control/teardown.spec.js +0 -93
- package/dist/lib/jsonc.spec.d.ts +0 -1
- package/dist/lib/jsonc.spec.js +0 -73
- package/dist/lib/logger.spec.d.ts +0 -1
- package/dist/lib/logger.spec.js +0 -507
- package/dist/lib/observables.spec.d.ts +0 -1
- package/dist/lib/observables.spec.js +0 -29
- package/dist/lib/output-writer.spec.d.ts +0 -1
- package/dist/lib/output-writer.spec.js +0 -96
- package/dist/lib/prefix-color-selector.spec.d.ts +0 -1
- package/dist/lib/prefix-color-selector.spec.js +0 -159
- package/dist/lib/spawn.spec.d.ts +0 -1
- package/dist/lib/spawn.spec.js +0 -100
- package/dist/lib/utils.spec.d.ts +0 -1
- package/dist/lib/utils.spec.js +0 -58
- /package/dist/{bin/index.spec.d.ts → lib/flow-control/flow-controller.js} +0 -0
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { createMockInstance } from '../__fixtures__/create-mock-instance.js';
|
|
3
|
-
import { createFakeCloseEvent, createFakeProcess, FakeCommand, } from '../__fixtures__/fake-command.js';
|
|
4
|
-
import { Logger } from '../logger.js';
|
|
5
|
-
import { KillOthers } from './kill-others.js';
|
|
6
|
-
let commands;
|
|
7
|
-
let logger;
|
|
8
|
-
let abortController;
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
commands = [new FakeCommand(), new FakeCommand()];
|
|
11
|
-
logger = createMockInstance(Logger);
|
|
12
|
-
abortController = new AbortController();
|
|
13
|
-
});
|
|
14
|
-
const createWithConditions = (conditions, opts) => new KillOthers({
|
|
15
|
-
logger,
|
|
16
|
-
abortController,
|
|
17
|
-
conditions,
|
|
18
|
-
killSignal: undefined,
|
|
19
|
-
...opts,
|
|
20
|
-
});
|
|
21
|
-
const assignProcess = (command) => {
|
|
22
|
-
const process = createFakeProcess(1);
|
|
23
|
-
command.pid = process.pid;
|
|
24
|
-
command.process = process;
|
|
25
|
-
};
|
|
26
|
-
const unassignProcess = (command) => {
|
|
27
|
-
command.pid = undefined;
|
|
28
|
-
command.process = undefined;
|
|
29
|
-
};
|
|
30
|
-
it('returns same commands', () => {
|
|
31
|
-
expect(createWithConditions(['success']).handle(commands)).toMatchObject({ commands });
|
|
32
|
-
expect(createWithConditions(['failure']).handle(commands)).toMatchObject({ commands });
|
|
33
|
-
});
|
|
34
|
-
it('does not kill others if condition does not match', () => {
|
|
35
|
-
createWithConditions(['failure']).handle(commands);
|
|
36
|
-
assignProcess(commands[1]);
|
|
37
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
38
|
-
expect(logger.logGlobalEvent).not.toHaveBeenCalled();
|
|
39
|
-
expect(commands[0].kill).not.toHaveBeenCalled();
|
|
40
|
-
expect(commands[1].kill).not.toHaveBeenCalled();
|
|
41
|
-
});
|
|
42
|
-
describe.each(['success', 'failure'])('on %s', (condition) => {
|
|
43
|
-
const exitCode = condition === 'success' ? 0 : 1;
|
|
44
|
-
const inversedCode = exitCode === 1 ? 0 : 1;
|
|
45
|
-
it('kills other processes', () => {
|
|
46
|
-
createWithConditions([condition]).handle(commands);
|
|
47
|
-
assignProcess(commands[1]);
|
|
48
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode }));
|
|
49
|
-
expect(logger.logGlobalEvent).toHaveBeenCalledExactlyOnceWith('Sending SIGTERM to other processes..');
|
|
50
|
-
expect(commands[0].kill).not.toHaveBeenCalled();
|
|
51
|
-
expect(commands[1].kill).toHaveBeenCalledWith(undefined);
|
|
52
|
-
});
|
|
53
|
-
it('kills other processes, with specified signal', () => {
|
|
54
|
-
createWithConditions([condition], { killSignal: 'SIGKILL' }).handle(commands);
|
|
55
|
-
assignProcess(commands[1]);
|
|
56
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode }));
|
|
57
|
-
expect(logger.logGlobalEvent).toHaveBeenCalledExactlyOnceWith('Sending SIGKILL to other processes..');
|
|
58
|
-
expect(commands[0].kill).not.toHaveBeenCalled();
|
|
59
|
-
expect(commands[1].kill).toHaveBeenCalledWith('SIGKILL');
|
|
60
|
-
});
|
|
61
|
-
it('sends abort signal on condition match', () => {
|
|
62
|
-
createWithConditions([condition]).handle(commands);
|
|
63
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode }));
|
|
64
|
-
expect(abortController.signal.aborted).toBe(true);
|
|
65
|
-
});
|
|
66
|
-
it('does not send abort signal on condition mismatch', () => {
|
|
67
|
-
createWithConditions([condition]).handle(commands);
|
|
68
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: inversedCode }));
|
|
69
|
-
expect(abortController.signal.aborted).toBe(false);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
it('does nothing if called without conditions', () => {
|
|
73
|
-
createWithConditions([]).handle(commands);
|
|
74
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
75
|
-
expect(logger.logGlobalEvent).not.toHaveBeenCalled();
|
|
76
|
-
expect(commands[0].kill).not.toHaveBeenCalled();
|
|
77
|
-
expect(commands[1].kill).not.toHaveBeenCalled();
|
|
78
|
-
});
|
|
79
|
-
it('does not try to kill processes already dead', () => {
|
|
80
|
-
createWithConditions(['failure']).handle(commands);
|
|
81
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
82
|
-
expect(logger.logGlobalEvent).not.toHaveBeenCalled();
|
|
83
|
-
expect(commands[0].kill).not.toHaveBeenCalled();
|
|
84
|
-
expect(commands[1].kill).not.toHaveBeenCalled();
|
|
85
|
-
});
|
|
86
|
-
it('force kills misbehaving processes after a timeout', () => {
|
|
87
|
-
vi.useFakeTimers();
|
|
88
|
-
commands.push(new FakeCommand());
|
|
89
|
-
createWithConditions(['failure'], { timeoutMs: 500 }).handle(commands);
|
|
90
|
-
assignProcess(commands[1]);
|
|
91
|
-
assignProcess(commands[2]);
|
|
92
|
-
commands[2].kill = vi.fn(() => unassignProcess(commands[2]));
|
|
93
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
94
|
-
vi.advanceTimersByTime(500);
|
|
95
|
-
expect(commands[1].kill).toHaveBeenCalledTimes(2);
|
|
96
|
-
expect(commands[1].kill).toHaveBeenCalledWith('SIGKILL');
|
|
97
|
-
expect(commands[2].kill).toHaveBeenCalledTimes(1);
|
|
98
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { beforeEach, expect, it } from 'vitest';
|
|
2
|
-
import { createMockInstance } from '../__fixtures__/create-mock-instance.js';
|
|
3
|
-
import { FakeCommand } from '../__fixtures__/fake-command.js';
|
|
4
|
-
import { Logger } from '../logger.js';
|
|
5
|
-
import { LogError } from './log-error.js';
|
|
6
|
-
let controller;
|
|
7
|
-
let logger;
|
|
8
|
-
let commands;
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
commands = [new FakeCommand(), new FakeCommand(), new FakeCommand()];
|
|
11
|
-
logger = createMockInstance(Logger);
|
|
12
|
-
controller = new LogError({ logger });
|
|
13
|
-
});
|
|
14
|
-
it('returns same commands', () => {
|
|
15
|
-
expect(controller.handle(commands)).toMatchObject({ commands });
|
|
16
|
-
});
|
|
17
|
-
it('logs the error event of each command', () => {
|
|
18
|
-
controller.handle(commands);
|
|
19
|
-
commands[0].error.next('error from command 0');
|
|
20
|
-
const error1 = new Error('test');
|
|
21
|
-
commands[1].error.next(error1);
|
|
22
|
-
// Testing Error without stack
|
|
23
|
-
const error2 = new Error('test');
|
|
24
|
-
error2.stack = '';
|
|
25
|
-
commands[2].error.next(error2);
|
|
26
|
-
expect(logger.logCommandEvent).toHaveBeenCalledTimes(6);
|
|
27
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(`Error occurred when executing command: ${commands[0].command}`, commands[0]);
|
|
28
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith('error from command 0', commands[0]);
|
|
29
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(`Error occurred when executing command: ${commands[1].command}`, commands[1]);
|
|
30
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(error1.stack, commands[1]);
|
|
31
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(`Error occurred when executing command: ${commands[2].command}`, commands[2]);
|
|
32
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(String(error2), commands[2]);
|
|
33
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { beforeEach, expect, it } from 'vitest';
|
|
2
|
-
import { createMockInstance } from '../__fixtures__/create-mock-instance.js';
|
|
3
|
-
import { createFakeCloseEvent, FakeCommand } from '../__fixtures__/fake-command.js';
|
|
4
|
-
import { Logger } from '../logger.js';
|
|
5
|
-
import { LogExit } from './log-exit.js';
|
|
6
|
-
let controller;
|
|
7
|
-
let logger;
|
|
8
|
-
let commands;
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
commands = [new FakeCommand(), new FakeCommand()];
|
|
11
|
-
logger = createMockInstance(Logger);
|
|
12
|
-
controller = new LogExit({ logger });
|
|
13
|
-
});
|
|
14
|
-
it('returns same commands', () => {
|
|
15
|
-
expect(controller.handle(commands)).toMatchObject({ commands });
|
|
16
|
-
});
|
|
17
|
-
it('logs the close event of each command', () => {
|
|
18
|
-
controller.handle(commands);
|
|
19
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
20
|
-
commands[1].close.next(createFakeCloseEvent({ exitCode: 'SIGTERM' }));
|
|
21
|
-
expect(logger.logCommandEvent).toHaveBeenCalledTimes(2);
|
|
22
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(`${commands[0].command} exited with code 0`, commands[0]);
|
|
23
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(`${commands[1].command} exited with code SIGTERM`, commands[1]);
|
|
24
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { Buffer } from 'node:buffer';
|
|
2
|
-
import { beforeEach, expect, it } from 'vitest';
|
|
3
|
-
import { createMockInstance } from '../__fixtures__/create-mock-instance.js';
|
|
4
|
-
import { FakeCommand } from '../__fixtures__/fake-command.js';
|
|
5
|
-
import { Logger } from '../logger.js';
|
|
6
|
-
import { LogOutput } from './log-output.js';
|
|
7
|
-
let controller;
|
|
8
|
-
let logger;
|
|
9
|
-
let commands;
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
commands = [new FakeCommand(), new FakeCommand()];
|
|
12
|
-
logger = createMockInstance(Logger);
|
|
13
|
-
controller = new LogOutput({ logger });
|
|
14
|
-
});
|
|
15
|
-
it('returns same commands', () => {
|
|
16
|
-
expect(controller.handle(commands)).toMatchObject({ commands });
|
|
17
|
-
});
|
|
18
|
-
it('logs the stdout of each command', () => {
|
|
19
|
-
controller.handle(commands);
|
|
20
|
-
commands[0].stdout.next(Buffer.from('foo'));
|
|
21
|
-
commands[1].stdout.next(Buffer.from('bar'));
|
|
22
|
-
expect(logger.logCommandText).toHaveBeenCalledTimes(2);
|
|
23
|
-
expect(logger.logCommandText).toHaveBeenCalledWith('foo', commands[0]);
|
|
24
|
-
expect(logger.logCommandText).toHaveBeenCalledWith('bar', commands[1]);
|
|
25
|
-
});
|
|
26
|
-
it('logs the stderr of each command', () => {
|
|
27
|
-
controller.handle(commands);
|
|
28
|
-
commands[0].stderr.next(Buffer.from('foo'));
|
|
29
|
-
commands[1].stderr.next(Buffer.from('bar'));
|
|
30
|
-
expect(logger.logCommandText).toHaveBeenCalledTimes(2);
|
|
31
|
-
expect(logger.logCommandText).toHaveBeenCalledWith('foo', commands[0]);
|
|
32
|
-
expect(logger.logCommandText).toHaveBeenCalledWith('bar', commands[1]);
|
|
33
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { beforeEach, expect, it } from 'vitest';
|
|
2
|
-
import { createMockInstance } from '../__fixtures__/create-mock-instance.js';
|
|
3
|
-
import { createFakeCloseEvent, FakeCommand } from '../__fixtures__/fake-command.js';
|
|
4
|
-
import { DateFormatter } from '../date-format.js';
|
|
5
|
-
import { Logger } from '../logger.js';
|
|
6
|
-
import { LogTimings } from './log-timings.js';
|
|
7
|
-
// shown in timing order
|
|
8
|
-
const startDate0 = new Date();
|
|
9
|
-
const startDate1 = new Date(startDate0.getTime() + 1000);
|
|
10
|
-
const endDate1 = new Date(startDate0.getTime() + 5000);
|
|
11
|
-
const endDate0 = new Date(startDate0.getTime() + 3000);
|
|
12
|
-
const timestampFormat = 'yyyy-MM-dd HH:mm:ss.SSS';
|
|
13
|
-
const getDurationText = (startDate, endDate) => `${(endDate.getTime() - startDate.getTime()).toLocaleString()}ms`;
|
|
14
|
-
const command0DurationTextMs = getDurationText(startDate0, endDate0);
|
|
15
|
-
const command1DurationTextMs = getDurationText(startDate1, endDate1);
|
|
16
|
-
let controller;
|
|
17
|
-
let logger;
|
|
18
|
-
let commands;
|
|
19
|
-
let command0ExitInfo;
|
|
20
|
-
let command1ExitInfo;
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
commands = [new FakeCommand('foo', 'command 1', 0), new FakeCommand('bar', 'command 2', 1)];
|
|
23
|
-
command0ExitInfo = createFakeCloseEvent({
|
|
24
|
-
command: commands[0],
|
|
25
|
-
timings: {
|
|
26
|
-
startDate: startDate0,
|
|
27
|
-
endDate: endDate0,
|
|
28
|
-
durationSeconds: endDate0.getTime() - startDate0.getTime(),
|
|
29
|
-
},
|
|
30
|
-
index: commands[0].index,
|
|
31
|
-
});
|
|
32
|
-
command1ExitInfo = createFakeCloseEvent({
|
|
33
|
-
command: commands[1],
|
|
34
|
-
timings: {
|
|
35
|
-
startDate: startDate1,
|
|
36
|
-
endDate: endDate1,
|
|
37
|
-
durationSeconds: endDate1.getTime() - startDate1.getTime(),
|
|
38
|
-
},
|
|
39
|
-
index: commands[1].index,
|
|
40
|
-
});
|
|
41
|
-
logger = createMockInstance(Logger);
|
|
42
|
-
controller = new LogTimings({ logger, timestampFormat });
|
|
43
|
-
});
|
|
44
|
-
it('returns same commands', () => {
|
|
45
|
-
expect(controller.handle(commands)).toMatchObject({ commands });
|
|
46
|
-
});
|
|
47
|
-
it("does not log timings and doesn't throw if no logger is provided", () => {
|
|
48
|
-
controller = new LogTimings({});
|
|
49
|
-
const { onFinish } = controller.handle(commands);
|
|
50
|
-
commands[0].timer.next({ startDate: startDate0 });
|
|
51
|
-
commands[1].timer.next({ startDate: startDate1 });
|
|
52
|
-
commands[1].timer.next({ startDate: startDate1, endDate: endDate1 });
|
|
53
|
-
commands[0].timer.next({ startDate: startDate0, endDate: endDate0 });
|
|
54
|
-
onFinish?.();
|
|
55
|
-
expect(logger.logCommandEvent).toHaveBeenCalledTimes(0);
|
|
56
|
-
});
|
|
57
|
-
it('logs the timings at the start and end (ie complete or error) event of each command', () => {
|
|
58
|
-
const formatter = new DateFormatter(timestampFormat);
|
|
59
|
-
controller.handle(commands);
|
|
60
|
-
commands[0].timer.next({ startDate: startDate0 });
|
|
61
|
-
commands[1].timer.next({ startDate: startDate1 });
|
|
62
|
-
commands[1].timer.next({ startDate: startDate1, endDate: endDate1 });
|
|
63
|
-
commands[0].timer.next({ startDate: startDate0, endDate: endDate0 });
|
|
64
|
-
expect(logger.logCommandEvent).toHaveBeenCalledTimes(4);
|
|
65
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(`${commands[0].command} started at ${formatter.format(startDate0)}`, commands[0]);
|
|
66
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(`${commands[1].command} started at ${formatter.format(startDate1)}`, commands[1]);
|
|
67
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(`${commands[1].command} stopped at ${formatter.format(endDate1)} after ${command1DurationTextMs}`, commands[1]);
|
|
68
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(`${commands[0].command} stopped at ${formatter.format(endDate0)} after ${command0DurationTextMs}`, commands[0]);
|
|
69
|
-
});
|
|
70
|
-
it('does not log timings summary if there was an error', () => {
|
|
71
|
-
const { onFinish } = controller.handle(commands);
|
|
72
|
-
commands[0].close.next(command0ExitInfo);
|
|
73
|
-
commands[1].error.next(undefined);
|
|
74
|
-
onFinish?.();
|
|
75
|
-
expect(logger.logTable).toHaveBeenCalledTimes(0);
|
|
76
|
-
});
|
|
77
|
-
it('logs the sorted timings summary when all processes close successfully after onFinish is called', () => {
|
|
78
|
-
const { onFinish } = controller.handle(commands);
|
|
79
|
-
commands[0].close.next(command0ExitInfo);
|
|
80
|
-
commands[1].close.next(command1ExitInfo);
|
|
81
|
-
expect(logger.logGlobalEvent).toHaveBeenCalledTimes(0);
|
|
82
|
-
onFinish?.();
|
|
83
|
-
expect(logger.logGlobalEvent).toHaveBeenCalledExactlyOnceWith('Timings:');
|
|
84
|
-
// sorted by duration
|
|
85
|
-
expect(logger.logTable).toHaveBeenCalledExactlyOnceWith([
|
|
86
|
-
LogTimings.mapCloseEventToTimingInfo(command1ExitInfo),
|
|
87
|
-
LogTimings.mapCloseEventToTimingInfo(command0ExitInfo),
|
|
88
|
-
]);
|
|
89
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { beforeEach, expect, it } from 'vitest';
|
|
2
|
-
import { createMockInstance } from '../__fixtures__/create-mock-instance.js';
|
|
3
|
-
import { FakeCommand } from '../__fixtures__/fake-command.js';
|
|
4
|
-
import { Logger } from '../logger.js';
|
|
5
|
-
import { LoggerPadding } from './logger-padding.js';
|
|
6
|
-
let logger;
|
|
7
|
-
let controller;
|
|
8
|
-
let commands;
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
commands = [new FakeCommand(), new FakeCommand()];
|
|
11
|
-
logger = createMockInstance(Logger);
|
|
12
|
-
controller = new LoggerPadding({ logger });
|
|
13
|
-
});
|
|
14
|
-
it('returns same commands', () => {
|
|
15
|
-
expect(controller.handle(commands)).toMatchObject({ commands });
|
|
16
|
-
});
|
|
17
|
-
it('sets the prefix length on handle', () => {
|
|
18
|
-
controller.handle(commands);
|
|
19
|
-
expect(logger.setPrefixLength).toHaveBeenCalledTimes(1);
|
|
20
|
-
});
|
|
21
|
-
it('updates the prefix length when commands emit a start timer', () => {
|
|
22
|
-
controller.handle(commands);
|
|
23
|
-
commands[0].timer.next({ startDate: new Date() });
|
|
24
|
-
expect(logger.setPrefixLength).toHaveBeenCalledTimes(2);
|
|
25
|
-
commands[1].timer.next({ startDate: new Date() });
|
|
26
|
-
expect(logger.setPrefixLength).toHaveBeenCalledTimes(3);
|
|
27
|
-
});
|
|
28
|
-
it('sets prefix length to the longest prefix of all commands', () => {
|
|
29
|
-
logger.getPrefixContent
|
|
30
|
-
.mockReturnValueOnce({ type: 'default', value: 'foobar' })
|
|
31
|
-
.mockReturnValueOnce({ type: 'default', value: 'baz' });
|
|
32
|
-
controller.handle(commands);
|
|
33
|
-
expect(logger.setPrefixLength).toHaveBeenCalledWith(6);
|
|
34
|
-
});
|
|
35
|
-
it('ignores color markers when measuring prefix length', () => {
|
|
36
|
-
logger.getPrefixContent
|
|
37
|
-
.mockReturnValueOnce({ type: 'template', value: '{color}foo{/color}' })
|
|
38
|
-
.mockReturnValueOnce({ type: 'template', value: '{color}abcd{/color}' });
|
|
39
|
-
controller.handle(commands);
|
|
40
|
-
expect(logger.setPrefixLength).toHaveBeenCalledWith(4);
|
|
41
|
-
});
|
|
42
|
-
it('does not shorten the prefix length', () => {
|
|
43
|
-
logger.getPrefixContent
|
|
44
|
-
.mockReturnValueOnce({ type: 'default', value: '100' })
|
|
45
|
-
.mockReturnValueOnce({ type: 'default', value: '1' });
|
|
46
|
-
controller.handle(commands);
|
|
47
|
-
commands[0].timer.next({ startDate: new Date() });
|
|
48
|
-
expect(logger.setPrefixLength).toHaveBeenCalledWith(3);
|
|
49
|
-
commands[0].timer.next({ startDate: new Date() });
|
|
50
|
-
expect(logger.setPrefixLength).toHaveBeenCalledWith(3);
|
|
51
|
-
});
|
|
52
|
-
it('unsubscribes from start timers on finish', () => {
|
|
53
|
-
logger.getPrefixContent.mockReturnValue({ type: 'default', value: '1' });
|
|
54
|
-
const { onFinish } = controller.handle(commands);
|
|
55
|
-
commands[0].timer.next({ startDate: new Date() });
|
|
56
|
-
expect(logger.setPrefixLength).toHaveBeenCalledTimes(2);
|
|
57
|
-
onFinish();
|
|
58
|
-
commands[0].timer.next({ startDate: new Date() });
|
|
59
|
-
expect(logger.setPrefixLength).toHaveBeenCalledTimes(2);
|
|
60
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { Writable } from 'node:stream';
|
|
2
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { FakeCommand } from '../__fixtures__/fake-command.js';
|
|
4
|
-
import { OutputErrorHandler } from './output-error-handler.js';
|
|
5
|
-
let controller;
|
|
6
|
-
let outputStream;
|
|
7
|
-
let abortController;
|
|
8
|
-
let commands;
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
commands = [new FakeCommand(), new FakeCommand()];
|
|
11
|
-
abortController = new AbortController();
|
|
12
|
-
outputStream = new Writable();
|
|
13
|
-
controller = new OutputErrorHandler({ abortController, outputStream });
|
|
14
|
-
});
|
|
15
|
-
it('returns same commands', () => {
|
|
16
|
-
expect(controller.handle(commands)).toMatchObject({ commands });
|
|
17
|
-
});
|
|
18
|
-
describe('on output stream error', () => {
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
controller.handle(commands);
|
|
21
|
-
outputStream.emit('error', new Error('test'));
|
|
22
|
-
});
|
|
23
|
-
it('kills every command', () => {
|
|
24
|
-
expect(commands[0].kill).toHaveBeenCalled();
|
|
25
|
-
expect(commands[1].kill).toHaveBeenCalled();
|
|
26
|
-
});
|
|
27
|
-
it('sends abort signal', () => {
|
|
28
|
-
expect(abortController.signal.aborted).toBe(true);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
describe('on finish', () => {
|
|
32
|
-
it('unsubscribes from output stream error', () => {
|
|
33
|
-
const { onFinish } = controller.handle(commands);
|
|
34
|
-
onFinish();
|
|
35
|
-
outputStream.on('error', vi.fn());
|
|
36
|
-
outputStream.emit('error', new Error('test'));
|
|
37
|
-
expect(commands[0].kill).not.toHaveBeenCalled();
|
|
38
|
-
expect(commands[1].kill).not.toHaveBeenCalled();
|
|
39
|
-
expect(abortController.signal.aborted).toBe(false);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { VirtualTimeScheduler } from 'rxjs';
|
|
2
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { createMockInstance } from '../__fixtures__/create-mock-instance.js';
|
|
4
|
-
import { createFakeCloseEvent, FakeCommand } from '../__fixtures__/fake-command.js';
|
|
5
|
-
import { Logger } from '../logger.js';
|
|
6
|
-
import { RestartProcess } from './restart-process.js';
|
|
7
|
-
let commands;
|
|
8
|
-
let controller;
|
|
9
|
-
let logger;
|
|
10
|
-
let scheduler;
|
|
11
|
-
beforeEach(() => {
|
|
12
|
-
commands = [new FakeCommand(), new FakeCommand()];
|
|
13
|
-
logger = createMockInstance(Logger);
|
|
14
|
-
// Don't use TestScheduler as it's hardcoded to a max number of "frames" (time),
|
|
15
|
-
// which don't work for some tests in this suite
|
|
16
|
-
scheduler = new VirtualTimeScheduler();
|
|
17
|
-
controller = new RestartProcess({
|
|
18
|
-
logger,
|
|
19
|
-
scheduler,
|
|
20
|
-
delay: 100,
|
|
21
|
-
tries: 2,
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
it('does not restart processes that complete with success', () => {
|
|
25
|
-
controller.handle(commands);
|
|
26
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
27
|
-
commands[1].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
28
|
-
scheduler.flush();
|
|
29
|
-
expect(commands[0].start).toHaveBeenCalledTimes(0);
|
|
30
|
-
expect(commands[1].start).toHaveBeenCalledTimes(0);
|
|
31
|
-
});
|
|
32
|
-
it('restarts processes that fail immediately, if no delay was passed', () => {
|
|
33
|
-
controller = new RestartProcess({ logger, scheduler, tries: 1 });
|
|
34
|
-
controller.handle(commands);
|
|
35
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
36
|
-
scheduler.flush();
|
|
37
|
-
expect(scheduler.now()).toBe(0);
|
|
38
|
-
expect(logger.logCommandEvent).toHaveBeenCalledExactlyOnceWith(`${commands[0].command} restarted`, commands[0]);
|
|
39
|
-
expect(commands[0].start).toHaveBeenCalledTimes(1);
|
|
40
|
-
});
|
|
41
|
-
it('restarts processes that fail after delay ms has passed', () => {
|
|
42
|
-
controller.handle(commands);
|
|
43
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
44
|
-
commands[1].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
45
|
-
scheduler.flush();
|
|
46
|
-
expect(scheduler.now()).toBe(100);
|
|
47
|
-
expect(logger.logCommandEvent).toHaveBeenCalledExactlyOnceWith(`${commands[0].command} restarted`, commands[0]);
|
|
48
|
-
expect(commands[0].start).toHaveBeenCalledTimes(1);
|
|
49
|
-
expect(commands[1].start).not.toHaveBeenCalled();
|
|
50
|
-
});
|
|
51
|
-
it('restarts processes that fail with an exponential back-off', () => {
|
|
52
|
-
const tries = 4;
|
|
53
|
-
controller = new RestartProcess({ logger, scheduler, tries, delay: 'exponential' });
|
|
54
|
-
controller.handle(commands);
|
|
55
|
-
let time = 0;
|
|
56
|
-
for (let i = 0; i < tries; i++) {
|
|
57
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
58
|
-
scheduler.flush();
|
|
59
|
-
time += 2 ** i * 1000;
|
|
60
|
-
expect(scheduler.now()).toBe(time);
|
|
61
|
-
expect(logger.logCommandEvent).toHaveBeenCalledTimes(i + 1);
|
|
62
|
-
expect(commands[0].start).toHaveBeenCalledTimes(i + 1);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
it('restarts processes up to tries', () => {
|
|
66
|
-
controller.handle(commands);
|
|
67
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
68
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 'SIGTERM' }));
|
|
69
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 'SIGTERM' }));
|
|
70
|
-
commands[1].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
71
|
-
scheduler.flush();
|
|
72
|
-
expect(logger.logCommandEvent).toHaveBeenCalledTimes(2);
|
|
73
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(`${commands[0].command} restarted`, commands[0]);
|
|
74
|
-
expect(commands[0].start).toHaveBeenCalledTimes(2);
|
|
75
|
-
});
|
|
76
|
-
it('restart processes forever, if tries is negative', () => {
|
|
77
|
-
controller = new RestartProcess({
|
|
78
|
-
logger,
|
|
79
|
-
scheduler,
|
|
80
|
-
delay: 100,
|
|
81
|
-
tries: -1,
|
|
82
|
-
});
|
|
83
|
-
expect(controller.tries).toBe(Infinity);
|
|
84
|
-
});
|
|
85
|
-
it('restarts processes until they succeed', () => {
|
|
86
|
-
controller.handle(commands);
|
|
87
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
88
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
89
|
-
commands[1].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
90
|
-
scheduler.flush();
|
|
91
|
-
expect(logger.logCommandEvent).toHaveBeenCalledExactlyOnceWith(`${commands[0].command} restarted`, commands[0]);
|
|
92
|
-
expect(commands[0].start).toHaveBeenCalledTimes(1);
|
|
93
|
-
});
|
|
94
|
-
describe('returned commands', () => {
|
|
95
|
-
it('are the same if 0 tries are to be attempted', () => {
|
|
96
|
-
controller = new RestartProcess({ logger, scheduler });
|
|
97
|
-
expect(controller.handle(commands)).toMatchObject({ commands });
|
|
98
|
-
});
|
|
99
|
-
it('are not the same, but with same length if 1+ tries are to be attempted', () => {
|
|
100
|
-
const { commands: newCommands } = controller.handle(commands);
|
|
101
|
-
expect(newCommands).not.toBe(commands);
|
|
102
|
-
expect(newCommands).toHaveLength(commands.length);
|
|
103
|
-
});
|
|
104
|
-
it('skip close events followed by restarts', () => {
|
|
105
|
-
const { commands: newCommands } = controller.handle(commands);
|
|
106
|
-
const callback = vi.fn();
|
|
107
|
-
newCommands[0].close.subscribe(callback);
|
|
108
|
-
newCommands[1].close.subscribe(callback);
|
|
109
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
110
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
111
|
-
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
112
|
-
commands[1].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
113
|
-
commands[1].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
114
|
-
scheduler.flush();
|
|
115
|
-
// 1 failure from commands[0], 1 success from commands[1]
|
|
116
|
-
expect(callback).toHaveBeenCalledTimes(2);
|
|
117
|
-
});
|
|
118
|
-
it('keep non-close streams from original commands', () => {
|
|
119
|
-
const { commands: newCommands } = controller.handle(commands);
|
|
120
|
-
newCommands.forEach((newCommand, i) => {
|
|
121
|
-
expect(newCommand.close).not.toBe(commands[i].close);
|
|
122
|
-
expect(newCommand.error).toBe(commands[i].error);
|
|
123
|
-
expect(newCommand.stdout).toBe(commands[i].stdout);
|
|
124
|
-
expect(newCommand.stderr).toBe(commands[i].stderr);
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { createMockInstance } from '../__fixtures__/create-mock-instance.js';
|
|
3
|
-
import { createFakeProcess, FakeCommand } from '../__fixtures__/fake-command.js';
|
|
4
|
-
import { Logger } from '../logger.js';
|
|
5
|
-
import { getSpawnOpts } from '../spawn.js';
|
|
6
|
-
import { Teardown } from './teardown.js';
|
|
7
|
-
let spawn;
|
|
8
|
-
const logger = createMockInstance(Logger);
|
|
9
|
-
const commands = [new FakeCommand()];
|
|
10
|
-
const teardown = 'cowsay bye';
|
|
11
|
-
beforeEach(() => {
|
|
12
|
-
spawn = vi.fn(() => createFakeProcess(1));
|
|
13
|
-
});
|
|
14
|
-
afterEach(() => {
|
|
15
|
-
vi.clearAllMocks();
|
|
16
|
-
});
|
|
17
|
-
const create = (teardown) => new Teardown({
|
|
18
|
-
spawn,
|
|
19
|
-
logger,
|
|
20
|
-
commands: teardown,
|
|
21
|
-
});
|
|
22
|
-
it('returns commands unchanged', () => {
|
|
23
|
-
const { commands: actual } = create([]).handle(commands);
|
|
24
|
-
expect(actual).toBe(commands);
|
|
25
|
-
});
|
|
26
|
-
describe('onFinish callback', () => {
|
|
27
|
-
it('does not spawn nothing if there are no teardown commands', () => {
|
|
28
|
-
create([]).handle(commands).onFinish();
|
|
29
|
-
expect(spawn).not.toHaveBeenCalled();
|
|
30
|
-
});
|
|
31
|
-
it('runs teardown command', () => {
|
|
32
|
-
create([teardown]).handle(commands).onFinish();
|
|
33
|
-
expect(spawn).toHaveBeenCalledWith(teardown, getSpawnOpts({ stdio: 'raw' }));
|
|
34
|
-
});
|
|
35
|
-
it('waits for teardown command to close', async () => {
|
|
36
|
-
const child = createFakeProcess(1);
|
|
37
|
-
spawn.mockReturnValue(child);
|
|
38
|
-
const result = create([teardown]).handle(commands).onFinish();
|
|
39
|
-
child.emit('close', 1, null);
|
|
40
|
-
await expect(result).resolves.toBeUndefined();
|
|
41
|
-
});
|
|
42
|
-
it('rejects if teardown command errors (string)', async () => {
|
|
43
|
-
const child = createFakeProcess(1);
|
|
44
|
-
spawn.mockReturnValue(child);
|
|
45
|
-
const result = create([teardown]).handle(commands).onFinish();
|
|
46
|
-
const error = 'fail';
|
|
47
|
-
child.emit('error', error);
|
|
48
|
-
await expect(result).rejects.toBe(error);
|
|
49
|
-
expect(logger.logGlobalEvent).toHaveBeenLastCalledWith('fail');
|
|
50
|
-
});
|
|
51
|
-
it('rejects if teardown command errors (error)', async () => {
|
|
52
|
-
const child = createFakeProcess(1);
|
|
53
|
-
spawn.mockReturnValue(child);
|
|
54
|
-
const result = create([teardown]).handle(commands).onFinish();
|
|
55
|
-
const error = new Error('fail');
|
|
56
|
-
child.emit('error', error);
|
|
57
|
-
await expect(result).rejects.toBe(error);
|
|
58
|
-
expect(logger.logGlobalEvent).toHaveBeenLastCalledWith(expect.stringMatching(/Error: fail/));
|
|
59
|
-
});
|
|
60
|
-
it('rejects if teardown command errors (error, no stack)', async () => {
|
|
61
|
-
const child = createFakeProcess(1);
|
|
62
|
-
spawn.mockReturnValue(child);
|
|
63
|
-
const result = create([teardown]).handle(commands).onFinish();
|
|
64
|
-
const error = new Error('fail');
|
|
65
|
-
delete error.stack;
|
|
66
|
-
child.emit('error', error);
|
|
67
|
-
await expect(result).rejects.toBe(error);
|
|
68
|
-
expect(logger.logGlobalEvent).toHaveBeenLastCalledWith('Error: fail');
|
|
69
|
-
});
|
|
70
|
-
it('runs multiple teardown commands in sequence', async () => {
|
|
71
|
-
const child1 = createFakeProcess(1);
|
|
72
|
-
const child2 = createFakeProcess(2);
|
|
73
|
-
spawn.mockReturnValueOnce(child1).mockReturnValueOnce(child2);
|
|
74
|
-
const result = create(['foo', 'bar']).handle(commands).onFinish();
|
|
75
|
-
expect(spawn).toHaveBeenCalledTimes(1);
|
|
76
|
-
expect(spawn).toHaveBeenLastCalledWith('foo', getSpawnOpts({ stdio: 'raw' }));
|
|
77
|
-
child1.emit('close', 1, null);
|
|
78
|
-
await new Promise((resolve) => setTimeout(resolve));
|
|
79
|
-
expect(spawn).toHaveBeenCalledTimes(2);
|
|
80
|
-
expect(spawn).toHaveBeenLastCalledWith('bar', getSpawnOpts({ stdio: 'raw' }));
|
|
81
|
-
child2.emit('close', 0, null);
|
|
82
|
-
await expect(result).resolves.toBeUndefined();
|
|
83
|
-
});
|
|
84
|
-
it('stops running teardown commands on SIGINT', async () => {
|
|
85
|
-
const child = createFakeProcess(1);
|
|
86
|
-
spawn.mockReturnValue(child);
|
|
87
|
-
const result = create(['foo', 'bar']).handle(commands).onFinish();
|
|
88
|
-
child.emit('close', null, 'SIGINT');
|
|
89
|
-
await result;
|
|
90
|
-
expect(spawn).toHaveBeenCalledTimes(1);
|
|
91
|
-
expect(spawn).toHaveBeenLastCalledWith('foo', expect.anything());
|
|
92
|
-
});
|
|
93
|
-
});
|
package/dist/lib/jsonc.spec.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|