"""Canonical icon, PWA, and fail-closed Hermes WebUI identity contracts.""" from __future__ import annotations import hashlib import importlib.util import json import os from pathlib import Path import shutil import struct import subprocess import sys from urllib.parse import urlsplit import pytest 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" MANIFEST_PATCHER = DOCKERFILES / "hermes-webui-manifest-patch.py" SMOKE = DOCKERFILES / "hermes-webui-smoke.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 _load(path: Path, name: str): spec = importlib.util.spec_from_file_location(name, path) assert spec is not None and spec.loader is not None module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module 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) env["HERMES_WEBUI_MANIFEST_SOURCE"] = str(MANIFEST) for patcher in (ATLAS_PATCHER, BRAND_PATCHER, MANIFEST_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(" 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("Hermes Chat") == 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 '' in index assert 'Hermes Chat' in index assert '' in index assert ( 'Hermes Agent' ) in index assert 'aria-label="Hermes caduceus"' not 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( "Hermes Chat", "Upstream changed", 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_manifest_patch_rejects_drift_before_wholesale_replacement( tmp_path: Path, ) -> None: """The branded file cannot hide a changed manifest in the pinned image.""" target = tmp_path / "hermes-webui" shutil.copytree(FIXTURE, target) installed = target / "static/manifest.json" installed.write_text( installed.read_text(encoding="utf-8").replace( '"name": "Hermes"', '"name": "Upstream drift"', 1 ), encoding="utf-8", ) before = installed.read_bytes() env = os.environ.copy() env["HERMES_WEBUI_PATCH_ROOT"] = str(target) env["HERMES_WEBUI_MANIFEST_SOURCE"] = str(MANIFEST) result = subprocess.run( [sys.executable, str(MANIFEST_PATCHER)], cwd=ROOT, env=env, check=False, capture_output=True, text=True, ) assert result.returncode != 0 assert "pinned upstream SHA-256 mismatch" in result.stderr assert installed.read_bytes() == before class _SmokeResponse: status = 200 def __init__(self, body: bytes, url: str) -> None: self.body = body self.url = url def __enter__(self): return self def __exit__(self, *_args): return None def read(self) -> bytes: return self.body def geturl(self) -> str: return self.url def test_server_smoke_resolves_served_manifest_icons_and_checks_root() -> None: """The image gate validates HTTP responses, not its own source files.""" module = _load(SMOKE, "hermes_webui_smoke_contract") manifest = json.loads(MANIFEST.read_text(encoding="utf-8")) root = ( b'' b'' b'' b'' ) calls = [] def open_fixture(request, *, timeout): assert timeout == 5 url = request.full_url calls.append(url) path = urlsplit(url).path if path == "/manifest.json": return _SmokeResponse( json.dumps(manifest).encode(), "http://assets.hermes.test/pwa/manifest.json", ) if path == "/": return _SmokeResponse(root, url) if path in ( "/pwa/static/hermes-agent-192.png", "/pwa/static/hermes-agent-512.png", "/static/hermes-agent-192.png", ): return _SmokeResponse(module.PNG_MAGIC + b"fixture", url) raise AssertionError(f"unexpected smoke URL: {url}") result = module.smoke("http://hermes.test/", opener=open_fixture) assert result["manifest"] == "http://assets.hermes.test/pwa/manifest.json" assert result["icon_requests"] == 3 assert calls.count("http://assets.hermes.test/pwa/static/hermes-agent-192.png") == 2 assert "http://assets.hermes.test/pwa/static/hermes-agent-512.png" in calls assert calls.count("http://hermes.test/static/hermes-agent-192.png") == 1 def test_server_smoke_rejects_unbranded_served_manifest() -> None: """A successful health check cannot mask the upstream PWA identity.""" module = _load(SMOKE, "hermes_webui_smoke_unbranded") def open_upstream(request, *, timeout): assert timeout == 5 payload = (FIXTURE / "static/manifest.json").read_bytes() return _SmokeResponse(payload, request.full_url) with pytest.raises(RuntimeError, match="not branded"): module.smoke("http://hermes.test/", opener=open_upstream) 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 "--hermes-violet: #9d8ee0" in dark assert "--hermes-grid: rgba(72, 207, 204, 0.025)" in dark assert ".empty-logo .hermes-agent-portrait" in css assert "width: 112px" in css assert ":root.dark .messages" in css assert "background-size: 32px 32px, 32px 32px, auto, auto" in css assert ":root.dark .session-item.active" in css assert ":root.dark .suggestion:focus-visible" 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 "transform: none !important" in reduced assert "animation:" not in css def test_empty_state_uses_canonical_character_without_inline_staff( tmp_path: Path, ) -> None: """The new-chat identity reuses the established full character artwork.""" index = (_patched_fixture(tmp_path) / "static/index.html").read_text( encoding="utf-8" ) empty_state = index.split('
', 1)[1] empty_state = empty_state.split('
', 1)[0] 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-patch.py" in dockerfile assert "python /tmp/hermes-webui-manifest-patch.py" in dockerfile assert ( "COPY dockerfiles/hermes-webui-manifest.json /tmp/hermes-webui-manifest.json" in dockerfile ) assert ( "COPY dockerfiles/hermes-webui-manifest.json /opt/hermes-webui/static/manifest.json" not in dockerfile ) assert "python /tmp/hermes-webui-smoke.py http://127.0.0.1:18787/" 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