Protect upload cleanup and atomic completion paths, add durable media worker leases and brand asset validation, and introduce verified migration/bootstrap/deployment gates.
58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
/** @jest-environment node */
|
|
|
|
import {
|
|
createVideoTranscodeRecoveryScheduler,
|
|
VIDEO_TRANSCODE_RECOVERY_INTERVAL_MS,
|
|
} from '@/lib/video-transcode-recovery'
|
|
|
|
describe('video transcode startup recovery scheduler', () => {
|
|
beforeEach(() => {
|
|
jest.useFakeTimers()
|
|
})
|
|
|
|
afterEach(() => {
|
|
jest.useRealTimers()
|
|
})
|
|
|
|
test('创建后立即扫描,并按 60 秒周期继续扫描', async () => {
|
|
const scan = jest.fn().mockResolvedValue(0)
|
|
const scheduler = createVideoTranscodeRecoveryScheduler({ scan })
|
|
|
|
await scheduler.initial
|
|
expect(scan).toHaveBeenCalledTimes(1)
|
|
|
|
await jest.advanceTimersByTimeAsync(VIDEO_TRANSCODE_RECOVERY_INTERVAL_MS)
|
|
expect(scan).toHaveBeenCalledTimes(2)
|
|
scheduler.stop()
|
|
})
|
|
|
|
test('本轮失败不会取消定时器,下一轮会重试', async () => {
|
|
const onError = jest.fn()
|
|
const scan = jest.fn()
|
|
.mockRejectedValueOnce(new Error('database unavailable'))
|
|
.mockResolvedValueOnce(2)
|
|
const scheduler = createVideoTranscodeRecoveryScheduler({ scan, onError })
|
|
|
|
await expect(scheduler.initial).rejects.toThrow('database unavailable')
|
|
expect(onError).toHaveBeenCalledTimes(1)
|
|
|
|
await jest.advanceTimersByTimeAsync(VIDEO_TRANSCODE_RECOVERY_INTERVAL_MS)
|
|
expect(scan).toHaveBeenCalledTimes(2)
|
|
scheduler.stop()
|
|
})
|
|
|
|
test('上一轮未结束时不启动重叠扫描', async () => {
|
|
let release!: (value: number) => void
|
|
const pending = new Promise<number>((resolve) => { release = resolve })
|
|
const scan = jest.fn(() => pending)
|
|
const scheduler = createVideoTranscodeRecoveryScheduler({ scan })
|
|
|
|
await jest.advanceTimersByTimeAsync(VIDEO_TRANSCODE_RECOVERY_INTERVAL_MS * 2)
|
|
expect(scan).toHaveBeenCalledTimes(1)
|
|
|
|
release(0)
|
|
await scheduler.initial
|
|
scheduler.stop()
|
|
})
|
|
})
|