71 lines
3.0 KiB
TypeScript
71 lines
3.0 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { GeminiFlashAdapter } from "../../apps/worker/src/ai-adapter-gemini-flash.js";
|
|
import { GeminiProAdapter } from "../../apps/worker/src/ai-adapter-gemini-pro.js";
|
|
import { GptImageAdapter } from "../../apps/worker/src/ai-adapter-gpt-image.js";
|
|
import {
|
|
gptImageRequestSizeForRatio,
|
|
normalizeImageOutputToRatio,
|
|
productDimensionsForRatio,
|
|
} from "../../apps/worker/src/image-output-normalizer.mjs";
|
|
|
|
const onePixelPng = Buffer.from(
|
|
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=",
|
|
"base64",
|
|
);
|
|
|
|
describe("TDD-WP7-EXT-001 exact image output normalization", () => {
|
|
it("uses only GPT Image 2 request sizes allowed by the upstream API", () => {
|
|
expect(["3:4", "1:1", "4:3", "9:16"].map((ratio) => gptImageRequestSizeForRatio(ratio))).toEqual([
|
|
"1056x1408",
|
|
"1088x1088",
|
|
"1408x1056",
|
|
"1008x1792",
|
|
]);
|
|
for (const ratio of ["3:4", "1:1", "4:3", "9:16"] as const) {
|
|
const [width, height] = gptImageRequestSizeForRatio(ratio).split("x").map(Number);
|
|
expect(width % 16).toBe(0);
|
|
expect(height % 16).toBe(0);
|
|
}
|
|
});
|
|
|
|
it("normalizes provider output to the frozen product dimensions", async () => {
|
|
const output = await normalizeImageOutputToRatio({ bytes: onePixelPng, mimeType: "image/png", ratio: "1:1" });
|
|
expect(output).toMatchObject({
|
|
mimeType: "image/png",
|
|
normalized: true,
|
|
pixelHeight: 1080,
|
|
pixelWidth: 1080,
|
|
upstreamPixelHeight: 1,
|
|
upstreamPixelWidth: 1,
|
|
});
|
|
expect(output.bytes.subarray(0, 8).toString("hex")).toBe("89504e470d0a1a0a");
|
|
expect(productDimensionsForRatio("9:16")).toEqual({ pixelHeight: 1920, pixelWidth: 1080 });
|
|
});
|
|
|
|
it("is used by all three production adapter boundaries", async () => {
|
|
const encoded = onePixelPng.toString("base64");
|
|
const adapters = [
|
|
new GeminiFlashAdapter({ transport: {
|
|
async start() { return { candidates: [{ inline_data: { data: encoded, mime_type: "image/png" }, pixelHeight: 1, pixelWidth: 1 }] }; },
|
|
async poll() { return {}; },
|
|
} }),
|
|
new GeminiProAdapter({ transport: {
|
|
async start() { return { operation: { done: true, response: { candidates: [{ inline_data: { data: encoded, mime_type: "image/png" }, pixelHeight: 1, pixelWidth: 1 }] } } }; },
|
|
async poll() { return {}; },
|
|
} }),
|
|
new GptImageAdapter({ transport: {
|
|
async start() { return { data: [{ b64_json: encoded, pixelHeight: 1, pixelWidth: 1 }] }; },
|
|
async poll() { return {}; },
|
|
} }),
|
|
];
|
|
for (const adapter of adapters) {
|
|
const result = await adapter.start({
|
|
configSnapshot: {}, generationId: `normalization-${adapter.modelId}`, modelId: adapter.modelId,
|
|
prompt: "sanitized fixture", ratio: "1:1", referenceAssetIds: [],
|
|
});
|
|
expect(result).toMatchObject({ status: "completed", outputs: [{ mimeType: "image/png", pixelHeight: 1080, pixelWidth: 1080 }] });
|
|
}
|
|
});
|
|
});
|