e4dcfc49aa
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
107 lines
3.8 KiB
Python
107 lines
3.8 KiB
Python
"""API tests for the in-app EduHub skill browser endpoints.
|
|
|
|
``GET /api/v1/skills/hub/catalog`` and ``/hub/detail`` proxy a hub's public
|
|
catalog so the web panel can render it in DeepTutor's own UI (no iframe, no
|
|
login). The hub provider is mocked over an ``httpx`` transport.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
try:
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
except Exception: # pragma: no cover - optional dependency in lightweight envs
|
|
FastAPI = None
|
|
TestClient = None
|
|
|
|
pytestmark = pytest.mark.skipif(
|
|
FastAPI is None or TestClient is None, reason="fastapi not installed"
|
|
)
|
|
|
|
from deeptutor.services.skill import hub as hub_module
|
|
from deeptutor.services.skill.hub import ClawHubProvider
|
|
|
|
|
|
def _mock_provider() -> ClawHubProvider:
|
|
def handler(request: httpx.Request) -> httpx.Response:
|
|
path = request.url.path
|
|
if path == "/api/v1/skills":
|
|
return httpx.Response(
|
|
200,
|
|
json={
|
|
"skills": [
|
|
{
|
|
"slug": "socratic-tutor",
|
|
"displayName": "Socratic Tutor",
|
|
"summary": "Teach by asking.",
|
|
"version": "1.0.0",
|
|
"stats": {"downloads": 8, "stars": 2},
|
|
"owner": {
|
|
"displayName": "DeepTutor",
|
|
"htmlUrl": "https://deeptutor.info",
|
|
},
|
|
}
|
|
]
|
|
},
|
|
)
|
|
if path == "/api/v1/skills/socratic-tutor":
|
|
return httpx.Response(
|
|
200,
|
|
json={
|
|
"skill": {
|
|
"slug": "socratic-tutor",
|
|
"displayName": "Socratic Tutor",
|
|
"summary": "Teach.",
|
|
"description": "---\nname: socratic-tutor\n---\n\n# Body\n",
|
|
"tags": ["tutor"],
|
|
"stats": {"downloads": 8, "stars": 2},
|
|
},
|
|
"owner": {"displayName": "DeepTutor", "htmlUrl": "https://deeptutor.info"},
|
|
"distTags": {"latest": "1.0.0"},
|
|
},
|
|
)
|
|
return httpx.Response(404, text="nope")
|
|
|
|
return ClawHubProvider(
|
|
"eduhub",
|
|
base_url="https://eduhub.deeptutor.info/api/v1",
|
|
client=httpx.Client(transport=httpx.MockTransport(handler)),
|
|
)
|
|
|
|
|
|
def _build_app() -> FastAPI:
|
|
router = importlib.import_module("deeptutor.api.routers.skills").router
|
|
app = FastAPI()
|
|
app.include_router(router, prefix="/api/v1/skills")
|
|
return app
|
|
|
|
|
|
def test_hub_catalog_endpoint(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(hub_module, "get_hub_provider", lambda name: _mock_provider())
|
|
client = TestClient(_build_app())
|
|
resp = client.get("/api/v1/skills/hub/catalog")
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["hub"] == "eduhub"
|
|
assert data["web_url"] == "https://eduhub.deeptutor.info"
|
|
row = data["skills"][0]
|
|
assert row["slug"] == "socratic-tutor"
|
|
assert row["downloads"] == 8 and row["stars"] == 2
|
|
assert row["owner"] == "DeepTutor"
|
|
|
|
|
|
def test_hub_detail_endpoint(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(hub_module, "get_hub_provider", lambda name: _mock_provider())
|
|
client = TestClient(_build_app())
|
|
resp = client.get("/api/v1/skills/hub/detail", params={"slug": "socratic-tutor"})
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["version"] == "1.0.0"
|
|
assert "# Body" in data["content"]
|
|
assert data["web_url"] == "https://eduhub.deeptutor.info/skills/socratic-tutor"
|