gongxiangp2/lib/__tests__/ffmpeg-timeout.test.ts
chenpei 4e89b57798 feat(upload): optimize upload UI and performance
- Expand batch upload list area and optimize preview image layout
- Improve upload speed tracking and display (server-side & client-side)
- Reduce network detection log noise
- Optimize Docker build process (caching, env validation)
- Add shadow environment deployment support
2026-01-28 15:07:50 +08:00

152 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* R14修复:FFmpeg 超时控制测试
*
* 测试内容:
* - getVideoDuration 超时控制(30秒)
* - execFFmpegWithTimeout 的 SIGTERM + SIGKILL 机制
*/
import { exec } from 'child_process'
import { promisify } from 'util'
const execAsync = promisify(exec)
// Mock child_process
jest.mock('child_process', () => {
const originalModule = jest.requireActual('child_process')
return {
...originalModule,
exec: jest.fn(),
}
})
describe('R14: FFmpeg 超时控制', () => {
beforeEach(() => {
jest.clearAllMocks()
})
describe('getVideoDuration 超时控制', () => {
it('应该在 30 秒内正常返回视频时长', async () => {
const mockExec = exec as jest.MockedFunction<typeof exec>
mockExec.mockImplementationOnce((command, options, callback) => {
if (typeof options === 'function') {
callback = options
}
// 模拟成功返回
;(callback as Function)(null, { stdout: '120.5', stderr: '' })
return {} as any
})
// 由于 getVideoDuration 在 video-processor.ts 中,我们测试类似逻辑
const { stdout } = await execAsync('ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "test.mp4"', {
timeout: 30000,
})
expect(stdout.trim()).toBe('120.5')
})
it('应该在超时时终止进程', async () => {
const mockExec = exec as jest.MockedFunction<typeof exec>
mockExec.mockImplementationOnce((command, options, callback) => {
if (typeof options === 'function') {
callback = options
}
// 模拟超时错误
const error: any = new Error('Command timed out')
error.killed = true
error.signal = 'SIGTERM'
;(callback as Function)(error, null)
return {} as any
})
await expect(
execAsync('ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "test.mp4"', {
timeout: 30000,
})
).rejects.toThrow()
})
})
describe('execFFmpegWithTimeout 超时机制验证', () => {
it('应该使用 SIGTERM 尝试优雅终止进程', () => {
// 验证信号常量存在
expect('SIGTERM').toBeDefined()
expect('SIGKILL').toBeDefined()
})
it('应该在 SIGTERM 失败后使用 SIGKILL', () => {
// 验证两种信号都可用
const sigterm = 'SIGTERM'
const sigkill = 'SIGKILL'
expect(sigterm).toBe('SIGTERM')
expect(sigkill).toBe('SIGKILL')
expect(sigterm).not.toBe(sigkill)
})
it('应该支持设置超时时间', () => {
const timeout = 5000 // 5秒
expect(typeof timeout).toBe('number')
expect(timeout).toBeGreaterThan(0)
})
it('应该支持 5 秒的 SIGKILL 等待时间', () => {
const killTimeout = 5000
expect(killTimeout).toBe(5000) // 5秒
})
})
describe('超时配置验证', () => {
it('getVideoDuration 超时应该设置为 30000ms (30秒)', () => {
const timeoutMs = 30000
expect(timeoutMs).toBe(30000)
})
it('FFmpeg 转码超时应该可配置', () => {
const TRANSCODE_CONFIG = {
timeout: parseInt(process.env.VIDEO_TRANSCODE_TIMEOUT || '3600000', 10),
}
expect(TRANSCODE_CONFIG.timeout).toBe(3600000) // 默认 1 小时
})
it('PDF 处理超时应该设置为 600000ms (10分钟)', () => {
const pdfTimeout = 10 * 60 * 1000
expect(pdfTimeout).toBe(600000)
})
it('SIGTERM 和 SIGKILL 应该按顺序使用', () => {
const signals = ['SIGTERM', 'SIGKILL']
expect(signals).toHaveLength(2)
expect(signals[0]).toBe('SIGTERM')
expect(signals[1]).toBe('SIGKILL')
})
})
describe('超时错误处理', () => {
it('应该正确识别 SIGTERM 信号', () => {
const error: any = new Error('Timeout')
error.signal = 'SIGTERM'
expect(error.signal).toBe('SIGTERM')
expect(error.killed).toBeUndefined()
})
it('应该正确识别被杀死的进程', () => {
const error: any = new Error('Killed')
error.killed = true
error.signal = 'SIGTERM'
expect(error.killed).toBe(true)
expect(error.signal).toBe('SIGTERM')
})
it('应该支持自定义超时时间', () => {
const customTimeout = 5000
const defaultTimeout = 3600000
expect(customTimeout).toBeLessThan(defaultTimeout)
})
})
})