Files
diegosouzapw--omniroute/tests/unit/google-flow-video-4569.test.ts
2026-07-13 13:39:12 +08:00

215 lines
8.3 KiB
TypeScript

/**
* #4569 — Google Flow video generation helpers.
*
* Covers the pure transformation logic (param normalization, Veo submit-body
* construction, operation-name parsing, LRO result parsing, credential
* resolution) and a source guard asserting the wire endpoint stays isolated and
* flagged for live validation (Hard Rule #18).
*/
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
import {
GOOGLE_FLOW_CREDENTIAL_PROVIDER,
GOOGLE_FLOW_HOST,
buildGoogleFlowSubmitBody,
normalizeFlowVideoParams,
parseFlowOperationName,
parseFlowOperationResult,
resolveFlowAccessToken,
resolveFlowProjectId,
resolveVideoCredentialProvider,
} from "../../open-sse/handlers/videoGeneration/googleFlow.ts";
import { getVideoProvider, parseVideoModel } from "../../open-sse/config/videoRegistry.ts";
const __dirname = dirname(fileURLToPath(import.meta.url));
test("normalizeFlowVideoParams: defaults sampleCount to 1 and stringifies prompt", () => {
const p = normalizeFlowVideoParams({ prompt: "a cat surfing" });
assert.equal(p.prompt, "a cat surfing");
assert.equal(p.sampleCount, 1);
assert.equal(p.aspectRatio, undefined);
assert.equal(p.durationSeconds, undefined);
});
test("normalizeFlowVideoParams: reads snake_case OpenAI fields", () => {
const p = normalizeFlowVideoParams({
prompt: "x",
aspect_ratio: "16:9",
duration: 8,
n: 2,
negative_prompt: "blurry",
resolution: "1080p",
});
assert.equal(p.aspectRatio, "16:9");
assert.equal(p.durationSeconds, 8);
assert.equal(p.sampleCount, 2);
assert.equal(p.negativePrompt, "blurry");
assert.equal(p.resolution, "1080p");
});
test("normalizeFlowVideoParams: reads camelCase native fields", () => {
const p = normalizeFlowVideoParams({ prompt: "x", aspectRatio: "9:16", durationSeconds: 4 });
assert.equal(p.aspectRatio, "9:16");
assert.equal(p.durationSeconds, 4);
});
test("normalizeFlowVideoParams: treats ratio-shaped size as aspectRatio, ignores pixel size", () => {
assert.equal(normalizeFlowVideoParams({ prompt: "x", size: "16:9" }).aspectRatio, "16:9");
assert.equal(normalizeFlowVideoParams({ prompt: "x", size: "1024x1024" }).aspectRatio, undefined);
});
test("normalizeFlowVideoParams: rejects non-positive / non-finite numbers", () => {
const p = normalizeFlowVideoParams({ prompt: "x", n: 0, duration: -5 });
assert.equal(p.sampleCount, 1);
assert.equal(p.durationSeconds, undefined);
});
test("buildGoogleFlowSubmitBody: emits documented Veo instances/parameters shape", () => {
const body = buildGoogleFlowSubmitBody({
prompt: "a dog",
aspectRatio: "16:9",
durationSeconds: 6,
sampleCount: 1,
negativePrompt: "ugly",
resolution: "720p",
});
assert.deepEqual(body.instances, [{ prompt: "a dog" }]);
assert.equal(body.parameters.sampleCount, 1);
assert.equal(body.parameters.aspectRatio, "16:9");
assert.equal(body.parameters.durationSeconds, 6);
assert.equal(body.parameters.negativePrompt, "ugly");
assert.equal(body.parameters.resolution, "720p");
});
test("buildGoogleFlowSubmitBody: omits absent optional parameters", () => {
const body = buildGoogleFlowSubmitBody({ prompt: "p", sampleCount: 3 });
assert.deepEqual(body.parameters, { sampleCount: 3 });
assert.ok(!("aspectRatio" in body.parameters));
assert.ok(!("durationSeconds" in body.parameters));
});
test("parseFlowOperationName: handles {name} and {operation:{name}} and rejects junk", () => {
assert.equal(parseFlowOperationName({ name: "operations/abc" }), "operations/abc");
assert.equal(parseFlowOperationName({ operation: { name: "operations/xyz" } }), "operations/xyz");
assert.equal(parseFlowOperationName({}), null);
assert.equal(parseFlowOperationName(null), null);
assert.equal(parseFlowOperationName({ name: "" }), null);
});
test("parseFlowOperationResult: not done → keep polling", () => {
assert.deepEqual(parseFlowOperationResult({ done: false }), { done: false });
assert.deepEqual(parseFlowOperationResult({}), { done: false });
});
test("parseFlowOperationResult: done with base64 video (documented shape)", () => {
const r = parseFlowOperationResult({
done: true,
response: { videos: [{ bytesBase64Encoded: "AAAA" }] },
});
assert.equal(r.done, true);
assert.equal(r.base64, "AAAA");
assert.equal(r.format, "mp4");
assert.equal(r.error, undefined);
});
test("parseFlowOperationResult: done with gcsUri/uri video", () => {
assert.equal(
parseFlowOperationResult({ done: true, response: { videos: [{ gcsUri: "gs://b/v.mp4" }] } }).url,
"gs://b/v.mp4"
);
assert.equal(
parseFlowOperationResult({ done: true, response: { videos: [{ uri: "https://x/v.mp4" }] } }).url,
"https://x/v.mp4"
);
});
test("parseFlowOperationResult: done with alternate generateVideoResponse shape", () => {
const r = parseFlowOperationResult({
done: true,
response: {
generateVideoResponse: { generatedSamples: [{ video: { uri: "https://x/sample.mp4" } }] },
},
});
assert.equal(r.url, "https://x/sample.mp4");
});
test("parseFlowOperationResult: done with error surfaces the message", () => {
const r = parseFlowOperationResult({ done: true, error: { message: "quota exceeded" } });
assert.equal(r.done, true);
assert.equal(r.error, "quota exceeded");
});
test("parseFlowOperationResult: done but empty → explicit no-video error", () => {
const r = parseFlowOperationResult({ done: true, response: {} });
assert.equal(r.done, true);
assert.match(r.error ?? "", /no video/i);
});
test("parseFlowOperationResult: null/empty array elements do not crash (defensive)", () => {
// Upstream returning a null element must not throw a TypeError (gemini-code-assist #4769).
for (const response of [
{ videos: [null] },
{ videos: [undefined] },
{ videos: [{}] },
{ generateVideoResponse: { generatedSamples: [null] } },
{ generateVideoResponse: { generatedSamples: [{ video: null }] } },
{ generateVideoResponse: { generatedSamples: [{}] } },
]) {
const r = parseFlowOperationResult({ done: true, response });
assert.equal(r.done, true);
assert.match(r.error ?? "", /no video/i);
assert.equal(r.base64, undefined);
assert.equal(r.url, undefined);
}
});
test("resolveFlowProjectId: direct, providerSpecificData, and missing", () => {
assert.equal(resolveFlowProjectId({ projectId: "proj-1" }), "proj-1");
assert.equal(resolveFlowProjectId({ providerSpecificData: { projectId: "proj-2" } }), "proj-2");
assert.equal(resolveFlowProjectId({}), null);
assert.equal(resolveFlowProjectId(null), null);
});
test("resolveFlowAccessToken: prefers accessToken, falls back to apiKey", () => {
assert.equal(resolveFlowAccessToken({ accessToken: "tok" }), "tok");
assert.equal(resolveFlowAccessToken({ apiKey: "key" }), "key");
assert.equal(resolveFlowAccessToken({}), null);
});
test("videoRegistry: googleflow provider is registered with oauth + google-flow format", () => {
const provider = getVideoProvider("googleflow");
assert.ok(provider, "googleflow provider must exist");
assert.equal(provider.format, "google-flow");
assert.equal(provider.authType, "oauth");
assert.ok(provider.models.length > 0, "must expose at least one Veo model");
});
test("videoRegistry: parseVideoModel resolves googleflow/<model> and its alias", () => {
const parsed = parseVideoModel("googleflow/veo-3.1-generate");
assert.equal(parsed.provider, "googleflow");
assert.equal(parsed.model, "veo-3.1-generate");
const aliased = parseVideoModel("flow/veo-3.1-generate");
assert.equal(aliased.provider, "googleflow");
});
test("resolveVideoCredentialProvider: googleflow reuses antigravity OAuth, others unchanged", () => {
assert.equal(resolveVideoCredentialProvider("googleflow"), "antigravity");
assert.equal(GOOGLE_FLOW_CREDENTIAL_PROVIDER, "antigravity");
assert.equal(resolveVideoCredentialProvider("vertex"), "vertex");
assert.equal(resolveVideoCredentialProvider("kie"), "kie");
});
test("source guard: wire endpoint stays isolated + flagged for live validation (Rule #18)", () => {
assert.match(GOOGLE_FLOW_HOST, /aisandbox-pa\.googleapis\.com/);
const src = readFileSync(
join(__dirname, "../../open-sse/handlers/videoGeneration/googleFlow.ts"),
"utf8"
);
assert.match(src, /PENDING LIVE VALIDATION/, "wire format must be flagged for HAR validation");
});