package providers import ( "strings" "testing" "github.com/sipeed/picoclaw/pkg/config" ) func TestResolveProviderSelection(t *testing.T) { tests := []struct { name string setup func(*config.Config) wantType providerType wantAPIBase string wantProxy string wantErrSubstr string }{ { name: "explicit claude-cli provider routes to cli provider type", setup: func(cfg *config.Config) { cfg.Agents.Defaults.Provider = "claude-cli" cfg.Agents.Defaults.Workspace = "/tmp/ws" }, wantType: providerTypeClaudeCLI, }, { name: "explicit copilot provider routes to github copilot type", setup: func(cfg *config.Config) { cfg.Agents.Defaults.Provider = "copilot" }, wantType: providerTypeGitHubCopilot, wantAPIBase: "localhost:4321", }, { name: "openrouter model uses openrouter defaults", setup: func(cfg *config.Config) { cfg.Agents.Defaults.Model = "openrouter/auto" cfg.Providers.OpenRouter.APIKey = "sk-or-test" }, wantType: providerTypeHTTPCompat, wantAPIBase: "https://openrouter.ai/api/v1", }, { name: "anthropic oauth routes to claude auth provider", setup: func(cfg *config.Config) { cfg.Agents.Defaults.Model = "claude-sonnet-4-5-20250929" cfg.Providers.Anthropic.AuthMethod = "oauth" }, wantType: providerTypeClaudeAuth, }, { name: "openai oauth routes to codex auth provider", setup: func(cfg *config.Config) { cfg.Agents.Defaults.Model = "gpt-4o" cfg.Providers.OpenAI.AuthMethod = "oauth" }, wantType: providerTypeCodexAuth, }, { name: "zhipu model uses zhipu base default", setup: func(cfg *config.Config) { cfg.Agents.Defaults.Model = "glm-4.7" cfg.Providers.Zhipu.APIKey = "zhipu-key" }, wantType: providerTypeHTTPCompat, wantAPIBase: "https://open.bigmodel.cn/api/paas/v4", }, { name: "groq model uses groq base default", setup: func(cfg *config.Config) { cfg.Agents.Defaults.Model = "groq/llama-3.3-70b" cfg.Providers.Groq.APIKey = "gsk-key" }, wantType: providerTypeHTTPCompat, wantAPIBase: "https://api.groq.com/openai/v1", }, { name: "moonshot model keeps proxy and default base", setup: func(cfg *config.Config) { cfg.Agents.Defaults.Model = "moonshot/kimi-k2.5" cfg.Providers.Moonshot.APIKey = "moonshot-key" cfg.Providers.Moonshot.Proxy = "http://127.0.0.1:7890" }, wantType: providerTypeHTTPCompat, wantAPIBase: "https://api.moonshot.cn/v1", wantProxy: "http://127.0.0.1:7890", }, { name: "missing keys returns model config error", setup: func(cfg *config.Config) { cfg.Agents.Defaults.Model = "custom-model" }, wantErrSubstr: "no API key configured for model", }, { name: "openrouter prefix without key returns provider key error", setup: func(cfg *config.Config) { cfg.Agents.Defaults.Model = "openrouter/auto" }, wantErrSubstr: "no API key configured for provider", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cfg := config.DefaultConfig() tt.setup(cfg) got, err := resolveProviderSelection(cfg) if tt.wantErrSubstr != "" { if err == nil { t.Fatalf("expected error containing %q, got nil", tt.wantErrSubstr) } if !strings.Contains(err.Error(), tt.wantErrSubstr) { t.Fatalf("error = %q, want substring %q", err.Error(), tt.wantErrSubstr) } return } if err != nil { t.Fatalf("resolveProviderSelection() error = %v", err) } if got.providerType != tt.wantType { t.Fatalf("providerType = %v, want %v", got.providerType, tt.wantType) } if tt.wantAPIBase != "" && got.apiBase != tt.wantAPIBase { t.Fatalf("apiBase = %q, want %q", got.apiBase, tt.wantAPIBase) } if tt.wantProxy != "" && got.proxy != tt.wantProxy { t.Fatalf("proxy = %q, want %q", got.proxy, tt.wantProxy) } }) } } func TestCreateProviderReturnsHTTPProviderForOpenRouter(t *testing.T) { cfg := config.DefaultConfig() cfg.Agents.Defaults.Model = "openrouter/auto" cfg.Providers.OpenRouter.APIKey = "sk-or-test" provider, err := CreateProvider(cfg) if err != nil { t.Fatalf("CreateProvider() error = %v", err) } if _, ok := provider.(*HTTPProvider); !ok { t.Fatalf("provider type = %T, want *HTTPProvider", provider) } }