sentiment-scope / backend /tests /test_models_endpoint.py
melkholy's picture
feat: single-image Hugging Face Spaces deployment with rate limiting
c1fa0b4 verified
Raw
History Blame Contribute Delete
3.59 kB
"""API tests for the model registry endpoint and default-endpoint guarding.
These are unit tests (SKIP_MODEL_LOAD=1) — no torch, no real weights. They
check wiring: the /api/models shape the frontend expects, the ?task= filter,
and that analyze/batch/csv reject a stray non-default model_id.
"""
def test_models_endpoint_returns_wrapped_list(client):
resp = client.get("/api/models")
assert resp.status_code == 200
body = resp.json()
# Frontend getModels() (Task 11) expects the list wrapped under "models".
assert "models" in body
ids = {m["id"] for m in body["models"]}
assert ids == {
"twitter-roberta",
"distilbert-sst2",
"finbert",
"xlm-twitter",
"desklib-ai-detector",
"fakespot-ai-detector",
"oxidane-ai-detector",
}
# Public metadata is present; internal local_path is not leaked.
first = body["models"][0]
assert {"id", "name", "task", "labels", "domain", "note", "default", "loaded"} <= first.keys()
assert "local_path" not in first
def test_models_endpoint_filters_to_sentiment(client):
resp = client.get("/api/models?task=sentiment")
assert resp.status_code == 200
models = resp.json()["models"]
assert {m["id"] for m in models} == {
"twitter-roberta",
"distilbert-sst2",
"finbert",
"xlm-twitter",
}
# StrEnum must serialize as its value, not "ModelTask.SENTIMENT".
assert all(m["task"] == "sentiment" for m in models)
default_ids = [m["id"] for m in models if m["default"]]
assert default_ids == ["twitter-roberta"]
def test_models_endpoint_filters_to_detectors(client):
resp = client.get("/api/models?task=ai_text_detection")
assert resp.status_code == 200
models = resp.json()["models"]
assert {m["id"] for m in models} == {
"desklib-ai-detector",
"fakespot-ai-detector",
"oxidane-ai-detector",
}
assert all(m["task"] == "ai_text_detection" for m in models)
def test_models_endpoint_rejects_unknown_task(client):
resp = client.get("/api/models?task=not-a-task")
assert resp.status_code == 422
def test_models_endpoint_reports_loaded_flag(client):
# A model shows loaded=true iff it is in the lazy cache. Membership is all
# the flag checks, so a bare sentinel is enough to simulate a loaded model.
client.app.state.model_cache["distilbert-sst2"] = object()
models = client.get("/api/models").json()["models"]
by_id = {m["id"]: m for m in models}
assert by_id["distilbert-sst2"]["loaded"] is True
assert by_id["finbert"]["loaded"] is False
def test_analyze_accepts_default_model_id(client_with_model):
resp = client_with_model.post(
"/api/analyze?model_id=twitter-roberta", json={"text": "I love this"}
)
assert resp.status_code == 200
def test_analyze_rejects_non_default_model_id(client_with_model):
resp = client_with_model.post(
"/api/analyze?model_id=distilbert-sst2", json={"text": "I love this"}
)
assert resp.status_code == 400
assert "compare" in resp.json()["detail"].lower()
def test_batch_rejects_non_default_model_id(client_with_model):
resp = client_with_model.post(
"/api/analyze/batch?model_id=finbert", json={"texts": ["a", "b"]}
)
assert resp.status_code == 400
def test_csv_rejects_non_default_model_id(client_with_model):
resp = client_with_model.post(
"/api/analyze/csv?model_id=finbert",
files={"file": ("data.csv", "text\nhello\n", "text/csv")},
)
assert resp.status_code == 400