Files
hope-voice-api/tests/test_agent_integration.py
Shane a1d59580f7 fix: ChatContext is not a sequence — use .items for len/list; add test suite
Bug fix:
- GemmaLLM.chat(): len(chat_ctx) → len(chat_ctx.items)
- GemmaLLM._compact_context(): list(chat_ctx) → list(chat_ctx.items)
  ChatContext in livekit-agents 1.7 is not iterable or sized directly;
  it exposes an .items list. The TypeError was silently killing all LLM
  responses (agent heard user but never spoke back).

Test suite (tests/):
- test_web_ui.py: static assets, token endpoint, LiveKit WS proxy
- test_llm_api.py: completion, tool calling, thinking-disabled latency, streaming
- test_mcp_tools.py: weather (Fahrenheit), time, memory, skills — all via
  docker exec + in-container MCP client
- test_agent_integration.py: process alive, worker registered, supervisord,
  container health, LiveKit API
- test_compaction.py: size-triggered compaction, system prompt preservation,
  short-context no-op
- conftest.py: shared fixtures (HTTPS client, token, LLM, docker exec helpers)
- Run with: .venv-tests/bin/python -m pytest tests/ -v
2026-08-22 17:09:29 -04:00

70 lines
2.2 KiB
Python

"""Integration tests for the running container: processes, health, LiveKit."""
import re
from conftest import docker_exec
def _proc_cmdlines() -> list[str]:
"""All process cmdlines in the container (no `ps` available)."""
proc = docker_exec(
"sh",
"-c",
'for p in /proc/[0-9]*/cmdline; do tr "\\0" " " < "$p" 2>/dev/null; echo; done',
)
return [line.strip() for line in proc.stdout.splitlines() if line.strip()]
def test_agent_process_alive():
cmdlines = _proc_cmdlines()
assert any("agent.py start" in c for c in cmdlines), (
"no agent.py process found in container"
)
def test_agent_registered_with_livekit():
"""`docker logs` runs on the host (not via docker exec)."""
import subprocess
proc = subprocess.run(
["docker", "logs", "--tail", "500", "voice"],
capture_output=True,
text=True,
timeout=30,
)
assert "registered worker" in proc.stdout or "worker registered" in proc.stdout, (
"agent never registered a worker with LiveKit"
)
def test_all_supervisord_processes_up():
"""The supervisorctl socket is not exposed in this image, so verify each
supervised program's process directly via /proc."""
cmdlines = _proc_cmdlines()
def has(pattern: str) -> bool:
return any(re.search(pattern, c) for c in cmdlines)
assert has(r"livekit --config"), "livekit server not running"
assert has(r"agent\.py start"), "voice agent not running"
assert has(r"nginx.*daemon off"), "nginx (web UI) not running"
assert has(r"token_server\.py"), "token server not running"
def test_container_healthy():
import subprocess
proc = subprocess.run(
["docker", "inspect", "--format", "{{.State.Health.Status}}", "voice"],
capture_output=True,
text=True,
timeout=30,
)
assert proc.stdout.strip() == "healthy"
def test_livekit_server_responds():
"""LiveKit HTTP API on :7880 (internal; reached via docker exec curl)."""
proc = docker_exec("curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "http://localhost:7880/")
assert proc.stdout.strip() in ("200", "404"), f"unexpected status {proc.stdout.strip()!r}"