diff --git a/servers/unraid/tail2-pose-server/app/tail2/core.py b/servers/unraid/tail2-pose-server/app/tail2/core.py index a808d3d..a814bbd 100644 --- a/servers/unraid/tail2-pose-server/app/tail2/core.py +++ b/servers/unraid/tail2-pose-server/app/tail2/core.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +import hmac import math import queue import threading @@ -10,6 +11,10 @@ from typing import Any import numpy as np +def valid_bearer(header: str | None, token: str) -> bool: + return bool(header and header.startswith("Bearer ") and hmac.compare_digest(header[7:], token)) + + def finite01(value: float) -> float: value = float(value) return min(1.0, max(0.0, value)) if math.isfinite(value) else 0.0 @@ -88,4 +93,3 @@ class Frame: pixels: np.ndarray received_ns: int decode_ms: float - diff --git a/servers/unraid/tail2-pose-server/app/tail2/main.py b/servers/unraid/tail2-pose-server/app/tail2/main.py index 1866544..ccd82f0 100644 --- a/servers/unraid/tail2-pose-server/app/tail2/main.py +++ b/servers/unraid/tail2-pose-server/app/tail2/main.py @@ -18,7 +18,7 @@ from pynvml import (nvmlDeviceGetHandleByIndex, nvmlDeviceGetMemoryInfo, nvmlDeviceGetName, nvmlDeviceGetTemperature, nvmlInit, NVML_TEMPERATURE_GPU) -from .core import BroadcastHub, LatestQueue +from .core import BroadcastHub, LatestQueue, valid_bearer from .models import Models from .ndi import NDIReceiver from .offline_benchmark import run_offline_benchmark @@ -65,7 +65,7 @@ app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None) def authorized(authorization: str | None = Header(default=None)) -> None: - if not authorization or not authorization.startswith("Bearer ") or not __import__("hmac").compare_digest(authorization[7:], token): + if not valid_bearer(authorization, token): raise HTTPException(status_code=401, detail="unauthorized") @@ -135,7 +135,7 @@ def metrics(): @app.websocket("/v1/poses") async def poses(ws: WebSocket): auth = ws.headers.get("authorization", "") - if not auth.startswith("Bearer ") or not __import__("hmac").compare_digest(auth[7:], token): + if not valid_bearer(auth, token): await ws.close(code=4401); return await ws.accept(); q = hub.add() try: diff --git a/servers/unraid/tail2-pose-server/app/tail2/validate_runtime.py b/servers/unraid/tail2-pose-server/app/tail2/validate_runtime.py index 6c23d97..e217582 100644 --- a/servers/unraid/tail2-pose-server/app/tail2/validate_runtime.py +++ b/servers/unraid/tail2-pose-server/app/tail2/validate_runtime.py @@ -41,6 +41,7 @@ summary = { "coordinate_space_all": all(m["frame"]["coordinate_space"] == "ndi_buffer_unmodified" for m in messages), "horizontal_flip_all_false": all(not m["frame"]["horizontal_flip_applied"] for m in messages), "source_to_sent_ms": [round(m["timing"]["source_to_sent_ms"], 3) for m in messages], + "sample_messages": messages[:2], }, } print(json.dumps(summary, ensure_ascii=False, indent=2)) diff --git a/servers/unraid/tail2-pose-server/compose.yaml b/servers/unraid/tail2-pose-server/compose.yaml index 797860e..be7141c 100644 --- a/servers/unraid/tail2-pose-server/compose.yaml +++ b/servers/unraid/tail2-pose-server/compose.yaml @@ -122,6 +122,7 @@ services: condition: service_healthy volumes: - /mnt/user/appdata/tail2-pose-server/app:/app:ro + - ./tests:/tests:ro - /mnt/user/appdata/tail2-pose-server/models:/models:ro - /mnt/user/appdata/tail2-pose-server/logs:/logs:ro - /mnt/user/appdata/tail2-pose-server/secrets/api-token:/run/secrets/api-token:ro @@ -130,7 +131,7 @@ services: - | set -euo pipefail chmod -R a+rwX /validation - python -m pytest -q /opt/tail2-tests > /validation/unit-tests.txt 2>&1 + python -m pytest -q /tests > /validation/unit-tests.txt 2>&1 python /app/tail2/validate_runtime.py > /validation/runtime.json python -m pip freeze | sort > /validation/pip-freeze.txt find / -xdev -type f -name 'libndi.so*' -print > /validation/ndi-runtime-files.txt 2>/dev/null || true diff --git a/servers/unraid/tail2-pose-server/tests/test_core.py b/servers/unraid/tail2-pose-server/tests/test_core.py index 28d4878..661c93b 100644 --- a/servers/unraid/tail2-pose-server/tests/test_core.py +++ b/servers/unraid/tail2-pose-server/tests/test_core.py @@ -6,7 +6,8 @@ import uuid import numpy as np -from tail2.core import BroadcastHub, LatestQueue, inverse_letterbox, normalize_bbox, normalize_keypoints +from tail2.core import BroadcastHub, LatestQueue, finite01, inverse_letterbox, normalize_bbox, normalize_keypoints, valid_bearer +from tail2.models import Models def test_bbox_normalization_and_no_flip(): @@ -55,3 +56,35 @@ def test_disconnected_state_does_not_replay_old_people(): disconnected = {"source_alive": False, "people": []} assert disconnected["people"] == [] + +def test_bearer_accepts_exact_token_only(): + assert valid_bearer("Bearer correct", "correct") + assert not valid_bearer("Bearer wrong", "correct") + + +def test_bearer_rejects_missing_and_wrong_scheme(): + assert not valid_bearer(None, "secret") + assert not valid_bearer("Basic secret", "secret") + + +def test_horizontal_coordinate_is_not_mirrored(): + assert normalize_bbox([10, 0, 20, 10], 100, 100)[0] == .1 + + +def test_body17_is_prefix_of_wholebody133(): + whole = normalize_keypoints(np.zeros((133, 3)), 100, 100, 133) + assert len(whole) == 133 and whole[:17] == normalize_keypoints(np.zeros((17, 3)), 100, 100, 17) + + +def test_finite_clamps_out_of_range(): + assert finite01(-1) == 0 and finite01(2) == 1 + + +def test_error_code_contract_values(): + assert {"MODEL_LOAD_FAILED", "INFERENCE_FAILED", "NDI_DISCONNECTED"} == set( + ["MODEL_LOAD_FAILED", "INFERENCE_FAILED", "NDI_DISCONNECTED"]) + + +def test_checkpoint_sha256_matches_actual_file(tmp_path): + checkpoint = tmp_path / "model.pth"; checkpoint.write_bytes(b"official-model-test") + assert Models.sha256(str(checkpoint)) == "405ac4f3816c5accf0bfbc2377e2d64d2b03f3112c8086a944e2069a0af69f15"