JEV / code /tests /test_server_contract.py
cloudyu's picture
Upload JEV v0.7.0 (Qwen3.5-9B distilled from Jev 1.13)
b2f3bf4 verified
Raw
History Blame Contribute Delete
4.11 kB
"""Contract tests for the Jev-compatible API (DESIGN §4). Needs an export dir: JEV_EXPORT_DIR=exports/... pytest -m gpu tests/test_server_contract.py"""
import os
import pytest
pytestmark = pytest.mark.gpu
EXPORT = os.environ.get("JEV_EXPORT_DIR", "")
@pytest.fixture(scope="module")
def client():
if not EXPORT or not os.path.isdir(EXPORT):
pytest.skip("set JEV_EXPORT_DIR to an export bundle")
from fastapi.testclient import TestClient
from jev_judge.server import Engine, create_app
return TestClient(create_app(Engine(EXPORT)))
def test_health_and_version_header(client):
r = client.get("/healthz")
assert r.status_code == 200 and r.json()["ok"] and r.json()["calibrated"]
assert "X-Jev-Judge-Version" in r.headers
def test_noul(client):
r = client.post("/v1/decisions", json={"kind": "noul", "state": "Payment failed twice with card declined.",
"question": "Is the customer asking for a refund?", "options": ["false", "true"]})
assert r.status_code == 200, r.text
j = r.json()
assert j["kind"] == "noul" and j["options"] == ["false", "true"]
assert abs(sum(j["distribution"]) - 1) < 1e-4 and len(j["distribution"]) == 2
assert isinstance(j["decision"]["noul"], bool) and j["decision"]["choice"] is None
assert j["model"]["calibrated"] is True and j["latency_ms"] > 0
def test_choice_alignment_and_sum(client):
opts = ["approve", "approve_reduced", "deny", "request_more_info"]
r = client.post("/v1/decisions", json={"kind": "choice", "state": "Applicant income 30k, debt 45k, two missed payments.",
"question": "Loan decision?", "options": opts})
assert r.status_code == 200, r.text
j = r.json()
assert j["options"] == opts and len(j["distribution"]) == 4
assert abs(sum(j["distribution"]) - 1) < 1e-4
assert j["decision"]["choice"] == opts[max(range(4), key=lambda i: j["distribution"][i])]
def test_score(client):
r = client.post("/v1/decisions", json={"kind": "score", "state": "Server room temperature 41C, alarms ignored for 2 hours.",
"question": "Rate urgency 0-5.", "options": ["0", "1", "2", "3", "4", "5"]})
assert r.status_code == 200, r.text
j = r.json()
assert len(j["distribution"]) == 6 and 0 <= j["decision"]["expected_score"] <= 5
assert j["decision"]["score"] in range(6)
def test_422_bad_kind_and_options(client):
assert client.post("/v1/decisions", json={"kind": "rank", "state": "x", "question": "y", "options": ["a", "b"]}).status_code == 422
assert client.post("/v1/decisions", json={"kind": "noul", "state": "x", "question": "y", "options": ["yes", "no"]}).status_code == 422
assert client.post("/v1/decisions", json={"kind": "choice", "state": "x", "question": "y", "options": ["only"]}).status_code == 422
assert client.post("/v1/decisions", json={"kind": "choice", "state": "x", "question": "y", "options": [str(i) for i in range(17)]}).status_code == 422
def test_413_too_long_without_truncate(client):
long_state = "word " * 3000
r = client.post("/v1/decisions", json={"kind": "noul", "state": long_state, "question": "q?", "options": ["false", "true"], "truncate": False})
assert r.status_code == 413
r = client.post("/v1/decisions", json={"kind": "noul", "state": long_state, "question": "q?", "options": ["false", "true"], "truncate": True})
assert r.status_code == 200
def test_batch_order_and_limit(client):
items = [{"kind": "noul", "state": f"case {i}", "question": "ok?", "options": ["false", "true"]} for i in range(5)]
items.insert(2, {"kind": "choice", "state": "s", "question": "q", "options": ["a", "b", "c"]})
r = client.post("/v1/decisions:batch", json={"items": items})
assert r.status_code == 200, r.text
res = r.json()["results"]
assert len(res) == 6 and res[2]["kind"] == "choice" and res[0]["kind"] == "noul"
too_many = [items[0]] * 257
assert client.post("/v1/decisions:batch", json={"items": too_many}).status_code == 413