package executor import ( "bytes" "testing" "github.com/tidwall/gjson" ) func TestApplyClaudeToolPrefix(t *testing.T) { input := []byte(`{"tools":[{"name":"alpha"},{"name":"proxy_bravo"}],"tool_choice":{"type":"tool","name":"charlie"},"messages":[{"role":"assistant","content":[{"type":"tool_use","name":"delta","id":"t1","input":{}}]}]}`) out := applyClaudeToolPrefix(input, "proxy_") if got := gjson.GetBytes(out, "tools.0.name").String(); got != "proxy_alpha" { t.Fatalf("tools.0.name = %q, want %q", got, "proxy_alpha") } if got := gjson.GetBytes(out, "tools.1.name").String(); got != "proxy_bravo" { t.Fatalf("tools.1.name = %q, want %q", got, "proxy_bravo") } if got := gjson.GetBytes(out, "tool_choice.name").String(); got != "proxy_charlie" { t.Fatalf("tool_choice.name = %q, want %q", got, "proxy_charlie") } if got := gjson.GetBytes(out, "messages.0.content.0.name").String(); got != "proxy_delta" { t.Fatalf("messages.0.content.0.name = %q, want %q", got, "proxy_delta") } } func TestApplyClaudeToolPrefix_BuiltinToolSkipped(t *testing.T) { body := []byte(`{ "tools": [ {"type": "web_search_20250305", "name": "web_search", "max_uses": 5}, {"name": "Read"} ], "messages": [ {"role": "user", "content": [ {"type": "tool_use", "name": "web_search", "id": "ws1", "input": {}}, {"type": "tool_use", "name": "Read", "id": "r1", "input": {}} ]} ] }`) out := applyClaudeToolPrefix(body, "proxy_") if got := gjson.GetBytes(out, "tools.0.name").String(); got != "web_search" { t.Fatalf("tools.0.name = %q, want %q", got, "web_search") } if got := gjson.GetBytes(out, "messages.0.content.0.name").String(); got != "web_search" { t.Fatalf("messages.0.content.0.name = %q, want %q", got, "web_search") } if got := gjson.GetBytes(out, "tools.1.name").String(); got != "proxy_Read" { t.Fatalf("tools.1.name = %q, want %q", got, "proxy_Read") } if got := gjson.GetBytes(out, "messages.0.content.1.name").String(); got != "proxy_Read" { t.Fatalf("messages.0.content.1.name = %q, want %q", got, "proxy_Read") } } func TestApplyClaudeToolPrefix_KnownBuiltinInHistoryOnly(t *testing.T) { body := []byte(`{ "tools": [ {"name": "Read"} ], "messages": [ {"role": "user", "content": [ {"type": "tool_use", "name": "web_search", "id": "ws1", "input": {}} ]} ] }`) out := applyClaudeToolPrefix(body, "proxy_") if got := gjson.GetBytes(out, "messages.0.content.0.name").String(); got != "web_search" { t.Fatalf("messages.0.content.0.name = %q, want %q", got, "web_search") } if got := gjson.GetBytes(out, "tools.0.name").String(); got != "proxy_Read" { t.Fatalf("tools.0.name = %q, want %q", got, "proxy_Read") } } func TestApplyClaudeToolPrefix_CustomToolsPrefixed(t *testing.T) { body := []byte(`{ "tools": [{"name": "Read"}, {"name": "Write"}], "messages": [ {"role": "user", "content": [ {"type": "tool_use", "name": "Read", "id": "r1", "input": {}}, {"type": "tool_use", "name": "Write", "id": "w1", "input": {}} ]} ] }`) out := applyClaudeToolPrefix(body, "proxy_") if got := gjson.GetBytes(out, "tools.0.name").String(); got != "proxy_Read" { t.Fatalf("tools.0.name = %q, want %q", got, "proxy_Read") } if got := gjson.GetBytes(out, "tools.1.name").String(); got != "proxy_Write" { t.Fatalf("tools.1.name = %q, want %q", got, "proxy_Write") } if got := gjson.GetBytes(out, "messages.0.content.0.name").String(); got != "proxy_Read" { t.Fatalf("messages.0.content.0.name = %q, want %q", got, "proxy_Read") } if got := gjson.GetBytes(out, "messages.0.content.1.name").String(); got != "proxy_Write" { t.Fatalf("messages.0.content.1.name = %q, want %q", got, "proxy_Write") } } func TestApplyClaudeToolPrefix_ToolChoiceBuiltin(t *testing.T) { body := []byte(`{ "tools": [ {"type": "web_search_20250305", "name": "web_search"}, {"name": "Read"} ], "tool_choice": {"type": "tool", "name": "web_search"} }`) out := applyClaudeToolPrefix(body, "proxy_") if got := gjson.GetBytes(out, "tool_choice.name").String(); got != "web_search" { t.Fatalf("tool_choice.name = %q, want %q", got, "web_search") } } func TestStripClaudeToolPrefixFromResponse(t *testing.T) { input := []byte(`{"content":[{"type":"tool_use","name":"proxy_alpha","id":"t1","input":{}},{"type":"tool_use","name":"bravo","id":"t2","input":{}}]}`) out := stripClaudeToolPrefixFromResponse(input, "proxy_") if got := gjson.GetBytes(out, "content.0.name").String(); got != "alpha" { t.Fatalf("content.0.name = %q, want %q", got, "alpha") } if got := gjson.GetBytes(out, "content.1.name").String(); got != "bravo" { t.Fatalf("content.1.name = %q, want %q", got, "bravo") } } func TestStripClaudeToolPrefixFromStreamLine(t *testing.T) { line := []byte(`data: {"type":"content_block_start","content_block":{"type":"tool_use","name":"proxy_alpha","id":"t1"},"index":0}`) out := stripClaudeToolPrefixFromStreamLine(line, "proxy_") payload := bytes.TrimSpace(out) if bytes.HasPrefix(payload, []byte("data:")) { payload = bytes.TrimSpace(payload[len("data:"):]) } if got := gjson.GetBytes(payload, "content_block.name").String(); got != "alpha" { t.Fatalf("content_block.name = %q, want %q", got, "alpha") } }