atlas-iac/testing/tests/test_hermes_webui_brand.py
2026-08-23 19:22:26 -03:00

422 lines
16 KiB
Python

"""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 re
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"
RELEASE_PATCHER = DOCKERFILES / "hermes-webui-release-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("<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 (
'<img class="hermes-agent-portrait" '
'src="static/hermes-agent-512.png" alt="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(
"<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_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'<link href="static/hermes-agent.ico">'
b'<link href="static/hermes-agent-192.png">'
b'<link href="static/hermes-agent-512.png">'
b'<link href="static/hermes-brand.css?v=reviewed">'
)
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('<div class="empty-state" id="emptyState">', 1)[1]
empty_state = empty_state.split('<div class="messages-inner"', 1)[0]
assert empty_state.count('class="hermes-agent-portrait"') == 1
assert empty_state.count('src="static/hermes-agent-512.png"') == 1
assert "<svg" not in empty_state.split('<h2 data-i18n="empty_title">', 1)[0]
def test_release_patch_gives_every_shell_url_an_immutable_build_token(
tmp_path: Path,
) -> None:
"""Atlas image releases never share a browser cache key."""
target = _patched_fixture(tmp_path)
env = os.environ.copy()
env["HERMES_WEBUI_PATCH_ROOT"] = str(target)
env["HERMES_WEBUI_RELEASE_ID"] = "git-0123456789abcdef-build-11"
subprocess.run(
[sys.executable, str(RELEASE_PATCHER)],
cwd=ROOT,
env=env,
check=True,
capture_output=True,
text=True,
)
index = (target / "static/index.html").read_text(encoding="utf-8")
worker = (target / "static/sw.js").read_text(encoding="utf-8")
token = "__WEBUI_VERSION__-git-0123456789abcdef-build-11"
assert token in index
assert token in worker
assert not re.search(r"__WEBUI_VERSION__(?!-git-0123456789abcdef-build-11)", index)
assert not re.search(r"__WEBUI_VERSION__(?!-git-0123456789abcdef-build-11)", worker)
@pytest.mark.parametrize("release_id", ["", "../escape", "UPPERCASE", "a" * 129])
def test_release_patch_rejects_missing_or_unsafe_tokens(
tmp_path: Path, release_id: str
) -> None:
"""A malformed build token cannot silently collapse browser cache keys."""
target = _patched_fixture(tmp_path)
env = os.environ.copy()
env["HERMES_WEBUI_PATCH_ROOT"] = str(target)
env["HERMES_WEBUI_RELEASE_ID"] = release_id
completed = subprocess.run(
[sys.executable, str(RELEASE_PATCHER)],
cwd=ROOT,
env=env,
capture_output=True,
text=True,
)
assert completed.returncode != 0
assert "safe immutable release token" in completed.stderr
def test_release_patch_rejects_upstream_without_version_markers(
tmp_path: Path,
) -> None:
"""Pinned upstream drift fails the image build before publication."""
target = _patched_fixture(tmp_path)
index = target / "static/index.html"
index.write_text(
index.read_text(encoding="utf-8").replace("__WEBUI_VERSION__", "fixed"),
encoding="utf-8",
)
env = os.environ.copy()
env["HERMES_WEBUI_PATCH_ROOT"] = str(target)
env["HERMES_WEBUI_RELEASE_ID"] = "git-0123456789abcdef-build-11"
completed = subprocess.run(
[sys.executable, str(RELEASE_PATCHER)],
cwd=ROOT,
env=env,
capture_output=True,
text=True,
)
assert completed.returncode != 0
assert "release patch context changed" in completed.stderr
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-release-patch.py" in dockerfile
assert "python /tmp/hermes-webui-release-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