import test from 'node:test' import assert from 'node:assert/strict' import fs from 'node:fs' import {validateRequest,compilePrompt,restoreInput,scopedFile} from '../shared/studio2/contracts.mjs' import {nativeVideoGraph} from '../shared/studio2/graphs.mjs' const template=JSON.parse(fs.readFileSync(new URL('../server/assets/studio2_minimax_native.json',import.meta.url))) const base={folderId:'folder',mode:'video',engine:'minimax',identityStillId:'hero',promptSections:{action:'walk'}} test('ten shots retain hero as Picture 1 while each start frame changes',()=>{ for(let i=0;i<10;i++) {const r=validateRequest({...base,startClipId:'clip'+i});const g=nativeVideoGraph(template,r,{hero:'hero.png',start:`last${i}.png`},'preview/job');assert.equal(g.hero.inputs.image,'hero.png');assert.deepEqual(g['155'].inputs['ref_images.ref_image_0'],['hero',0]);assert.equal(g.load_start.inputs.image,`last${i}.png`);assert.equal(r.identityStillId,'hero');assert.equal(g['155'].inputs.ref_image_size,'max')} }) test('compose without B and refine without mask are 400',()=>{ for(const mode of ['compose','refine']) assert.throws(()=>validateRequest({...base,mode,engine:'flux',imageAId:'a'}),e=>e.statusCode===400) }) test('Use as input round-trips camera chip and never invents one for flat text',()=>{ const r=validateRequest({...base,promptSections:{action:'walk',cameraChip:{family:'dolly in',amplitude:'subtle',speed:'slow'}}}); assert.deepEqual(restoreInput({studio2:r}).promptSections,r.promptSections);assert.equal(restoreInput({prompt:'old text'}).promptSections.cameraChip,undefined);assert.match(compilePrompt(r.promptSections),/dolly in/) }) test('purge accepts only exact preview path boundary',()=>{ assert.equal(scopedFile({subfolder:'video/MiniMax_H3_preview/job',filename:'a.png'},'video/MiniMax_H3_preview'),true) for(const subfolder of ['video/MiniMax_H3','video/MiniMax_H3_preview_other','video/MiniMax_H3_preview/../MiniMax_H3']) assert.equal(scopedFile({subfolder,filename:'a.png'},'video/MiniMax_H3_preview'),false) })