package gemini import ( "strings" "testing" ) func TestGeminiMessagesFromRequestPreservesFunctionRoundtrip(t *testing.T) { req := map[string]any{ "contents": []any{ map[string]any{ "role": "model", "parts": []any{ map[string]any{ "functionCall": map[string]any{ "id": "call_g1", "name": "search_web", "args": map[string]any{"query": "ai"}, }, }, }, }, map[string]any{ "role": "user", "parts": []any{ map[string]any{ "functionResponse": map[string]any{ "id": "call_g1", "name": "search_web", "response": "ok", }, }, }, }, }, } got := geminiMessagesFromRequest(req) if len(got) != 2 { t.Fatalf("expected two normalized messages, got %#v", got) } assistant, _ := got[0].(map[string]any) if assistant["role"] != "assistant" { t.Fatalf("expected assistant first, got %#v", assistant) } tc, _ := assistant["tool_calls"].([]any) if len(tc) != 1 { t.Fatalf("expected one tool call, got %#v", assistant["tool_calls"]) } toolMsg, _ := got[1].(map[string]any) if toolMsg["role"] != "tool" || toolMsg["tool_call_id"] != "call_g1" { t.Fatalf("expected tool message with call id, got %#v", toolMsg) } } func TestGeminiMessagesFromRequestPreservesUnknownPartAsRawJSONText(t *testing.T) { req := map[string]any{ "contents": []any{ map[string]any{ "role": "user", "parts": []any{ map[string]any{"text": "hello"}, map[string]any{"inlineData": map[string]any{"mimeType": "image/png", "data": strings.Repeat("A", 2048)}}, }, }, }, } got := geminiMessagesFromRequest(req) if len(got) != 1 { t.Fatalf("expected one normalized message, got %#v", got) } msg, _ := got[0].(map[string]any) content, _ := msg["content"].(string) if !strings.Contains(content, "hello") || !strings.Contains(content, "inlineData") { t.Fatalf("expected unknown part preserved as raw json text, got %q", content) } if !strings.Contains(content, "[omitted_binary_payload]") { t.Fatalf("expected inlineData payload to be redacted, got %q", content) } if strings.Contains(content, strings.Repeat("A", 100)) { t.Fatalf("expected raw base64 payload not to be embedded, got %q", content) } } func TestGeminiMessagesFromRequestBackfillsFunctionResponseCallIDByName(t *testing.T) { req := map[string]any{ "contents": []any{ map[string]any{ "role": "model", "parts": []any{ map[string]any{ "functionCall": map[string]any{ "name": "search_web", "args": map[string]any{"query": "docs"}, }, }, }, }, map[string]any{ "role": "user", "parts": []any{ map[string]any{ "functionResponse": map[string]any{ "name": "search_web", "response": map[string]any{"ok": true}, }, }, }, }, }, } got := geminiMessagesFromRequest(req) if len(got) != 2 { t.Fatalf("expected two normalized messages, got %#v", got) } assistant, _ := got[0].(map[string]any) tc, _ := assistant["tool_calls"].([]any) call, _ := tc[0].(map[string]any) callID, _ := call["id"].(string) if !strings.HasPrefix(callID, "call_gemini_") { t.Fatalf("expected generated call id prefix, got %#v", call) } toolMsg, _ := got[1].(map[string]any) if toolMsg["tool_call_id"] != callID { t.Fatalf("expected tool response to inherit generated call id, tool=%#v call=%#v", toolMsg, call) } }