@@ -211,3 +211,69 @@ def test_record_without_key_context_stores_null(db):
211211 collector .record (username = "u" , provider = "trae" , model = "m" , ok = True )
212212 assert db .connect ().execute (
213213 "SELECT api_key_id FROM usage_events" ).fetchone ()["api_key_id" ] is None
214+
215+
216+ # ------------------------------------------------------- 会话标识(E1 的地基)
217+
218+ def test_session_id_defaults_none_and_round_trips ():
219+ from src .request_context import get_session_id , set_session_id
220+
221+ assert get_session_id () is None
222+ set_session_id ("sess-1" )
223+ assert get_session_id () == "sess-1"
224+ set_session_id (None )
225+ assert get_session_id () is None
226+
227+
228+ def test_sanitize_session_id_accepts_and_rejects ():
229+ from src .request_context import SESSION_ID_PATTERN , sanitize_session_id
230+
231+ for raw in ("abc" , "a-b.c_d:e" , "x" * 128 ):
232+ assert sanitize_session_id (raw ) == raw
233+ for raw in (None , "" , " " , "has space" , "line\n break" , "x" * 129 ):
234+ assert sanitize_session_id (raw ) is None
235+ assert SESSION_ID_PATTERN .match ("ok" )
236+
237+
238+ def test_session_id_from_headers_prefers_earlier_header_names ():
239+ """按 SESSION_ID_HEADERS 顺序取第一个合法值(头名大小写不敏感)。"""
240+ from src .request_context import session_id_from_headers
241+
242+ headers = [(b"conversation-id" , b"third" ), (b"x-session-id" , b"first" ),
243+ (b"x-opencode-session" , b"second" )]
244+ assert session_id_from_headers (headers ) == "first"
245+ only_later = [(b"conversation-id" , b"conversation" )]
246+ assert session_id_from_headers (only_later ) == "conversation"
247+ # 大小写混合的头名同样识别
248+ assert session_id_from_headers ([(b"X-Session-Id" , b"mixed" )]) == "mixed"
249+
250+
251+ def test_session_id_from_headers_skips_invalid_and_returns_none ():
252+ from src .request_context import session_id_from_headers
253+
254+ assert session_id_from_headers ([]) is None
255+ assert session_id_from_headers ([(b"x-session-id" , b"bad id" )]) is None
256+ # 第一个头非法时继续看下一个头,而不是整体放弃
257+ assert session_id_from_headers ([(b"x-session-id" , b"bad id" ),
258+ (b"x-opencode-session" , b"good" )]) == "good"
259+
260+
261+ def test_middleware_sets_and_clears_session_id ():
262+ """中间件从入站头设置会话标识,请求结束后复原。"""
263+ from fastapi import FastAPI as _FastAPI
264+ from fastapi .testclient import TestClient as _TestClient
265+
266+ from src .request_context import get_session_id
267+
268+ app = _FastAPI ()
269+ app .add_middleware (RequestIdMiddleware )
270+
271+ @app .get ("/sess" )
272+ async def sess ():
273+ return {"session" : get_session_id ()}
274+
275+ with _TestClient (app ) as client :
276+ response = client .get ("/sess" , headers = {"x-session-id" : "abc-123" })
277+ assert response .json ()["session" ] == "abc-123"
278+ # 复原:下一个请求不带头时读不到上一次的值
279+ assert client .get ("/sess" ).json ()["session" ] is None
0 commit comments