lexia / tests /test_admin.py
davidhf2026's picture
chore: establish LexIA baseline on Windows
c349d51
Raw History Blame Contribute Delete
5.68 kB
import json
import pytest
pytest.importorskip("fastapi")
pytest.importorskip("httpx")
from fastapi import FastAPI # noqa: E402
from fastapi.testclient import TestClient # noqa: E402
class VozFalsa:
frecuencia_destino = 16000
def sintetizar(self, texto):
import numpy as np
yield np.zeros(1600, dtype=np.int16)
class TranscriptorFalso:
def transcribe(self, muestras):
return "¿tengo que avisar de que uso un chatbot?"
class OrquestadorFalso:
def ask(self, pregunta):
from types import SimpleNamespace
respuesta = SimpleNamespace(text="Sí, debe informarlo.", citations=[])
return SimpleNamespace(answer=respuesta, topic="TRANSPARENCIA")
@pytest.fixture()
def entorno(tmp_path, monkeypatch):
monkeypatch.setenv("LEXIA_RUNTIME", str(tmp_path / "rt"))
import importlib
from pathlib import Path
from lexia.core import paths as modulo_paths
importlib.reload(modulo_paths)
for nombre in ("lexia.core.config", "lexia.core.logging", "lexia.admin.security"):
importlib.reload(importlib.import_module(nombre))
from lexia.admin import routes as modulo_rutas
from lexia.admin import security
from lexia.web import remote_voice
from lexia.web.camera import CameraStream
importlib.reload(modulo_rutas)
modulo_paths.ensure_runtime()
camara = CameraStream()
sesion = remote_voice.RemoteVoiceSession()
consola = remote_voice.RemoteVoiceConsole(
OrquestadorFalso(), TranscriptorFalso(), VozFalsa(), sesion
)
app = FastAPI()
panel = Path(modulo_rutas.__file__).resolve().parent.parent / "static" / "admin.html"
modulo_rutas.register_admin(
app, panel=panel, camera=camara, session=sesion, console_provider=lambda: consola
)
return TestClient(app), security.ensure_token(), camara, sesion
def test_sin_token_no_se_ve_nada(entorno):
cliente, *_ = entorno
for ruta in ("/api/admin/overview", "/api/admin/logs", "/api/admin/camera/stream"):
assert cliente.get(ruta).status_code == 401, ruta
def test_un_token_incorrecto_no_entra(entorno):
cliente, *_ = entorno
assert cliente.post("/api/admin/login", json={"token": "0" * 40}).status_code == 401
def test_con_token_se_entra_y_se_ve_el_estado(entorno):
cliente, token, *_ = entorno
assert cliente.post("/api/admin/login", json={"token": token}).status_code == 200
datos = cliente.get("/api/admin/overview").json()
assert "…" in datos["token"], "el token nunca se muestra entero"
assert datos["camera"]["enabled"] is False, "la cámara arranca apagada"
assert datos["camera"]["recording"] is False
assert "admin" not in datos["settings"], "los ajustes públicos no llevan el token"
def test_la_camara_apagada_no_emite(entorno):
cliente, token, camara, _ = entorno
cliente.post("/api/admin/login", json={"token": token})
assert cliente.get("/api/admin/camera/stream").status_code == 409
cliente.post("/api/admin/camera", json={"enabled": True})
assert camara.enabled is True
cliente.post("/api/admin/camera", json={"enabled": False})
assert camara.enabled is False
def test_el_token_no_se_puede_escribir_desde_los_ajustes(entorno):
cliente, token, *_ = entorno
cliente.post("/api/admin/login", json={"token": token})
respuesta = cliente.patch("/api/admin/settings", json={"admin": {"token": "x" * 40}})
assert respuesta.status_code == 400
def test_un_ajuste_invalido_no_se_aplica(entorno):
cliente, token, *_ = entorno
cliente.post("/api/admin/login", json={"token": token})
assert cliente.patch(
"/api/admin/settings", json={"retrieval": {"require_evidence": False}}
).status_code == 422
def test_un_ajuste_valido_se_aplica(entorno):
cliente, token, *_ = entorno
cliente.post("/api/admin/login", json={"token": token})
datos = cliente.patch("/api/admin/settings", json={"expression": {"expressiveness": 0.3}}).json()
assert datos["expression"]["expressiveness"] == pytest.approx(0.3)
def test_hay_que_reclamar_la_sesion_antes_de_hablar(entorno):
cliente, token, _, sesion = entorno
cliente.post("/api/admin/login", json={"token": token})
assert cliente.post("/api/admin/voice/ask", content=b"\x00\x00" * 800).status_code == 409
assert cliente.post("/api/admin/voice/claim").json()["active"] is True
respuesta = cliente.post("/api/admin/voice/ask", content=b"\x00\x00" * 8000)
assert respuesta.status_code == 200
assert respuesta.headers["content-type"] == "audio/wav"
assert respuesta.content[:4] == b"RIFF"
cliente.post("/api/admin/voice/release")
assert sesion.active is False
def test_el_registro_se_puede_leer(entorno):
cliente, token, *_ = entorno
cliente.post("/api/admin/login", json={"token": token})
from lexia.core.logging import log
log("prueba_de_registro", valor=1)
datos = cliente.get("/api/admin/logs?lines=50").json()
assert any("prueba_de_registro" in linea for linea in datos["lines"])
assert all(json.loads(linea) for linea in datos["lines"])
def test_el_panel_expone_las_metricas_de_recuperacion(entorno):
"""Separar recuperar de redactar es lo que hace accionable una queja."""
cliente, token, *_ = entorno
respuesta = cliente.get("/api/admin/retrieval?days=7", headers={"x-lexia-token": token})
assert respuesta.status_code == 200
datos = respuesta.json()
assert {"consultas", "etapas", "abstenciones", "evidencia_media"} <= set(datos)
def test_las_metricas_de_recuperacion_piden_token(entorno):
cliente, *_ = entorno
assert cliente.get("/api/admin/retrieval").status_code == 401