Files
hms-mediaengine/tests/unit/test_plugin_manifest.py
T

135 lines
4.5 KiB
Python
Raw Normal View History

"""Unit-Tests Plugin-Manifest-Validierung (PLAN.md §14, §27.2)."""
from __future__ import annotations
import json
import zipfile
from pathlib import Path
from hms_plugin_sdk import load_manifest, validate_manifest, validate_plugin_zip
REPO = Path(__file__).resolve().parents[2]
EXAMPLES = REPO / "plugins" / "examples"
def _valid_manifest() -> dict:
return json.loads(
(EXAMPLES / "com.hms.fx.example_passthrough" / "plugin.json").read_text(encoding="utf-8")
)
def test_example_passthrough_validates_with_shaders() -> None:
manifest, errors = load_manifest(EXAMPLES / "com.hms.fx.example_passthrough")
assert errors == [], errors
assert manifest["id"] == "com.hms.fx.example_passthrough"
def test_example_gaussian_blur_validates_with_shaders() -> None:
manifest, errors = load_manifest(EXAMPLES / "com.hms.fx.gaussian_blur")
assert errors == [], errors
variants = manifest["adaptive_quality"]["variants"]
assert [v["id"] for v in variants] == ["low", "medium", "high"]
assert [v["samples"] for v in variants] == [5, 9, 17]
def test_valid_manifest_without_root_ok() -> None:
assert validate_manifest(_valid_manifest()) == []
def test_wrong_schema_version_rejected() -> None:
m = _valid_manifest()
m["schema_version"] = 99
assert any("schema_version" in e for e in validate_manifest(m))
def test_invalid_plugin_id_rejected() -> None:
m = _valid_manifest()
m["id"] = "../evil"
assert any("invalid plugin id" in e for e in validate_manifest(m))
def test_invalid_semver_rejected() -> None:
m = _valid_manifest()
m["version"] = "1.0"
assert any("semantic" in e for e in validate_manifest(m))
def test_dmx_footprint_over_8_slots_rejected() -> None:
m = _valid_manifest()
m["parameters"] = [
{"id": f"p{i}", "label": f"P{i}", "type": "float", "minimum": 0, "maximum": 1,
"default": 0, "dmx_slots": [i]}
for i in range(1, 10)
]
assert any("exceeds 8" in e for e in validate_manifest(m)) # §14.7
def test_unsafe_shader_path_rejected() -> None:
m = _valid_manifest()
m["entrypoints"]["gl"]["passes"][0]["fragment"] = "../../evil.frag"
assert any("unsafe shader path" in e for e in validate_manifest(m))
def test_missing_shader_file_detected_with_root() -> None:
m = _valid_manifest()
m["entrypoints"]["gl"]["passes"][0]["fragment"] = "shaders/gl/missing.frag"
errors = validate_manifest(m, plugin_root=EXAMPLES / "com.hms.fx.example_passthrough")
assert any("missing shader file" in e for e in errors)
def test_invalid_failure_mode_rejected() -> None:
m = _valid_manifest()
m["failure_mode"] = "crash"
assert any("failure_mode" in e for e in validate_manifest(m))
def test_duplicate_parameter_ids_rejected() -> None:
m = _valid_manifest()
m["parameters"].append(dict(m["parameters"][0]))
assert any("duplicate parameter id" in e for e in validate_manifest(m))
def _make_zip(tmp_path: Path, files: dict[str, str | bytes]) -> Path:
zpath = tmp_path / "plugin.zip"
with zipfile.ZipFile(zpath, "w") as zf:
for name, content in files.items():
zf.writestr(name, content)
return zpath
def test_valid_zip_passes(tmp_path: Path) -> None:
plugin_dir = EXAMPLES / "com.hms.fx.example_passthrough"
files: dict[str, str | bytes] = {}
for f in sorted(plugin_dir.rglob("*")):
if f.is_file():
rel = f.relative_to(plugin_dir.parent)
files[str(rel)] = f.read_text(encoding="utf-8")
zpath = _make_zip(tmp_path, files)
assert validate_plugin_zip(zpath) == []
def test_zip_traversal_rejected(tmp_path: Path) -> None:
files = {"pkg/plugin.json": json.dumps(_valid_manifest()), "../evil.frag": "x"}
zpath = _make_zip(tmp_path, files)
assert any("unsafe path" in e for e in validate_plugin_zip(zpath))
def test_zip_disallowed_file_type_rejected(tmp_path: Path) -> None:
files = {
"pkg/plugin.json": json.dumps(_valid_manifest()),
"pkg/evil.exe": "MZ",
}
zpath = _make_zip(tmp_path, files)
assert any("disallowed file type" in e for e in validate_plugin_zip(zpath))
def test_zip_without_manifest_rejected(tmp_path: Path) -> None:
zpath = _make_zip(tmp_path, {"pkg/shader.frag": "void main(){}"})
assert any("plugin.json not found" in e for e in validate_plugin_zip(zpath))
def test_corrupt_zip_rejected(tmp_path: Path) -> None:
zpath = tmp_path / "broken.zip"
zpath.write_bytes(b"not a zip at all")
assert any("not a valid zip" in e for e in validate_plugin_zip(zpath))