Spaces:
Sleeping
Sleeping
| """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 | |