1515 * inside the regression harness (`bun run --cwd packages/producer docker:test`).
1616 */
1717
18- import { spawnSync } from "node:child_process" ;
1918import { afterAll , beforeAll , describe , expect , it } from "bun:test" ;
19+ import { spawnSync } from "node:child_process" ;
2020import { existsSync , mkdirSync , mkdtempSync , readFileSync , rmSync , writeFileSync } from "node:fs" ;
2121import { tmpdir } from "node:os" ;
2222import { join } from "node:path" ;
@@ -788,10 +788,11 @@ describe("plan() — golden planDir + planHash determinism", () => {
788788 // variant generates a 1s silent wav via `ffmpeg`, references it from
789789 // the composition, and runs plan() — exercising the audio-mix path
790790 // that produced the original bug.
791- const HAS_FFMPEG = ( ( ) => {
792- const { spawnSync } = require ( "node:child_process" ) as typeof import ( "node:child_process" ) ;
793- return spawnSync ( "ffmpeg" , [ "-version" ] ) . status === 0 ;
794- } ) ( ) ;
791+ const HAS_FFMPEG = spawnSync ( "ffmpeg" , [ "-version" ] ) . status === 0 ;
792+ const ffmpegOrThrow = ( args : string [ ] ) => {
793+ const ffmpeg = spawnSync ( "ffmpeg" , [ "-nostdin" , "-loglevel" , "error" , ...args ] ) ;
794+ if ( ffmpeg . status !== 0 ) throw new Error ( `ffmpeg fixture failed: ${ ffmpeg . stderr ?. toString ( ) } ` ) ;
795+ } ;
795796
796797 it . skipIf ( ! HAS_FFMPEG ) (
797798 "plan.json.planHash matches recompute on an audio-bearing composition" ,
@@ -801,11 +802,7 @@ describe("plan() — golden planDir + planHash determinism", () => {
801802 // Generate a 1s mono silent wav. PCM keeps the file tiny without
802803 // pulling in an audio asset fixture.
803804 const audioPath = join ( audioProjectDir , "silence.wav" ) ;
804- const { spawnSync } = require ( "node:child_process" ) as typeof import ( "node:child_process" ) ;
805- const ffmpeg = spawnSync ( "ffmpeg" , [
806- "-nostdin" ,
807- "-v" ,
808- "error" ,
805+ ffmpegOrThrow ( [
809806 "-f" ,
810807 "lavfi" ,
811808 "-i" ,
@@ -816,9 +813,6 @@ describe("plan() — golden planDir + planHash determinism", () => {
816813 "pcm_s16le" ,
817814 audioPath ,
818815 ] ) ;
819- if ( ffmpeg . status !== 0 ) {
820- throw new Error ( `ffmpeg silence-wav generation failed: ${ ffmpeg . stderr ?. toString ( ) } ` ) ;
821- }
822816 writeFileSync (
823817 join ( audioProjectDir , "index.html" ) ,
824818 `<!doctype html>
@@ -852,6 +846,46 @@ describe("plan() — golden planDir + planHash determinism", () => {
852846 } ,
853847 TIMEOUT_MS ,
854848 ) ;
849+
850+ it . skipIf ( ! HAS_FFMPEG ) (
851+ "plans a video-bearing project to the same planHash from different plan folders" ,
852+ async ( ) => {
853+ const videoProjectDir = join ( runRoot , "project-with-video" ) ;
854+ mkdirSync ( videoProjectDir , { recursive : true } ) ;
855+ ffmpegOrThrow ( [
856+ "-f" ,
857+ "lavfi" ,
858+ "-i" ,
859+ "testsrc=size=320x240:rate=30:duration=1" ,
860+ "-pix_fmt" ,
861+ "yuv420p" ,
862+ "-c:v" ,
863+ "libx264" ,
864+ join ( videoProjectDir , "clip.mp4" ) ,
865+ ] ) ;
866+ writeFileSync (
867+ join ( videoProjectDir , "index.html" ) ,
868+ `<!doctype html>
869+ <div data-composition-id="root" data-width="320" data-height="240" data-duration="1">
870+ <video id="clip" src="clip.mp4" data-start="0" muted></video>
871+ </div>` ,
872+ "utf-8" ,
873+ ) ;
874+
875+ const config = { fps : 30 as const , width : 320 , height : 240 , format : "mp4" as const } ;
876+ const dirA = join ( runRoot , "plan-video-a" ) ;
877+ const dirB = join ( runRoot , "nested" , "plan-video-b" ) ;
878+ for ( const dir of [ dirA , dirB ] ) mkdirSync ( dir , { recursive : true } ) ;
879+ const a = await plan ( videoProjectDir , config , dirA ) ;
880+ const b = await plan ( videoProjectDir , config , dirB ) ;
881+
882+ expect ( a . planHash ) . toBe ( b . planHash ) ;
883+ const videosJson = ( dir : string ) => readFileSync ( join ( dir , "meta" , "videos.json" ) , "utf-8" ) ;
884+ expect ( videosJson ( dirB ) ) . toBe ( videosJson ( dirA ) ) ;
885+ expect ( JSON . parse ( videosJson ( dirA ) ) . extracted [ 0 ] . srcPath ) . toBe ( "compiled/clip.mp4" ) ;
886+ } ,
887+ TIMEOUT_MS ,
888+ ) ;
855889} ) ;
856890
857891describe . skipIf ( spawnSync ( "ffmpeg" , [ "-version" ] ) . status !== 0 ) ( "stitched mp4 plans" , ( ) => {
0 commit comments