Spaces:
Running
Running
File size: 4,560 Bytes
39ff632 a35b2b8 39ff632 324b9a7 39ff632 324b9a7 a35b2b8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | """Headless render verification: bundle the fixture factory exactly like the
runtime does and execute it in node (scene-graph smoke, no WebGL needed)."""
from __future__ import annotations
import json
import shutil
import subprocess
from pathlib import Path
import pytest
from app import forge_bridge
REPO_ROOT = Path(__file__).resolve().parents[1]
ESBUILD = REPO_ROOT / "node_modules" / ".bin" / "esbuild"
NODE = shutil.which("node")
pytestmark = pytest.mark.skipif(
not (ESBUILD.exists() and NODE), reason="node/esbuild not installed (run npm ci)")
def test_fixture_factory_bundles_and_executes(tmp_path, factory_fixture_ts):
entry = tmp_path / "entry.js"
entry.write_text(
'export { createMugModel as makeModel, createMugLookDevLights as makeLights } '
f'from "{factory_fixture_ts}";\n'
f'export {{ mountViewer }} from "{REPO_ROOT / "app" / "static" / "viewer-core.js"}";\n',
encoding="utf-8")
bundle = tmp_path / "model.bundle.js"
link = tmp_path / "node_modules"
link.symlink_to(REPO_ROOT / "node_modules", target_is_directory=True)
build = subprocess.run(
[str(ESBUILD), str(entry), "--bundle", "--format=esm",
"--target=es2022", "--minify", f"--outfile={bundle}"],
cwd=tmp_path, capture_output=True, text=True, timeout=120)
assert build.returncode == 0, build.stderr
assert bundle.stat().st_size > 100_000
smoke = subprocess.run(
[NODE, str(REPO_ROOT / "scripts" / "node_smoke.mjs"), str(bundle),
str(REPO_ROOT / "tests" / "fixtures" / "canned_spec.json")],
capture_output=True, text=True, timeout=120)
assert smoke.returncode == 0, f"{smoke.stdout}\n{smoke.stderr}"
report = json.loads(smoke.stdout.splitlines()[0])
assert report["meshes"] >= 1
assert report["children"] >= 1
assert report["runtimeNodes"] >= 1
assert all(isinstance(v, (int, float)) for v in report["boundingBox"])
bad_spec = json.loads(
(REPO_ROOT / "tests" / "fixtures" / "canned_spec.json").read_text(
encoding="utf-8"))
body = next(component for component in bad_spec["componentTree"]
if component["id"] == "body")
body["dimensions"]["width"] *= 2
bad_spec_path = tmp_path / "bad-spec.json"
bad_spec_path.write_text(json.dumps(bad_spec), encoding="utf-8")
rejected = subprocess.run(
[NODE, str(REPO_ROOT / "scripts" / "node_smoke.mjs"), str(bundle),
str(bad_spec_path)],
capture_output=True, text=True, timeout=120)
assert rejected.returncode == 1
assert "body dimensions disagree with spec" in rejected.stderr
def test_axis_attachment_preserves_declared_dimensions(tmp_path, canned_spec):
"""Attachment endpoints orient axial primitives without silently replacing
their declared physical dimensions."""
spec = json.loads(json.dumps(canned_spec))
handle = next(component for component in spec["componentTree"]
if component["id"] == "handle")
handle["primitive"] = "cylinder"
# Deliberately disagree with the 0.06 m declared height. The compiler must
# retain dimensions as the geometry contract even when endpoints are used
# to position and orient the mesh.
handle["attachment"]["localStart"] = [0.04, 0.07, 0.0]
handle["attachment"]["localEnd"] = [0.04, 0.03, 0.0]
preview = forge_bridge.prepare_hosted_preview(spec)
spec_path = tmp_path / "compile-spec.json"
spec_path.write_text(json.dumps(preview), encoding="utf-8")
factory = tmp_path / "factory.ts"
forge_bridge.generate_factory(
spec_path, factory, pass_id=forge_bridge.HOSTED_PREVIEW_PASS)
entry = tmp_path / "entry.js"
entry.write_text(
f'export {{ {forge_bridge.factory_export_name(factory.read_text(encoding="utf-8"))} '
f'as makeModel }} from "{factory}";\n',
encoding="utf-8")
bundle = tmp_path / "model.bundle.js"
(tmp_path / "node_modules").symlink_to(
REPO_ROOT / "node_modules", target_is_directory=True)
build = subprocess.run(
[str(ESBUILD), str(entry), "--bundle", "--format=esm",
"--target=es2022", "--minify", f"--outfile={bundle}"],
cwd=tmp_path, capture_output=True, text=True, timeout=120)
assert build.returncode == 0, build.stderr
smoke = subprocess.run(
[NODE, str(REPO_ROOT / "scripts" / "node_smoke.mjs"), str(bundle),
str(spec_path)],
capture_output=True, text=True, timeout=120)
assert smoke.returncode == 0, f"{smoke.stdout}\n{smoke.stderr}"
|