"""M17 memory capsule (lane G): bundle structure, baked-JSON validity, serve.py Range. Builds a real capsule from the synthetic project (temp data dir per conftest) against a fabricated frontend dist (spec allows this — running npm inside tests would be neither hermetic nor fast), then boots the bundled ``serve.py`` in a subprocess and exercises it over HTTP: 200s, JSON content types on the extension-less baked API files, and the 206 Range behavior video seeking depends on (phase-5 pitfall #4). """ from __future__ import annotations import json import shutil import socket import subprocess import sys import time import urllib.error import urllib.request import pytest pytestmark = pytest.mark.skipif( shutil.which("ffmpeg") is None or shutil.which("ffprobe") is None, reason="ffmpeg/ffprobe required to build the synthetic capsule fixture", ) CAMPATH = { "version": 1, "keyframes": [ {"t_global": 0.2, "pos": [0.0, 1.0, 5.0], "quat": [0.0, 0.0, 0.0, 1.0], "fov": 50.0}, {"t_global": 0.8, "pos": [2.0, 1.0, 4.0], "quat": [0.0, 0.5, 0.0, 0.8660254], "fov": 42.0}, ], } BEATS = {"tempo_bpm": 120.0, "beats_s": [0.25, 0.75], "onsets": [], "generated_by": "festival4d.audio_features"} SPLAT_BYTES = b"ply\nfake-splat-for-tests\n" API_SNIPPET = '' # --------------------------------------------------------------------------- # Fixtures # --------------------------------------------------------------------------- @pytest.fixture(scope="module") def project(): """Synthetic project + every optional artifact seeded (beats, splat, a saved path).""" from festival4d import config, db, synthetic synthetic.build(duration_s=1.0, run_ffmpeg=True) # into the temp data dir (conftest) db.init_engine() db.init_db() config.BEATS_JSON.parent.mkdir(parents=True, exist_ok=True) config.BEATS_JSON.write_text(json.dumps(BEATS)) config.SPLAT_PLY.write_bytes(SPLAT_BYTES) saved = db.add_path("capsule sweep", json.dumps(CAMPATH)) yield {"path_id": saved.id} # Leave the shared session data dir as later test modules expect to find it. config.BEATS_JSON.unlink(missing_ok=True) config.SPLAT_PLY.unlink(missing_ok=True) db.delete_path(saved.id) def _make_dist(root): """A minimal Vite-shaped dist: index.html with a module \n" " \n
\n