Files
openclaw/extensions/vydra/video-generation-provider.test.ts
2026-04-06 19:47:43 +01:00

159 lines
4.7 KiB
TypeScript

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",
});
});
});