import { afterEach, describe, expect, it } from "vitest"; import { bailianResultUrls, bailianOutputVideoSeconds, bailianStatus, buildBailianImagePayload, buildBailianVideoPayload, deriveBailianNativeBaseUrl } from "@/lib/bailian/client"; afterEach(() => { delete process.env.BAILIAN_IMAGE_MODEL; delete process.env.BAILIAN_VIDEO_MODEL; delete process.env.BAILIAN_API_KEY; delete process.env.DASHSCOPE_API_KEY; }); describe("Bailian client", () => { it("derives native API base from compatible-mode URL", () => { expect(deriveBailianNativeBaseUrl("https://workspace.cn-beijing.maas.aliyuncs.com/compatible-mode/v1")) .toBe("https://workspace.cn-beijing.maas.aliyuncs.com"); }); it("builds Wan 2.7 text-to-image and reference-image payloads", () => { const text = buildBailianImagePayload("image.generate", { prompt: "海边酒店", width: 2048, height: 2048 }); expect(text).toMatchObject({ model: "wan2.7-image-pro", parameters: { size: "2048*2048", thinking_mode: true, watermark: false } }); const edit = buildBailianImagePayload("image.generate", { prompt: "保持人物一致", imageUrls: ["https://example.com/a.png"], width: 2048, height: 2048 }); expect((edit.input.messages[0].content as Array>)[0]).toEqual({ image: "https://example.com/a.png" }); expect(edit.parameters).not.toHaveProperty("thinking_mode"); }); it("preserves a user-selected 9:16 output size for reference images", () => { const payload = buildBailianImagePayload("image.generate", { prompt: "x", imageUrls: ["https://example.com/a.png"], width: 1440, height: 2560 }); expect(payload.parameters.size).toBe("1440*2560"); }); it("rejects reference-image output only when total pixels exceed the 2K budget", () => { expect(() => buildBailianImagePayload("image.generate", { prompt: "x", imageUrls: ["https://example.com/a.png"], width: 2048, height: 2560 })) .toThrow("总像素"); }); it("maps one or two video images to first and last frames", () => { const payload = buildBailianVideoPayload({ prompt: "镜头推进", origin: "https://app.example.com", materials: [{ type: "image", url: "/first.png" }, { type: "image", url: "/last.png" }], settings: { duration: 10, resolution: "1080P" } }); expect(payload.input.media).toEqual([ { type: "first_frame", url: "https://app.example.com/first.png" }, { type: "last_frame", url: "https://app.example.com/last.png" } ]); expect(payload.parameters).toMatchObject({ duration: 10, resolution: "1080P", prompt_extend: true, watermark: false }); expect(payload.parameters).not.toHaveProperty("size"); }); it("maps Wan 3.0 references while retaining optional first and last frames", () => { const materials = Array.from({ length: 10 }, (_, index) => ({ type: "image" as const, url: `/image-${index}.png` })); const reference = buildBailianVideoPayload({ model: "wan3.0-video", prompt: "combine", materials, origin: "https://app.test", settings: { duration: 30, resolution: "480P", ratio: "9:16", inputMode: "reference" } }); expect(reference.input.media).toHaveLength(10); expect(reference.input.media?.every((item) => item.type === "reference_image")).toBe(true); expect(reference.parameters.ratio).toBe("9:16"); const frames = buildBailianVideoPayload({ model: "wan3.0-video", prompt: "animate", materials: materials.slice(0, 2), origin: "https://app.test", settings: { inputMode: "frames" } }); expect(frames.input.media?.map((item) => item.type)).toEqual(["first_frame", "last_frame"]); expect(buildBailianVideoPayload({ model: "wan3.0-video", prompt: "landscape", materials: [], origin: "https://app.test", settings: { inputMode: "reference" } }).input).toEqual({ prompt: "landscape" }); expect(() => buildBailianVideoPayload({ model: "wan2.7-i2v-2026-04-25", prompt: "x", materials, origin: "https://app.test", settings: { inputMode: "reference" } })).toThrow("请选择 Wan 3.0"); expect(bailianOutputVideoSeconds({ usage: { output_video_duration: 5.25, duration: 8 } })).toBe(5.25); }); it("normalizes task state and result URLs", () => { const response = { output: { task_status: "SUCCEEDED", results: [{ url: "https://example.com/result.png" }] } }; expect(bailianStatus(response)).toBe("succeeded"); expect(bailianResultUrls(response, "image")).toEqual(["https://example.com/result.png"]); }); it("extracts Wan 2.7 image URLs from choices content", () => { const response = { output: { task_status: "SUCCEEDED", choices: [{ message: { content: [{ type: "image", image: "https://example.com/choice.png" }] } }] } }; expect(bailianResultUrls(response, "image")).toEqual(["https://example.com/choice.png"]); }); });