atlas-iac/testing/tests/test_hermes_webui_brand.py

528 lines
21 KiB
Python
Raw Normal View History

2026-08-23 01:08:35 +00:00
"""Canonical icon, PWA, and fail-closed Hermes WebUI identity contracts."""
from __future__ import annotations
import hashlib
2026-08-23 04:42:08 +00:00
import importlib.util
2026-08-23 01:08:35 +00:00
import json
import os
from pathlib import Path
import re
2026-08-23 01:08:35 +00:00
import shutil
import struct
import subprocess
import sys
2026-08-23 04:42:08 +00:00
from urllib.parse import urlsplit
import pytest
2026-08-23 01:08:35 +00:00
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"
2026-08-23 04:42:08 +00:00
MANIFEST_PATCHER = DOCKERFILES / "hermes-webui-manifest-patch.py"
RELEASE_PATCHER = DOCKERFILES / "hermes-webui-release-patch.py"
2026-08-23 04:42:08 +00:00
SMOKE = DOCKERFILES / "hermes-webui-smoke.py"
2026-08-23 01:08:35 +00:00
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",
}
2026-08-23 04:42:08 +00:00
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
2026-08-23 01:08:35 +00:00
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)
2026-08-23 04:42:08 +00:00
env["HERMES_WEBUI_MANIFEST_SOURCE"] = str(MANIFEST)
for patcher in (ATLAS_PATCHER, BRAND_PATCHER, MANIFEST_PATCHER):
2026-08-23 01:08:35 +00:00
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
2026-08-23 01:08:35 +00:00
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
2026-08-23 04:42:08 +00:00
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"))
release_identity = "exp-v0.52.181-git-0123456789abcdef-build-13"
2026-08-23 04:42:08 +00:00
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">'
+ b"".join(f'<link href="{item}">'.encode() for item in module.HUX_LINKS)
+ (
"<script>window.__HERMES_WEBUI_BUNDLE_VERSION__='"
f"{release_identity}';</script>"
).encode()
2026-08-23 04:42:08 +00:00
)
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 == "/api/settings":
return _SmokeResponse(
json.dumps(
{"webui_bundle_version": release_identity}
).encode(),
url,
)
if path == "/sw.js":
return _SmokeResponse(
f"const CACHE_NAME='hermes-shell-{release_identity}';".encode(),
url,
)
if path.startswith("/static/hux/"):
return _SmokeResponse(b"/* reviewed HUX asset */", url)
2026-08-23 04:42:08 +00:00
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 result["release_identity"] == release_identity
assert result["hux_assets"] == len(module.HUX_LINKS)
2026-08-23 04:42:08 +00:00
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_a_client_server_release_mismatch() -> None:
"""A build cannot publish the permanently stale banner regression."""
module = _load(SMOKE, "hermes_webui_smoke_release_mismatch")
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
client = "exp-v0.52.181-git-0123456789abcdef-build-13"
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">'
+ b"".join(f'<link href="{item}">'.encode() for item in module.HUX_LINKS)
+ (
"<script>window.__HERMES_WEBUI_BUNDLE_VERSION__='"
f"{client}';</script>"
).encode()
)
def open_mismatch(request, *, timeout):
assert timeout == 5
url = request.full_url
path = urlsplit(url).path
if path == "/manifest.json":
return _SmokeResponse(json.dumps(manifest).encode(), url)
if path == "/":
return _SmokeResponse(root, url)
if path == "/api/settings":
return _SmokeResponse(
json.dumps(
{"webui_bundle_version": client + "-different"}
).encode(),
url,
)
if path.startswith("/static/hux/"):
return _SmokeResponse(b"/* reviewed HUX asset */", url)
if path.endswith(".png"):
return _SmokeResponse(module.PNG_MAGIC + b"fixture", url)
raise AssertionError(f"unexpected smoke URL: {url}")
with pytest.raises(RuntimeError, match="identities do not match"):
module.smoke("http://hermes.test/", opener=open_mismatch)
2026-08-23 04:42:08 +00:00
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)
2026-08-23 01:08:35 +00:00
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-shell" in css
assert "background-size: 32px 32px, 32px 32px, auto, auto" in css
assert "background-repeat: repeat, repeat, no-repeat, no-repeat" in css
messages = css.rsplit(":root.dark .messages {", 1)[1].split("}", 1)[0]
assert "background-image: none" in messages
assert "radial-gradient" not in messages
assert ":root.dark .session-item.active" in css
assert ":root.dark .suggestion:focus-visible" in css
2026-08-23 01:08:35 +00:00
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
2026-08-23 01:08:35 +00:00
assert "animation:" not in css
# The composer reserves the device bottom safe-area (home indicator / gesture
# bar) so its control row is not hidden behind it, additive with the app's
# keyboard inset. env() is 0 on inset-less devices, so this is desktop-inert.
assert ".composer-wrap" in css
composer = css.rsplit(".composer-wrap {", 1)[1].split("}", 1)[0]
assert "env(safe-area-inset-bottom, 0px)" in composer
assert "var(--keyboard-bottom-inset, 0px)" in composer
def test_base_patch_and_dockerfile_opt_into_edge_to_edge_safe_area() -> None:
"""viewport-fit=cover is required for env(safe-area-inset-*) to carry real
values; the base patch adds it and the Dockerfile verifies it landed."""
base_patch = (DOCKERFILES / "hermes-webui-base-patch.py").read_text(encoding="utf-8")
assert "viewport-fit=cover" in base_patch
assert "user-scalable=no, viewport-fit=cover" in base_patch
dockerfile = (DOCKERFILES / "Dockerfile.hermes-webui").read_text(encoding="utf-8")
assert "grep -Fq 'user-scalable=no, viewport-fit=cover'" in dockerfile
2026-08-23 01:08:35 +00:00
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 releases share one server/client identity and 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")
routes = (target / "api/routes.py").read_text(encoding="utf-8")
panels = (target / "static/panels.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)
assert (
'settings["webui_bundle_version"] = '
'f"{WEBUI_VERSION}-git-0123456789abcdef-build-11"'
) in routes
assert (
"settings.webui_bundle_version||settings.webui_version"
) in panels
assert "if(client===server) return;" not in panels
assert "if(banner) banner.style.display='none';" in panels
assert "if(_isBannerVisible()) return;" not in panels
assert (
"if(_isBannerVisible()){ clearInterval(_pollTimer);"
not in panels
)
@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
2026-08-23 01:08:35 +00:00
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
2026-08-23 04:42:08 +00:00
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 'settings["webui_bundle_version"]' in dockerfile
assert (
"settings.webui_bundle_version||settings.webui_version"
in dockerfile
)
2026-08-23 04:42:08 +00:00
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
2026-08-23 01:08:35 +00:00
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