package protocol_test import ( "strings" "testing" "ccLoad/internal/protocol" "ccLoad/internal/protocol/builtin" ) func TestRegistryRequestSemantics(t *testing.T) { t.Run("anthropic to openai keeps assistant thinking as reasoning_content", func(t *testing.T) { reg := protocol.NewRegistry() builtin.Register(reg) raw := []byte(`{"model":"claude-3-5-sonnet","messages":[{"role":"assistant","content":[{"type":"thinking","thinking":"step by step"},{"type":"text","text":"hello"}]}]}`) out, err := reg.TranslateRequest(protocol.Anthropic, protocol.OpenAI, "gpt-4o", raw, false) if err != nil { t.Fatalf("TranslateRequest failed: %v", err) } body := string(out) if !strings.Contains(body, `"reasoning_content":"step by step"`) || !strings.Contains(body, `"content":"hello"`) { t.Fatalf("unexpected openai request body: %s", body) } }) t.Run("anthropic to codex preserves redacted_thinking as encrypted_content", func(t *testing.T) { reg := protocol.NewRegistry() builtin.Register(reg) raw := []byte(`{"model":"claude-3-5-sonnet","messages":[{"role":"assistant","content":[{"type":"redacted_thinking","data":"enc_1"},{"type":"text","text":"hello"}]}]}`) out, err := reg.TranslateRequest(protocol.Anthropic, protocol.Codex, "gpt-5-codex", raw, false) if err != nil { t.Fatalf("TranslateRequest failed: %v", err) } body := string(out) if !strings.Contains(body, `"type":"reasoning"`) || !strings.Contains(body, `"encrypted_content":"enc_1"`) || !strings.Contains(body, `"type":"output_text"`) || !strings.Contains(body, `"text":"hello"`) { t.Fatalf("unexpected codex request body: %s", body) } }) t.Run("anthropic rich tool_result becomes structured openai tool content", func(t *testing.T) { reg := protocol.NewRegistry() builtin.Register(reg) raw := []byte(`{"model":"claude-3-5-sonnet","messages":[{"role":"assistant","content":[{"type":"tool_use","id":"toolu_1","name":"lookup","input":{"q":"go"}}]},{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_1","content":[{"type":"text","text":"tool ok"},{"type":"image","source":{"type":"url","url":"https://example.com/tool.png","media_type":"image/png"}},{"type":"document","source":{"type":"base64","media_type":"application/pdf","data":"cGRm"},"title":"doc.pdf"}]}]}]}`) out, err := reg.TranslateRequest(protocol.Anthropic, protocol.OpenAI, "gpt-4o", raw, false) if err != nil { t.Fatalf("TranslateRequest failed: %v", err) } body := string(out) if !strings.Contains(body, `"role":"tool"`) || !strings.Contains(body, `"type":"image_url"`) || !strings.Contains(body, `"type":"file"`) { t.Fatalf("unexpected openai request body: %s", body) } }) } func TestRegistryRequestJSONTopLevelOrderStable(t *testing.T) { reg := protocol.NewRegistry() builtin.Register(reg) cases := []struct { name string from protocol.Protocol to protocol.Protocol model string raw []byte order []string exact bool }{ { name: "anthropic to codex keeps cache-sensitive prefix stable", from: protocol.Anthropic, to: protocol.Codex, model: "gpt-5-codex", raw: []byte(`{"model":"claude-3-5-sonnet","system":[{"type":"text","text":"be careful"}],"messages":[{"role":"user","content":[{"type":"text","text":"hello"}]}],"stream":true}`), order: []string{`"model"`, `"instructions"`, `"input"`, `"stream"`}, exact: true, }, { name: "anthropic to openai top-level order stable", from: protocol.Anthropic, to: protocol.OpenAI, model: "gpt-4o", raw: []byte(`{"model":"claude-3-5-sonnet","messages":[{"role":"user","content":[{"type":"text","text":"hello"}]}],"stream":true}`), order: []string{`"model"`, `"messages"`, `"stream"`}, exact: true, }, { name: "openai to anthropic top-level order stable", from: protocol.OpenAI, to: protocol.Anthropic, model: "claude-3-5-sonnet", raw: []byte(`{"model":"gpt-4o","messages":[{"role":"user","content":"hello"}],"stream":true}`), order: []string{`"model"`, `"messages"`, `"stream"`, `"tools"`, `"max_tokens"`, `"metadata"`}, }, { name: "openai to gemini top-level order stable", from: protocol.OpenAI, to: protocol.Gemini, model: "gemini-2.5-pro", raw: []byte(`{"model":"gpt-4o","messages":[{"role":"system","content":"be careful"},{"role":"user","content":"hello"}],"stream":true}`), order: []string{`"contents"`, `"systemInstruction"`}, exact: true, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { var first string for i := 0; i < 20; i++ { out, err := reg.TranslateRequest(tc.from, tc.to, tc.model, tc.raw, true) if err != nil { t.Fatalf("TranslateRequest failed: %v", err) } body := string(out) if i == 0 { first = body assertJSONFieldOrder(t, body, tc.order...) continue } if tc.exact && body != first { t.Fatalf("request JSON changed between runs:\nfirst=%s\nrun%d=%s", first, i, body) } assertJSONFieldOrder(t, body, tc.order...) } }) } } func assertJSONFieldOrder(t *testing.T, body string, fields ...string) { t.Helper() prev := -1 for _, field := range fields { idx := strings.Index(body, field) if idx < 0 { t.Fatalf("field %s missing in %s", field, body) } if idx <= prev { t.Fatalf("field order broken at %s in %s", field, body) } prev = idx } }