openclaw / src /media /ffmpeg-exec.test.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
3144483 verified
Raw History Blame Contribute Delete
4.11 kB
// FFmpeg exec tests cover command execution wrappers and error mapping.
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const { runExecMock, resolveSystemBinMock } = vi.hoisted(() => ({
runExecMock: vi.fn(),
resolveSystemBinMock: vi.fn(),
}));
vi.mock("../process/exec.js", () => ({
runExec: runExecMock,
}));
vi.mock("../infra/resolve-system-bin.js", () => ({
resolveSystemBin: resolveSystemBinMock,
}));
let parseFfprobeCodecAndSampleRate: typeof import("./ffmpeg-exec.js").parseFfprobeCodecAndSampleRate;
let resolveFfmpegBin: typeof import("./ffmpeg-exec.js").resolveFfmpegBin;
let runFfprobe: typeof import("./ffmpeg-exec.js").runFfprobe;
beforeAll(async () => {
vi.resetModules();
({ parseFfprobeCodecAndSampleRate, resolveFfmpegBin, runFfprobe } =
await import("./ffmpeg-exec.js"));
});
afterAll(() => {
vi.doUnmock("../process/exec.js");
vi.doUnmock("../infra/resolve-system-bin.js");
vi.resetModules();
});
beforeEach(() => {
runExecMock.mockReset();
resolveSystemBinMock.mockReset();
resolveSystemBinMock.mockReturnValue("/usr/bin/ffprobe");
});
describe("parseFfprobeCodecAndSampleRate", () => {
function expectParsedCodecAndSampleRateCase(
input: string,
expected: { codec: string | null; sampleRateHz: number | null },
) {
expect(parseFfprobeCodecAndSampleRate(input)).toEqual(expected);
}
it.each([
{
name: "normalizes codec casing and parses numeric sample rates",
input: "Opus,48000\n",
expected: {
codec: "opus",
sampleRateHz: 48_000,
},
},
{
name: "keeps codec when the sample rate is not numeric",
input: "opus,not-a-number",
expected: {
codec: "opus",
sampleRateHz: null,
},
},
{
name: "rejects partially numeric sample rates",
input: "opus,48000hz",
expected: {
codec: "opus",
sampleRateHz: null,
},
},
{
name: "rejects missing sample rates",
input: "opus,",
expected: {
codec: "opus",
sampleRateHz: null,
},
},
{
name: "rejects zero sample rates",
input: "opus,0",
expected: {
codec: "opus",
sampleRateHz: null,
},
},
{
name: "rejects signed sample rates",
input: "opus,-48000",
expected: {
codec: "opus",
sampleRateHz: null,
},
},
] as const)("$name", ({ input, expected }) => {
expectParsedCodecAndSampleRateCase(input, expected);
});
});
describe("runFfprobe", () => {
it("passes stdin and limits through the canonical exec wrapper", async () => {
const input = Buffer.from("audio");
runExecMock.mockResolvedValue({ stdout: "ok", stderr: "" });
await expect(
runFfprobe(["pipe:0"], { input, timeoutMs: 1234, maxBufferBytes: 5678 }),
).resolves.toBe("ok");
expect(runExecMock).toHaveBeenCalledWith("/usr/bin/ffprobe", ["pipe:0"], {
input,
logOutput: false,
maxBuffer: 5678,
timeoutMs: 1234,
});
});
it("passes an inherited file descriptor through the canonical exec wrapper", async () => {
runExecMock.mockResolvedValue({ stdout: "ok", stderr: "" });
await expect(runFfprobe(["pipe:0"], { stdinFileDescriptor: 17 })).resolves.toBe("ok");
expect(runExecMock).toHaveBeenCalledWith("/usr/bin/ffprobe", ["pipe:0"], {
logOutput: false,
maxBuffer: 10 * 1024 * 1024,
stdinFileDescriptor: 17,
timeoutMs: 10_000,
});
});
it("preserves wrapper execution errors", async () => {
const childError = new Error("ffprobe failed");
runExecMock.mockRejectedValue(childError);
await expect(runFfprobe(["pipe:0"], { input: Buffer.from("audio") })).rejects.toBe(childError);
});
});
describe("resolveFfmpegBin", () => {
it("resolves ffmpeg from trusted system paths", () => {
resolveSystemBinMock.mockReturnValue("/usr/bin/ffmpeg");
expect(resolveFfmpegBin()).toBe("/usr/bin/ffmpeg");
expect(resolveSystemBinMock).toHaveBeenCalledWith("ffmpeg", { trust: "standard" });
});
});