Something went wrong. Try again.
sloproxy
Something went wrong. Try again.
6.9 kB · 228 lines
Go
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229package util
import ( "testing"
"github.com/tidwall/gjson")
func TestCollectResponsesToolDescriptors_PriorityAndNamespace(t *testing.T) { raw := `{ "tools": [ {"type": "function", "name": "top_fn", "description": "top function"} ], "input": [ { "type": "additional_tools", "tools": [ { "type": "namespace", "name": "ns1", "tools": [ {"type": "function", "name": "child_fn", "description": "child function"}, {"type": "custom", "name": "child_custom", "description": "child custom"} ] }, {"type": "custom", "name": "direct_custom"} ] } ] }`
root := gjson.Parse(raw) descriptors := CollectResponsesToolDescriptors(root) if len(descriptors) != 4 { t.Fatalf("expected 4 descriptors, got %d", len(descriptors)) }
decls, forwardMap, reverseMap := BuildGeminiFunctionDeclarations(root) if len(decls) != 4 { t.Fatalf("expected 4 declarations, got %d", len(decls)) }
if forwardMap["ns1__child_fn"] != "ns1__child_fn" { t.Fatalf("forwardMap['ns1__child_fn'] = %q, want ns1__child_fn", forwardMap["ns1__child_fn"]) }
childCustomIdentity := reverseMap["ns1__child_custom"] if childCustomIdentity.Name != "child_custom" || childCustomIdentity.Namespace != "ns1" || !childCustomIdentity.Custom { t.Fatalf("unexpected reverseMap for ns1__child_custom: %+v", childCustomIdentity) }
topFnIdentity := reverseMap["top_fn"] if topFnIdentity.Name != "top_fn" || topFnIdentity.Namespace != "" || topFnIdentity.Custom { t.Fatalf("unexpected reverseMap for top_fn: %+v", topFnIdentity) }}
func TestResponsesToolWinners_TopLevelBeatsAdditionalTools(t *testing.T) { raw := `{ "tools": [ {"type": "function", "name": "shared_fn", "description": "top level"} ], "input": [ { "type": "additional_tools", "tools": [ {"type": "function", "name": "shared_fn", "description": "additional"} ] } ] }`
root := gjson.Parse(raw) winners := CollectResponsesToolWinners(root) winner := winners["shared_fn"] if winner.SourcePriority != 0 { t.Fatalf("winner priority = %d, want 0", winner.SourcePriority) } if winner.Tool.Get("description").String() != "top level" { t.Fatalf("winner description = %q, want 'top level'", winner.Tool.Get("description").String()) }}
func TestResponsesToolWinners_DirectBeatsNamespaceChild(t *testing.T) { raw := `{ "tools": [ {"type": "namespace", "name": "n", "tools": [{"type": "function", "name": "x", "description": "namespace child"}]}, {"type": "custom", "name": "n__x", "description": "direct"} ] }`
root := gjson.Parse(raw) winners := CollectResponsesToolWinners(root) winner := winners["n__x"] if !winner.Direct { t.Fatalf("winner direct = %v, want true", winner.Direct) } if winner.ToolType != "custom" { t.Fatalf("winner toolType = %q, want custom", winner.ToolType) }}
func TestConvertResponsesToolChoiceToGemini(t *testing.T) { tests := []struct { name string choiceJSON string forwardMap map[string]string wantMode string wantNames []string }{ { name: "auto string", choiceJSON: `"auto"`, wantMode: "AUTO", }, { name: "none string", choiceJSON: `"none"`, wantMode: "NONE", }, { name: "required string", choiceJSON: `"required"`, wantMode: "ANY", }, { name: "function object with namespace", choiceJSON: `{"type": "function", "name": "my_fn", "namespace": "my_ns"}`, forwardMap: map[string]string{"my_ns__my_fn": "my_ns__my_fn"}, wantMode: "ANY", wantNames: []string{"my_ns__my_fn"}, }, { name: "custom object", choiceJSON: `{"type": "custom", "name": "exec", "namespace": "functions"}`, forwardMap: map[string]string{"functions__exec": "functions__exec"}, wantMode: "ANY", wantNames: []string{"functions__exec"}, }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { choice := gjson.Parse(tt.choiceJSON) out, ok := ConvertResponsesToolChoiceToGemini(choice, tt.forwardMap) if !ok { t.Fatalf("ConvertResponsesToolChoiceToGemini returned false") } mode := gjson.GetBytes(out, "mode").String() if mode != tt.wantMode { t.Fatalf("mode = %q, want %q", mode, tt.wantMode) } if len(tt.wantNames) > 0 { names := gjson.GetBytes(out, "allowedFunctionNames").Array() if len(names) != len(tt.wantNames) { t.Fatalf("allowedFunctionNames count = %d, want %d", len(names), len(tt.wantNames)) } for i, want := range tt.wantNames { if names[i].String() != want { t.Fatalf("allowedFunctionNames[%d] = %q, want %q", i, names[i].String(), want) } } } }) }}
func TestUnwrapResponsesCustomToolInput(t *testing.T) { tests := []struct { input string want string }{ {input: `{"input":"pwd"}`, want: "pwd"}, {input: `{"input":{"cmd":"ls"}}`, want: `{"cmd":"ls"}`}, {input: `"direct text"`, want: "direct text"}, {input: `{}`, want: ""}, {input: ``, want: ""}, }
for _, tt := range tests { got := UnwrapResponsesCustomToolInput(tt.input) if got != tt.want { t.Errorf("UnwrapResponsesCustomToolInput(%q) = %q, want %q", tt.input, got, tt.want) } }}
func TestBuildGeminiFunctionDeclarations_DisambiguationAndLongNames(t *testing.T) { // Two tools that genuinely collide after sanitization (e.g. "read/file" vs "read_file"), and one > 64 chars raw := `{ "tools": [ {"type": "function", "name": "read/file", "description": "tool with slash"}, {"type": "function", "name": "read_file", "description": "tool with underscore"}, {"type": "custom", "name": "mcp__very_very_very_very_very_very_long_namespace_name__very_very_very_long_custom_tool_name_that_exceeds_sixty_four_chars"} ] }`
root := gjson.Parse(raw) decls, forwardMap, reverseMap := BuildGeminiFunctionDeclarations(root) if len(decls) != 3 { t.Fatalf("expected 3 decls, got %d", len(decls)) }
name1 := forwardMap["read/file"] name2 := forwardMap["read_file"] if name1 == name2 { t.Fatalf("colliding tools mapped to identical name: %q", name1) }
identity1 := reverseMap[name1] if identity1.Name != "read/file" { t.Fatalf("reverseMap[%q].Name = %q, want read/file", name1, identity1.Name) } identity2 := reverseMap[name2] if identity2.Name != "read_file" { t.Fatalf("reverseMap[%q].Name = %q, want read_file", name2, identity2.Name) }
longName := forwardMap["mcp__very_very_very_very_very_very_long_namespace_name__very_very_very_long_custom_tool_name_that_exceeds_sixty_four_chars"] if len(longName) > 64 { t.Fatalf("long tool name length = %d > 64: %q", len(longName), longName) }
identityLong := reverseMap[longName] if !identityLong.Custom || identityLong.Name != "mcp__very_very_very_very_very_very_long_namespace_name__very_very_very_long_custom_tool_name_that_exceeds_sixty_four_chars" { t.Fatalf("unexpected reverse identity for long name: %+v", identityLong) }}