File size: 4,110 Bytes
b2f3bf4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""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