nexstream-api / backend /tests /core /process_lifecycle.test.ts
circleci
deploy ccb248c
65fc551
Raw
History Blame Contribute Delete
1.65 kB
import { describe, it, expect, vi } from 'vitest';
import { EventEmitter } from 'node:events';
import { spawn } from 'node:child_process';
import { setupStreamListeners } from '../../src/utils/media/stream.util.js';
import { Response } from 'express';
import path from 'node:path';
import { Readable } from 'node:stream';
describe('FFmpeg Process Lifecycle & Sandbox Cleanup', () => {
it('Should systematically terminate active OS processes when client disconnects abruptly', async () => {
// spawn FFmpeg
// loop process
const fixturePath = path.resolve(
__dirname,
'../fixtures/audio/minimal_sine.mp3'
);
const dummyProcess = spawn(
'ffmpeg',
[
'-stream_loop',
'-1', // loop indefinitely
'-i',
fixturePath,
'-f',
'mp3',
'pipe:1',
],
{ detached: true }
);
const pid = dummyProcess.pid;
expect(pid).toBeGreaterThan(0);
// mock Response
const mockRes = new EventEmitter() as unknown as Response;
mockRes.writableEnded = false;
mockRes.write = vi.fn();
mockRes.end = vi.fn();
const totalBytes = { value: 0 };
// bind hooks
setupStreamListeners(
dummyProcess.stdout as unknown as Readable,
mockRes,
'test-client-id',
totalBytes
);
// wait init
await new Promise((resolve) => setTimeout(resolve, 50));
// trigger kill
dummyProcess.kill('SIGKILL');
// wait cleanup
await new Promise((resolve) => setTimeout(resolve, 150));
// verify death
// expect ESRCH
expect(() => process.kill(pid as number, 0)).toThrow();
});
});