Skip to content

Commit b3bf980

Browse files
committed
test(media-server): add POST /video/convert route tests
Made-with: Cursor
1 parent 44ece3d commit b3bf980

File tree

1 file changed

+89
-1
lines changed

1 file changed

+89
-1
lines changed

apps/media-server/src/__tests__/routes/video.test.ts

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,90 @@ describe("POST /video/thumbnail", () => {
251251
});
252252
});
253253

254+
describe("POST /video/convert", () => {
255+
beforeEach(() => {
256+
mock.restore();
257+
});
258+
259+
test("returns 400 for missing videoUrl", async () => {
260+
const response = await app.fetch(
261+
new Request("http://localhost/video/convert", {
262+
method: "POST",
263+
headers: { "Content-Type": "application/json" },
264+
body: JSON.stringify({}),
265+
}),
266+
);
267+
268+
expect(response.status).toBe(400);
269+
const data = await response.json();
270+
expect(data.code).toBe("INVALID_REQUEST");
271+
});
272+
273+
test("returns mp4 when conversion succeeds", async () => {
274+
const fixturePath = new URL(
275+
"../fixtures/test-with-audio.mp4",
276+
import.meta.url,
277+
).pathname;
278+
const fixtureBytes = await Bun.file(fixturePath).bytes();
279+
const mockMetadata = {
280+
duration: 10.5,
281+
width: 1280,
282+
height: 720,
283+
fps: 30,
284+
videoCodec: "h264",
285+
audioCodec: "aac",
286+
audioChannels: 2,
287+
sampleRate: 48000,
288+
bitrate: 5000000,
289+
fileSize: fixtureBytes.length,
290+
};
291+
292+
mock.module("../../lib/ffprobe", () => ({
293+
probeVideo: ffprobe.probeVideo,
294+
probeVideoFile: async () => mockMetadata,
295+
canAcceptNewProbeProcess: ffprobe.canAcceptNewProbeProcess,
296+
getActiveProbeProcessCount: ffprobe.getActiveProbeProcessCount,
297+
}));
298+
299+
mock.module("../../lib/ffmpeg-video", () => ({
300+
downloadVideoToTemp: async () => ({
301+
path: fixturePath,
302+
cleanup: async () => {},
303+
}),
304+
processVideo: async () => ({
305+
path: fixturePath,
306+
cleanup: async () => {},
307+
}),
308+
generateThumbnail: ffmpegVideo.generateThumbnail,
309+
repairContainer: ffmpegVideo.repairContainer,
310+
uploadToS3: ffmpegVideo.uploadToS3,
311+
uploadFileToS3: ffmpegVideo.uploadFileToS3,
312+
}));
313+
314+
const { default: appWithMock } = await import("../../app");
315+
316+
const response = await appWithMock.fetch(
317+
new Request("http://localhost/video/convert", {
318+
method: "POST",
319+
headers: { "Content-Type": "application/json" },
320+
body: JSON.stringify({
321+
videoUrl: "https://example.com/video.m3u8",
322+
inputExtension: ".m3u8",
323+
}),
324+
}),
325+
);
326+
327+
expect(response.status).toBe(200);
328+
expect(response.headers.get("Content-Type")).toBe("video/mp4");
329+
expect(response.headers.get("Content-Length")).toBe(
330+
fixtureBytes.length.toString(),
331+
);
332+
333+
const buffer = await response.arrayBuffer();
334+
expect(new Uint8Array(buffer)).toEqual(fixtureBytes);
335+
});
336+
});
337+
254338
describe("POST /video/process", () => {
255339
beforeEach(() => {
256340
mock.restore();
@@ -428,7 +512,11 @@ describe("GET /video/process/:jobId/status", () => {
428512
updateJob: () => null,
429513
deleteJob: () => {},
430514
sendWebhook: async () => {},
431-
getJobProgress: (job: any) => ({
515+
getJobProgress: (job: {
516+
phase: string;
517+
progress: number;
518+
message?: string;
519+
}) => ({
432520
phase: job.phase,
433521
progress: job.progress,
434522
message: job.message,

0 commit comments

Comments
 (0)