cligr 1.0.0
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/.claude/settings.local.json +12 -0
- package/README.md +65 -0
- package/dist/index.js +94 -0
- package/package.json +26 -0
- package/scripts/build.js +20 -0
- package/scripts/test.js +164 -0
- package/src/commands/config.ts +121 -0
- package/src/commands/down.ts +6 -0
- package/src/commands/groups.ts +68 -0
- package/src/commands/ls.ts +26 -0
- package/src/commands/up.ts +44 -0
- package/src/config/loader.ts +103 -0
- package/src/config/types.ts +20 -0
- package/src/index.ts +96 -0
- package/src/process/manager.ts +199 -0
- package/src/process/template.ts +72 -0
- package/tests/integration/blocking-processes-fixed.test.ts +255 -0
- package/tests/integration/blocking-processes.test.ts +497 -0
- package/tests/integration/commands.test.ts +674 -0
- package/tests/integration/config-loader.test.ts +426 -0
- package/tests/integration/process-manager.test.ts +391 -0
- package/tests/integration/template-expander.test.ts +362 -0
- package/tsconfig.json +15 -0
- package/usage.md +9 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for blocking/long-running processes
|
|
3
|
+
*
|
|
4
|
+
* These tests verify the ProcessManager's ability to handle
|
|
5
|
+
* processes that run indefinitely or for extended periods.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, before, after } from 'node:test';
|
|
9
|
+
import assert from 'node:assert';
|
|
10
|
+
import { spawn } from 'child_process';
|
|
11
|
+
import { ProcessManager } from '../../src/process/manager.js';
|
|
12
|
+
import type { ProcessItem } from '../../src/config/types.js';
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import os from 'node:os';
|
|
16
|
+
|
|
17
|
+
describe('Blocking Processes Integration Tests', () => {
|
|
18
|
+
let manager: ProcessManager;
|
|
19
|
+
let testScriptsDir: string;
|
|
20
|
+
|
|
21
|
+
before(() => {
|
|
22
|
+
manager = new ProcessManager();
|
|
23
|
+
|
|
24
|
+
// Create a directory for test scripts
|
|
25
|
+
testScriptsDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cligr-blocking-test-'));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
after(() => {
|
|
29
|
+
// Clean up any running processes
|
|
30
|
+
manager.killAll();
|
|
31
|
+
|
|
32
|
+
// Clean up test scripts directory
|
|
33
|
+
if (fs.existsSync(testScriptsDir)) {
|
|
34
|
+
fs.rmSync(testScriptsDir, { recursive: true, force: true });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
function createInfiniteLoopScript(scriptName: string, delayMs: number = 1000): string {
|
|
39
|
+
const scriptPath = path.join(testScriptsDir, scriptName);
|
|
40
|
+
const scriptContent = `
|
|
41
|
+
// Infinite loop script - simulates a long-running process
|
|
42
|
+
let counter = 0;
|
|
43
|
+
const interval = setInterval(() => {
|
|
44
|
+
counter++;
|
|
45
|
+
console.log(\[${scriptName}] Running iteration: \${counter}\`);
|
|
46
|
+
}, ${delayMs});
|
|
47
|
+
|
|
48
|
+
// Keep process alive
|
|
49
|
+
process.on('SIGTERM', () => {
|
|
50
|
+
console.log(\[${scriptName}] Received SIGTERM, shutting down...\`);
|
|
51
|
+
clearInterval(interval);
|
|
52
|
+
process.exit(0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
process.on('SIGINT', () => {
|
|
56
|
+
console.log(\[${scriptName}] Received SIGINT, shutting down...\`);
|
|
57
|
+
clearInterval(interval);
|
|
58
|
+
process.exit(0);
|
|
59
|
+
});
|
|
60
|
+
`;
|
|
61
|
+
fs.writeFileSync(scriptPath, scriptContent);
|
|
62
|
+
return scriptPath;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function createBlockingScript(scriptName: string): string {
|
|
66
|
+
const scriptPath = path.join(testScriptsDir, scriptName);
|
|
67
|
+
const scriptContent = `
|
|
68
|
+
// Blocking script - simulates CPU-intensive work
|
|
69
|
+
console.log('Starting blocking process...');
|
|
70
|
+
|
|
71
|
+
// Simulate blocking work with periodic output
|
|
72
|
+
let iterations = 0;
|
|
73
|
+
const blockingWork = () => {
|
|
74
|
+
const start = Date.now();
|
|
75
|
+
while (Date.now() - start < 100) {
|
|
76
|
+
// Busy wait for 100ms - simulates blocking CPU work
|
|
77
|
+
Math.sqrt(Math.random() * 10000);
|
|
78
|
+
}
|
|
79
|
+
iterations++;
|
|
80
|
+
console.log(\[${scriptName}] Completed \${iterations} blocking cycles\`);
|
|
81
|
+
|
|
82
|
+
// Continue blocking work
|
|
83
|
+
if (iterations < 1000) {
|
|
84
|
+
setTimeout(blockingWork, 50);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
blockingWork();
|
|
89
|
+
|
|
90
|
+
process.on('SIGTERM', () => {
|
|
91
|
+
console.log(\[${scriptName}] Shutting down after \${iterations} cycles\`);
|
|
92
|
+
process.exit(0);
|
|
93
|
+
});
|
|
94
|
+
`;
|
|
95
|
+
fs.writeFileSync(scriptPath, scriptContent);
|
|
96
|
+
return scriptPath;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function createServerScript(scriptName: string, port: number): string {
|
|
100
|
+
const scriptPath = path.join(testScriptsDir, scriptName);
|
|
101
|
+
const scriptContent = `
|
|
102
|
+
// HTTP server script - simulates a service that stays running
|
|
103
|
+
import http from 'http';
|
|
104
|
+
|
|
105
|
+
const server = http.createServer((req, res) => {
|
|
106
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
107
|
+
res.end('Hello from test server\\n');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
server.listen(${port}, () => {
|
|
111
|
+
console.log(\`${scriptName} listening on port \${port}\`);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Keep server running
|
|
115
|
+
process.on('SIGTERM', () => {
|
|
116
|
+
console.log(\`${scriptName} shutting down...\`);
|
|
117
|
+
server.close(() => {
|
|
118
|
+
process.exit(0);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
process.on('SIGINT', () => {
|
|
123
|
+
console.log(\`${scriptName} shutting down...\`);
|
|
124
|
+
server.close(() => {
|
|
125
|
+
process.exit(0);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
`;
|
|
129
|
+
fs.writeFileSync(scriptPath, scriptContent);
|
|
130
|
+
return scriptPath;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
describe('Infinite loop processes', () => {
|
|
134
|
+
it('should manage infinite loop processes with setInterval', async () => {
|
|
135
|
+
const scriptPath = createInfiniteLoopScript('infinite-loop.js', 500);
|
|
136
|
+
|
|
137
|
+
const items: ProcessItem[] = [
|
|
138
|
+
{ name: 'loop1', args: [scriptPath], fullCmd: `node ${scriptPath}` },
|
|
139
|
+
{ name: 'loop2', args: [scriptPath], fullCmd: `node ${scriptPath}` }
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
manager.spawnGroup('infinite-loop-group', items, 'no');
|
|
143
|
+
|
|
144
|
+
// Verify processes are running
|
|
145
|
+
assert.strictEqual(manager.isGroupRunning('infinite-loop-group'), true);
|
|
146
|
+
|
|
147
|
+
// Wait a bit to ensure processes started
|
|
148
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
149
|
+
|
|
150
|
+
// Kill the group
|
|
151
|
+
manager.killGroup('infinite-loop-group');
|
|
152
|
+
|
|
153
|
+
// Verify processes were killed
|
|
154
|
+
assert.strictEqual(manager.isGroupRunning('infinite-loop-group'), false);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should handle infinite while(true) loop processes', async () => {
|
|
158
|
+
const scriptPath = path.join(testScriptsDir, 'while-true.js');
|
|
159
|
+
const scriptContent = `
|
|
160
|
+
// Infinite while loop
|
|
161
|
+
console.log('Starting infinite while(true) loop...');
|
|
162
|
+
let counter = 0;
|
|
163
|
+
|
|
164
|
+
while (true) {
|
|
165
|
+
counter++;
|
|
166
|
+
if (counter % 100000 === 0) {
|
|
167
|
+
console.log(\`While loop iteration: \${counter}\`);
|
|
168
|
+
// Small yield to prevent complete CPU lock
|
|
169
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
170
|
+
}
|
|
171
|
+
if (counter > 1000000) break; // Safety break
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
process.on('SIGTERM', () => {
|
|
175
|
+
console.log('Process terminated after', counter, 'iterations');
|
|
176
|
+
process.exit(0);
|
|
177
|
+
});
|
|
178
|
+
`;
|
|
179
|
+
fs.writeFileSync(scriptPath, scriptContent);
|
|
180
|
+
|
|
181
|
+
const items: ProcessItem[] = [
|
|
182
|
+
{ name: 'while-true', args: [scriptPath], fullCmd: `node ${scriptPath}` }
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
manager.spawnGroup('while-true-group', items, 'no');
|
|
186
|
+
|
|
187
|
+
assert.strictEqual(manager.isGroupRunning('while-true-group'), true);
|
|
188
|
+
|
|
189
|
+
// Let it run briefly
|
|
190
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
191
|
+
|
|
192
|
+
manager.killGroup('while-true-group');
|
|
193
|
+
|
|
194
|
+
assert.strictEqual(manager.isGroupRunning('while-true-group'), false);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe('Blocking CPU-intensive processes', () => {
|
|
199
|
+
it('should manage blocking CPU processes', async () => {
|
|
200
|
+
const scriptPath = createBlockingScript('blocking-cpu.js');
|
|
201
|
+
|
|
202
|
+
const items: ProcessItem[] = [
|
|
203
|
+
{ name: 'cpu-blocker', args: [scriptPath], fullCmd: `node ${scriptPath}` }
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
manager.spawnGroup('blocking-group', items, 'no');
|
|
207
|
+
|
|
208
|
+
assert.strictEqual(manager.isGroupRunning('blocking-group'), true);
|
|
209
|
+
|
|
210
|
+
// Let the blocking work run
|
|
211
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
212
|
+
|
|
213
|
+
// Verify it's still running despite blocking work
|
|
214
|
+
assert.strictEqual(manager.isGroupRunning('blocking-group'), true);
|
|
215
|
+
|
|
216
|
+
manager.killGroup('blocking-group');
|
|
217
|
+
|
|
218
|
+
assert.strictEqual(manager.isGroupRunning('blocking-group'), false);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should handle multiple blocking processes simultaneously', async () => {
|
|
222
|
+
const scriptPath = createBlockingScript('multi-blocker.js');
|
|
223
|
+
|
|
224
|
+
const items: ProcessItem[] = [
|
|
225
|
+
{ name: 'blocker1', args: [scriptPath], fullCmd: `node ${scriptPath}` },
|
|
226
|
+
{ name: 'blocker2', args: [scriptPath], fullCmd: `node ${scriptPath}` },
|
|
227
|
+
{ name: 'blocker3', args: [scriptPath], fullCmd: `node ${scriptPath}` }
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
manager.spawnGroup('multi-blocking-group', items, 'no');
|
|
231
|
+
|
|
232
|
+
assert.strictEqual(manager.isGroupRunning('multi-blocking-group'), true);
|
|
233
|
+
|
|
234
|
+
// Let them run
|
|
235
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
236
|
+
|
|
237
|
+
manager.killGroup('multi-blocking-group');
|
|
238
|
+
|
|
239
|
+
assert.strictEqual(manager.isGroupRunning('multi-blocking-group'), false);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe('Server processes', () => {
|
|
244
|
+
it('should manage HTTP server processes', async () => {
|
|
245
|
+
const port = 18080;
|
|
246
|
+
const scriptPath = createServerScript('test-server.js', port);
|
|
247
|
+
|
|
248
|
+
const items: ProcessItem[] = [
|
|
249
|
+
{ name: 'http-server', args: [scriptPath], fullCmd: `node ${scriptPath}` }
|
|
250
|
+
];
|
|
251
|
+
|
|
252
|
+
manager.spawnGroup('server-group', items, 'no');
|
|
253
|
+
|
|
254
|
+
assert.strictEqual(manager.isGroupRunning('server-group'), true);
|
|
255
|
+
|
|
256
|
+
// Wait for server to start
|
|
257
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
258
|
+
|
|
259
|
+
// Optionally verify server is responding (would need fetch/http client)
|
|
260
|
+
// For now, just verify it's running
|
|
261
|
+
assert.strictEqual(manager.isGroupRunning('server-group'), true);
|
|
262
|
+
|
|
263
|
+
manager.killGroup('server-group');
|
|
264
|
+
|
|
265
|
+
assert.strictEqual(manager.isGroupRunning('server-group'), false);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should manage multiple server processes on different ports', async () => {
|
|
269
|
+
const ports = [18081, 18082, 18083];
|
|
270
|
+
const items: ProcessItem[] = [];
|
|
271
|
+
|
|
272
|
+
for (let i = 0; i < ports.length; i++) {
|
|
273
|
+
const scriptPath = createServerScript(`server-${i}.js`, ports[i]);
|
|
274
|
+
items.push({
|
|
275
|
+
name: `server-${ports[i]}`,
|
|
276
|
+
args: [scriptPath],
|
|
277
|
+
fullCmd: `node ${scriptPath}`
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
manager.spawnGroup('multi-server-group', items, 'no');
|
|
282
|
+
|
|
283
|
+
assert.strictEqual(manager.isGroupRunning('multi-server-group'), true);
|
|
284
|
+
|
|
285
|
+
// Wait for servers to start
|
|
286
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
287
|
+
|
|
288
|
+
manager.killGroup('multi-server-group');
|
|
289
|
+
|
|
290
|
+
assert.strictEqual(manager.isGroupRunning('multi-server-group'), false);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe('Long-running sleep processes', () => {
|
|
295
|
+
it('should manage long sleep processes', async () => {
|
|
296
|
+
// Use platform-specific sleep command
|
|
297
|
+
const sleepCmd = process.platform === 'win32' ? 'timeout' : 'sleep';
|
|
298
|
+
const sleepArgs = process.platform === 'win32' ? ['/t', '60'] : ['60'];
|
|
299
|
+
|
|
300
|
+
const items: ProcessItem[] = [
|
|
301
|
+
{
|
|
302
|
+
name: 'long-sleep',
|
|
303
|
+
args: sleepArgs,
|
|
304
|
+
fullCmd: `${sleepCmd} ${sleepArgs.join(' ')}`
|
|
305
|
+
}
|
|
306
|
+
];
|
|
307
|
+
|
|
308
|
+
manager.spawnGroup('sleep-group', items, 'no');
|
|
309
|
+
|
|
310
|
+
assert.strictEqual(manager.isGroupRunning('sleep-group'), true);
|
|
311
|
+
|
|
312
|
+
// Let it sleep briefly
|
|
313
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
314
|
+
|
|
315
|
+
// Should still be running (sleeping for 60 seconds)
|
|
316
|
+
assert.strictEqual(manager.isGroupRunning('sleep-group'), true);
|
|
317
|
+
|
|
318
|
+
// Kill it before it completes
|
|
319
|
+
manager.killGroup('sleep-group');
|
|
320
|
+
|
|
321
|
+
assert.strictEqual(manager.isGroupRunning('sleep-group'), false);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
describe('Process lifecycle with restart policies', () => {
|
|
326
|
+
it('should restart crashing processes with restart=yes', async () => {
|
|
327
|
+
const scriptPath = path.join(testScriptsDir, 'crash-restart.js');
|
|
328
|
+
const scriptContent = `
|
|
329
|
+
// Script that crashes after a short time
|
|
330
|
+
console.log('Starting crash test script...');
|
|
331
|
+
setTimeout(() => {
|
|
332
|
+
console.log('Crashing now!');
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}, 500);
|
|
335
|
+
`;
|
|
336
|
+
fs.writeFileSync(scriptPath, scriptContent);
|
|
337
|
+
|
|
338
|
+
const items: ProcessItem[] = [
|
|
339
|
+
{ name: 'crasher', args: [scriptPath], fullCmd: `node ${scriptPath}` }
|
|
340
|
+
];
|
|
341
|
+
|
|
342
|
+
manager.spawnGroup('crash-restart-group', items, 'yes');
|
|
343
|
+
|
|
344
|
+
assert.strictEqual(manager.isGroupRunning('crash-restart-group'), true);
|
|
345
|
+
|
|
346
|
+
// Wait for first crash and restart attempt
|
|
347
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
348
|
+
|
|
349
|
+
// Group should still be tracked (process may have restarted)
|
|
350
|
+
assert.strictEqual(manager.isGroupRunning('crash-restart-group'), true);
|
|
351
|
+
|
|
352
|
+
manager.killGroup('crash-restart-group');
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('should not restart with restart=no', async () => {
|
|
356
|
+
const scriptPath = path.join(testScriptsDir, 'no-restart.js');
|
|
357
|
+
const scriptContent = `
|
|
358
|
+
console.log('Starting no-restart test...');
|
|
359
|
+
setTimeout(() => {
|
|
360
|
+
console.log('Exiting gracefully');
|
|
361
|
+
process.exit(0);
|
|
362
|
+
}, 500);
|
|
363
|
+
`;
|
|
364
|
+
fs.writeFileSync(scriptPath, scriptContent);
|
|
365
|
+
|
|
366
|
+
const items: ProcessItem[] = [
|
|
367
|
+
{ name: 'no-restart', args: [scriptPath], fullCmd: `node ${scriptPath}` }
|
|
368
|
+
];
|
|
369
|
+
|
|
370
|
+
manager.spawnGroup('no-restart-group', items, 'no');
|
|
371
|
+
|
|
372
|
+
assert.strictEqual(manager.isGroupRunning('no-restart-group'), true);
|
|
373
|
+
|
|
374
|
+
// Wait for process to exit
|
|
375
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
376
|
+
|
|
377
|
+
// Group tracking remains even after process exits
|
|
378
|
+
assert.strictEqual(manager.isGroupRunning('no-restart-group'), true);
|
|
379
|
+
|
|
380
|
+
manager.killGroup('no-restart-group');
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
describe('Stress tests with many blocking processes', () => {
|
|
385
|
+
it('should handle 10 simultaneous blocking processes', async () => {
|
|
386
|
+
const scriptPath = createInfiniteLoopScript('stress-test.js', 1000);
|
|
387
|
+
const items: ProcessItem[] = [];
|
|
388
|
+
|
|
389
|
+
for (let i = 0; i < 10; i++) {
|
|
390
|
+
items.push({
|
|
391
|
+
name: `stress-${i}`,
|
|
392
|
+
args: [scriptPath],
|
|
393
|
+
fullCmd: `node ${scriptPath}`
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
manager.spawnGroup('stress-group', items, 'no');
|
|
398
|
+
|
|
399
|
+
assert.strictEqual(manager.isGroupRunning('stress-group'), true);
|
|
400
|
+
assert.strictEqual(manager.getGroupStatus('stress-group').length, 10);
|
|
401
|
+
|
|
402
|
+
// Let them run
|
|
403
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
404
|
+
|
|
405
|
+
manager.killGroup('stress-group');
|
|
406
|
+
|
|
407
|
+
assert.strictEqual(manager.isGroupRunning('stress-group'), false);
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
describe('Signal handling', () => {
|
|
412
|
+
it('should properly handle SIGTERM on blocking processes', async () => {
|
|
413
|
+
const scriptPath = path.join(testScriptsDir, 'sigterm-test.js');
|
|
414
|
+
const scriptContent = `
|
|
415
|
+
console.log('SIGTERM test started');
|
|
416
|
+
|
|
417
|
+
// Simulate blocking work
|
|
418
|
+
let running = true;
|
|
419
|
+
const workInterval = setInterval(() => {
|
|
420
|
+
if (running) {
|
|
421
|
+
console.log('Working...');
|
|
422
|
+
}
|
|
423
|
+
}, 500);
|
|
424
|
+
|
|
425
|
+
process.on('SIGTERM', () => {
|
|
426
|
+
console.log('Received SIGTERM, cleaning up...');
|
|
427
|
+
running = false;
|
|
428
|
+
clearInterval(workInterval);
|
|
429
|
+
setTimeout(() => {
|
|
430
|
+
console.log('Cleanup complete, exiting');
|
|
431
|
+
process.exit(0);
|
|
432
|
+
}, 100);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// Keep process alive indefinitely
|
|
436
|
+
console.log('Process waiting for signals...');
|
|
437
|
+
`;
|
|
438
|
+
fs.writeFileSync(scriptPath, scriptContent);
|
|
439
|
+
|
|
440
|
+
const items: ProcessItem[] = [
|
|
441
|
+
{ name: 'sigterm-handler', args: [scriptPath], fullCmd: `node ${scriptPath}` }
|
|
442
|
+
];
|
|
443
|
+
|
|
444
|
+
manager.spawnGroup('sigterm-group', items, 'no');
|
|
445
|
+
|
|
446
|
+
assert.strictEqual(manager.isGroupRunning('sigterm-group'), true);
|
|
447
|
+
|
|
448
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
449
|
+
|
|
450
|
+
manager.killGroup('sigterm-group');
|
|
451
|
+
|
|
452
|
+
assert.strictEqual(manager.isGroupRunning('sigterm-group'), false);
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
describe('Mixed process types', () => {
|
|
457
|
+
it('should handle mix of servers, loops, and sleep processes', async () => {
|
|
458
|
+
const items: ProcessItem[] = [];
|
|
459
|
+
|
|
460
|
+
// Add a server
|
|
461
|
+
const serverScript = createServerScript('mixed-server.js', 18090);
|
|
462
|
+
items.push({
|
|
463
|
+
name: 'server',
|
|
464
|
+
args: [serverScript],
|
|
465
|
+
fullCmd: `node ${serverScript}`
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// Add an infinite loop
|
|
469
|
+
const loopScript = createInfiniteLoopScript('mixed-loop.js', 800);
|
|
470
|
+
items.push({
|
|
471
|
+
name: 'loop',
|
|
472
|
+
args: [loopScript],
|
|
473
|
+
fullCmd: `node ${loopScript}`
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// Add a sleep process
|
|
477
|
+
const sleepCmd = process.platform === 'win32' ? 'timeout' : 'sleep';
|
|
478
|
+
const sleepArgs = process.platform === 'win32' ? ['/t', '30'] : ['30'];
|
|
479
|
+
items.push({
|
|
480
|
+
name: 'sleep',
|
|
481
|
+
args: sleepArgs,
|
|
482
|
+
fullCmd: `${sleepCmd} ${sleepArgs.join(' ')}`
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
manager.spawnGroup('mixed-group', items, 'no');
|
|
486
|
+
|
|
487
|
+
assert.strictEqual(manager.isGroupRunning('mixed-group'), true);
|
|
488
|
+
assert.strictEqual(manager.getGroupStatus('mixed-group').length, 3);
|
|
489
|
+
|
|
490
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
491
|
+
|
|
492
|
+
manager.killGroup('mixed-group');
|
|
493
|
+
|
|
494
|
+
assert.strictEqual(manager.isGroupRunning('mixed-group'), false);
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
});
|