Files
hermes-agent/tests/scripts/test_docker_frontend_inputs.py
ethernet 5e4a2a3d24 refactor(pm): remove legacy dependency and launch managers
Competing installers and checkout-local venv assumptions bypassed PM
selection, install consent, and generation lifetimes. Route consumers
through PM and installation-bound launchers. Refresh source launchers
before obsolete Python entries can be collected.

Remove Node, browser, and CUA acquisition engines, obsolete venv-holder
handling, detached sync, and unused PM APIs. Keep historical updater
exports inert and preserve external tool ownership and native integration.

Share product freshness and prepared inputs across builders. Align plugin
admission, Docker provisioning, setup instructions, and behavioral tests.

Verified targeted Python and JavaScript tests, desktop and web typechecks,
scoped lint, real product builds, and the Docker frontend smoke test.
The missed post-setup test cleanup is included and verified.

Native Windows/macOS execution, full Rust compilation, and the complete
repository suite remain unverified. Historical compatibility requirements
were preserved and extended, not fully rescanned.
2026-09-12 14:57:38 -04:00

80 lines
3.4 KiB
Python

"""Exercise the frontend COPY closure before an image/network build.
PM environment construction is the only substituted boundary. The copied icon
provider must load, ask for its real dependency group, and invoke the copied
generator. Image-level runtime/permission coverage stays in tests/docker.
"""
from pathlib import Path
import runpy
import shlex
import shutil
from types import SimpleNamespace
from types import ModuleType
from pathspec import PathSpec
import pm
def test_frontend_copy_closure_reaches_the_icon_provider(tmp_path, monkeypatch):
repo = Path(__file__).resolve().parents[2]
# Apply the frontend's local COPY declarations, not a duplicated filename
# allowlist. Runtime-base supplies PM itself and is separately image-tested.
frontend = False
ignored = PathSpec.from_lines("gitignore", (repo / ".dockerignore").read_text().splitlines())
def excluded(directory, names):
return [name for name in names if ignored.match_file(
(Path(directory) / name).relative_to(repo).as_posix() +
("/" if (Path(directory) / name).is_dir() else ""))]
for line in (repo / "Dockerfile").read_text().splitlines():
if not line.startswith(("FROM ", "COPY ")):
continue
words = shlex.split(line.rstrip("\\"), comments=True)
if not words:
continue
if words[0] == "FROM":
frontend = words[-1] == "frontend_build"
if not frontend or words[0] != "COPY" or any(word.startswith("--") for word in words[1:]):
continue
sources, destination = words[1:-1], tmp_path / words[-1]
for pattern in sources:
for source in repo.glob(pattern):
target = destination / source.name if words[-1].endswith("/") else destination
if source.is_dir():
shutil.copytree(source, destination, dirs_exist_ok=True,
ignore=excluded)
else:
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target)
acquired = []
def environment(**kwargs):
acquired.append(kwargs)
return Path("/prepared/icon-python")
launched = []
monkeypatch.setattr(pm, "build_environment", environment)
provider = runpy.run_path(str(tmp_path / "scripts/build/icon_environment.py"))
def run(argv, *, cwd):
assert Path(argv[2]).is_file(), "generator must also be in the stage"
assert cwd == tmp_path
launched.append(argv)
return SimpleNamespace(returncode=0)
monkeypatch.setattr(provider["subprocess"], "run", run)
assert provider["main"](["--source", str(tmp_path), "--out", str(tmp_path / "icons")]) == 0
assert acquired[0]["source"] == tmp_path
assert acquired[0]["groups"] == ["icon-build"]
assert acquired[0]["only_groups"] is True
assert launched[0][:2] == ["/prepared/icon-python", "-I"]
# Exercise the generator's own source loader. Rasterization is not needed
# to prove that every composed SVG's artwork made it into the build context.
monkeypatch.setitem(__import__("sys").modules, "resvg_py", ModuleType("resvg_py"))
generator = runpy.run_path(str(tmp_path / "scripts/generate_icons.py"))
monkeypatch.setitem(generator["IconArt"].__init__.__globals__, "girl_bbox", lambda *args: (0, 0, 512, 512))
art = generator["IconArt"](tmp_path)
assert art.master_mac and art.master_mac_dark