Download src/media/audio-transcode.test.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 9.06 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/media/audio-transcode.test.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/media/audio-transcode.test.ts
-
curl -L -o audio-transcode.test.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/media/audio-transcode.test.ts
9.06 kB
| // Audio transcode tests cover ffmpeg-backed audio conversion behavior. | |
| import { existsSync, realpathSync } from "node:fs"; | |
| import { readFile, writeFile } from "node:fs/promises"; | |
| import path from "node:path"; | |
| import { __setFsSafeTestHooksForTest } from "@openclaw/fs-safe/test-hooks"; | |
| import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest"; | |
| import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js"; | |
| const runFfmpegMock = vi.hoisted(() => vi.fn()); | |
| const runCommandWithTimeoutMock = vi.hoisted(() => vi.fn()); | |
| vi.mock("./ffmpeg-exec.js", () => ({ | |
| runFfmpeg: runFfmpegMock, | |
| })); | |
| vi.mock("../process/exec.js", () => ({ | |
| runCommandWithTimeout: runCommandWithTimeoutMock, | |
| })); | |
| let transcodeAudioBuffer: typeof import("./audio-transcode.js").transcodeAudioBuffer; | |
| let transcodeAudioBufferToOpus: typeof import("./audio-transcode.js").transcodeAudioBufferToOpus; | |
| beforeAll(async () => { | |
| vi.resetModules(); | |
| ({ transcodeAudioBuffer, transcodeAudioBufferToOpus } = await import("./audio-transcode.js")); | |
| }); | |
| afterAll(() => { | |
| vi.doUnmock("./ffmpeg-exec.js"); | |
| vi.resetModules(); | |
| }); | |
| type MockWithCalls = { mock: { calls: unknown[][] } }; | |
| function firstMockCall(mock: MockWithCalls, label: string): unknown[] { | |
| const call = mock.mock.calls[0]; | |
| if (!call) { | |
| throw new Error(`expected ${label} call`); | |
| } | |
| return call; | |
| } | |
| describe("transcodeAudioBufferToOpus", () => { | |
| afterEach(() => { | |
| runFfmpegMock.mockReset(); | |
| }); | |
| it("writes input audio, runs ffmpeg for 48k mono Opus, and cleans temp files", async () => { | |
| let capturedInputPath: string | undefined; | |
| let capturedOutputPath: string | undefined; | |
| runFfmpegMock.mockImplementationOnce(async (args: string[]) => { | |
| capturedInputPath = args[args.indexOf("-i") + 1]; | |
| capturedOutputPath = args.at(-1); | |
| const inputPath = capturedInputPath; | |
| const outputPath = capturedOutputPath; | |
| if (!inputPath || !outputPath) { | |
| throw new Error("missing ffmpeg paths"); | |
| } | |
| await expect(readFile(inputPath)).resolves.toEqual(Buffer.from("source-mp3")); | |
| await import("node:fs/promises").then((fs) => | |
| fs.writeFile(outputPath, Buffer.from("opus-output")), | |
| ); | |
| }); | |
| await expect( | |
| transcodeAudioBufferToOpus({ | |
| audioBuffer: Buffer.from("source-mp3"), | |
| inputExtension: "mp3", | |
| tempPrefix: "tts-test-", | |
| timeoutMs: 1234, | |
| }), | |
| ).resolves.toEqual(Buffer.from("opus-output")); | |
| expect(runFfmpegMock).toHaveBeenCalledTimes(1); | |
| expect(firstMockCall(runFfmpegMock, "runFfmpeg")).toStrictEqual([ | |
| [ | |
| "-hide_banner", | |
| "-loglevel", | |
| "error", | |
| "-y", | |
| "-i", | |
| capturedInputPath, | |
| "-vn", | |
| "-sn", | |
| "-dn", | |
| "-c:a", | |
| "libopus", | |
| "-b:a", | |
| "64k", | |
| "-ar", | |
| "48000", | |
| "-ac", | |
| "1", | |
| "-f", | |
| "opus", | |
| capturedOutputPath, | |
| ], | |
| { timeoutMs: 1234 }, | |
| ]); | |
| const tempRoot = realpathSync(resolvePreferredOpenClawTmpDir()); | |
| expect(capturedInputPath?.startsWith(path.join(tempRoot, "tts-test-"))).toBe(true); | |
| expect(capturedInputPath ? existsSync(capturedInputPath) : true).toBe(false); | |
| expect(capturedOutputPath ? existsSync(capturedOutputPath) : true).toBe(false); | |
| }); | |
| it("sanitizes unsafe input extensions", async () => { | |
| runFfmpegMock.mockImplementationOnce(async (args: string[]) => { | |
| const inputPath = args[args.indexOf("-i") + 1]; | |
| const outputPath = args.at(-1); | |
| if (!inputPath || !outputPath) { | |
| throw new Error("missing ffmpeg paths"); | |
| } | |
| expect(path.basename(inputPath)).toBe("input.audio"); | |
| await import("node:fs/promises").then((fs) => | |
| fs.writeFile(outputPath, Buffer.from("opus-output")), | |
| ); | |
| }); | |
| await transcodeAudioBufferToOpus({ | |
| audioBuffer: Buffer.from("source"), | |
| inputExtension: "../bad", | |
| }); | |
| }); | |
| it("passes the maximum duration to ffmpeg when requested", async () => { | |
| runFfmpegMock.mockImplementationOnce(async (args: string[]) => { | |
| const outputPath = args.at(-1); | |
| if (!outputPath) { | |
| throw new Error("missing ffmpeg output path"); | |
| } | |
| await writeFile(outputPath, Buffer.from("ogg-opus-output")); | |
| }); | |
| await transcodeAudioBufferToOpus({ | |
| audioBuffer: Buffer.from("source-m4a"), | |
| maxDurationSeconds: 300, | |
| }); | |
| const ffmpegArgs = firstMockCall(runFfmpegMock, "runFfmpeg")[0] as string[]; | |
| expect(ffmpegArgs).toEqual(expect.arrayContaining(["-t", "300", "-c:a", "libopus"])); | |
| }); | |
| it("keeps temp prefixes and output names inside the preferred temp root", async () => { | |
| let capturedInputPath: string | undefined; | |
| let capturedOutputPath: string | undefined; | |
| runFfmpegMock.mockImplementationOnce(async (args: string[]) => { | |
| capturedInputPath = args[args.indexOf("-i") + 1]; | |
| capturedOutputPath = args.at(-1); | |
| const outputPath = capturedOutputPath; | |
| if (!outputPath) { | |
| throw new Error("missing ffmpeg output path"); | |
| } | |
| const outputBaseName = path.basename(outputPath); | |
| expect(outputBaseName).toContain("escape.opus"); | |
| expect(outputBaseName).toMatch(/\.part$/); | |
| await import("node:fs/promises").then((fs) => | |
| fs.writeFile(outputPath, Buffer.from("opus-output")), | |
| ); | |
| }); | |
| await transcodeAudioBufferToOpus({ | |
| audioBuffer: Buffer.from("source"), | |
| inputFileName: "voice.wav", | |
| outputFileName: "../escape.opus", | |
| tempPrefix: "../bad-prefix", | |
| }); | |
| const tempRoot = realpathSync(resolvePreferredOpenClawTmpDir()); | |
| expect(capturedInputPath?.startsWith(tempRoot)).toBe(true); | |
| expect(capturedOutputPath ? existsSync(capturedOutputPath) : true).toBe(false); | |
| }); | |
| it("preserves Windows-style output filename leaves on POSIX hosts", async () => { | |
| let capturedOutputPath: string | undefined; | |
| runFfmpegMock.mockImplementationOnce(async (args: string[]) => { | |
| capturedOutputPath = args.at(-1); | |
| const outputPath = capturedOutputPath; | |
| if (!outputPath) { | |
| throw new Error("missing ffmpeg output path"); | |
| } | |
| expect(path.basename(outputPath)).toContain("reply.opus"); | |
| await import("node:fs/promises").then((fs) => | |
| fs.writeFile(outputPath, Buffer.from("opus-output")), | |
| ); | |
| }); | |
| await transcodeAudioBufferToOpus({ | |
| audioBuffer: Buffer.from("source"), | |
| outputFileName: String.raw`C:\Users\Ada\Downloads\reply.opus`, | |
| }); | |
| expect(capturedOutputPath ? existsSync(capturedOutputPath) : true).toBe(false); | |
| }); | |
| }); | |
| describe("transcodeAudioBuffer", () => { | |
| afterEach(() => { | |
| __setFsSafeTestHooksForTest(undefined); | |
| vi.restoreAllMocks(); | |
| runCommandWithTimeoutMock.mockReset(); | |
| runFfmpegMock.mockReset(); | |
| }); | |
| it("returns a failure and cleans its workspace when input staging fails", async () => { | |
| vi.spyOn(process, "platform", "get").mockReturnValue("darwin"); | |
| let workspaceDir: string | undefined; | |
| __setFsSafeTestHooksForTest({ | |
| beforeFileStoreSyncPrivateWrite: (filePath) => { | |
| workspaceDir = path.dirname(filePath); | |
| throw Object.assign(new Error("input exceeds bounded staging limit"), { code: "EFBIG" }); | |
| }, | |
| }); | |
| const result = await transcodeAudioBuffer({ | |
| audioBuffer: Buffer.from("payload"), | |
| sourceExtension: "mp3", | |
| targetExtension: "caf", | |
| }); | |
| expect(result).toEqual({ | |
| ok: false, | |
| reason: "transcoder-failed", | |
| detail: "input exceeds bounded staging limit", | |
| }); | |
| expect(workspaceDir).toBeDefined(); | |
| expect(workspaceDir ? existsSync(workspaceDir) : true).toBe(false); | |
| expect(runCommandWithTimeoutMock).not.toHaveBeenCalled(); | |
| }); | |
| it.each([ | |
| [ | |
| "returns noop-same-container when source and target containers match", | |
| "mp3", | |
| ".mp3", | |
| "noop-same-container", | |
| ], | |
| [ | |
| "returns no-recipe when no afconvert recipe is defined for the requested pair", | |
| "mp3", | |
| "flac", | |
| "no-recipe", | |
| ], | |
| ["returns invalid-extension for an empty source extension", "", "caf", "invalid-extension"], | |
| ["returns invalid-extension for an empty target extension", "mp3", "", "invalid-extension"], | |
| ["rejects path-traversal style extensions", "../etc/passwd", "caf", "invalid-extension"], | |
| ])("%s", async (_name, sourceExtension, targetExtension, reason) => { | |
| const result = await transcodeAudioBuffer({ | |
| audioBuffer: Buffer.from("payload"), | |
| sourceExtension, | |
| targetExtension, | |
| }); | |
| expect(result).toEqual({ ok: false, reason }); | |
| }); | |
| it("returns platform-unsupported off-Darwin without invoking afconvert", async () => { | |
| if (process.platform === "darwin") { | |
| return; | |
| } | |
| const result = await transcodeAudioBuffer({ | |
| audioBuffer: Buffer.from("payload"), | |
| sourceExtension: "mp3", | |
| targetExtension: "caf", | |
| }); | |
| expect(result).toEqual({ ok: false, reason: "platform-unsupported" }); | |
| }); | |
| }); | |