first commit
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
// Node #186: end-to-end wiring test for every OpenAI-protocol built-in provider.
|
||||
//
|
||||
// For each provider reachable via --provider (US-005) this asserts three things:
|
||||
// (a) the registry spec reports Protocol "openai" and the PRD-mandated default
|
||||
// base URL,
|
||||
// (b) its primary API-key env var resolves through envAPIKey (the same path
|
||||
// auth.go uses at request time), and
|
||||
// (c) the generic OpenAI-compatible construction path used by main.go's
|
||||
// resolveNamedProvider builds a non-nil driver bound to the spec's model.
|
||||
//
|
||||
// It also pins the restored legacy key aliases (CLAUDE_API_KEY, GOOGLE_API_KEY,
|
||||
// NVIDIA_NIM_API_KEY) and the non-standard HF_TOKEN key name.
|
||||
//
|
||||
// This file is intentionally separate from providers_test.go: a sibling node
|
||||
// edits that file concurrently.
|
||||
package provider
|
||||
|
||||
import "testing"
|
||||
|
||||
// openAIWiringCase describes one OpenAI-protocol provider's expected registry
|
||||
// metadata and primary key env var.
|
||||
type openAIWiringCase struct {
|
||||
name string
|
||||
baseURL string
|
||||
primaryEnv string
|
||||
}
|
||||
|
||||
// openAIProviderCases lists every OpenAI-protocol provider that must work
|
||||
// end-to-end via --provider (US-005). Base URLs mirror the PRD's Technical
|
||||
// Considerations table.
|
||||
var openAIProviderCases = []openAIWiringCase{
|
||||
{"groq", "https://api.groq.com/openai/v1", "GROQ_API_KEY"},
|
||||
{"xai", "https://api.x.ai/v1", "XAI_API_KEY"},
|
||||
{"cerebras", "https://api.cerebras.ai/v1", "CEREBRAS_API_KEY"},
|
||||
{"mistral", "https://api.mistral.ai", "MISTRAL_API_KEY"},
|
||||
{"moonshotai", "https://api.moonshot.ai/v1", "MOONSHOT_API_KEY"},
|
||||
{"moonshotai-cn", "https://api.moonshot.cn/v1", "MOONSHOT_API_KEY"},
|
||||
{"fireworks", "https://api.fireworks.ai/inference", "FIREWORKS_API_KEY"},
|
||||
{"together", "https://api.together.ai/v1", "TOGETHER_API_KEY"},
|
||||
{"openrouter", "https://openrouter.ai/api/v1", "OPENROUTER_API_KEY"},
|
||||
{"nvidia", "https://integrate.api.nvidia.com/v1", "NVIDIA_API_KEY"},
|
||||
{"zai", "https://api.z.ai/api/coding/paas/v4", "ZAI_API_KEY"},
|
||||
{"zai-coding-cn", "https://open.bigmodel.cn/api/coding/paas/v4", "ZAI_CODING_CN_API_KEY"},
|
||||
{"kimi-coding", "https://api.kimi.com/coding", "KIMI_API_KEY"},
|
||||
{"opencode", "https://opencode.ai/zen", "OPENCODE_API_KEY"},
|
||||
{"opencode-go", "https://opencode.ai/zen/go", "OPENCODE_API_KEY"},
|
||||
{"huggingface", "https://router.huggingface.co/v1", "HF_TOKEN"},
|
||||
{"ant-ling", "https://api.ant-ling.com/v1", "ANT_LING_API_KEY"},
|
||||
{"vercel-ai-gateway", "https://ai-gateway.vercel.sh", "AI_GATEWAY_API_KEY"},
|
||||
{"xiaomi", "https://api.xiaomimimo.com/v1", "XIAOMI_API_KEY"},
|
||||
{"xiaomi-token-plan-cn", "https://token-plan-cn.xiaomimimo.com/v1", "XIAOMI_TOKEN_PLAN_CN_API_KEY"},
|
||||
{"xiaomi-token-plan-ams", "https://token-plan-ams.xiaomimimo.com/v1", "XIAOMI_TOKEN_PLAN_AMS_API_KEY"},
|
||||
{"xiaomi-token-plan-sgp", "https://token-plan-sgp.xiaomimimo.com/v1", "XIAOMI_TOKEN_PLAN_SGP_API_KEY"},
|
||||
}
|
||||
|
||||
func TestOpenAIProviderWiring(t *testing.T) {
|
||||
for _, tc := range openAIProviderCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// (a) registry spec: protocol + default base URL + primary env var.
|
||||
spec, ok := LookupProviderSpec(tc.name)
|
||||
if !ok {
|
||||
t.Fatalf("LookupProviderSpec(%q): not found in registry", tc.name)
|
||||
}
|
||||
if spec.Protocol != ProtocolOpenAI {
|
||||
t.Errorf("Protocol = %q, want %q", spec.Protocol, ProtocolOpenAI)
|
||||
}
|
||||
if spec.DefaultBaseURL != tc.baseURL {
|
||||
t.Errorf("DefaultBaseURL = %q, want %q", spec.DefaultBaseURL, tc.baseURL)
|
||||
}
|
||||
if len(spec.EnvVars) == 0 || spec.EnvVars[0] != tc.primaryEnv {
|
||||
t.Errorf("primary EnvVar = %v, want first = %q", spec.EnvVars, tc.primaryEnv)
|
||||
}
|
||||
|
||||
// (b) key resolution via the primary env var, using the same
|
||||
// envAPIKey path auth.go relies on at request time.
|
||||
t.Setenv(tc.primaryEnv, "sk-"+tc.name)
|
||||
if got := envAPIKey(tc.name); got != "sk-"+tc.name {
|
||||
t.Errorf("envAPIKey(%q) = %q, want %q", tc.name, got, "sk-"+tc.name)
|
||||
}
|
||||
|
||||
// (c) construction path equivalent to main.go's resolveNamedProvider
|
||||
// for an openai-protocol spec: build a generic OpenAI-compatible
|
||||
// driver against the spec's base URL, bound to the spec's model.
|
||||
models := []Model{{Provider: spec.Name, ID: "test-model", SupportsImages: true}}
|
||||
drv := NewOpenAICompatibleProvider(spec.DefaultBaseURL, models)
|
||||
if drv == nil {
|
||||
t.Fatalf("NewOpenAICompatibleProvider(%q) returned nil", spec.DefaultBaseURL)
|
||||
}
|
||||
got := drv.Models()
|
||||
if len(got) != 1 || got[0].Provider != spec.Name || got[0].ID != "test-model" {
|
||||
t.Errorf("driver Models() = %+v, want one model bound to provider %q", got, spec.Name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestLegacyKeyAliases pins the secondary env vars restored to the registry so
|
||||
// credentials set under older names still resolve.
|
||||
func TestLegacyKeyAliases(t *testing.T) {
|
||||
aliases := []struct {
|
||||
provider string
|
||||
primary string // primary env var (must NOT be set for the alias to be exercised)
|
||||
alias string // legacy alias env var under test
|
||||
}{
|
||||
{"anthropic", "ANTHROPIC_API_KEY", "CLAUDE_API_KEY"},
|
||||
{"google", "GEMINI_API_KEY", "GOOGLE_API_KEY"},
|
||||
{"nvidia", "NVIDIA_API_KEY", "NVIDIA_NIM_API_KEY"},
|
||||
}
|
||||
for _, a := range aliases {
|
||||
t.Run(a.provider, func(t *testing.T) {
|
||||
// Ensure the alias is listed in the registry's EnvVars.
|
||||
spec, ok := LookupProviderSpec(a.provider)
|
||||
if !ok {
|
||||
t.Fatalf("LookupProviderSpec(%q): not found", a.provider)
|
||||
}
|
||||
found := false
|
||||
for _, e := range spec.EnvVars {
|
||||
if e == a.alias {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("EnvVars %v missing legacy alias %q", spec.EnvVars, a.alias)
|
||||
}
|
||||
// Clear the primary so only the alias can satisfy resolution, then
|
||||
// assert the alias resolves.
|
||||
t.Setenv(a.primary, "")
|
||||
t.Setenv(a.alias, "legacy-"+a.provider)
|
||||
if got := envAPIKey(a.provider); got != "legacy-"+a.provider {
|
||||
t.Errorf("envAPIKey(%q) via %s = %q, want %q", a.provider, a.alias, got, "legacy-"+a.provider)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestHuggingFaceTokenEnv asserts the non-standard HF_TOKEN key name resolves
|
||||
// for huggingface (it does not follow the <PROVIDER>_API_KEY convention).
|
||||
func TestHuggingFaceTokenEnv(t *testing.T) {
|
||||
spec, ok := LookupProviderSpec("huggingface")
|
||||
if !ok {
|
||||
t.Fatal("LookupProviderSpec(\"huggingface\"): not found")
|
||||
}
|
||||
if len(spec.EnvVars) == 0 || spec.EnvVars[0] != "HF_TOKEN" {
|
||||
t.Fatalf("huggingface EnvVars = %v, want first = HF_TOKEN", spec.EnvVars)
|
||||
}
|
||||
t.Setenv("HF_TOKEN", "hf-secret")
|
||||
if got := envAPIKey("huggingface"); got != "hf-secret" {
|
||||
t.Errorf("envAPIKey(\"huggingface\") = %q, want %q", got, "hf-secret")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user