CoolFace
Apppublic

pollen-robotics/reachy_mini_central

sourceHugging Faceupdated 10d agoView on Hugging Face
1likes
test_resolver.py139 linesDownload Raw Back to root
1"""Unit tests for the HF token resolver dependency in ``app.py``.2 3The resolver is the first line of defence for authenticated endpoints;4a regression here can silently re-introduce token leaks or accept5malformed Authorization schemes.6 7No test infrastructure exists on this HF Space yet. Run manually with::8 9    pip install pytest httpx fastapi10    python -m pytest test_resolver.py -v11 12Keep these tests here so whoever adds CI can wire them up without13writing the suite from scratch.14"""15 16from __future__ import annotations17 18from typing import Optional19from unittest.mock import MagicMock20 21import pytest22from fastapi import HTTPException23 24from app import _resolve_hf_token25 26 27def _req(ip: str = "1.2.3.4") -> MagicMock:28    """Build a minimal ``Request``-shaped mock with .client.host."""29    request = MagicMock()30    request.client.host = ip31    return request32 33 34def _run(35    authorization: Optional[str] = None,36    token: str = "",37    ip: str = "1.2.3.4",38) -> str:39    """Invoke the resolver synchronously for a given input."""40    import asyncio41 42    return asyncio.run(_resolve_hf_token(_req(ip), authorization, token))43 44 45# ---- Header form (preferred) ----46 47def test_bearer_header_is_accepted():48    assert _run(authorization="Bearer hf_abc123") == "hf_abc123"49 50 51def test_bearer_header_case_insensitive_scheme():52    assert _run(authorization="bearer hf_abc123") == "hf_abc123"53    assert _run(authorization="BEARER hf_abc123") == "hf_abc123"54 55 56def test_bearer_header_trims_whitespace():57    assert _run(authorization="Bearer   hf_abc123  ") == "hf_abc123"58 59 60def test_bearer_header_empty_token_is_rejected():61    """'Bearer' with nothing after must 401, not return ''."""62    with pytest.raises(HTTPException) as exc:63        _run(authorization="Bearer")64    assert exc.value.status_code == 40165 66 67def test_bearer_header_whitespace_only_token_is_rejected():68    with pytest.raises(HTTPException) as exc:69        _run(authorization="Bearer    ")70    assert exc.value.status_code == 40171 72 73# ---- Unknown / malformed Authorization schemes ----74 75def test_basic_auth_scheme_is_rejected():76    """Basic auth must NOT leak through as a bare-string token."""77    with pytest.raises(HTTPException) as exc:78        _run(authorization="Basic dXNlcjpwdw==")79    assert exc.value.status_code == 40180    assert "Bearer" in exc.value.detail81 82 83def test_digest_scheme_is_rejected():84    with pytest.raises(HTTPException) as exc:85        _run(authorization="Digest username=foo")86    assert exc.value.status_code == 40187 88 89def test_bare_token_in_header_is_rejected():90    """A raw token with no scheme is not RFC 6750 shaped — reject it."""91    with pytest.raises(HTTPException) as exc:92        _run(authorization="hf_abc123")93    assert exc.value.status_code == 40194 95 96# ---- Query-string fallback ----97 98def test_query_token_is_accepted_and_deprecation_is_logged_once(caplog):99    # Clear the sampler state between tests — the module-level set100    # persists within a process.101    from app import _deprecation_warned_ips102 103    _deprecation_warned_ips.clear()104    with caplog.at_level("WARNING"):105        assert _run(token="hf_legacy") == "hf_legacy"106    warnings = [r for r in caplog.records if "deprecation" in r.message]107    assert len(warnings) == 1108 109    # Second call from the same IP does NOT re-log.110    caplog.clear()111    with caplog.at_level("WARNING"):112        assert _run(token="hf_legacy") == "hf_legacy"113    warnings = [r for r in caplog.records if "deprecation" in r.message]114    assert len(warnings) == 0115 116    # But a different IP triggers its own one-shot warning.117    caplog.clear()118    with caplog.at_level("WARNING"):119        assert _run(token="hf_legacy", ip="9.9.9.9") == "hf_legacy"120    warnings = [r for r in caplog.records if "deprecation" in r.message]121    assert len(warnings) == 1122 123 124# ---- Precedence (both forms present) ----125 126def test_header_takes_precedence_over_query():127    """If both are sent, the Authorization header wins and the query128    is ignored without logging deprecation (header users are compliant)."""129    assert _run(authorization="Bearer from_header", token="from_query") == "from_header"130 131 132# ---- Neither form present ----133 134def test_missing_both_raises_401():135    with pytest.raises(HTTPException) as exc:136        _run()137    assert exc.value.status_code == 401138    assert "Missing" in exc.value.detail139