151 lines
5.4 KiB
Python
151 lines
5.4 KiB
Python
|
|
"""Tests for backend.api_server.APIServer REST endpoints."""
|
||
|
|
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
from unittest.mock import MagicMock, patch
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
import pytest_asyncio
|
||
|
|
|
||
|
|
# Ensure project root is on path
|
||
|
|
project_root = Path(__file__).resolve().parent.parent.parent
|
||
|
|
sys.path.insert(0, str(project_root))
|
||
|
|
|
||
|
|
|
||
|
|
# ── GET /api/status ─────────────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_get_status(api_client):
|
||
|
|
resp = await api_client.get("/api/status")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "state" in data
|
||
|
|
assert "is_transcribing" in data
|
||
|
|
assert "version" in data
|
||
|
|
assert "web_server" in data
|
||
|
|
|
||
|
|
|
||
|
|
# ── GET /api/config ─────────────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_get_config(api_client):
|
||
|
|
resp = await api_client.get("/api/config")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
# The config should be a dict (the raw config mapping)
|
||
|
|
assert isinstance(data, dict)
|
||
|
|
|
||
|
|
|
||
|
|
# ── PUT /api/config ─────────────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_put_config(api_client, controller):
|
||
|
|
"""Updating config via PUT should persist and return success."""
|
||
|
|
# Patch reload_engine to avoid heavy lifting
|
||
|
|
controller.reload_engine = MagicMock(return_value=(True, "ok"))
|
||
|
|
controller.current_model_size = "base.en"
|
||
|
|
controller.current_device_config = "auto"
|
||
|
|
controller.config.set("transcription.model", "base.en")
|
||
|
|
controller.config.set("transcription.device", "auto")
|
||
|
|
|
||
|
|
resp = await api_client.put(
|
||
|
|
"/api/config",
|
||
|
|
json={"settings": {"display.font_size": 24}},
|
||
|
|
)
|
||
|
|
assert resp.status_code == 200
|
||
|
|
body = resp.json()
|
||
|
|
assert body["status"] == "ok"
|
||
|
|
|
||
|
|
# Verify the value was actually saved
|
||
|
|
assert controller.config.get("display.font_size") == 24
|
||
|
|
|
||
|
|
|
||
|
|
# ── POST /api/start (engine not ready) ─────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_start_when_not_ready(api_client, controller):
|
||
|
|
"""Starting transcription without an engine should return 400."""
|
||
|
|
controller.transcription_engine = None
|
||
|
|
resp = await api_client.post("/api/start")
|
||
|
|
assert resp.status_code == 400
|
||
|
|
|
||
|
|
|
||
|
|
# ── POST /api/clear ─────────────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_clear(api_client, controller):
|
||
|
|
from client.transcription_engine_realtime import TranscriptionResult
|
||
|
|
from datetime import datetime
|
||
|
|
|
||
|
|
controller.transcriptions = [
|
||
|
|
TranscriptionResult(text="One", is_final=True, timestamp=datetime.now(), user_name="U"),
|
||
|
|
]
|
||
|
|
|
||
|
|
resp = await api_client.post("/api/clear")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
body = resp.json()
|
||
|
|
assert body["cleared"] == 1
|
||
|
|
assert len(controller.transcriptions) == 0
|
||
|
|
|
||
|
|
|
||
|
|
# ── GET /api/audio-devices ──────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_get_audio_devices(api_client, controller):
|
||
|
|
"""Audio devices endpoint should return a list, even when mocked."""
|
||
|
|
# Mock sounddevice so the test works without audio hardware
|
||
|
|
with patch("backend.app_controller.AppController.get_audio_devices",
|
||
|
|
return_value=[{"index": 0, "name": "Mock Mic"}]):
|
||
|
|
resp = await api_client.get("/api/audio-devices")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "devices" in data
|
||
|
|
assert len(data["devices"]) >= 1
|
||
|
|
|
||
|
|
|
||
|
|
# ── GET /api/compute-devices ────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_get_compute_devices(api_client, controller):
|
||
|
|
resp = await api_client.get("/api/compute-devices")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "devices" in data
|
||
|
|
# At minimum we get the "Auto-detect" entry
|
||
|
|
assert any(d["id"] == "auto" for d in data["devices"])
|
||
|
|
|
||
|
|
|
||
|
|
# ── GET /api/check-update ──────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_check_update(api_client, controller):
|
||
|
|
"""check-update should return a dict with an 'available' key."""
|
||
|
|
with patch.object(controller, "check_for_updates",
|
||
|
|
return_value={"available": False, "current_version": "1.0.0"}):
|
||
|
|
resp = await api_client.get("/api/check-update")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "available" in data
|
||
|
|
|
||
|
|
|
||
|
|
# ── GET /api/version ────────────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_version(api_client):
|
||
|
|
resp = await api_client.get("/api/version")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "version" in data
|
||
|
|
# Should be a non-empty string
|
||
|
|
assert isinstance(data["version"], str)
|
||
|
|
assert len(data["version"]) > 0
|