atlas-iac/testing/tests/test_hermes_voice_instrument.py

161 lines
5.7 KiB
Python
Raw Normal View History

"""Shipped patch and DOM contracts for the hands-free conversation instrument."""
from __future__ import annotations
import os
from pathlib import Path
import re
import shutil
import subprocess
import sys
ROOT = Path(__file__).resolve().parents[2]
FIXTURE = ROOT / "testing/fixtures/hermes-webui-0.52.181"
PATCHER = ROOT / "dockerfiles/hermes-webui-atlas-patch.py"
VOICE_JS = ROOT / "dockerfiles/hermes-webui-atlas-voice.js"
VOICE_CSS = ROOT / "dockerfiles/hermes-webui-atlas-voice.css"
DOM_PROBE = ROOT / "testing/probes/hermes_voice_instrument_probe.js"
MEDIARECORDER_FIXTURE = (
ROOT / "testing/fixtures/mediarecorder/chromium-webm-opus.json"
)
def _patched_fixture(tmp_path: Path) -> Path:
target = tmp_path / "hermes-webui"
shutil.copytree(FIXTURE, target)
env = os.environ.copy()
env["HERMES_WEBUI_PATCH_ROOT"] = str(target)
subprocess.run(
[sys.executable, str(PATCHER)],
cwd=ROOT,
env=env,
check=True,
capture_output=True,
text=True,
)
return target
def test_real_upstream_fixture_receives_visual_instrument_contract(tmp_path: Path):
"""Apply the production patcher to exact fragments from pinned WebUI 0.52.181."""
target = _patched_fixture(tmp_path)
index = (target / "static/index.html").read_text(encoding="utf-8")
assert index.count('id="voiceInstrumentStyles"') == 1
assert (
'href="static/atlas-voice.css?v=__WEBUI_VERSION__"' in index
)
assert 'role="status"' in index
assert 'aria-live="polite"' in index
assert 'aria-atomic="true"' in index
for layer in (
"voice-instrument-halo",
"voice-instrument-ripple",
"voice-instrument-orbit",
"voice-instrument-core",
"voice-instrument-symbol",
):
assert layer in index
assert '<button' not in index[index.index('id="voiceModeBar"') : index.index('<textarea')]
def test_patched_webui_has_no_user_voice_choice_or_client_voice_field(
tmp_path: Path,
):
"""The pinned settings DOM and every TTS path leave speakers to policy."""
target = _patched_fixture(tmp_path)
index = (target / "static/index.html").read_text(encoding="utf-8")
ui = (target / "static/ui.js").read_text(encoding="utf-8")
panels = (target / "static/panels.js").read_text(encoding="utf-8")
boot = (target / "static/boot.js").read_text(encoding="utf-8")
i18n = (target / "static/i18n.js").read_text(encoding="utf-8")
config = (target / "api/config.py").read_text(encoding="utf-8")
routes = (target / "api/routes.py").read_text(encoding="utf-8")
assert "settingsTtsVoice" not in index
assert "settings_label_tts_voice" not in index
assert "settings_desc_tts_voice" not in index
assert "Default system voice" not in index
assert 'id="settingsTtsEngine"' in index
assert 'id="btnVoiceMode"' in index
assert 'id="voiceModeBar"' in index
assert "hermes-tts-voice" not in ui
assert "voice:voice" not in ui
assert (
"body:JSON.stringify({text:chunk, rate:rate, pitch:pitch, "
"engine:engineOverride||'edge'})"
) in ui
assert "settingsTtsVoice" not in panels
assert "tts_voice" not in panels
assert "localStorage.removeItem('hermes-tts-voice')" in panels
assert panels.count("hermes-tts-voice") == 1
assert "hermes-tts-voice" not in boot
assert "tts_voice" not in boot
assert "text: clean, voice" not in boot
assert '"tts_voice"' not in config
assert "settings_label_tts_voice" not in i18n
assert "settings_desc_tts_voice" not in i18n
atlas_route = routes.split('if engine == "atlas":', 1)[1].split(
"# ── ElevenLabs TTS", 1
)[0]
request_body = atlas_route.split("request_body = json.dumps({", 1)[1].split(
"}).encode", 1
)[0]
assert '"input": text' in request_body
assert '"voice"' not in request_body
assert '"language"' not in request_body
def test_visual_states_have_distinct_layers_finite_error_and_reduced_motion():
css = VOICE_CSS.read_text(encoding="utf-8")
for state in ("listening", "transcribing", "thinking", "speaking", "error"):
assert f".voice-mode-indicator.{state}" in css
for animation in (
"voice-instrument-breathe",
"voice-instrument-orbit",
"voice-instrument-speaking-pulse",
):
assert f"@keyframes {animation}" in css
assert (
".voice-mode-indicator.speaking.is-playing .voice-instrument-halo"
in css
)
error_rules = "\n".join(
match.group(0)
for match in re.finditer(r"[^{}]*\.error[^{}]*\{[^{}]*\}", css)
)
assert "animation:" not in error_rules
assert "@media (prefers-reduced-motion: reduce)" in css
reduced = css.split("@media (prefers-reduced-motion: reduce)", 1)[1]
assert "animation: none !important" in reduced
assert "transition: none !important" in reduced
def test_dom_probe_exercises_actual_injected_voice_script():
result = subprocess.run(
["node", str(DOM_PROBE), str(VOICE_JS), str(MEDIARECORDER_FIXTURE)],
cwd=ROOT,
check=True,
capture_output=True,
text=True,
)
assert result.stdout.strip() == "voice instrument DOM contract passed"
def test_visual_slice_preserves_private_voice_request_and_capture_contract():
script = VOICE_JS.read_text(encoding="utf-8")
assert script.count("navigator.mediaDevices.getUserMedia(") == 1
assert "form.append('file',new File([blob],'voice-input.'+ext" in script
assert "fetch('/api/transcribe',{method:'POST',body:form})" in script
assert "body:JSON.stringify({text:chunk,engine:'atlas'})" in script
assert "speakResponse(generation)" in script
assert "window._voiceModeImmediateSend" in script
assert "mute" not in script.lower()
assert "language" not in script.lower()