#!/usr/bin/env bash # End-to-end smoke test against a throwaway data dir on a scratch port. # Validates: operators load, settings CRUD, upload, ffprobe/ffmpeg_frames/ # blender_convert run to completion, outputs register, cancel + retry work. set -euo pipefail cd "$(dirname "$0")/.." PORT=${PORT:-8799} TMP=$(mktemp -d) BLENDER="/Applications/Blender.app/Contents/MacOS/Blender" UV=/opt/homebrew/bin/uv pass=0; fail=0 say() { printf "\n\033[1m== %s ==\033[0m\n" "$1"; } ok() { printf " \033[32mPASS\033[0m %s\n" "$1"; pass=$((pass+1)); } bad() { printf " \033[31mFAIL\033[0m %s\n" "$1"; fail=$((fail+1)); } j() { python3 -c "import json,sys;d=json.load(sys.stdin);print($1)"; } cleanup() { kill "${SRV:-0}" 2>/dev/null || true; rm -rf "$TMP"; } trap cleanup EXIT say "fixtures" ffmpeg -y -f lavfi -i "testsrc2=size=640x360:rate=30:duration=3" -pix_fmt yuv420p "$TMP/clip.mp4" >/dev/null 2>&1 ok "test video" "$BLENDER" -b --python-expr " import bpy bpy.ops.wm.read_factory_settings(use_empty=True) bpy.ops.mesh.primitive_monkey_add() bpy.ops.export_scene.gltf(filepath='$TMP/mesh.glb', export_format='GLB')" >/dev/null 2>&1 ok "test glb" say "start server (MODELBEAST_DATA=$TMP/data, port $PORT)" MODELBEAST_DATA="$TMP/data" "$UV" run uvicorn server.main:app --host 127.0.0.1 --port "$PORT" >"$TMP/server.log" 2>&1 & SRV=$! for i in $(seq 1 40); do curl -sf "http://127.0.0.1:$PORT/api/operators" >/dev/null 2>&1 && break; sleep 0.5; done B="http://127.0.0.1:$PORT" say "operators + settings" NOPS=$(curl -s "$B/api/operators" | j "len(d)") [ "$NOPS" -ge 3 ] && ok "operators loaded ($NOPS)" || bad "operators ($NOPS)" curl -s -X PUT "$B/api/settings" -H 'Content-Type: application/json' -d '{"archive_path":"/tmp/xyz"}' >/dev/null AP=$(curl -s "$B/api/settings" | j "d['archive_path']") [ "$AP" = "/tmp/xyz" ] && ok "settings persist" || bad "settings ($AP)" # secret masking curl -s -X PUT "$B/api/settings" -H 'Content-Type: application/json' -d '{"fal_key":"sk-secret-123456"}' >/dev/null FK=$(curl -s "$B/api/settings" | j "d['fal_key']") [ "$FK" = "••••••••" ] && ok "secret masked in GET" || bad "secret leak ($FK)" say "upload + ffprobe" VID=$(curl -s -F "file=@$TMP/clip.mp4" "$B/api/assets" | j "d['id']") GLB=$(curl -s -F "file=@$TMP/mesh.glb" "$B/api/assets" | j "d['id']") JOB=$(curl -s -X POST "$B/api/jobs" -H 'Content-Type: application/json' -d "{\"operator\":\"ffprobe\",\"asset_id\":\"$VID\"}" | j "d['id']") for i in $(seq 1 30); do S=$(curl -s "$B/api/jobs/$JOB" | j "d['status']"); [ "$S" = done ] || [ "$S" = error ] && break; sleep 0.5; done [ "$S" = done ] && ok "ffprobe done" || bad "ffprobe ($S)" say "ffmpeg_frames" JOB=$(curl -s -X POST "$B/api/jobs" -H 'Content-Type: application/json' -d "{\"operator\":\"ffmpeg_frames\",\"asset_id\":\"$VID\",\"params\":{\"fps\":4,\"max_frames\":8}}" | j "d['id']") for i in $(seq 1 40); do S=$(curl -s "$B/api/jobs/$JOB" | j "d['status']"); [ "$S" = done ] || [ "$S" = error ] && break; sleep 0.5; done [ "$S" = done ] && ok "ffmpeg_frames done" || bad "ffmpeg_frames ($S)" NFR=$(curl -s "$B/api/assets" | j "sum(1 for a in d if a['kind']=='frames')") [ "$NFR" -ge 1 ] && ok "frames asset registered" || bad "no frames asset" say "blender_convert glb->fbx (bool param regression)" JOB=$(curl -s -X POST "$B/api/jobs" -H 'Content-Type: application/json' -d "{\"operator\":\"blender_convert\",\"asset_id\":\"$GLB\",\"params\":{\"target\":\"fbx\",\"apply_transforms\":true}}" | j "d['id']") for i in $(seq 1 60); do S=$(curl -s "$B/api/jobs/$JOB" | j "d['status']"); [ "$S" = done ] || [ "$S" = error ] && break; sleep 0.5; done [ "$S" = done ] && ok "blender_convert done" || bad "blender_convert ($S)" NFBX=$(curl -s "$B/api/assets" | j "sum(1 for a in d if a['name'].endswith('.fbx'))") [ "$NFBX" -ge 1 ] && ok "fbx registered" || bad "no fbx" say "retry + delete" NEW=$(curl -s -X POST "$B/api/jobs/$JOB/retry" | j "d['id']") [ -n "$NEW" ] && [ "$NEW" != "$JOB" ] && ok "retry spawns new job" || bad "retry" sleep 1 DEL=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE "$B/api/jobs/$JOB") [ "$DEL" = 200 ] && ok "delete job" || bad "delete ($DEL)" printf "\n\033[1mResult: %d passed, %d failed\033[0m\n" "$pass" "$fail" [ "$fail" -eq 0 ]