mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-14 18:01:20 +08:00
8b6d35e226
* fix(producer): honor variables + outputResolution in HTTP render server The producer HTTP server's parseRenderOptions read only fps/quality/workers/gpu/debug/entryFile/format from the request body. `variables` and `outputResolution` were silently dropped, so any caller of the server render path (the cloud-render sidecar that experiment-framework POSTs to) got the composition's declared variable defaults and its intrinsic dimensions regardless of what was requested. RenderConfig already supports both fields (the local CLI `render` command passes them); the server just never forwarded them. Wire them through RenderInput, parseRenderOptions, and a shared buildRenderJobConfig used by the sync + streaming handlers. outputResolution now drives the same resolveDeviceScaleFactor supersampling path the local CLI uses, so a 4k render against a matching-aspect composition produces true 4k. Validation: a non-object `variables` or an unknown `outputResolution` returns a clean 400 instead of being silently ignored. Also extracts resolvePreparedRenderOutput + parseRenderOverrides helpers to keep both handlers DRY. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(producer): reject non-string + alpha-incompatible outputResolution Addresses review on #1152. - A non-string `outputResolution` (e.g. a JSON number) was coerced to `undefined` by parseRenderOverrides and silently ignored — the same silent-drop this validation exists to prevent. Now rejected with a 400. - `outputResolution` + an alpha format (webm/mov) is rejected up front: supersampling runs through a deviceScaleFactor the alpha capture path can't apply, so resolveDeviceScaleFactor throws mid-render. Guarding it here makes the producer self-defending for every caller (not just the CLI / external API), and closes the 1080p-webm regression window during the producer-honors-outputResolution rollout. Extracted validateOutputResolutionOverride to keep validateRenderOverrides under the complexity gate. +2 prepareRenderBody tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
88 lines
3.7 KiB
TypeScript
88 lines
3.7 KiB
TypeScript
import { describe, expect, it } from "bun:test";
|
|
import { mkdtempSync, writeFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { tmpdir } from "node:os";
|
|
|
|
import { parseRenderOptions, prepareRenderBody } from "./server.js";
|
|
|
|
describe("parseRenderOptions — variables", () => {
|
|
it("forwards a plain JSON object", () => {
|
|
const opts = parseRenderOptions({ variables: { title: "Hello", n: 3 } });
|
|
expect(opts.variables).toEqual({ title: "Hello", n: 3 });
|
|
});
|
|
|
|
it("drops non-object variables to undefined", () => {
|
|
expect(parseRenderOptions({ variables: [1, 2] }).variables).toBeUndefined();
|
|
expect(parseRenderOptions({ variables: "nope" }).variables).toBeUndefined();
|
|
expect(parseRenderOptions({ variables: null }).variables).toBeUndefined();
|
|
expect(parseRenderOptions({}).variables).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("parseRenderOptions — outputResolution", () => {
|
|
it("normalizes canonical presets and aliases", () => {
|
|
expect(parseRenderOptions({ outputResolution: "landscape-4k" }).outputResolution).toBe(
|
|
"landscape-4k",
|
|
);
|
|
expect(parseRenderOptions({ outputResolution: "4k" }).outputResolution).toBe("landscape-4k");
|
|
expect(parseRenderOptions({ outputResolution: "portrait-4k" }).outputResolution).toBe(
|
|
"portrait-4k",
|
|
);
|
|
expect(parseRenderOptions({ outputResolution: "square" }).outputResolution).toBe("square");
|
|
});
|
|
|
|
it("drops unknown / non-string resolutions to undefined", () => {
|
|
expect(parseRenderOptions({ outputResolution: "8k" }).outputResolution).toBeUndefined();
|
|
expect(parseRenderOptions({ outputResolution: 123 }).outputResolution).toBeUndefined();
|
|
expect(parseRenderOptions({}).outputResolution).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("prepareRenderBody — validation", () => {
|
|
it("rejects an explicitly-supplied non-object variables", async () => {
|
|
const result = await prepareRenderBody({ variables: [1, 2], html: "<html></html>" });
|
|
expect(result).toHaveProperty("error");
|
|
expect((result as { error: string }).error).toContain("variables must be a JSON object");
|
|
});
|
|
|
|
it("rejects an explicitly-supplied invalid outputResolution", async () => {
|
|
const result = await prepareRenderBody({ outputResolution: "8k", html: "<html></html>" });
|
|
expect(result).toHaveProperty("error");
|
|
expect((result as { error: string }).error).toContain("Invalid outputResolution");
|
|
});
|
|
|
|
it("rejects a non-string outputResolution instead of silently dropping it", async () => {
|
|
const result = await prepareRenderBody({ outputResolution: 123, html: "<html></html>" });
|
|
expect(result).toHaveProperty("error");
|
|
expect((result as { error: string }).error).toContain("must be a string preset");
|
|
});
|
|
|
|
it("rejects outputResolution combined with an alpha format (webm/mov)", async () => {
|
|
for (const format of ["webm", "mov"]) {
|
|
const result = await prepareRenderBody({
|
|
outputResolution: "4k",
|
|
format,
|
|
html: "<html></html>",
|
|
});
|
|
expect(result).toHaveProperty("error");
|
|
expect((result as { error: string }).error).toContain("can't supersample");
|
|
}
|
|
});
|
|
|
|
it("threads variables + outputResolution into the prepared render input", async () => {
|
|
const dir = mkdtempSync(join(tmpdir(), "hf-server-test-"));
|
|
writeFileSync(join(dir, "index.html"), "<html><body></body></html>", "utf-8");
|
|
|
|
const result = await prepareRenderBody({
|
|
projectDir: dir,
|
|
variables: { title: "Q4" },
|
|
outputResolution: "4k",
|
|
});
|
|
|
|
expect(result).toHaveProperty("prepared");
|
|
const { input } = (result as { prepared: { input: Record<string, unknown> } }).prepared;
|
|
expect(input.variables).toEqual({ title: "Q4" });
|
|
expect(input.outputResolution).toBe("landscape-4k");
|
|
});
|
|
});
|