import { afterEach, describe, expect, it } from "vitest"; import { bailianResultUrls, bailianStatus, buildBailianImagePayload, buildBailianVideoPayload, deriveBailianNativeBaseUrl, shouldMockBailian } 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; delete process.env.BAILIAN_MOCK; }); 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("does not silently mock when the API key is missing", () => { expect(shouldMockBailian()).toBe(false); process.env.BAILIAN_MOCK = "true"; expect(shouldMockBailian()).toBe(true); }); 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 }); }); 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"]); }); });