package gateway import ( "net/http" "testing" ) // 客户端自带的会话标识必须被采纳:它比「首条 user 消息指纹」更准 // (历史压缩后指纹会漂移),也是唯一能天然按会话隔离的来源。 func TestClientSessionFromRequestHeaders(t *testing.T) { cases := []struct { name string headers map[string]string bodyKey string want string }{ {"pi 默认格式 x-session-affinity", map[string]string{"X-Session-Affinity": "ses-1"}, "", "ses-1"}, {"openrouter 格式 x-session-id", map[string]string{"X-Session-Id": "ses-2"}, "", "ses-2"}, {"openai 格式 session_id", map[string]string{"Session-Id": "ses-3"}, "", "ses-3"}, {"body prompt_cache_key 兜底", nil, "cache-4", "cache-4"}, {"头优先于 body", map[string]string{"X-Session-Affinity": "ses-5"}, "cache-x", "ses-5"}, {"空白不算", map[string]string{"X-Session-Affinity": " "}, "", ""}, {"都没有则为空(适配器自行推导)", nil, "", ""}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { r, _ := http.NewRequest(http.MethodPost, "/v1/chat/completions", nil) for k, v := range tc.headers { r.Header.Set(k, v) } if got := clientSessionFromRequest(r, tc.bodyKey); got != tc.want { t.Errorf("clientSessionFromRequest = %q, want %q", got, tc.want) } }) } } // 刻意不采纳 x-client-request-id:名字含 request,部分客户端每请求都换, // 拿它当会话会让上游前缀缓存永不命中。 func TestClientRequestIDIsNotUsedAsSession(t *testing.T) { r, _ := http.NewRequest(http.MethodPost, "/v1/chat/completions", nil) r.Header.Set("X-Client-Request-Id", "per-request-uuid") if got := clientSessionFromRequest(r, ""); got != "" { t.Errorf("x-client-request-id 不应被当作会话(否则缓存永不命中),got %q", got) } }