import hashlib import hmac from collections.abc import AsyncIterator import httpx import pytest from knowledge_adapter.app import create_app from knowledge_adapter.auth import DifyKeyAuthenticator CURRENT_KEY = "c" * 32 NEXT_KEY = "n" * 32 VALID_PAYLOAD = { "knowledge_id": "3904b40e-278b-4273-a6fa-db6cbc975587", "query": "test query", "retrieval_setting": {"top_k": 3, "score_threshold": 0.5}, } def sha256_hex(value: str) -> str: return hashlib.sha256(value.encode()).hexdigest() @pytest.fixture async def client() -> AsyncIterator[httpx.AsyncClient]: authenticator = DifyKeyAuthenticator( current_hash=sha256_hex(CURRENT_KEY), next_hash=sha256_hex(NEXT_KEY), ) transport = httpx.ASGITransport(app=create_app(authenticator=authenticator)) async with httpx.AsyncClient(transport=transport, base_url="http://test") as value: yield value async def test_retrieval_without_bearer_key_returns_401(client: httpx.AsyncClient) -> None: response = await client.post("/retrieval", json=VALID_PAYLOAD) assert response.status_code == 401 async def test_retrieval_with_wrong_bearer_key_returns_401(client: httpx.AsyncClient) -> None: response = await client.post( "/retrieval", headers={"Authorization": f"Bearer {'x' * 32}"}, json=VALID_PAYLOAD, ) assert response.status_code == 401 async def test_retrieval_with_current_key_returns_200(client: httpx.AsyncClient) -> None: response = await client.post( "/retrieval", headers={"Authorization": f"Bearer {CURRENT_KEY}"}, json=VALID_PAYLOAD, ) assert response.status_code == 200 async def test_retrieval_with_next_key_returns_200(client: httpx.AsyncClient) -> None: response = await client.post( "/retrieval", headers={"Authorization": f"Bearer {NEXT_KEY}"}, json=VALID_PAYLOAD, ) assert response.status_code == 200 async def test_current_match_does_not_short_circuit_next_hash_comparison( client: httpx.AsyncClient, monkeypatch: pytest.MonkeyPatch, ) -> None: original_compare = hmac.compare_digest compared_hashes: list[str] = [] def recording_compare(digest: str, configured_hash: str) -> bool: compared_hashes.append(configured_hash) return original_compare(digest, configured_hash) monkeypatch.setattr("knowledge_adapter.auth.hmac.compare_digest", recording_compare) response = await client.post( "/retrieval", headers={"Authorization": f"Bearer {CURRENT_KEY}"}, json=VALID_PAYLOAD, ) assert response.status_code == 200 assert len(compared_hashes) == 2