File size: 4,386 Bytes
17b22d9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9a95b72
 
 
 
 
 
 
 
 
 
 
 
 
 
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""The HTTP surface, exercised with no network and no credentials."""

from __future__ import annotations

import pytest
from fastapi.testclient import TestClient

from server import catalog
from server.main import app


@pytest.fixture(autouse=True)
def no_credentials(monkeypatch):
    for var in ("HF_TOKEN", "HUGGING_FACE_HUB_TOKEN", "SPACE_ID",
                "ASE_ALLOW_SPACE_CREDITS"):
        monkeypatch.delenv(var, raising=False)
    # The suite must never pick up the developer's real key.txt.
    monkeypatch.setenv("ASE_KEY_FILE", "/nonexistent/ase-glm-test-key")


@pytest.fixture
def client():
    return TestClient(app)


def test_health(client):
    assert client.get("/api/health").json()["ok"] is True


def test_config_lists_the_models_and_the_billing_mode(client):
    body = client.get("/api/config").json()
    ids = [m["id"] for m in body["models"]]
    assert "zai-org/GLM-5.3" in ids
    assert body["defaultModel"] == catalog.DEFAULT_MODEL
    assert body["auth"]["billsViewer"] is False
    assert "token" not in body["auth"]


def test_every_catalog_model_is_well_formed():
    for m in catalog.MODELS:
        j = m.to_json()
        assert "/" in j["id"], "an id must be owner/name to route"
        assert j["label"] and j["vendor"]
        assert j["providers"], f"{j['id']} has no route"
        assert j["contextTokens"] is None or j["contextTokens"] > 0


def sse_events(response) -> list[tuple[str, str]]:
    out, name, data = [], "message", []
    for line in response.text.split("\n"):
        if line.startswith("event:"):
            name = line[6:].strip()
        elif line.startswith("data:"):
            data.append(line[5:].lstrip(" "))
        elif not line.strip() and data:
            out.append((name, "\n".join(data)))
            name, data = "message", []
    if data:
        out.append((name, "\n".join(data)))
    return out


def test_a_missing_credential_is_reported_in_the_stream_not_as_a_4xx(client):
    # The client renders errors into the transcript. A 4xx would show as a
    # transport failure somewhere the reader is not looking.
    r = client.post("/api/chat", json={
        "model": "zai-org/GLM-5.3",
        "messages": [{"role": "user", "content": "hi"}],
    })
    assert r.status_code == 200
    events = sse_events(r)
    assert events[0][0] == "error"
    assert "credential" in events[0][1].lower()


def test_an_unknown_model_is_refused_before_any_upstream_call(client):
    r = client.post("/api/chat", json={
        "model": "../../etc/passwd",
        "messages": [{"role": "user", "content": "hi"}],
    })
    assert sse_events(r)[0][0] == "error"
    assert "Unknown model" in sse_events(r)[0][1]


@pytest.mark.parametrize("body", [
    {"model": "zai-org/GLM-5.3"},
    {"model": "zai-org/GLM-5.3", "messages": []},
    {"model": "zai-org/GLM-5.3", "messages": "not a list"},
])
def test_a_request_with_no_messages_is_refused(client, body):
    assert sse_events(client.post("/api/chat", json=body))[0][0] == "error"


def test_model_insight_rejects_a_model_not_in_the_catalog(client):
    r = client.get("/api/model", params={"id": "evil/model"})
    assert r.status_code == 400


def test_an_unknown_api_path_is_a_404_not_the_index_page(client):
    # The SPA catch-all matches anything the declared routes did not. If it
    # answers an /api path, a typo'd endpoint returns 200 full of HTML and the
    # client reports a JSON parse error a long way from the cause.
    r = client.get("/api/does-not-exist")
    assert r.status_code == 404
    assert "<html" not in r.text.lower()


def test_a_non_api_path_still_falls_through_to_the_app(client):
    # Only reachable once the bundle has been built; skip rather than fail in a
    # bare checkout, since CI builds it before the deploy either way.
    r = client.get("/some/client/route")
    assert r.status_code in (200, 404)


def test_the_page_is_not_cached(client):
    """A cached index.html keeps pointing at the previous bundle.

    The assets are content-hashed and may cache forever; the page that names
    them may not, or a deploy is invisible until the browser feels like
    re-fetching — which reads as "the deploy did nothing".
    """
    for path in ("/", "/anything"):
        r = client.get(path)
        if r.status_code != 200:
            continue
        assert "no-cache" in r.headers.get("cache-control", "")