emma-hermes/tests/gateway/test_api_server_session_own...

117 lines
4.6 KiB
Python

from unittest.mock import MagicMock
from gateway.platforms.api_server import APIServerAdapter
from gateway.config import PlatformConfig
def _make_adapter(api_key: str = "sk-secret") -> APIServerAdapter:
extra = {"key": api_key} if api_key else {}
return APIServerAdapter(PlatformConfig(enabled=True, extra=extra))
def _request(headers=None):
req = MagicMock(headers=headers or {})
req.query = {}
return req
class TestSessionOwnerHelpers:
def test_request_identity_fields_prefer_headers_over_metadata(self):
adapter = _make_adapter()
req = _request(
{
"X-Hermes-User-Id": "header-user",
"X-Hermes-User-Email": "header@bremen.com.tw",
"X-Hermes-Session-Key": "header-key",
}
)
user_id, email, session_key = adapter._request_identity_fields(
req,
{
"metadata": {
"user_id": "meta-user",
"user_email": "meta@bremen.com.tw",
}
},
)
assert user_id == "header-user"
assert email == "header@bremen.com.tw"
assert session_key == "header-key"
def test_authorize_session_owner_allows_unowned_session(self):
adapter = _make_adapter()
assert adapter._authorize_session_owner(_request(), {"id": "s1", "user_id": None}) is None
def test_authorize_session_owner_allows_matching_session_key(self):
adapter = _make_adapter()
req = _request({"X-Hermes-Session-Key": "webui:user-42"})
assert adapter._authorize_session_owner(req, {"id": "s1", "user_id": "key:webui:user-42"}) is None
def test_authorize_session_owner_rejects_mismatch(self):
adapter = _make_adapter()
req = _request({"X-Hermes-Session-Key": "webui:user-7"})
resp = adapter._authorize_session_owner(req, {"id": "s1", "user_id": "key:webui:user-42"})
assert resp is not None
assert resp.status == 403
def test_owner_candidates_prefer_principal_then_email_then_user_then_key(self, monkeypatch):
adapter = _make_adapter()
class _Store:
def get_principal_context(self, source):
return {"principal_id": "p-123"}
def get_verified_email_for_source(self, source):
return "dk96@bremen.com.tw"
monkeypatch.setattr("gateway.platforms.api_server.GatewayUserStore", lambda: _Store())
monkeypatch.setattr(adapter, "_verified_email_target_if_bound", lambda chat_id, email, explicit_user_requested=False: email)
candidates = adapter._owner_candidates_from_identity(
user_id="openwebui-42",
email="DK96@bremen.com.tw",
session_key="webui:user-42",
)
assert candidates[0] == "principal:p-123"
assert "email:dk96@bremen.com.tw" in candidates
assert "user:openwebui-42" in candidates
assert "key:webui:user-42" in candidates
class TestSessionListScope:
def test_list_sessions_uses_resolved_owner_scope(self, monkeypatch):
adapter = _make_adapter()
db = MagicMock()
db.list_sessions_rich.return_value = []
adapter._session_db = db
req = _request({"Authorization": "Bearer sk-secret", "X-Hermes-Session-Key": "webui:user-42", "X-Hermes-User-Id": "openwebui-42"})
monkeypatch.setattr(adapter, "_session_owner_from_request_body", lambda request, body=None: "principal:p-123")
import asyncio
resp = asyncio.run(adapter._handle_list_sessions(req))
assert resp.status == 200
kwargs = db.list_sessions_rich.call_args.kwargs
assert kwargs["user_id"] == "principal:p-123"
class TestSessionCreateOwner:
def test_create_session_persists_owner_from_resolved_identity(self, monkeypatch):
adapter = _make_adapter()
db = MagicMock()
db.get_session.side_effect = [None, {"id": "sess-1", "source": "api_server", "user_id": "principal:p-123"}]
adapter._session_db = db
req = _request({"Authorization": "Bearer sk-secret", "X-Hermes-Session-Key": "webui:user-42", "X-Hermes-User-Id": "openwebui-42"})
async def _json():
return {"id": "sess-1", "model": "hermes-agent", "metadata": {"user_id": "openwebui-42", "user_email": "dk96@bremen.com.tw"}}
req.json = _json
monkeypatch.setattr(adapter, "_session_owner_from_request_body", lambda request, body=None: "principal:p-123")
import asyncio
resp = asyncio.run(adapter._handle_create_session(req))
assert resp.status == 201
kwargs = db.create_session.call_args.kwargs
assert kwargs["user_id"] == "principal:p-123"