ai-chatbot/tests/routes/video-canvas-audio-remote.test.ts

137 lines
4.7 KiB
TypeScript

import { test, expect } from "@playwright/test";
import { mkdtemp, writeFile, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { createHash } from "node:crypto";
import {
remoteAudio,
remoteAudioCapabilities,
} from "@/lib/video-canvas/smart-edit/remote-audio";
import { ProviderTaskUncertainError } from "@/lib/video-canvas/video-task-lifecycle";
test.describe.configure({ mode: "serial" });
const originalFetch = global.fetch,
oldUrl = process.env.SMART_EDIT_AUDIO_URL,
oldToken = process.env.SMART_EDIT_AUDIO_TOKEN;
test.beforeEach(() => {
process.env.SMART_EDIT_AUDIO_URL = "http://127.0.0.1:19342";
process.env.SMART_EDIT_AUDIO_TOKEN = "test-".repeat(8);
});
test.afterEach(() => {
global.fetch = originalFetch;
if (oldUrl === undefined) delete process.env.SMART_EDIT_AUDIO_URL;
else process.env.SMART_EDIT_AUDIO_URL = oldUrl;
if (oldToken === undefined) delete process.env.SMART_EDIT_AUDIO_TOKEN;
else process.env.SMART_EDIT_AUDIO_TOKEN = oldToken;
});
test("audio uploads once, resumes same identity, and isolates tenant/task/content", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "audio-identity-")),
file = path.join(dir, "input.wav");
await writeFile(file, "fixture bytes");
const ids: string[] = [],
jobs = new Set<string>();
let uploads = 0;
let expected = "fixture bytes";
global.fetch = (async (_url: any, init: any) => {
if (init.method === "POST") {
const input = JSON.parse(init.body);
ids.push(input.id);
return Response.json({
id: input.id,
status: jobs.has(input.id) ? "succeeded" : "awaiting-input",
stage: "ready",
...(jobs.has(input.id)
? {
result: {
durationSeconds: 1,
subtitles: [{ start: 0, end: 800000, text: "测试" }],
},
}
: {}),
});
}
const id = String(_url).split("/").at(-2)!;
let data = "";
for await (const c of init.body) data += c;
expect(data).toBe(expected);
uploads++;
jobs.add(id);
return Response.json({
id,
status: "succeeded",
stage: "done",
result: {
durationSeconds: 1,
subtitles: [{ start: 0, end: 800000, text: "测试" }],
},
});
}) as any;
try {
await remoteAudio("transcribe", file, "owner", "task", dir);
await remoteAudio("transcribe", file, "owner", "task", dir);
expect(uploads).toBe(1);
expect(ids[0]).toBe(ids[1]);
await remoteAudio("transcribe", file, "other", "task", dir);
await remoteAudio("transcribe", file, "owner", "other-task", dir);
expect(new Set(ids).size).toBe(3);
expected = "updated fixture";
await writeFile(file, expected);
await remoteAudio("transcribe", file, "owner", "task", dir);
expect(new Set(ids).size).toBe(4);
expect(uploads).toBe(4);
} finally {
await rm(dir, { recursive: true, force: true });
}
});
test("unavailable remote audio remains recoverable and does not advertise local fallback", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "audio-offline-")),
file = path.join(dir, "input.wav");
await writeFile(file, "audio");
global.fetch = (async () => {
throw Error("offline");
}) as any;
try {
expect(await remoteAudioCapabilities()).toEqual({
asr: false,
separation: false,
});
await expect(
remoteAudio("transcribe", file, "u", "t", dir)
).rejects.toBeInstanceOf(ProviderTaskUncertainError);
} finally {
await rm(dir, { recursive: true, force: true });
}
});
test("corrupt separated download never replaces saved stem", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "audio-integrity-")),
file = path.join(dir, "input.wav");
await writeFile(file, "audio");
await writeFile(path.join(dir, "vocals.wav"), "previous");
const bytes = Buffer.from("expected"),
sha256 = createHash("sha256").update(bytes).digest("hex");
global.fetch = (async (_url: any, init: any) =>
init.method === "POST"
? Response.json({
id: JSON.parse(init.body).id,
status: "succeeded",
stage: "done",
result: {
durationSeconds: 1,
stems: {
vocals: { bytes: bytes.length, sha256 },
background: { bytes: bytes.length, sha256 },
},
},
})
: new Response("corrupt!")) as any;
try {
await expect(
remoteAudio("separate", file, "u", "t", dir)
).rejects.toBeInstanceOf(ProviderTaskUncertainError);
expect(await readFile(path.join(dir, "vocals.wav"), "utf8")).toBe(
"previous"
);
} finally {
await rm(dir, { recursive: true, force: true });
}
});