Spaces:
Paused
Paused
| 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(); | |
| }); | |
| }); | |