- 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
152 lines
4.4 KiB
TypeScript
152 lines
4.4 KiB
TypeScript
/**
|
||
* 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)
|
||
})
|
||
})
|
||
})
|