| package translatorcliproxy |
|
|
| import ( |
| "strings" |
| "testing" |
|
|
| sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator" |
| ) |
|
|
| func TestToOpenAIClaude(t *testing.T) { |
| raw := []byte(`{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"stream":false}`) |
| got := ToOpenAI(sdktranslator.FormatClaude, "claude-sonnet-4-5", raw, false) |
| s := string(got) |
| if !strings.Contains(s, `"messages"`) || !strings.Contains(s, `"model"`) { |
| t.Fatalf("unexpected translated request: %s", s) |
| } |
| } |
|
|
| func TestToOpenAIGeminiThinkingBudgetZeroDisablesReasoning(t *testing.T) { |
| raw := []byte(`{"contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`) |
| got := string(ToOpenAI(sdktranslator.FormatGemini, "gemini-2.5-flash", raw, false)) |
| if !strings.Contains(got, `"reasoning_effort":"none"`) { |
| t.Fatalf("expected Gemini thinkingBudget=0 to translate to reasoning_effort none, got: %s", got) |
| } |
| } |
|
|
| func TestFromOpenAINonStreamClaude(t *testing.T) { |
| original := []byte(`{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"stream":false}`) |
| translatedReq := []byte(`{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"stream":false}`) |
| openaibody := []byte(`{"id":"chatcmpl_1","object":"chat.completion","created":1,"model":"claude-sonnet-4-5","choices":[{"index":0,"message":{"role":"assistant","content":"hello"},"finish_reason":"stop"}],"usage":{"prompt_tokens":1,"completion_tokens":1,"total_tokens":2}}`) |
| got := FromOpenAINonStream(sdktranslator.FormatClaude, "claude-sonnet-4-5", original, translatedReq, openaibody) |
| if !strings.Contains(string(got), `"type":"message"`) { |
| t.Fatalf("expected claude response format, got: %s", string(got)) |
| } |
| } |
|
|
| func TestFromOpenAINonStreamClaudePreservesUsageFromOpenAI(t *testing.T) { |
| original := []byte(`{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"stream":false}`) |
| translatedReq := []byte(`{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"stream":false}`) |
| openaibody := []byte(`{"id":"chatcmpl_1","object":"chat.completion","created":1,"model":"claude-sonnet-4-5","choices":[{"index":0,"message":{"role":"assistant","content":"hello"},"finish_reason":"stop"}],"usage":{"prompt_tokens":11,"completion_tokens":29,"total_tokens":40}}`) |
| got := string(FromOpenAINonStream(sdktranslator.FormatClaude, "claude-sonnet-4-5", original, translatedReq, openaibody)) |
| if !strings.Contains(got, `"input_tokens":11`) || !strings.Contains(got, `"output_tokens":29`) { |
| t.Fatalf("expected claude usage to preserve prompt/completion tokens, got: %s", got) |
| } |
| } |
|
|
| func TestFromOpenAINonStreamGeminiPreservesUsageFromOpenAI(t *testing.T) { |
| original := []byte(`{"contents":[{"role":"user","parts":[{"text":"hi"}]}]}`) |
| translatedReq := []byte(`{"model":"gemini-2.5-pro","messages":[{"role":"user","content":"hi"}],"stream":false}`) |
| openaibody := []byte(`{"id":"chatcmpl_1","object":"chat.completion","created":1,"model":"gemini-2.5-pro","choices":[{"index":0,"message":{"role":"assistant","content":"hello"},"finish_reason":"stop"}],"usage":{"prompt_tokens":11,"completion_tokens":29,"total_tokens":40}}`) |
| got := string(FromOpenAINonStream(sdktranslator.FormatGemini, "gemini-2.5-pro", original, translatedReq, openaibody)) |
| if !strings.Contains(got, `"promptTokenCount":11`) || !strings.Contains(got, `"candidatesTokenCount":29`) || !strings.Contains(got, `"totalTokenCount":40`) { |
| t.Fatalf("expected gemini usageMetadata to preserve prompt/completion tokens, got: %s", got) |
| } |
| } |
|
|
| func TestFromOpenAINonStreamPreservesResponsesUsageShape(t *testing.T) { |
| original := []byte(`{"contents":[{"role":"user","parts":[{"text":"hi"}]}]}`) |
| translatedReq := []byte(`{"model":"gemini-2.5-pro","messages":[{"role":"user","content":"hi"}],"stream":false}`) |
| openaibody := []byte(`{"id":"resp_1","object":"response","model":"gemini-2.5-pro","usage":{"input_tokens":"11","output_tokens":"29","total_tokens":"40"}}`) |
| gotGemini := string(FromOpenAINonStream(sdktranslator.FormatGemini, "gemini-2.5-pro", original, translatedReq, openaibody)) |
| if !strings.Contains(gotGemini, `"promptTokenCount":11`) || !strings.Contains(gotGemini, `"candidatesTokenCount":29`) || !strings.Contains(gotGemini, `"totalTokenCount":40`) { |
| t.Fatalf("expected gemini usageMetadata from input/output usage fields, got: %s", gotGemini) |
| } |
|
|
| origClaude := []byte(`{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"stream":false}`) |
| gotClaude := string(FromOpenAINonStream(sdktranslator.FormatClaude, "claude-sonnet-4-5", origClaude, origClaude, openaibody)) |
| if !strings.Contains(gotClaude, `"input_tokens":11`) || !strings.Contains(gotClaude, `"output_tokens":29`) { |
| t.Fatalf("expected claude usage from input/output usage fields, got: %s", gotClaude) |
| } |
| } |
|
|
| func TestParseFormatAliases(t *testing.T) { |
| cases := map[string]sdktranslator.Format{ |
| "responses": sdktranslator.FormatOpenAIResponse, |
| "anthropic": sdktranslator.FormatClaude, |
| "geminicli": sdktranslator.FormatGeminiCLI, |
| "openai-codex": sdktranslator.FormatCodex, |
| "antigravity": sdktranslator.FormatAntigravity, |
| "chat-completions": sdktranslator.FormatOpenAI, |
| } |
| for in, want := range cases { |
| if got := ParseFormat(in); got != want { |
| t.Fatalf("ParseFormat(%q)=%q want %q", in, got, want) |
| } |
| } |
| } |
|
|
| func TestToOpenAIByNameAllSupportedFormats(t *testing.T) { |
| tests := []struct { |
| name string |
| format string |
| model string |
| body string |
| }{ |
| {name: "openai", format: "openai", model: "gpt-4.1", body: `{"model":"gpt-4.1","messages":[{"role":"user","content":"hi"}],"stream":false}`}, |
| {name: "responses", format: "responses", model: "gpt-4.1", body: `{"model":"gpt-4.1","input":"hello","stream":false}`}, |
| {name: "claude", format: "claude", model: "claude-sonnet-4-5", body: `{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hello"}],"stream":false}`}, |
| {name: "gemini", format: "gemini", model: "gemini-2.5-pro", body: `{"contents":[{"role":"user","parts":[{"text":"hello"}]}]}`}, |
| {name: "gemini-cli", format: "gemini-cli", model: "gemini-2.5-pro", body: `{"model":"gemini-2.5-pro","messages":[{"role":"user","content":"hello"}],"stream":false}`}, |
| {name: "codex", format: "codex", model: "gpt-5-codex", body: `{"model":"gpt-5-codex","messages":[{"role":"user","content":"hello"}],"stream":false}`}, |
| {name: "antigravity", format: "antigravity", model: "gpt-4.1", body: `{"model":"gpt-4.1","messages":[{"role":"user","content":"hello"}],"stream":false}`}, |
| } |
|
|
| for _, tc := range tests { |
| t.Run(tc.name, func(t *testing.T) { |
| got := ToOpenAIByName(tc.format, tc.model, []byte(tc.body), false) |
| if len(got) == 0 { |
| t.Fatalf("expected non-empty conversion result for format=%s", tc.format) |
| } |
| if !strings.Contains(string(got), `"model"`) { |
| t.Fatalf("expected model field in converted payload, got=%s", string(got)) |
| } |
| }) |
| } |
| } |
|
|