Something went wrong. Try again.
sloproxy
Something went wrong. Try again.
23 kB · 649 lines
Go
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650package session
import ( "net/http" "strings" "testing" "unicode/utf8"
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor")
func TestExtractSessionInfoAllClients(t *testing.T) { t.Parallel()
// 1. Claude Code with Subagent claudeHeaders := http.Header{ "X-Claude-Code-Session-Id": []string{"claude-root-123"}, "X-Claude-Code-Agent-Id": []string{"subagent-checker"}, } info, ok := ExtractSessionInfo(claudeHeaders, nil, nil) if !ok { t.Fatalf("ExtractSessionInfo failed for Claude Code") } if info.ClientType != "claude" { t.Errorf("ClientType = %q, want claude", info.ClientType) } if info.SessionID != "claude:claude-root-123:agent:subagent-checker" { t.Errorf("SessionID = %q", info.SessionID) } if info.ParentSessionID != "claude:claude-root-123" { t.Errorf("ParentSessionID = %q", info.ParentSessionID) } if info.AgentName != "subagent-checker" { t.Errorf("AgentName = %q", info.AgentName) }
// 2. Codex CLI with parent thread codexHeaders := http.Header{ "Session-Id": []string{"codex-child-555"}, "x-codex-parent-thread-id": []string{"codex-parent-111"}, } info, ok = ExtractSessionInfo(codexHeaders, nil, nil) if !ok || info.ClientType != "codex" { t.Fatalf("ExtractSessionInfo failed for Codex CLI") } if info.SessionID != "codex:codex-child-555" || info.ParentSessionID != "codex:codex-parent-111" { t.Errorf("Codex session ids mismatch: %+v", info) }
// 2b. Codex CLI fork with X-Codex-Turn-Metadata codexForkHeaders := http.Header{ "Session-Id": []string{"codex-fork-666"}, "X-Codex-Turn-Metadata": []string{ `{"session_id":"codex-fork-666","forked_from_thread_id":"codex-parent-111","request_kind":"turn"}`, }, } info, ok = ExtractSessionInfo(codexForkHeaders, nil, nil) if !ok || info.ClientType != "codex" { t.Fatalf("ExtractSessionInfo failed for Codex fork") } if info.SessionID != "codex:codex-fork-666" || info.ParentSessionID != "codex:codex-parent-111" { t.Errorf("Codex fork session ids mismatch: %+v", info) } if !info.IsFork { t.Errorf("expected IsFork=true for Codex fork: %+v", info) }
// 2c. Codex CLI Multi-Agent v2 (collab_spawn) codexSubagentHeaders := http.Header{ "Session-Id": []string{"codex-root-001"}, "Thread-Id": []string{"codex-sub-thread-002"}, "X-Openai-Subagent": []string{"collab_spawn"}, "X-Codex-Turn-Metadata": []string{ `{"session_id":"codex-root-001","thread_id":"codex-sub-thread-002","agent_name":"/root/check_readme","parent_thread_id":"codex-root-001","subagent_kind":"thread_spawn"}`, }, } info, ok = ExtractSessionInfo(codexSubagentHeaders, nil, nil) if !ok || info.ClientType != "codex" { t.Fatalf("ExtractSessionInfo failed for Codex Multi-Agent v2") } if info.SessionID != "codex:codex-root-001:agent:check_readme" || info.ParentSessionID != "codex:codex-root-001" { t.Errorf("Codex Multi-Agent session ids mismatch: %+v", info) } if info.AgentName != "check_readme" || !info.IsSubagent { t.Errorf("Codex Multi-Agent metadata mismatch: AgentName=%q, IsSubagent=%v", info.AgentName, info.IsSubagent) }
// 3. Pi Slot Session piHeaders := http.Header{ "X-Slot-Session-Id": []string{"pi-slot-777"}, } info, ok = ExtractSessionInfo(piHeaders, nil, nil) if !ok || info.ClientType != "pi" || info.SessionID != "slot:pi-slot-777" { t.Errorf("Pi slot mismatch: %+v", info) }
// 4. OpenCode Session Affinity & Parent opencodeHeaders := http.Header{ "X-Session-Affinity": []string{"oc-child-999"}, "X-Parent-Session-Affinity": []string{"oc-parent-333"}, } info, ok = ExtractSessionInfo(opencodeHeaders, nil, nil) if !ok || info.ClientType != "opencode" || info.SessionID != "affinity:oc-child-999" || info.ParentSessionID != "affinity:oc-parent-333" { t.Errorf("OpenCode mismatch: %+v", info) }
// 4b. Body-only fork in thread_id bodyForkPayload := []byte(`{"thread_id":"child-thread-01","forked_from_thread_id":"parent-thread-00"}`) info, ok = ExtractSessionInfo(nil, bodyForkPayload, nil) if !ok || info.SessionID != "thread:child-thread-01" || info.ParentSessionID != "thread:parent-thread-00" || !info.IsFork || info.IsSubagent { t.Errorf("body-only fork mismatch: %+v", info) }
// 4c. Codex fork with both Session-Id and Thread-Id codexForkBothHeaders := http.Header{ "Session-Id": []string{"parent-thread-00"}, "Thread-Id": []string{"child-thread-01"}, "X-Codex-Turn-Metadata": []string{ `{"session_id":"parent-thread-00","thread_id":"child-thread-01","forked_from_thread_id":"parent-thread-00"}`, }, } info, ok = ExtractSessionInfo(codexForkBothHeaders, nil, nil) if !ok || info.SessionID != "codex:child-thread-01" || info.ParentSessionID != "codex:parent-thread-00" || !info.IsFork { t.Errorf("Codex fork with both sid and tid mismatch: %+v", info) }
// 4d. Nested metadata forked_from_thread_id in body nestedForkPayload := []byte(`{"thread_id":"child-t-99","metadata":{"forked_from_thread_id":"parent-t-88"}}`) info, ok = ExtractSessionInfo(nil, nestedForkPayload, nil) if !ok || info.SessionID != "thread:child-t-99" || info.ParentSessionID != "thread:parent-t-88" || !info.IsFork { t.Errorf("nested body-only fork mismatch: %+v", info) }
// 4e. Codex fork with Session-Id in header and thread_id + metadata.forked_from_thread_id in body codexHeaderBodyForkHeaders := http.Header{"Session-Id": []string{"parent-sess-uuid"}} codexHeaderBodyForkPayload := []byte(`{"thread_id":"child-thread-uuid","metadata":{"forked_from_thread_id":"parent-sess-uuid"}}`) info, ok = ExtractSessionInfo(codexHeaderBodyForkHeaders, codexHeaderBodyForkPayload, nil) if !ok || info.SessionID != "codex:child-thread-uuid" || info.ParentSessionID != "codex:parent-sess-uuid" || !info.IsFork { t.Errorf("Codex fork with Session-Id header and body thread_id mismatch: %+v", info) }
// 5. Antigravity X-Http-Session-Id agyHeaders := http.Header{ "X-Http-Session-Id": []string{"agy-sess-888"}, } info, ok = ExtractSessionInfo(agyHeaders, nil, nil) if !ok || info.ClientType != "agy" || info.SessionID != "agy:agy-sess-888" { t.Errorf("Antigravity mismatch: %+v", info) }
// 6. Payload with metadata.agent_id and parent_session_id payload := []byte(`{ "session_id": "payload-child-10", "parent_session_id": "payload-parent-01", "metadata": { "agent_id": "analyzer" } }`) metadata := map[string]any{ cliproxyexecutor.CallerScopeMetadataKey: "test-scope", } info, ok = ExtractSessionInfo(nil, payload, metadata) if !ok || info.ClientType != "generic" { t.Fatalf("ExtractSessionInfo failed for payload") } if info.SessionID != "session:payload-child-10:agent:analyzer" { t.Errorf("SessionID = %q", info.SessionID) } if info.ParentSessionID != "session:payload-parent-01" { t.Errorf("ParentSessionID = %q", info.ParentSessionID) } if info.CallerScope != "test-scope" { t.Errorf("CallerScope = %q", info.CallerScope) }}
func TestExtractSessionInfoCanonicalizesPayloadParentForHeaderSessions(t *testing.T) { t.Parallel()
tests := []struct { name string headers http.Header wantParent string }{ { name: "generic header", headers: http.Header{"X-Session-ID": []string{"child"}}, wantParent: "header:parent", }, { name: "codex header", headers: http.Header{"Session-Id": []string{"child"}}, wantParent: "codex:parent", }, { name: "claude header", headers: http.Header{"X-Claude-Code-Session-Id": []string{"child"}}, wantParent: "claude:parent", }, }
payload := []byte(`{"parent_session_id":"parent"}`) for _, test := range tests { t.Run(test.name, func(t *testing.T) { t.Parallel() info, ok := ExtractSessionInfo(test.headers, payload, nil) if !ok { t.Fatal("ExtractSessionInfo() returned no session") } if info.ParentSessionID != test.wantParent { t.Fatalf("ParentSessionID = %q, want %q", info.ParentSessionID, test.wantParent) } }) }}
func TestExtractSessionInfoRejectsControlCharacters(t *testing.T) { t.Parallel()
payloadWithNewline := []byte(`{"session_id": "test\nsession"}`) if _, ok := ExtractSessionInfo(nil, payloadWithNewline, nil); ok { t.Fatal("ExtractSessionInfo accepted session_id with newline") }
payloadWithNull := []byte(`{"session_id": "test\u0000session"}`) if _, ok := ExtractSessionInfo(nil, payloadWithNull, nil); ok { t.Fatal("ExtractSessionInfo accepted session_id with null byte") }}
func TestExtractSessionInfoClaudeNestedParent(t *testing.T) { t.Parallel()
// Nested metadata.user_id containing session_id and parent_session_id payload := []byte(`{ "metadata": { "user_id": "{\"session_id\":\"child-session-123\",\"parent_session_id\":\"parent-session-456\",\"agent_id\":\"subagent-worker\"}" } }`) info, ok := ExtractSessionInfo(nil, payload, nil) if !ok { t.Fatal("ExtractSessionInfo failed on nested Claude user_id") } if info.SessionID != "claude:child-session-123:agent:subagent-worker" { t.Fatalf("expected child session with agent, got %q", info.SessionID) } if info.ParentSessionID != "claude:parent-session-456" { t.Fatalf("expected parent claude:parent-session-456, got %q", info.ParentSessionID) }
// Explicit header + payload body parent_session_id headerPayload := []byte(`{"parent_session_id":"parent-session-789"}`) headers := http.Header{} headers.Set("X-Claude-Code-Session-Id", "header-child-123") info2, ok := ExtractSessionInfo(headers, headerPayload, nil) if !ok { t.Fatal("ExtractSessionInfo failed on header + body parent") } if info2.SessionID != "claude:header-child-123" { t.Fatalf("expected session claude:header-child-123, got %q", info2.SessionID) } if info2.ParentSessionID != "claude:parent-session-789" { t.Fatalf("expected parent claude:parent-session-789, got %q", info2.ParentSessionID) }}
func TestExtractSessionInfoGeminiAndAntigravityHierarchy(t *testing.T) { t.Parallel()
// Gemini cachedContent with parent_session_id geminiPayload := []byte(`{"cachedContent":"cache-child-1","parent_session_id":"cache-parent-1"}`) info, ok := ExtractSessionInfo(nil, geminiPayload, nil) if !ok { t.Fatal("ExtractSessionInfo failed on Gemini cachedContent") } if info.SessionID != "geminicache:cache-child-1" { t.Fatalf("expected session geminicache:cache-child-1, got %q", info.SessionID) } if info.ParentSessionID != "geminicache:cache-parent-1" { t.Fatalf("expected parent geminicache:cache-parent-1, got %q", info.ParentSessionID) } if info.AgentName != "subagent" { t.Fatalf("expected subagent, got %q", info.AgentName) }
// Antigravity headers with parent headers := http.Header{} headers.Set("X-Http-Session-Id", "agy-child-2") headers.Set("X-Parent-Session-ID", "agy-parent-2") info2, ok := ExtractSessionInfo(headers, nil, nil) if !ok { t.Fatal("ExtractSessionInfo failed on Antigravity headers") } if info2.SessionID != "agy:agy-child-2" { t.Fatalf("expected session agy:agy-child-2, got %q", info2.SessionID) } if info2.ParentSessionID != "agy:agy-parent-2" { t.Fatalf("expected parent agy:agy-parent-2, got %q", info2.ParentSessionID) } if info2.AgentName != "subagent" { t.Fatalf("expected subagent, got %q", info2.AgentName) }}
func TestExtractSessionInfoNestedAntigravityRequest(t *testing.T) { t.Parallel()
payload := []byte(`{ "project_id": "proj-123", "request": { "parentSessionId": "parent-sess-456", "sessionId": "child-sess-789" } }`) info, ok := ExtractSessionInfo(nil, payload, nil) if !ok { t.Fatal("ExtractSessionInfo failed on nested Antigravity payload") } if info.SessionID != "session:child-sess-789" { t.Fatalf("SessionID = %q, want session:child-sess-789", info.SessionID) } if info.ParentSessionID != "session:parent-sess-456" { t.Fatalf("ParentSessionID = %q, want session:parent-sess-456", info.ParentSessionID) }}
func TestExtractSessionInfoThreadAndConversation(t *testing.T) { t.Parallel()
// 1. Thread Header threadHeaders := http.Header{ "X-Thread-Id": []string{"thread-abc-123"}, } info, ok := ExtractSessionInfo(threadHeaders, nil, nil) if !ok || info.ClientType != "openai-thread" || info.SessionID != "thread:thread-abc-123" { t.Fatalf("thread header failed: %+v", info) }
// 2. Conversation Header convHeaders := http.Header{ "X-Conversation-Id": []string{"conv-xyz-789"}, } info, ok = ExtractSessionInfo(convHeaders, nil, nil) if !ok || info.ClientType != "conv" || info.SessionID != "conv:conv-xyz-789" { t.Fatalf("conversation header failed: %+v", info) }
// 3. Payload Thread with parent threadPayload := []byte(`{ "thread_id": "thread-child-1", "parent_thread_id": "thread-parent-1" }`) info, ok = ExtractSessionInfo(nil, threadPayload, nil) if !ok || info.ClientType != "openai-thread" || info.SessionID != "thread:thread-child-1" || info.ParentSessionID != "thread:thread-parent-1" { t.Fatalf("thread payload failed: %+v", info) }
// 4. Payload Conversation with parent convPayload := []byte(`{ "conversation_id": "conv-child-2", "parent_conversation_id": "conv-parent-2" }`) info, ok = ExtractSessionInfo(nil, convPayload, nil) if !ok || info.ClientType != "conv" || info.SessionID != "conv:conv-child-2" || info.ParentSessionID != "conv:conv-parent-2" { t.Fatalf("conv payload failed: %+v", info) }}
func TestExtractSessionInfoSelfReferentialParentFiltered(t *testing.T) { t.Parallel()
headers := http.Header{ "X-Session-ID": []string{"self-session-123"}, } payload := []byte(`{"parent_session_id": "self-session-123"}`) info, ok := ExtractSessionInfo(headers, payload, nil) if !ok { t.Fatal("ExtractSessionInfo failed") } if info.SessionID != "header:self-session-123" { t.Fatalf("SessionID = %q, want header:self-session-123", info.SessionID) } if info.ParentSessionID != "" { t.Fatalf("ParentSessionID = %q, want empty (self-referential parent must be rejected)", info.ParentSessionID) }}
func TestDeprecatedInMemorySessionTreeStoreCompatibility(t *testing.T) { t.Parallel()
var store SessionTreeStore = NewInMemorySessionTreeStore(100, 0) node := store.RecordNode(SessionTreeInfo{ SessionID: "child-1", ParentSessionID: "root-1", AgentName: "reviewer", ClientType: "pi", }) if node == nil || node.SessionID != "child-1" || node.TreeDepth != 1 { t.Fatalf("RecordNode returned invalid node: %+v", node) }
gotNode, ok := store.GetNode("child-1") if !ok || gotNode == nil || gotNode.SessionID != "child-1" { t.Fatalf("GetNode failed: %+v", gotNode) }
tree := store.GetTree("root-1") if len(tree) != 1 || tree[0].SessionID != "child-1" { t.Fatalf("GetTree failed: %+v", tree) }
ancestors := store.Ancestors("child-1") if len(ancestors) != 1 || ancestors[0] != "root-1" { t.Fatalf("Ancestors failed: %+v", ancestors) }
if !store.UpdateAffinity("child-1", "auth-99", "claude", "sonnet") { t.Fatal("UpdateAffinity failed") }
if store.Len() != 1 { t.Fatalf("Len() = %d, want 1", store.Len()) }
store.Clear() if store.Len() != 0 { t.Fatalf("Len() after Clear = %d, want 0", store.Len()) }}
func TestExtractSessionInfoClaudePayloadOutranksGenericHeader(t *testing.T) { t.Parallel()
headers := http.Header{ "X-Session-ID": []string{"generic-fallback-session"}, } payload := []byte(`{ "metadata": { "user_id": "{\"session_id\":\"claude-real-session\",\"agent_id\":\"reviewer\"}" } }`) info, ok := ExtractSessionInfo(headers, payload, nil) if !ok { t.Fatal("ExtractSessionInfo failed") } if info.ClientType != "claude" || info.SessionID != "claude:claude-real-session:agent:reviewer" { t.Fatalf("expected claude identity to outrank generic header, got %+v", info) }}
func TestExtractSessionInfoPromptCacheKeyAndClientReqAndMetadata(t *testing.T) { t.Parallel()
// 1. prompt_cache_key with conversation alias (not a parent-child edge) payloadPCK := []byte(`{"prompt_cache_key":"prompt-key-123","conversation":{"id":"conv-456"}}`) info, ok := ExtractSessionInfo(nil, payloadPCK, nil) if !ok || info.SessionID != "pck:prompt-key-123" || info.ParentSessionID != "" { t.Fatalf("pck extraction failed (should not treat conv alias as parent): %+v", info) }
// 1b. prompt_cache_key with explicit parentCandidate payloadPCKWithParent := []byte(`{"prompt_cache_key":"prompt-key-123","conversation":{"id":"conv-456"},"parent_session_id":"pck-parent-789"}`) infoWithParent, okWithParent := ExtractSessionInfo(nil, payloadPCKWithParent, nil) if !okWithParent || infoWithParent.SessionID != "pck:prompt-key-123" || infoWithParent.ParentSessionID != "pck:pck-parent-789" { t.Fatalf("pck with parent candidate extraction failed: %+v", infoWithParent) }
// 2. plain metadata.user_id payloadUser := []byte(`{"metadata":{"user_id":"user-999"}}`) info, ok = ExtractSessionInfo(nil, payloadUser, nil) if !ok || info.SessionID != "user:user-999" { t.Fatalf("user_id extraction failed: %+v", info) }
// 3. X-Client-Request-Id headersReqID := http.Header{"X-Client-Request-Id": []string{"client-req-001"}} info, ok = ExtractSessionInfo(headersReqID, nil, nil) if !ok || info.SessionID != "clientreq:client-req-001" { t.Fatalf("clientreq extraction failed: %+v", info) }
// 4. ExecutionSessionMetadataKey meta := map[string]any{ "execution_session_id": "exec-777", } info, ok = ExtractSessionInfo(nil, nil, meta) if !ok || info.SessionID != "execution:exec-777" { t.Fatalf("execution extraction failed: %+v", info) }}
func TestExtractSessionInfoNestedRequestAgent(t *testing.T) { t.Parallel()
payload := []byte(`{ "request": { "sessionId": "child-sess-1", "metadata": { "agent_id": "worker-sub" } } }`) info, ok := ExtractSessionInfo(nil, payload, nil) if !ok { t.Fatal("ExtractSessionInfo failed on nested request") } if info.SessionID != "session:child-sess-1:agent:worker-sub" { t.Fatalf("SessionID = %q, want session:child-sess-1:agent:worker-sub", info.SessionID) } if info.ParentSessionID != "session:child-sess-1" { t.Fatalf("ParentSessionID = %q, want session:child-sess-1", info.ParentSessionID) } if info.AgentName != "worker-sub" { t.Fatalf("AgentName = %q, want worker-sub", info.AgentName) }}
func TestExtractSessionInfoClaudeMetadataUserIDWithAgentHeader(t *testing.T) { t.Parallel()
headers := http.Header{ "X-Claude-Code-Agent-Id": []string{"subagent-uuid-123"}, } payload := []byte(`{ "metadata": { "user_id": "{\"device_id\":\"dev-1\",\"session_id\":\"main-sess-456\"}" } }`) info, ok := ExtractSessionInfo(headers, payload, nil) if !ok { t.Fatal("ExtractSessionInfo failed on Claude metadata user_id with agent header") } if info.ClientType != "claude" { t.Fatalf("ClientType = %q, want claude", info.ClientType) } if info.SessionID != "claude:main-sess-456:agent:subagent-uuid-123" { t.Fatalf("SessionID = %q, want claude:main-sess-456:agent:subagent-uuid-123", info.SessionID) } if info.ParentSessionID != "claude:main-sess-456" { t.Fatalf("ParentSessionID = %q, want claude:main-sess-456", info.ParentSessionID) } if info.AgentName != "subagent-uuid-123" { t.Fatalf("AgentName = %q, want subagent-uuid-123", info.AgentName) }}
func TestExtractSessionInfoNestedSubagentIDAndUserID(t *testing.T) { t.Parallel()
// 1. Nested request with subagent_id payloadSub := []byte(`{ "request": { "sessionId": "main-sess-999", "metadata": { "subagent_id": "worker-sub-999" } } }`) info, ok := ExtractSessionInfo(nil, payloadSub, nil) if !ok { t.Fatal("ExtractSessionInfo failed on nested subagent_id request") } if info.SessionID != "session:main-sess-999:agent:worker-sub-999" { t.Fatalf("SessionID = %q, want session:main-sess-999:agent:worker-sub-999", info.SessionID) } if info.ParentSessionID != "session:main-sess-999" { t.Fatalf("ParentSessionID = %q, want session:main-sess-999", info.ParentSessionID) } if info.AgentName != "worker-sub-999" { t.Fatalf("AgentName = %q, want worker-sub-999", info.AgentName) }
// 2. Nested request with plain user_id payloadUser := []byte(`{ "request": { "metadata": { "user_id": "nested-user-123" } } }`) infoUser, okUser := ExtractSessionInfo(nil, payloadUser, nil) if !okUser { t.Fatal("ExtractSessionInfo failed on nested user_id request") } if infoUser.SessionID != "user:nested-user-123" { t.Fatalf("SessionID = %q, want user:nested-user-123", infoUser.SessionID) }
// 3. Nested request with promptCacheKey payloadPCK := []byte(`{ "request": { "promptCacheKey": "nested-pck-456" } }`) infoPCK, okPCK := ExtractSessionInfo(nil, payloadPCK, nil) if !okPCK { t.Fatal("ExtractSessionInfo failed on nested promptCacheKey request") } if infoPCK.SessionID != "pck:nested-pck-456" { t.Fatalf("SessionID = %q, want pck:nested-pck-456", infoPCK.SessionID) }
// 4. Nested promptCacheKey when top-level prompt_cache_key is empty string payloadPCKShadow := []byte(`{ "prompt_cache_key": "", "request": { "promptCacheKey": "nested-pck-valid" } }`) infoShadow, okShadow := ExtractSessionInfo(nil, payloadPCKShadow, nil) if !okShadow { t.Fatal("ExtractSessionInfo failed on shadowed promptCacheKey request") } if infoShadow.SessionID != "pck:nested-pck-valid" { t.Fatalf("SessionID = %q, want pck:nested-pck-valid", infoShadow.SessionID) }}
func TestBoundSessionIdentitySafetyAndUniqueness(t *testing.T) { t.Parallel()
// Short ID remains unchanged shortID := "session:normal-length-session" if got := BoundSessionIdentity(shortID); got != shortID { t.Fatalf("shortID = %q, want %q", got, shortID) }
// Long ID with Chinese UTF-8 characters exceeding 256 bytes longChinese := strings.Repeat("会话测试超长标识符", 20) // ~18 bytes * 20 = 360 bytes bounded1 := BoundSessionIdentity(longChinese) if len(bounded1) > 256 { t.Fatalf("bounded length = %d, want <= 256", len(bounded1)) } if !utf8.ValidString(bounded1) { t.Fatalf("bounded string is not valid UTF-8: %q", bounded1) }
// Uniqueness: two long strings differing only at the end must produce distinct bounded IDs longA := strings.Repeat("a", 250) + "-worker-1" longB := strings.Repeat("a", 250) + "-worker-2" boundedA := BoundSessionIdentity(longA) boundedB := BoundSessionIdentity(longB) if boundedA == boundedB { t.Fatalf("collision detected between boundedA and boundedB: %q", boundedA) } if len(boundedA) > 256 || len(boundedB) > 256 { t.Fatalf("bounded lengths = (%d, %d), want <= 256", len(boundedA), len(boundedB)) }}