fix(producer): tighten chunk-boundary test gates + narrow VIDEO_EXT indexing

Address @vanceingalls and @miguel-heygen review findings on #852:

1. Asymmetric soft-skip — only the N=1 plan+render+assemble call was
   wrapped in the host-Chrome-failure catch; an SwiftShader / cold-Chrome
   flake on the N=4 call would hard-fail instead of soft-skip. Factor a
   local runRender() helper and wrap both calls.

2. Vacuously-passing length assertion — 'expect(framesOne.length).toBe(
   framesFour.length)' passes when both runs produce 0 frames. Pin the
   absolute count (EXPECTED_FRAME_COUNT = 60) so a regression that
   identically truncates both renders shows red.

3. CDN version drift — anime-boundary loaded gsap@3.14.2 from jsdelivr
   while every other boundary fixture loaded 3.12.2 from cdnjs. Unify on
   cdnjs@3.12.2 so the next reader doesn't have to wonder why one fixture
   diverges. (gsap is an empty duration-driver in all six fixtures so
   the version was never load-bearing — but the divergence reads as
   intentional and isn't.)

4. VIDEO_EXT type narrowing — the lookup is Record<"mp4"|"mov"|"webm">
   but outputFormat includes "png-sequence". The isPngSequence ternary
   short-circuits before png-sequence can reach the indexing site, but TS
   can't narrow through that. Add an explicit cast at the indexing site
   (not the lookup definition — over-widening to include "png-sequence":
   undefined would defeat the existence guarantee).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
James
2026-05-15 02:47:20 +00:00
committed by James Russo
parent bd21d00b13
commit 6b3ad09436
3 changed files with 43 additions and 26 deletions
+8 -1
View File
@@ -722,12 +722,19 @@ async function runTestSuite(
// png-sequence output is a directory (basename = "frames"); encoded video
// formats produce a single file (basename = "output.<ext>"). One lookup
// covers both shapes for the in-temp render and the on-disk baseline.
// `VIDEO_EXT` is intentionally typed against only the encoded-video set —
// the `isPngSequence` ternary below short-circuits before `outputFormat`
// can be `"png-sequence"`, but TS can't narrow through that, so we
// assert the narrowing at the indexing site rather than over-widening
// the lookup table.
const VIDEO_EXT: Record<"mp4" | "mov" | "webm", string> = {
mp4: ".mp4",
mov: ".mov",
webm: ".webm",
};
const outputBasename = isPngSequence ? "frames" : `output${VIDEO_EXT[outputFormat]}`;
const outputBasename = isPngSequence
? "frames"
: `output${VIDEO_EXT[outputFormat as "mp4" | "mov" | "webm"]}`;
const renderedOutputPath = join(tempRoot, outputBasename);
// Snapshot files stored in test's output/ directory. For png-sequence the
@@ -37,6 +37,11 @@ const HOST_CHROME_FAILURE_PATTERNS =
// assemble pipeline so no `output/` baseline is required.
const ADAPTERS = ["gsap", "anime", "three", "lottie", "css", "waapi"] as const;
// Every adapter fixture is a 2-second composition at 30fps. Pin the absolute
// count so a regression that produces fewer frames in both runs (e.g. a
// probe stage that reads duration as 0s) doesn't pass vacuously.
const EXPECTED_FRAME_COUNT = 60;
let runRoot: string;
let testsDistributedDir: string;
@@ -122,30 +127,30 @@ describe("per-adapter chunk-boundary byte equality", () => {
mkdirSync(workOne, { recursive: true });
mkdirSync(workFour, { recursive: true });
let outOne: string;
try {
outOne = await planAndAssemble({
projectDir,
workDir: workOne,
chunkSize: 60,
});
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
if (HOST_CHROME_FAILURE_PATTERNS.test(message)) {
console.warn(
`[chunkBoundary.test] skipping ${adapter} — host Chrome can't render. ` +
"Docker harness covers the contract. Diagnostic:",
message.slice(0, 240),
);
return;
// Soft-skip when host Chrome can't render. Wrap *both* renders —
// cold-Chrome / SwiftShader flakes happen on the second render
// as readily as the first, and a hard-fail on the N=4 path would
// diverge from the rest of the harness's soft-skip convention.
const runRender = async (workDir: string, chunkSize: number): Promise<string | null> => {
try {
return await planAndAssemble({ projectDir, workDir, chunkSize });
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
if (HOST_CHROME_FAILURE_PATTERNS.test(message)) {
console.warn(
`[chunkBoundary.test] skipping ${adapter} — host Chrome can't render. ` +
"Docker harness covers the contract. Diagnostic:",
message.slice(0, 240),
);
return null;
}
throw err;
}
throw err;
}
const outFour = await planAndAssemble({
projectDir,
workDir: workFour,
chunkSize: 15,
});
};
const outOne = await runRender(workOne, 60);
if (outOne === null) return;
const outFour = await runRender(workFour, 15);
if (outFour === null) return;
// Per-frame byte equality across the two frames directories. A
// boundary regression in the adapter's seek-determinism would
@@ -157,7 +162,12 @@ describe("per-adapter chunk-boundary byte equality", () => {
const framesFour = readdirSync(outFour)
.filter((n) => n.toLowerCase().endsWith(".png"))
.sort();
expect(framesOne.length).toBe(framesFour.length);
// Pin the absolute count, not just equality between the two runs.
// Otherwise a regression that truncates BOTH renders identically
// (e.g. a probe stage that misreads duration as 0s) would pass
// vacuously — `0 === 0` is true.
expect(framesOne.length).toBe(EXPECTED_FRAME_COUNT);
expect(framesFour.length).toBe(EXPECTED_FRAME_COUNT);
expect(framesOne).toEqual(framesFour);
for (let i = 0; i < framesOne.length; i++) {
const frameName = framesOne[i];
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<title>chunk-boundary: anime.js</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/animejs@4.0.2/lib/anime.iife.min.js"></script>
<style>
body,