japanese-learning-avatar / tests /test_vendor.py
WolfDavid's picture
feat(01-10): vendor the 3D runtime and take the CDN out of the render path
c23e6ce
Raw History Blame
5.93 kB
"""Static guards on avatar/vendor/: the 3D runtime ships with the app, not from a CDN.
01-RESEARCH.md names the risk plainly: a flagship portfolio Space that renders a blank
canvas because a volunteer-run CDN blipped during a recruiter's visit is an unacceptable
failure mode. scripts/vendor_modules.py retires it; these tests keep it retired. They
read files, never the network, so they belong to the quick loop.
"""
from __future__ import annotations
import hashlib
import re
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parent.parent
AVATAR = REPO_ROOT / "avatar"
VENDOR = AVATAR / "vendor"
README = VENDOR / "README.md"
# Minimum plausible sizes. three's es2022 build is ~730 KB, three-vrm ~155 KB and
# GLTFLoader ~46 KB; a truncated download or an esm.sh error page is far below these.
MIN_BYTES = {
"three.mjs": 400_000,
"three-vrm.mjs": 100_000,
"GLTFLoader.mjs": 20_000,
}
LICENSE_FILES = ("LICENSE-three.txt", "LICENSE-three-vrm.txt")
# The plan's own acceptance greps, verbatim in spirit: a `from` or `import` whose
# specifier is an absolute URL or a root-relative path. esm.sh output is minified, so the
# whitespace is optional. Anchored on the keyword, not the bare word `from`, because the
# builds contain prose such as `colors from "srgb-linear"` (a template string in
# GLTFLoader) that is not an import.
REMOTE_SPECIFIER = re.compile(
r"""\b(?:import|export)\s*(?:\*\s*as\s+[\w$]+|\{[^}]*\}|\*|[\w$]+)?\s*from\s*["'](?:https?:)?/"""
r"""|\bimport\s*\(?\s*["'](?:https?:)?/"""
)
LOCAL_THREE = re.compile(r"""from\s*["']\./three\.mjs["']""")
# The one file that may still name esm.sh. avatar/asr.js loads @huggingface/transformers
# from the CDN, and vendoring that JS shim alone would buy nothing: the library pulls
# its own multi-megabyte ONNX Runtime WASM assets and the 135.8 MB Whisper model from
# the Hugging Face CDN regardless, so the push-to-talk path depends on a CDN either way.
# The RENDER path is what must survive a CDN outage, and it now does; the exemption is
# deliberate and recorded in avatar/vendor/README.md.
ESM_SH_EXEMPT = {"asr.js"}
def _readme_rows() -> dict[str, tuple[int, str]]:
"""``name -> (bytes, sha256)`` from every table row of avatar/vendor/README.md."""
text = README.read_text(encoding="utf-8")
rows = re.findall(r"^\| `([^`]+)` \| ([\d,]+) \| `([0-9a-f]{64})` \|", text, re.M)
assert rows, "avatar/vendor/README.md has no hash rows; run scripts/vendor_modules.py"
return {name: (int(size.replace(",", "")), digest) for name, size, digest in rows}
def test_vendor_files_exist_and_are_plausible_sizes():
for name, minimum in MIN_BYTES.items():
path = VENDOR / name
assert path.is_file(), f"{path.relative_to(REPO_ROOT)} is missing"
size = path.stat().st_size
assert size > minimum, f"{name} is {size:,} bytes; anything under {minimum:,} is not it"
for name in LICENSE_FILES:
text = (VENDOR / name).read_text(encoding="utf-8")
assert "Permission is hereby granted" in text, f"{name} is not an MIT licence text"
assert README.is_file()
@pytest.mark.parametrize("name", sorted(MIN_BYTES))
def test_no_remote_imports_in_vendored_modules(name):
"""Zero absolute or root-relative specifiers. One survivor loads a second three.js,
three-vrm's instanceof checks fail, and the avatar is a T-posed statue with no error."""
source = (VENDOR / name).read_text(encoding="utf-8")
leaks = [m.group(0) for m in REMOTE_SPECIFIER.finditer(source)]
assert leaks == [], f"{name} still imports from outside avatar/vendor/: {leaks}"
if name != "three.mjs":
assert LOCAL_THREE.search(source), f"{name} never imports ./three.mjs; rewrite missed"
def test_vendored_hashes_match_readme():
"""The README's SHA256s are the files on disk, so a hand-edit of a module is caught."""
recorded = _readme_rows()
for name in (*MIN_BYTES, *LICENSE_FILES):
assert name in recorded, f"{name} has no row in avatar/vendor/README.md"
data = (VENDOR / name).read_bytes()
size, digest = recorded[name]
assert len(data) == size, f"{name}: README says {size:,} bytes, disk has {len(data):,}"
assert hashlib.sha256(data).hexdigest() == digest, (
f"{name} differs from the hash in avatar/vendor/README.md; "
"regenerate with scripts/vendor_modules.py rather than editing by hand"
)
total = sum(recorded[n][0] for n in MIN_BYTES)
assert f"{total:,}" in README.read_text(encoding="utf-8"), "README total bytes is stale"
def test_runtime_has_no_esm_sh():
"""No file under avatar/ names the CDN, except avatar/asr.js (see ESM_SH_EXEMPT).
Two layers: the URL form ``https://esm.sh`` is forbidden in every file including the
vendored builds (which carry an ``/* esm.sh - ... */`` banner comment, not a URL), and
the bare token is forbidden in every .js/.html runtime source.
"""
offenders: list[str] = []
for path in sorted(AVATAR.rglob("*")):
if not path.is_file() or path == README:
continue
rel = path.relative_to(AVATAR).as_posix()
if path.suffix in {".vrm", ".wav"}:
continue
text = path.read_text(encoding="utf-8", errors="replace")
if path.name in ESM_SH_EXEMPT:
urls = re.findall(r"https://esm\.sh/[^\s'\"]+", text)
assert len(set(urls)) == 1 and urls[0].startswith(
"https://esm.sh/@huggingface/transformers@"
), f"asr.js must load exactly one esm.sh URL, transformers.js; found {urls}"
continue
if re.search(r"https?://esm\.sh", text):
offenders.append(f"{rel} (CDN URL)")
elif path.suffix in {".js", ".html"} and "esm.sh" in text:
offenders.append(f"{rel} (token)")
assert offenders == [], f"esm.sh is back in the runtime path: {offenders}"