fix: move media server to 8093 — YOURLS/Apache owns 8092 on xNAS
Rebuild & Deploy / rebuild (push) Successful in 1m0s
Rebuild & Deploy / rebuild (push) Successful in 1m0s
The Unraid deployment's /media/api requests were answered by an Apache/YOURLS stack squatting on 8092, surfacing as UI 503s; the CI healthcheck (curl without -f) counted that 503 as healthy, so run #15 deployed green against a broken media server. - media server port 8092 -> 8093 everywhere (server, MCP, agent, nginx, compose, CI, .env.example, docs, tests). - healthchecks use curl -f in compose and rebuild.yml so HTTP error statuses fail deployment. - tests default to the Unraid deployment (VOICE_WEB_URL= https://192.168.86.2:9178); token goes through nginx (loopback :8091 is unreachable off-host); bind-mount-coupled tests skip on remote targets. AGENTS.md documents the convention and both failure modes.
This commit is contained in:
1 parent
d7c385301e
commit
ce8fd1e046
11 files changed
+72
-28
No files matched your search
+21
-8
@@ -1,23 +1,35 @@
|
||||
"""Shared fixtures for the Hope voice-assistant test suite.
|
||||
|
||||
Tests run on the HOST machine against the live "voice" container:
|
||||
- HTTPS web UI -> https://localhost:9178 (self-signed cert, verify=False)
|
||||
- Token server -> http://127.0.0.1:8091 (host network mode)
|
||||
Tests run against the **Unraid deployment** by default — that is the
|
||||
instance users hit and the one CI (rebuild.yml) keeps up to date:
|
||||
- HTTPS web UI -> https://192.168.86.2:9178 (self-signed cert, verify=False)
|
||||
- Token server -> via the same nginx (https://.../token; the loopback
|
||||
:8091 socket is only reachable on the deployment host itself)
|
||||
- LLM API -> LLM_BASE_URL (default http://192.168.86.2:3005/api)
|
||||
- In-container -> docker exec voice ...
|
||||
- In-container -> docker exec voice ... (targets the LOCAL dev container
|
||||
unless a docker context/DOCKER_HOST for the Unraid host is configured)
|
||||
|
||||
Override VOICE_WEB_URL to test a different instance (e.g. the local dev
|
||||
compose deployment at https://localhost:9178).
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
CONTAINER = "voice"
|
||||
WEB_BASE_URL = os.environ.get("VOICE_WEB_URL", "https://localhost:9178")
|
||||
TOKEN_URL = os.environ.get("VOICE_TOKEN_URL", "http://127.0.0.1:8091/token")
|
||||
WEB_BASE_URL = os.environ.get("VOICE_WEB_URL", "https://192.168.86.2:9178").rstrip("/")
|
||||
TOKEN_URL = os.environ.get(
|
||||
"VOICE_TOKEN_URL", f"{WEB_BASE_URL}/token"
|
||||
)
|
||||
# Filesystem-coupled tests (repo ./media bind mount) only make sense when
|
||||
# the test host IS the deployment host.
|
||||
IS_LOCAL_DEPLOYMENT = urlparse(WEB_BASE_URL).hostname in ("localhost", "127.0.0.1")
|
||||
LLM_BASE_URL = (
|
||||
os.environ.get("LLM_BASE_URL")
|
||||
or os.environ.get("GEMMA_BASE_URL")
|
||||
@@ -91,8 +103,9 @@ def client(base_url):
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def token() -> dict:
|
||||
"""Fetch a fresh LiveKit token from the token endpoint (direct, no proxy)."""
|
||||
resp = httpx.post(TOKEN_URL, json={}, timeout=10)
|
||||
"""Fetch a fresh LiveKit token (via nginx by default; the loopback
|
||||
:8091 socket is only reachable on the deployment host itself)."""
|
||||
resp = httpx.post(TOKEN_URL, json={}, timeout=10, verify=False)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
assert "token" in data, f"token endpoint did not return a token: {data.keys()}"
|
||||
|
||||
+15
-6
@@ -17,7 +17,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from conftest import TOKEN_URL, docker_exec
|
||||
from conftest import TOKEN_URL, docker_exec, IS_LOCAL_DEPLOYMENT
|
||||
|
||||
MEDIA_API = "/media/api"
|
||||
TEST_ROOM = f"media-test-{uuid.uuid4().hex[:8]}"
|
||||
@@ -200,7 +200,12 @@ def test_media_stream_proxy_and_range(client):
|
||||
|
||||
|
||||
def test_media_files_static_range_support(client):
|
||||
"""Downloaded tracks are nginx static files with native Range support."""
|
||||
"""Downloaded tracks are nginx static files with native Range support.
|
||||
|
||||
Writes a probe file into the repo's ./media bind mount, so it only runs
|
||||
when the test host IS the deployment host (local dev deployment)."""
|
||||
if not IS_LOCAL_DEPLOYMENT:
|
||||
pytest.skip("writes to the repo ./media bind mount — local deployments only")
|
||||
import pathlib
|
||||
|
||||
marker = uuid.uuid4().hex[:8]
|
||||
@@ -222,7 +227,11 @@ def test_media_files_static_range_support(client):
|
||||
|
||||
|
||||
def test_media_library_persists_in_volume(client):
|
||||
"""The library JSON is written to the ./media bind mount (permanent)."""
|
||||
"""The library JSON is written to the ./media bind mount (permanent).
|
||||
|
||||
Reads the repo's ./media directory — local deployments only."""
|
||||
if not IS_LOCAL_DEPLOYMENT:
|
||||
pytest.skip("reads the repo ./media bind mount — local deployments only")
|
||||
import pathlib
|
||||
|
||||
upstream, url = _serve_fake_audio(b"\x00" * 16)
|
||||
@@ -271,7 +280,7 @@ def test_ui_media_styles_serve(client):
|
||||
|
||||
|
||||
def test_token_response_includes_room():
|
||||
resp = httpx.post(TOKEN_URL, json={}, timeout=10)
|
||||
resp = httpx.post(TOKEN_URL, json={}, timeout=10, verify=False)
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["room"].startswith("voice-")
|
||||
|
||||
@@ -279,7 +288,7 @@ def test_token_response_includes_room():
|
||||
# ── Hope's media MCP toolset ────────────────────────────────────────────────
|
||||
|
||||
MEDIA_MCP_ENV = {
|
||||
"MEDIA_SERVER_URL": "http://127.0.0.1:8092",
|
||||
"MEDIA_SERVER_URL": "http://127.0.0.1:8093",
|
||||
"MEDIA_ROOM": "mcp-test-room",
|
||||
}
|
||||
|
||||
@@ -334,7 +343,7 @@ def test_mcp_media_resume_without_load(mcp_client_factory, client):
|
||||
)
|
||||
text, is_error = mcp_client_factory(
|
||||
"media_mcp.py", "resume_music", {}, env={
|
||||
"MEDIA_SERVER_URL": "http://127.0.0.1:8092",
|
||||
"MEDIA_SERVER_URL": "http://127.0.0.1:8093",
|
||||
"MEDIA_ROOM": "mcp-empty-room",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -54,8 +54,11 @@ def test_livekit_client_bundle_serves(client):
|
||||
|
||||
|
||||
def test_token_endpoint_direct():
|
||||
"""Token server on 127.0.0.1:8091 (host network mode)."""
|
||||
resp = httpx.post("http://127.0.0.1:8091/token", json={}, timeout=10)
|
||||
"""Token endpoint via TOKEN_URL (nginx path on remote deployments,
|
||||
loopback :8091 when VOICE_TOKEN_URL points at the deployment host)."""
|
||||
import conftest
|
||||
|
||||
resp = httpx.post(conftest.TOKEN_URL, json={}, timeout=10, verify=False)
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert "token" in data and data["token"].count(".") == 2 # JWT shape
|
||||
|
||||
Reference in new issue
Block a user