sub2api/backend/internal/pkg/antigravity/claude_code_tool_map_test.go
win 435ae221bc
Some checks failed
CI / test (push) Failing after 1m32s
CI / golangci-lint (push) Failing after 31s
Security Scan / backend-security (push) Failing after 1m32s
Security Scan / frontend-security (push) Failing after 9s
x
2026-04-16 19:11:47 +08:00

161 lines
4.5 KiB
Go

package antigravity
import (
"encoding/json"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestNormalizeClaudeCodeToolName(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
expected string
}{
{name: "read alias", input: "read_file", expected: "Read"},
{name: "grep alias", input: "search_files", expected: "Grep"},
{name: "webfetch alias", input: "fetch", expected: "WebFetch"},
{name: "plan alias", input: "enter_plan_mode", expected: "EnterPlanMode"},
{name: "native passthrough", input: "TaskUpdate", expected: "TaskUpdate"},
{name: "mcp passthrough", input: "mcp__github__list_prs", expected: "mcp__github__list_prs"},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
require.Equal(t, tt.expected, normalizeClaudeCodeToolName(tt.input))
})
}
}
func TestBuildPartsNormalizesClaudeCodeToolNames(t *testing.T) {
t.Parallel()
toolIDToName := make(map[string]string)
assistantParts, stripped, err := buildParts(json.RawMessage(`[
{"type":"tool_use","id":"tool-1","name":"read_file","input":{"file_path":"/tmp/demo.txt"}}
]`), toolIDToName, false)
require.NoError(t, err)
require.False(t, stripped)
require.Len(t, assistantParts, 1)
require.NotNil(t, assistantParts[0].FunctionCall)
require.Equal(t, "Read", assistantParts[0].FunctionCall.Name)
require.Equal(t, "Read", toolIDToName["tool-1"])
userParts, stripped, err := buildParts(json.RawMessage(`[
{"type":"tool_result","tool_use_id":"tool-1","content":[{"type":"text","text":"ok"}]}
]`), toolIDToName, false)
require.NoError(t, err)
require.False(t, stripped)
require.Len(t, userParts, 1)
require.NotNil(t, userParts[0].FunctionResponse)
require.Equal(t, "Read", userParts[0].FunctionResponse.Name)
}
func TestBuildToolsNormalizesClaudeCodeBuiltinNamesOnly(t *testing.T) {
t.Parallel()
result := buildTools([]ClaudeTool{
{
Name: "search_files",
Description: "Search the workspace",
InputSchema: map[string]any{
"type": "object",
},
},
{
Name: "mcp__github__list_prs",
Description: "List pull requests",
InputSchema: map[string]any{
"type": "object",
},
},
})
require.Len(t, result, 1)
require.Len(t, result[0].FunctionDeclarations, 2)
require.Equal(t, "Grep", result[0].FunctionDeclarations[0].Name)
require.Equal(t, "mcp__github__list_prs", result[0].FunctionDeclarations[1].Name)
}
func TestNonStreamingProcessorNormalizesClaudeCodeToolName(t *testing.T) {
t.Parallel()
processor := NewNonStreamingProcessor()
response := processor.Process(&GeminiResponse{
Candidates: []GeminiCandidate{
{
Content: &GeminiContent{
Parts: []GeminiPart{
{
FunctionCall: &GeminiFunctionCall{
Name: "web_fetch",
Args: map[string]any{"url": "https://example.com"},
},
},
},
},
FinishReason: "STOP",
},
},
}, "resp-1", "claude-sonnet-4-5")
require.Len(t, response.Content, 1)
require.Equal(t, "tool_use", response.Content[0].Type)
require.Equal(t, "WebFetch", response.Content[0].Name)
require.True(t, strings.HasPrefix(response.Content[0].ID, "WebFetch-"))
require.NotNil(t, response.Content[0].Caller)
require.Equal(t, "direct", response.Content[0].Caller.Type)
require.Equal(t, "tool_use", response.StopReason)
}
func TestStreamingProcessorNormalizesClaudeCodeToolName(t *testing.T) {
t.Parallel()
processor := NewStreamingProcessor("claude-sonnet-4-5")
output := processor.processFunctionCall(&GeminiFunctionCall{
Name: "search_files",
Args: map[string]any{"pattern": "TODO"},
}, "")
events := parseSSEDataEvents(t, output)
require.Len(t, events, 3)
contentBlock, ok := events[0]["content_block"].(map[string]any)
require.True(t, ok)
require.Equal(t, "tool_use", contentBlock["type"])
require.Equal(t, "Grep", contentBlock["name"])
toolID, ok := contentBlock["id"].(string)
require.True(t, ok)
require.True(t, strings.HasPrefix(toolID, "Grep-"))
caller, ok := contentBlock["caller"].(map[string]any)
require.True(t, ok)
require.Equal(t, "direct", caller["type"])
}
func parseSSEDataEvents(t *testing.T, payload []byte) []map[string]any {
t.Helper()
lines := strings.Split(string(payload), "\n")
events := make([]map[string]any, 0)
for _, line := range lines {
if !strings.HasPrefix(line, "data: ") {
continue
}
var event map[string]any
require.NoError(t, json.Unmarshal([]byte(strings.TrimPrefix(line, "data: ")), &event))
events = append(events, event)
}
return events
}