import * as providerAuth from "openclaw/plugin-sdk/provider-auth-runtime"; import { afterEach, describe, expect, it, vi } from "vitest"; import { buildVydraVideoGenerationProvider } from "./video-generation-provider.js"; describe("vydra video-generation provider", () => { afterEach(() => { vi.unstubAllGlobals(); vi.restoreAllMocks(); }); it("submits veo3 jobs and downloads the completed video", async () => { vi.spyOn(providerAuth, "resolveApiKeyForProvider").mockResolvedValue({ apiKey: "vydra-test-key", source: "env", mode: "api-key", }); const fetchMock = vi .fn() .mockResolvedValueOnce( new Response(JSON.stringify({ jobId: "job-123", status: "processing" }), { status: 200, headers: { "Content-Type": "application/json" }, }), ) .mockResolvedValueOnce( new Response( JSON.stringify({ jobId: "job-123", status: "completed", videoUrl: "https://cdn.vydra.ai/generated/test.mp4", }), { status: 200, headers: { "Content-Type": "application/json" }, }, ), ) .mockResolvedValueOnce( new Response(Buffer.from("mp4-data"), { status: 200, headers: { "Content-Type": "video/mp4" }, }), ); vi.stubGlobal("fetch", fetchMock); const provider = buildVydraVideoGenerationProvider(); const result = await provider.generateVideo({ provider: "vydra", model: "veo3", prompt: "tiny city at sunrise", cfg: {}, }); expect(fetchMock).toHaveBeenNthCalledWith( 1, "https://www.vydra.ai/api/v1/models/veo3", expect.objectContaining({ method: "POST", body: JSON.stringify({ prompt: "tiny city at sunrise" }), }), ); expect(fetchMock).toHaveBeenNthCalledWith( 2, "https://www.vydra.ai/api/v1/jobs/job-123", expect.objectContaining({ method: "GET" }), ); expect(result.videos[0]?.mimeType).toBe("video/mp4"); expect(result.metadata).toEqual({ jobId: "job-123", videoUrl: "https://cdn.vydra.ai/generated/test.mp4", status: "completed", }); }); it("requires a remote image url for kling", async () => { vi.spyOn(providerAuth, "resolveApiKeyForProvider").mockResolvedValue({ apiKey: "vydra-test-key", source: "env", mode: "api-key", }); vi.stubGlobal("fetch", vi.fn()); const provider = buildVydraVideoGenerationProvider(); await expect( provider.generateVideo({ provider: "vydra", model: "kling", prompt: "animate this image", cfg: {}, inputImages: [{ buffer: Buffer.from("png"), mimeType: "image/png" }], }), ).rejects.toThrow("Vydra kling currently requires a remote image URL reference."); }); it("submits kling jobs with a remote image url", async () => { vi.spyOn(providerAuth, "resolveApiKeyForProvider").mockResolvedValue({ apiKey: "vydra-test-key", source: "env", mode: "api-key", }); const fetchMock = vi .fn() .mockResolvedValueOnce( new Response(JSON.stringify({ jobId: "job-kling", status: "processing" }), { status: 200, headers: { "Content-Type": "application/json" }, }), ) .mockResolvedValueOnce( new Response( JSON.stringify({ jobId: "job-kling", status: "completed", videoUrl: "https://cdn.vydra.ai/generated/kling.mp4", }), { status: 200, headers: { "Content-Type": "application/json" }, }, ), ) .mockResolvedValueOnce( new Response(Buffer.from("mp4-data"), { status: 200, headers: { "Content-Type": "video/mp4" }, }), ); vi.stubGlobal("fetch", fetchMock); const provider = buildVydraVideoGenerationProvider(); const result = await provider.generateVideo({ provider: "vydra", model: "kling", prompt: "animate this image", cfg: {}, inputImages: [{ url: "https://example.com/reference.png" }], }); expect(fetchMock).toHaveBeenNthCalledWith( 1, "https://www.vydra.ai/api/v1/models/kling", expect.objectContaining({ method: "POST", body: JSON.stringify({ prompt: "animate this image", image_url: "https://example.com/reference.png", video_url: "https://example.com/reference.png", }), }), ); expect(result.videos[0]?.mimeType).toBe("video/mp4"); expect(result.metadata).toEqual({ jobId: "job-kling", videoUrl: "https://cdn.vydra.ai/generated/kling.mp4", status: "completed", }); }); });