summaryrefslogtreecommitdiff
path: root/tests/test_models.py
blob: 177b86b4d4f34a5bd6f0b13d66191d5c8e6d2a53 (plain)
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
"""Tests for model listing and capability routes."""

from __future__ import annotations

from unittest.mock import patch

from starlette.testclient import TestClient

from ollama_web.app import create_app
from ollama_web.config import settings


def _login_client(client: TestClient) -> str:
    """Authenticate the test client and return the CSRF token."""
    login = client.post("/api/auth/login", json={"pin": str(settings.pin)})
    return str(login.headers["x-csrf-token"])


class _FakeClient:
    def __init__(self, capabilities: list[str] | None = None) -> None:
        self._capabilities = capabilities or []

    def show(self, _model: str) -> dict[str, list[str]]:
        return {"capabilities": self._capabilities}

    def list(self) -> dict[str, list[dict[str, str]]]:
        return {"models": []}


def test_model_capabilities_route_supports_slash_in_model_name() -> None:
    """HuggingFace-style model names contain slashes and must reach the API.

    Starlette's default path converter does not allow ``/`` inside a segment,
    so the route uses the ``path`` converter. Encoding ``%2F`` must decode to
    the full model name, not split the URL into multiple segments.
    """
    slash_model = "hf.co/hotdogs/gemma-4-E4B-it-ultra-uncensored-heretic-GGUF:Q5_K_M"
    encoded = slash_model.replace("/", "%2F").replace(":", "%3A")
    fake = _FakeClient(["completion", "vision", "tools", "thinking"])

    with patch("ollama_web.llm.get_client", return_value=fake), TestClient(create_app()) as client:
        csrf = _login_client(client)
        response = client.get(
            f"/api/models/{encoded}/capabilities",
            headers={"X-CSRF-Token": csrf},
        )
        assert response.status_code == 200
        payload = response.json()
        assert "capabilities" in payload
        assert all(
            cap in [c.lower() for c in payload["capabilities"]]
            for cap in ("completion", "vision", "tools", "thinking")
        )


def test_model_capabilities_route_still_works_without_slashes() -> None:
    """Model names without slashes (e.g. cloud aliases) must keep working."""
    fake = _FakeClient(["completion"])

    with patch("ollama_web.llm.get_client", return_value=fake), TestClient(create_app()) as client:
        csrf = _login_client(client)
        response = client.get(
            "/api/models/glm-5.2%3Acloud/capabilities",
            headers={"X-CSRF-Token": csrf},
        )
        assert response.status_code == 200
        payload = response.json()
        assert "capabilities" in payload
        assert "completion" in [c.lower() for c in payload["capabilities"]]