import assert from 'node:assert/strict'; import {loadInputs, prepareData, validateInputs, measured, absolute} from './data.mjs'; const inputs = loadInputs(); validateInputs(inputs.project, inputs.context, inputs.anchors); const data = prepareData(inputs); assert.equal(data.visual_engine, 'Blender'); assert.equal(data.clips.filter((c) => c.video_path && c.anchors).length, 31); assert.equal(data.clips.reduce((n, c) => n + c.frames, 0), 5528); const cases = [ ['scenes 미승인', (p) => {p.reviews.find((r) => r.review_id === 'scenes').decision = 'pending';}], ['옛 승인', (p) => {p.reviews.find((r) => r.review_id === 'scenes').artifact_refs[1].revision = 2;}], ['stale 결과', (p) => {p.stages.find((s) => s.stage_id === '12-motion').status = 'stale';}], ['다른 현재 경로', (p) => {p.stages.find((s) => s.stage_id === '12-motion').artifact_path = 'foreign.json';}], ['다른 선행 버전', (p, c) => {c['12-motion'].inputs[2].revision = 1;}], ['옛 엔진', (p) => {p.settings.visual_engine = 'Image';}], ['누락 컷', (p, c) => {c['12-motion'].payload.result.clips.pop();}], ['미검증 컷', (p, c) => {c['12-motion'].payload.result.clips[0].media.verified = false;}], ['다른 시각 버전', (p, c) => {c['12-motion'].payload.result.clips[0].media.path = 'projects/water-hydrogen-bond-surface-tension/stages/12-motion/r1/clip.mp4';}], ['누락 끝점', (p, c) => {c['11-images'].payload.result.frames.pop();}], ['프로필 혼입', (p, c) => {c['12-motion'].payload.request.render_profile.samples = 32;}], ['원본 혼입', (p, c, a) => {a.source_sha256 = '0'.repeat(64);}], ['시간 격자 오류', (p, c) => {c['07-cuts'].payload.cuts[1].start_ms += 1;}], ['연속 경계 불일치', (p, c) => {c['11-images'].payload.result.frames.find((f) => f.frame_id === 'c02-start').media.path = c['11-images'].payload.result.frames[2].media.path + 'wrong.png';}], ['다른 Scene 앵커', (p, c, a) => {a.cuts[0].scene_name = 'WrongScene';}], ['움직임 키 누락', (p, c, a) => {a.cuts.find((a) => a.motion.some((m) => m.animated)).motion.find((m) => m.animated).keys.pop();}], ['옛 대표 원본', (p, c) => {c['08-scenes'].payload.preview.scene_file.path = 'projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r1/old.blend';}], ['미검증 대표 화면', (p, c) => {c['08-scenes'].payload.preview.samples[0].media.verified = false;}], ]; for (const [name, mutate] of cases) { const {project, context, anchors} = structuredClone(inputs); mutate(project, context, anchors); assert.throws(() => validateInputs(project, context, anchors), undefined, name); } assert.throws(() => measured(absolute(data.clips[1].video_path), data.clips[0].end_ms), /실제 시간 불일치/); assert.throws(() => measured(absolute('projects/water-hydrogen-bond-surface-tension/missing.mp4'), 20)); for (const [name, mutate] of [ ['없는 통합 WAV', (i) => {i.context['06-tts'].payload.result.narration.path = 'projects/water-hydrogen-bond-surface-tension/media/06-tts/r1/missing.wav';}], ['미검증 단위 WAV', (i) => {i.context['06-tts'].payload.result.files[0].media.verified = false;}], ['실측 WAV 시간 오류', (i) => {i.context['06-tts'].payload.result.files[0].media.duration_ms += 20;}], ['실제 원본 SHA 오류', (i) => {i.context['11-images'].payload.result.scene_file.sha256 = '0'.repeat(64); i.anchors.source_sha256 = '0'.repeat(64);}], ]) { const changed = structuredClone(inputs); mutate(changed); assert.throws(() => prepareData(changed), undefined, name); } console.log(`실제 Blender 입력과 ${cases.length + 6}개 거부 사례 통과: 31컷·62끝점·38자막·5528프레임`);