diff --git a/routes/cookbook_routes.py b/routes/cookbook_routes.py index 0799d3ace..5f83983ea 100644 --- a/routes/cookbook_routes.py +++ b/routes/cookbook_routes.py @@ -3930,7 +3930,7 @@ def setup_cookbook_routes() -> APIRouter: return {"ok": False, "files": [], "error": f"HF API HTTP {resp.status_code}"} data = resp.json() except Exception: - logger.exception("HF GGUF file scan failed for %s", repo) + logger.exception("HF GGUF file scan failed for %s", repo_id) return {"ok": False, "files": [], "error": "HF API request failed"} files = [ str(s.get("rfilename") or "") diff --git a/tests/test_cookbook_hf_gguf_files.py b/tests/test_cookbook_hf_gguf_files.py new file mode 100644 index 000000000..888c6def8 --- /dev/null +++ b/tests/test_cookbook_hf_gguf_files.py @@ -0,0 +1,62 @@ +import pytest +import httpx +from unittest.mock import patch, AsyncMock +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from routes.cookbook_routes import setup_cookbook_routes +from src.auth_helpers import require_user + +app = FastAPI() +router = setup_cookbook_routes() +app.include_router(router) +app.dependency_overrides[require_user] = lambda: "test_user" + +client = TestClient(app) + + +def test_hf_gguf_files_success(): + mock_resp = httpx.Response( + 200, + json={ + "siblings": [ + {"rfilename": "model-q4_k_m.gguf"}, + {"rfilename": "model-q8_0.gguf"}, + {"rfilename": "README.md"}, + ] + }, + request=httpx.Request("GET", "https://huggingface.co/api/models/TheBloke/Llama-2-7B-GGUF"), + ) + + with patch("httpx.AsyncClient.get", new_callable=AsyncMock, return_value=mock_resp): + response = client.get("/api/cookbook/hf-gguf-files?repo_id=TheBloke/Llama-2-7B-GGUF") + assert response.status_code == 200 + data = response.json() + assert data["ok"] is True + assert data["repo_id"] == "TheBloke/Llama-2-7B-GGUF" + assert data["files"] == ["model-q4_k_m.gguf", "model-q8_0.gguf"] + + +def test_hf_gguf_files_http_error(): + mock_resp = httpx.Response( + 404, + json={"error": "Model not found"}, + request=httpx.Request("GET", "https://huggingface.co/api/models/invalid/repo"), + ) + + with patch("httpx.AsyncClient.get", new_callable=AsyncMock, return_value=mock_resp): + response = client.get("/api/cookbook/hf-gguf-files?repo_id=invalid/repo") + assert response.status_code == 200 + data = response.json() + assert data["ok"] is False + assert "HF API HTTP 404" in data["error"] + + +def test_hf_gguf_files_exception_handling(): + with patch("httpx.AsyncClient.get", new_callable=AsyncMock, side_effect=httpx.ConnectError("Network unreachable")): + response = client.get("/api/cookbook/hf-gguf-files?repo_id=TheBloke/Llama-2-7B-GGUF") + # Should gracefully return JSON error payload, not raise NameError / 500 + assert response.status_code == 200 + data = response.json() + assert data["ok"] is False + assert data["error"] == "HF API request failed"