// 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 _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") } }