atlas-iac/testing/tests/test_hermes_webui_brand.py
2026-08-23 03:52:03 +00:00

193 lines
7.7 KiB
Python

"""Canonical icon, PWA, and fail-closed Hermes WebUI identity contracts."""
from __future__ import annotations
import hashlib
import json
import os
from pathlib import Path
import shutil
import struct
import subprocess
import sys
ROOT = Path(__file__).resolve().parents[2]
DOCKERFILES = ROOT / "dockerfiles"
FIXTURE = ROOT / "testing/fixtures/hermes-webui-0.52.181"
AGENT_FIXTURE = ROOT / "testing/fixtures/hermes-agent"
ATLAS_PATCHER = DOCKERFILES / "hermes-webui-atlas-patch.py"
BRAND_PATCHER = DOCKERFILES / "hermes-webui-brand-patch.py"
ASSETS = DOCKERFILES / "hermes-webui-assets"
MANIFEST = DOCKERFILES / "hermes-webui-manifest.json"
BRAND_CSS = DOCKERFILES / "hermes-webui-brand.css"
EXPECTED_HASHES = {
"hermes-agent.ico": "aefe65e6574e6f46d3382588f46c508e1b3f3b3c9ce3dec6d335403a5374add9",
"hermes-agent-192.png": "0e4102cc715372058dd6ab55e9cde2567e46fee5ab6557b564cdd212ccd2616f",
"hermes-agent-512.png": "6661e5ca0ecc690af213b9f85f961541e6d3d0e36946ede9d3c2c97dfbd3c23d",
}
def _patched_fixture(tmp_path: Path) -> Path:
target = tmp_path / "hermes-webui"
agent_target = tmp_path / "hermes-agent"
shutil.copytree(FIXTURE, target)
shutil.copytree(AGENT_FIXTURE, agent_target)
env = os.environ.copy()
env["HERMES_WEBUI_PATCH_ROOT"] = str(target)
env["HERMES_AGENT_PATCH_ROOT"] = str(agent_target)
for patcher in (ATLAS_PATCHER, BRAND_PATCHER):
subprocess.run(
[sys.executable, str(patcher)],
cwd=ROOT,
env=env,
check=True,
capture_output=True,
text=True,
)
return target
def _png_size(path: Path) -> tuple[int, int]:
payload = path.read_bytes()
assert payload.startswith(b"\x89PNG\r\n\x1a\n")
assert payload[12:16] == b"IHDR"
return struct.unpack(">II", payload[16:24])
def test_canonical_icon_provenance_format_and_pwa_derivatives() -> None:
"""The supplied persona is tracked exactly and only resized for PWA use."""
for name, expected in EXPECTED_HASHES.items():
payload = (ASSETS / name).read_bytes()
assert hashlib.sha256(payload).hexdigest() == expected
ico = (ASSETS / "hermes-agent.ico").read_bytes()
reserved, image_type, count = struct.unpack_from("<HHH", ico)
assert (reserved, image_type, count) == (0, 1, 3)
sizes = set()
for index in range(count):
offset = 6 + index * 16
width, height, _colors, _reserved, planes, depth, length, start = (
struct.unpack_from("<BBBBHHII", ico, offset)
)
sizes.add((width or 256, height or 256))
assert (planes, depth) == (1, 32)
assert ico[start : start + 8] == b"\x89PNG\r\n\x1a\n"
assert start + length <= len(ico)
assert sizes == {(16, 16), (32, 32), (48, 48)}
assert _png_size(ASSETS / "hermes-agent-192.png") == (192, 192)
assert _png_size(ASSETS / "hermes-agent-512.png") == (512, 512)
provenance = (ASSETS / "SOURCE.md").read_text(encoding="utf-8")
assert "/opt/hermes/web/public/favicon.ico" in provenance
assert EXPECTED_HASHES["hermes-agent.ico"] in provenance
assert "LANCZOS" in provenance
def test_manifest_is_installable_scoped_and_uses_only_canonical_persona() -> None:
"""The app has both mandatory icon sizes and no remote or secret-bearing data."""
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
assert manifest["id"] == "./"
assert manifest["name"] == "Hermes Chat"
assert manifest["short_name"] == "Hermes"
assert manifest["start_url"].startswith("./")
assert manifest["scope"] == "./"
assert manifest["display"] == "standalone"
assert manifest["background_color"] == "#070A12"
assert manifest["theme_color"] == "#0D1420"
icons = manifest["icons"]
assert {(icon["sizes"], icon["type"]) for icon in icons} == {
("192x192", "image/png"),
("512x512", "image/png"),
}
assert {icon["src"] for icon in icons} == {
"static/hermes-agent-192.png",
"static/hermes-agent-512.png",
}
assert all(icon["purpose"] == "any" for icon in icons)
serialized = json.dumps(manifest).lower()
assert "http:" not in serialized and "https:" not in serialized
assert "secret" not in serialized and "token" not in serialized
def test_production_patchers_apply_title_icons_theme_and_cache_contract(
tmp_path: Path,
) -> None:
"""Exercise the shipped patchers against pinned upstream source fragments."""
target = _patched_fixture(tmp_path)
index = (target / "static/index.html").read_text(encoding="utf-8")
worker = (target / "static/sw.js").read_text(encoding="utf-8")
assert index.count("<title>Hermes Chat</title>") == 1
assert index.count('id="hermesBrandStyles"') == 1
assert 'href="static/hermes-agent.ico"' in index
assert 'sizes="192x192" href="static/hermes-agent-192.png"' in index
assert 'sizes="512x512" href="static/hermes-agent-512.png"' in index
assert '<meta name="apple-mobile-web-app-title" content="Hermes Chat">' in index
assert '<meta name="theme-color" content="#0D1420"' in index
assert 'id="appTitlebarTitle">Hermes Chat</span>' in index
assert '<img src="static/hermes-agent-192.png" alt="">' in index
assert "favicon.svg" not in index
assert "favicon-32.png" not in index
assert worker.count("'./static/hermes-brand.css' + VQ") == 1
for name in EXPECTED_HASHES:
assert worker.count(f"'./static/{name}'") == 1
assert "favicon.svg" not in worker
assert "favicon-32.png" not in worker
def test_brand_patch_rejects_upstream_drift_before_partial_success(
tmp_path: Path,
) -> None:
"""A changed pinned title/favicon context cannot silently ship partial branding."""
target = _patched_fixture(tmp_path)
index = target / "static/index.html"
index.write_text(
index.read_text(encoding="utf-8").replace(
"<title>Hermes Chat</title>", "<title>Upstream changed</title>", 1
),
encoding="utf-8",
)
env = os.environ.copy()
env["HERMES_WEBUI_PATCH_ROOT"] = str(target)
result = subprocess.run(
[sys.executable, str(BRAND_PATCHER)],
cwd=ROOT,
env=env,
check=False,
capture_output=True,
text=True,
)
assert result.returncode != 0
assert "brand patch context changed" in result.stderr
def test_brand_css_is_accessible_dark_and_reduced_motion_aware() -> None:
"""Identity colors retain system controls and disable cosmetic motion."""
css = BRAND_CSS.read_text(encoding="utf-8")
dark = css.split(":root.dark {", 1)[1]
assert "color-scheme: dark" in dark
assert "--bg: #070a12" in dark
assert "--text: #e8f1f4" in dark
assert "--accent: #48cfcc" in dark
assert "--voice-accent: 72, 207, 204" in css
assert "--voice-accent-secondary: 76, 164, 205" in css
assert "@media (prefers-reduced-motion: reduce)" in css
reduced = css.split("@media (prefers-reduced-motion: reduce)", 1)[1]
assert "transition: none !important" in reduced
assert "animation:" not in css
def test_dockerfile_copies_and_verifies_every_tracked_brand_asset() -> None:
"""The immutable image, not a runtime coordinator path, owns PWA assets."""
dockerfile = (DOCKERFILES / "Dockerfile.hermes-webui").read_text(encoding="utf-8")
assert "/opt/hermes/web/public/favicon.ico" not in dockerfile
assert "COPY dockerfiles/hermes-webui-brand-patch.py" in dockerfile
assert "python /tmp/hermes-webui-brand-patch.py" in dockerfile
assert "COPY dockerfiles/hermes-webui-manifest.json" in dockerfile
assert "COPY dockerfiles/hermes-webui-brand.css" in dockerfile
for name, digest in EXPECTED_HASHES.items():
assert f"COPY dockerfiles/hermes-webui-assets/{name}" in dockerfile
assert digest in dockerfile