-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathVideoTranscoder.test.ts
More file actions
91 lines (74 loc) · 3.25 KB
/
VideoTranscoder.test.ts
File metadata and controls
91 lines (74 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import { VideoTranscoder } from './VideoTranscoder';
import { spawn } from 'child_process';
import { promises as fsPromises } from 'fs';
import { S3Client } from '@aws-sdk/client-s3';
import { EventEmitter } from 'events';
// --- Mock Dependencies ---
jest.mock('child_process', () => ({
spawn: jest.fn(),
}));
jest.mock('fs', () => ({
createReadStream: jest.fn().mockReturnValue('mocked-stream'),
promises: {
mkdir: jest.fn().mockResolvedValue(undefined),
readdir: jest.fn().mockResolvedValue(['master.m3u8', '1080p_playlist.m3u8', '1080p_sequence_0.ts']),
rm: jest.fn().mockResolvedValue(undefined),
},
}));
jest.mock('@aws-sdk/client-s3', () => {
return {
S3Client: jest.fn().mockImplementation(() => ({
send: jest.fn().mockResolvedValue({}),
})),
PutObjectCommand: jest.fn().mockImplementation((args) => args),
};
});
describe('VideoTranscoder Pipeline', () => {
let transcoder: VideoTranscoder;
const outputDir = '/tmp/hls-out';
const inputPath = '/tmp/raw/upload.mp4';
const s3Prefix = 'videos/creator123';
beforeEach(() => {
jest.clearAllMocks();
transcoder = new VideoTranscoder('us-east-1', 'substream-video-bucket');
});
it('should process a video to HLS, upload to S3, and cleanup local files', async () => {
// Setup mock FFmpeg process
const mockFfmpegProcess = new EventEmitter() as any;
mockFfmpegProcess.stderr = new EventEmitter();
(spawn as jest.Mock).mockReturnValue(mockFfmpegProcess);
// Execute the transcoder asynchronously
const processPromise = transcoder.processAndUpload(inputPath, outputDir, s3Prefix);
// Simulate FFmpeg finishing successfully
setTimeout(() => mockFfmpegProcess.emit('close', 0), 10);
await processPromise;
// 1. Verify Directory was created
expect(fsPromises.mkdir).toHaveBeenCalledWith(outputDir, { recursive: true });
// 2. Verify FFmpeg was spawned with expected HLS and Resolution arguments
expect(spawn).toHaveBeenCalledWith('ffmpeg', expect.arrayContaining([
'-i', inputPath,
'-s:v:0', '1920x1080', // 1080p
'-s:v:1', '1280x720', // 720p
'-s:v:2', '854x480', // 480p
'-master_pl_name', 'master.m3u8',
'-f', 'hls'
]));
// 3. Verify S3 client was invoked for all mocked directory files
// Since we mocked 3 files in fsPromises.readdir, we expect 3 S3 uploads
expect(S3Client).toHaveBeenCalledTimes(1);
const s3Instance = (S3Client as jest.Mock).mock.results[0].value;
expect(s3Instance.send).toHaveBeenCalledTimes(3);
// 4. Verify cleanup was triggered
expect(fsPromises.rm).toHaveBeenCalledWith(outputDir, { recursive: true, force: true });
});
it('should throw an error and still cleanup if FFmpeg fails', async () => {
const mockFfmpegProcess = new EventEmitter() as any;
mockFfmpegProcess.stderr = new EventEmitter();
(spawn as jest.Mock).mockReturnValue(mockFfmpegProcess);
const processPromise = transcoder.processAndUpload(inputPath, outputDir, s3Prefix);
// Simulate FFmpeg failing
setTimeout(() => mockFfmpegProcess.emit('close', 1), 10);
await expect(processPromise).rejects.toThrow('FFmpeg process exited with code 1');
expect(fsPromises.rm).toHaveBeenCalledWith(outputDir, { recursive: true, force: true });
});
});