from __future__ import annotations from pathlib import Path from starlette.applications import Starlette from starlette.responses import JSONResponse from starlette.testclient import TestClient from host_agent.mcp_token import McpTokenStore from host_agent.web.mcp_auth import BearerAuthMiddleware def _make_client(tmp_path: Path) -> tuple[TestClient, str]: store = McpTokenStore(tmp_path / "host_mcp_token.json") token = store.load_or_create().token async def hello(request): # type: ignore[no-untyped-def] return JSONResponse({"ok": True}) inner = Starlette(routes=[]) inner.router.add_route("/", hello, methods=["GET"]) wrapped = Starlette() wrapped.add_middleware(BearerAuthMiddleware, token_store=store) wrapped.mount("/", inner) return TestClient(wrapped), token def test_no_header_returns_401(tmp_path: Path) -> None: client, _ = _make_client(tmp_path) resp = client.get("/") assert resp.status_code == 401 assert resp.headers["WWW-Authenticate"] == "Bearer" assert resp.json() == {"error": "invalid token"} def test_wrong_token_returns_401(tmp_path: Path) -> None: client, _ = _make_client(tmp_path) resp = client.get("/", headers={"Authorization": "Bearer wrong"}) assert resp.status_code == 401 def test_correct_token_passes_through(tmp_path: Path) -> None: client, token = _make_client(tmp_path) resp = client.get("/", headers={"Authorization": f"Bearer {token}"}) assert resp.status_code == 200 assert resp.json() == {"ok": True} def test_non_bearer_scheme_returns_401(tmp_path: Path) -> None: client, token = _make_client(tmp_path) resp = client.get("/", headers={"Authorization": f"Basic {token}"}) assert resp.status_code == 401 def test_header_case_insensitive(tmp_path: Path) -> None: client, token = _make_client(tmp_path) resp = client.get("/", headers={"authorization": f"Bearer {token}"}) assert resp.status_code == 200