Spaces:
Build error
Build error
File size: 3,348 Bytes
18d028b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | """Tests for the FastAPI backend."""
from __future__ import annotations
import base64
import io
import numpy as np
import pytest
from fastapi.testclient import TestClient
from PIL import Image
from signbridge.backend import app
@pytest.fixture()
def client() -> TestClient:
return TestClient(app)
def _frame_b64(rgb: tuple[int, int, int] = (128, 128, 128), size: int = 64) -> str:
arr = np.full((size, size, 3), rgb, dtype=np.uint8)
img = Image.fromarray(arr)
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=80)
return base64.b64encode(buf.getvalue()).decode("ascii")
class TestHealth:
def test_healthz_returns_ok(self, client: TestClient) -> None:
r = client.get("/healthz")
assert r.status_code == 200
assert r.json() == {"status": "ok"}
def test_info_returns_provider_block(self, client: TestClient) -> None:
r = client.get("/info")
assert r.status_code == 200
body = r.json()
for key in ("provider", "composer_model", "vlm_model", "tts_model", "recognizer_mode"):
assert key in body
class TestRecognize:
def test_empty_frame_rejected(self, client: TestClient) -> None:
r = client.post("/recognize", json={"frame": ""})
assert r.status_code == 400
def test_invalid_base64_rejected(self, client: TestClient) -> None:
r = client.post("/recognize", json={"frame": "%%%not-base64%%%"})
assert r.status_code == 400
def test_valid_frame_no_provider_returns_empty_token(self, client: TestClient) -> None:
# Without API keys, the VLM path returns ("", 0.0). The endpoint should
# still respond 200 OK.
r = client.post("/recognize", json={"frame": _frame_b64()})
assert r.status_code == 200
body = r.json()
assert body == {"token": "", "confidence": 0.0}
def test_data_url_prefix_tolerated(self, client: TestClient) -> None:
b64 = _frame_b64()
r = client.post(
"/recognize", json={"frame": f"data:image/jpeg;base64,{b64}"}
)
assert r.status_code == 200
class TestCompose:
def test_empty_signs(self, client: TestClient) -> None:
r = client.post("/compose", json={"signs": []})
assert r.status_code == 200
assert r.json() == {"sentence": ""}
def test_fingerspelled_word(self, client: TestClient) -> None:
r = client.post(
"/compose", json={"signs": ["L", "U", "C", "A", "S"]}
)
assert r.status_code == 200
# Naive joiner produces "Lucas."
assert "Lucas" in r.json()["sentence"]
def test_glosses(self, client: TestClient) -> None:
r = client.post(
"/compose", json={"signs": ["hello", "thank_you"]}
)
assert r.status_code == 200
out = r.json()["sentence"].lower()
assert "hello" in out
assert "thank you" in out
class TestSpeak:
def test_empty_text_rejected(self, client: TestClient) -> None:
r = client.post("/speak", json={"text": ""})
assert r.status_code == 400
def test_returns_audio(self, client: TestClient) -> None:
r = client.post("/speak", json={"text": "hello"})
assert r.status_code == 200
assert r.headers["content-type"].startswith("audio/")
assert len(r.content) > 0
|