Files
2026-07-13 13:39:12 +08:00

217 lines
6.9 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
process.env.DATA_DIR = mkdtempSync(join(tmpdir(), "omniroute-video-dashscope-"));
const { handleVideoGeneration } = await import("../../open-sse/handlers/videoGeneration.ts");
const { VIDEO_PROVIDERS } = await import("../../open-sse/config/videoRegistry.ts");
// Makes poll-interval waits resolve instantly so tests don't sleep.
function immediateTimeout(callback, _ms, ...args) {
if (typeof callback === "function") callback(...args);
return 0;
}
const CREATE_URL =
"https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis";
const POLL_URL_PREFIX = "https://dashscope-intl.aliyuncs.com/api/v1/tasks/";
function jsonResponse(body, status = 200) {
return new Response(JSON.stringify(body), {
status,
headers: { "content-type": "application/json" },
});
}
test("VIDEO_PROVIDERS exposes the alibaba dashscope-video entry", () => {
assert.ok(VIDEO_PROVIDERS.alibaba, "alibaba video provider is registered");
assert.equal(VIDEO_PROVIDERS.alibaba.format, "dashscope-video");
assert.ok(
VIDEO_PROVIDERS.alibaba.models.some((m) => m.id === "wan2.7-t2v"),
"wan2.7-t2v is listed"
);
});
test("handleVideoGeneration creates + polls a DashScope task and returns mp4 URL", async () => {
const originalFetch = globalThis.fetch;
const originalSetTimeout = globalThis.setTimeout;
let createRequest;
globalThis.setTimeout = immediateTimeout;
globalThis.fetch = async (url, options = {}) => {
const stringUrl = String(url);
if (stringUrl === CREATE_URL) {
createRequest = {
url: stringUrl,
headers: options.headers,
body: JSON.parse(String(options.body || "{}")),
};
return jsonResponse({
output: { task_id: "ds-task-1", task_status: "PENDING" },
request_id: "req-1",
});
}
if (stringUrl.startsWith(POLL_URL_PREFIX)) {
return jsonResponse({
output: {
task_status: "SUCCEEDED",
video_url: "https://dashscope-cdn.example.com/wan-out.mp4",
},
request_id: "req-2",
usage: { video_count: 1 },
});
}
throw new Error(`Unexpected URL: ${stringUrl}`);
};
try {
const result = await handleVideoGeneration({
body: {
model: "alibaba/wan2.7-t2v",
prompt: "a neon city in the rain",
negative_prompt: "blurry",
aspect_ratio: "16:9",
duration: 5,
},
credentials: { apiKey: "dashscope-key" },
log: null,
});
// Create request shape
assert.equal(createRequest.headers["X-DashScope-Async"], "enable");
assert.equal(createRequest.headers["Authorization"], "Bearer dashscope-key");
assert.equal(createRequest.body.model, "wan2.7-t2v");
assert.equal(createRequest.body.input.prompt, "a neon city in the rain");
assert.equal(createRequest.body.input.negative_prompt, "blurry");
// aspect_ratio "16:9" → DashScope size "1280*720"
assert.equal(createRequest.body.parameters.size, "1280*720");
assert.equal(createRequest.body.parameters.duration, 5);
// Response shape
assert.equal(result.success, true);
assert.equal(result.data.data[0].url, "https://dashscope-cdn.example.com/wan-out.mp4");
assert.equal(result.data.data[0].format, "mp4");
} finally {
globalThis.fetch = originalFetch;
globalThis.setTimeout = originalSetTimeout;
}
});
test("handleVideoGeneration rejects DashScope requests without credentials", async () => {
const result = await handleVideoGeneration({
body: { model: "alibaba/wan2.7-t2v", prompt: "x" },
credentials: null,
log: null,
});
assert.equal(result.success, false);
assert.equal(result.status, 401);
assert.match(result.error, /DashScope API key is required/);
});
test("handleVideoGeneration surfaces a 502 when DashScope returns no task_id", async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () => jsonResponse({ message: "Invalid API key", request_id: "x" }, 401);
try {
const result = await handleVideoGeneration({
body: { model: "alibaba/wan2.7-t2v", prompt: "x" },
credentials: { apiKey: "bad-key" },
log: null,
});
assert.equal(result.success, false);
assert.equal(result.status, 502);
assert.equal(result.error, "Invalid API key");
} finally {
globalThis.fetch = originalFetch;
}
});
test("handleVideoGeneration returns 502 when the DashScope task FAILED", async () => {
const originalFetch = globalThis.fetch;
const originalSetTimeout = globalThis.setTimeout;
globalThis.setTimeout = immediateTimeout;
globalThis.fetch = async (url) => {
const stringUrl = String(url);
if (stringUrl === CREATE_URL) {
return jsonResponse({ output: { task_id: "ds-fail", task_status: "PENDING" } });
}
if (stringUrl.startsWith(POLL_URL_PREFIX)) {
return jsonResponse({
output: { task_status: "FAILED", message: "content policy violation" },
});
}
throw new Error(`Unexpected URL: ${stringUrl}`);
};
try {
const result = await handleVideoGeneration({
body: { model: "alibaba/wan2.7-t2v", prompt: "x" },
credentials: { apiKey: "dashscope-key" },
log: null,
});
assert.equal(result.success, false);
assert.equal(result.status, 502);
assert.equal(result.error, "content policy violation");
} finally {
globalThis.fetch = originalFetch;
globalThis.setTimeout = originalSetTimeout;
}
});
test("handleVideoGeneration returns 504 when the DashScope task never completes", async () => {
const originalFetch = globalThis.fetch;
const originalSetTimeout = globalThis.setTimeout;
const originalNow = Date.now;
globalThis.setTimeout = immediateTimeout;
// Deterministic clock: start at 1000, allow exactly one poll iteration, then
// jump past the deadline so the while-loop exits on the next check.
let nowCalls = 0;
Date.now = () => {
nowCalls += 1;
return nowCalls === 1 ? 1000 : nowCalls === 2 ? 2000 : 1_000_000;
};
globalThis.fetch = async (url) => {
const stringUrl = String(url);
if (stringUrl === CREATE_URL) {
return jsonResponse({ output: { task_id: "ds-stuck", task_status: "PENDING" } });
}
if (stringUrl.startsWith(POLL_URL_PREFIX)) {
return jsonResponse({ output: { task_status: "RUNNING" } });
}
throw new Error(`Unexpected URL: ${stringUrl}`);
};
try {
const result = await handleVideoGeneration({
body: {
model: "alibaba/wan2.7-t2v",
prompt: "x",
timeout_ms: 5000,
poll_interval_ms: 100,
},
credentials: { apiKey: "dashscope-key" },
log: null,
});
assert.equal(result.success, false);
assert.equal(result.status, 504);
assert.match(result.error, /timed out/);
} finally {
globalThis.fetch = originalFetch;
globalThis.setTimeout = originalSetTimeout;
Date.now = originalNow;
}
});