package provider import ( "path/filepath" "testing" "llmsproxy/internal/config" "llmsproxy/internal/lua" ) // ★ 内核兜底鉴权必须让位给适配器已经给出的凭据头。 // // 背景:anthropic 基座(及继承它的 channels-zcode / channels-cline 等)现在在 // build_headers 里发 `x-api-key`(Anthropic 协议用它,不是 Bearer)。 // 而内核在 buildHeaders 末尾有一段兜底:`Authorization` 为空就补 // `Bearer `。若不改,同一请求会同时携带两套鉴权方案 —— // 部分 Anthropic 兼容上游对歧义鉴权直接 401,等于把刚修好的头废掉。 // // 本判据咬的是**真实产出**(p.buildHeaders 就是发请求前那条路径)。 func TestBuildHeadersSkipsBearerWhenAdapterSuppliesXAPIKey(t *testing.T) { vm := lua.NewVM(filepath.Join(t.TempDir(), "adapters")) if err := vm.Start(); err != nil { t.Fatal(err) } t.Cleanup(vm.Stop) s := src("claude-direct", "http://127.0.0.1:1", "anthropic", "claude-x") s.APIKey = "sk-ant-SECRET" p := New(s, vm) h, err := p.buildHeaders(`{"model":"claude-x"}`, "http://127.0.0.1:1/v1/messages", "") if err != nil { t.Fatalf("buildHeaders: %v", err) } if got := h.Get("X-Api-Key"); got != "sk-ant-SECRET" { t.Fatalf("★ x-api-key 应携带凭据,实得 %q", got) } if got := h.Get("Authorization"); got != "" { t.Fatalf("★ 适配器已给 x-api-key 时,内核**不得**再补 Authorization: Bearer(两套鉴权冲突)"+ ",实得 %q", got) } } // 配对判据:普通适配器(不支持 x-api-key 的那些)兜底行为必须**不变**。 // 这条防「改 anthropic 顺手把其它十几个适配器的鉴权搞没了」。 func TestBuildHeadersKeepsBearerFallbackForNonAnthropicAdapters(t *testing.T) { vm := lua.NewVM(filepath.Join(t.TempDir(), "adapters")) if err := vm.Start(); err != nil { t.Fatal(err) } t.Cleanup(vm.Stop) for _, adapter := range []string{"openai", "deepseek", "groq"} { t.Run(adapter, func(t *testing.T) { src := config.Source{ Name: "s-" + adapter, BaseURL: "http://127.0.0.1:1", APIKey: "sk-plain", Adapter: adapter, MaxConcurrent: 1, Models: []config.Model{{ID: "m", Kind: "chat"}}, } p := New(src, vm) h, err := p.buildHeaders(`{"model":"m"}`, "http://127.0.0.1:1/chat/completions", "") if err != nil { t.Fatalf("buildHeaders: %v", err) } if got := h.Get("Authorization"); got != "Bearer sk-plain" { t.Fatalf("★ %s 的兜底 Bearer 必须保持原样,实得 %q", adapter, got) } if got := h.Get("X-Api-Key"); got != "" { t.Fatalf("%s 不该凭空多出 x-api-key,实得 %q", adapter, got) } }) } }