@@ -2,6 +2,8 @@ package config
@@ -10,6 +12,13 @@ import (
10 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/assert"
15+func TestMain(m *testing.M) {
16+ slog.SetDefault(slog.New(slog.NewTextHandler(io.Discard, nil)))
13func TestConfig_LoadFromReaders(t *testing.T) {
22func TestConfig_LoadFromReaders(t *testing.T) {
14 data1 := strings.NewReader(`{"providers": {"openai": {"api_key": "key1", "base_url": "https://api.openai.com/v1"}}}`)
23 data1 := strings.NewReader(`{"providers": {"openai": {"api_key": "key1", "base_url": "https://api.openai.com/v1"}}}`)
15 data2 := strings.NewReader(`{"providers": {"openai": {"api_key": "key2", "base_url": "https://api.openai.com/v2"}}}`)
24 data2 := strings.NewReader(`{"providers": {"openai": {"api_key": "key2", "base_url": "https://api.openai.com/v2"}}}`)
@@ -152,6 +161,8 @@ func TestConfig_configureProvidersWithNewProvider(t *testing.T) {
153 // We want to make sure that we keep the configured API key as a placeholder
162 // We want to make sure that we keep the configured API key as a placeholder
154 assert.Equal(t, "xyz", cfg.Providers["custom"].APIKey)
163 assert.Equal(t, "xyz", cfg.Providers["custom"].APIKey)
164+ // Make sure we set the ID correctly
165+ assert.Equal(t, "custom", cfg.Providers["custom"].ID)
155 assert.Equal(t, "https://api.someendpoint.com/v2", cfg.Providers["custom"].BaseURL)
166 assert.Equal(t, "https://api.someendpoint.com/v2", cfg.Providers["custom"].BaseURL)
156 assert.Len(t, cfg.Providers["custom"].Models, 1)
167 assert.Len(t, cfg.Providers["custom"].Models, 1)
@@ -315,3 +326,437 @@ func TestConfig_configureProvidersVertexAIMissingProject(t *testing.T) {
315 // Provider should not be configured without project
326 // Provider should not be configured without project
316 assert.Len(t, cfg.Providers, 0)
327 assert.Len(t, cfg.Providers, 0)
330+func TestConfig_configureProvidersSetProviderID(t *testing.T) {
331+ knownProviders := []provider.Provider{
334+ APIKey: "$OPENAI_API_KEY",
335+ APIEndpoint: "https://api.openai.com/v1",
336+ Models: []provider.Model{{
343+ cfg.setDefaults("/tmp")
344+ env := env.NewFromMap(map[string]string{
345+ "OPENAI_API_KEY": "test-key",
347+ resolver := NewEnvironmentVariableResolver(env)
348+ err := cfg.configureProviders(env, resolver, knownProviders)
349+ assert.NoError(t, err)
350+ assert.Len(t, cfg.Providers, 1)
352+ // Provider ID should be set
353+ assert.Equal(t, "openai", cfg.Providers["openai"].ID)
356+func TestConfig_EnabledProviders(t *testing.T) {
357+ t.Run("all providers enabled", func(t *testing.T) {
359+ Providers: map[string]ProviderConfig{
373+ enabled := cfg.EnabledProviders()
374+ assert.Len(t, enabled, 2)
377+ t.Run("some providers disabled", func(t *testing.T) {
379+ Providers: map[string]ProviderConfig{
393+ enabled := cfg.EnabledProviders()
394+ assert.Len(t, enabled, 1)
395+ assert.Equal(t, "openai", enabled[0].ID)
398+ t.Run("empty providers map", func(t *testing.T) {
400+ Providers: map[string]ProviderConfig{},
403+ enabled := cfg.EnabledProviders()
404+ assert.Len(t, enabled, 0)
408+func TestConfig_IsConfigured(t *testing.T) {
409+ t.Run("returns true when at least one provider is enabled", func(t *testing.T) {
411+ Providers: map[string]ProviderConfig{
420+ assert.True(t, cfg.IsConfigured())
423+ t.Run("returns false when no providers are configured", func(t *testing.T) {
425+ Providers: map[string]ProviderConfig{},
428+ assert.False(t, cfg.IsConfigured())
431+ t.Run("returns false when all providers are disabled", func(t *testing.T) {
433+ Providers: map[string]ProviderConfig{
447+ assert.False(t, cfg.IsConfigured())
451+func TestConfig_configureProvidersWithDisabledProvider(t *testing.T) {
452+ knownProviders := []provider.Provider{
455+ APIKey: "$OPENAI_API_KEY",
456+ APIEndpoint: "https://api.openai.com/v1",
457+ Models: []provider.Model{{
464+ Providers: map[string]ProviderConfig{
470+ cfg.setDefaults("/tmp")
472+ env := env.NewFromMap(map[string]string{
473+ "OPENAI_API_KEY": "test-key",
475+ resolver := NewEnvironmentVariableResolver(env)
476+ err := cfg.configureProviders(env, resolver, knownProviders)
477+ assert.NoError(t, err)
479+ // Provider should be removed from config when disabled
480+ assert.Len(t, cfg.Providers, 0)
481+ _, exists := cfg.Providers["openai"]
482+ assert.False(t, exists)
485+func TestConfig_configureProvidersCustomProviderValidation(t *testing.T) {
486+ t.Run("custom provider with missing API key is removed", func(t *testing.T) {
488+ Providers: map[string]ProviderConfig{
490+ BaseURL: "https://api.custom.com/v1",
491+ Models: []provider.Model{{
497+ cfg.setDefaults("/tmp")
499+ env := env.NewFromMap(map[string]string{})
500+ resolver := NewEnvironmentVariableResolver(env)
501+ err := cfg.configureProviders(env, resolver, []provider.Provider{})
502+ assert.NoError(t, err)
504+ assert.Len(t, cfg.Providers, 0)
505+ _, exists := cfg.Providers["custom"]
506+ assert.False(t, exists)
509+ t.Run("custom provider with missing BaseURL is removed", func(t *testing.T) {
511+ Providers: map[string]ProviderConfig{
514+ Models: []provider.Model{{
520+ cfg.setDefaults("/tmp")
522+ env := env.NewFromMap(map[string]string{})
523+ resolver := NewEnvironmentVariableResolver(env)
524+ err := cfg.configureProviders(env, resolver, []provider.Provider{})
525+ assert.NoError(t, err)
527+ assert.Len(t, cfg.Providers, 0)
528+ _, exists := cfg.Providers["custom"]
529+ assert.False(t, exists)
532+ t.Run("custom provider with no models is removed", func(t *testing.T) {
534+ Providers: map[string]ProviderConfig{
537+ BaseURL: "https://api.custom.com/v1",
538+ Models: []provider.Model{},
542+ cfg.setDefaults("/tmp")
544+ env := env.NewFromMap(map[string]string{})
545+ resolver := NewEnvironmentVariableResolver(env)
546+ err := cfg.configureProviders(env, resolver, []provider.Provider{})
547+ assert.NoError(t, err)
549+ assert.Len(t, cfg.Providers, 0)
550+ _, exists := cfg.Providers["custom"]
551+ assert.False(t, exists)
554+ t.Run("custom provider with unsupported type is removed", func(t *testing.T) {
556+ Providers: map[string]ProviderConfig{
559+ BaseURL: "https://api.custom.com/v1",
560+ Type: "unsupported",
561+ Models: []provider.Model{{
567+ cfg.setDefaults("/tmp")
569+ env := env.NewFromMap(map[string]string{})
570+ resolver := NewEnvironmentVariableResolver(env)
571+ err := cfg.configureProviders(env, resolver, []provider.Provider{})
572+ assert.NoError(t, err)
574+ assert.Len(t, cfg.Providers, 0)
575+ _, exists := cfg.Providers["custom"]
576+ assert.False(t, exists)
579+ t.Run("valid custom provider is kept and ID is set", func(t *testing.T) {
581+ Providers: map[string]ProviderConfig{
584+ BaseURL: "https://api.custom.com/v1",
585+ Type: provider.TypeOpenAI,
586+ Models: []provider.Model{{
592+ cfg.setDefaults("/tmp")
594+ env := env.NewFromMap(map[string]string{})
595+ resolver := NewEnvironmentVariableResolver(env)
596+ err := cfg.configureProviders(env, resolver, []provider.Provider{})
597+ assert.NoError(t, err)
599+ assert.Len(t, cfg.Providers, 1)
600+ customProvider, exists := cfg.Providers["custom"]
601+ assert.True(t, exists)
602+ assert.Equal(t, "custom", customProvider.ID)
603+ assert.Equal(t, "test-key", customProvider.APIKey)
604+ assert.Equal(t, "https://api.custom.com/v1", customProvider.BaseURL)
607+ t.Run("disabled custom provider is removed", func(t *testing.T) {
609+ Providers: map[string]ProviderConfig{
612+ BaseURL: "https://api.custom.com/v1",
613+ Type: provider.TypeOpenAI,
615+ Models: []provider.Model{{
621+ cfg.setDefaults("/tmp")
623+ env := env.NewFromMap(map[string]string{})
624+ resolver := NewEnvironmentVariableResolver(env)
625+ err := cfg.configureProviders(env, resolver, []provider.Provider{})
626+ assert.NoError(t, err)
628+ assert.Len(t, cfg.Providers, 0)
629+ _, exists := cfg.Providers["custom"]
630+ assert.False(t, exists)
634+func TestConfig_configureProvidersEnhancedCredentialValidation(t *testing.T) {
635+ t.Run("VertexAI provider removed when credentials missing with existing config", func(t *testing.T) {
636+ knownProviders := []provider.Provider{
638+ ID: provider.InferenceProviderVertexAI,
641+ Models: []provider.Model{{
648+ Providers: map[string]ProviderConfig{
650+ BaseURL: "custom-url",
654+ cfg.setDefaults("/tmp")
656+ env := env.NewFromMap(map[string]string{
657+ "GOOGLE_GENAI_USE_VERTEXAI": "false",
659+ resolver := NewEnvironmentVariableResolver(env)
660+ err := cfg.configureProviders(env, resolver, knownProviders)
661+ assert.NoError(t, err)
663+ assert.Len(t, cfg.Providers, 0)
664+ _, exists := cfg.Providers["vertexai"]
665+ assert.False(t, exists)
668+ t.Run("Bedrock provider removed when AWS credentials missing with existing config", func(t *testing.T) {
669+ knownProviders := []provider.Provider{
671+ ID: provider.InferenceProviderBedrock,
674+ Models: []provider.Model{{
675+ ID: "anthropic.claude-sonnet-4-20250514-v1:0",
681+ Providers: map[string]ProviderConfig{
683+ BaseURL: "custom-url",
687+ cfg.setDefaults("/tmp")
689+ env := env.NewFromMap(map[string]string{})
690+ resolver := NewEnvironmentVariableResolver(env)
691+ err := cfg.configureProviders(env, resolver, knownProviders)
692+ assert.NoError(t, err)
694+ assert.Len(t, cfg.Providers, 0)
695+ _, exists := cfg.Providers["bedrock"]
696+ assert.False(t, exists)
699+ t.Run("provider removed when API key missing with existing config", func(t *testing.T) {
700+ knownProviders := []provider.Provider{
703+ APIKey: "$MISSING_API_KEY",
704+ APIEndpoint: "https://api.openai.com/v1",
705+ Models: []provider.Model{{
712+ Providers: map[string]ProviderConfig{
714+ BaseURL: "custom-url",
718+ cfg.setDefaults("/tmp")
720+ env := env.NewFromMap(map[string]string{})
721+ resolver := NewEnvironmentVariableResolver(env)
722+ err := cfg.configureProviders(env, resolver, knownProviders)
723+ assert.NoError(t, err)
725+ assert.Len(t, cfg.Providers, 0)
726+ _, exists := cfg.Providers["openai"]
727+ assert.False(t, exists)
730+ t.Run("known provider should still be added if the endpoint is missing the client will use default endpoints", func(t *testing.T) {
731+ knownProviders := []provider.Provider{
734+ APIKey: "$OPENAI_API_KEY",
735+ APIEndpoint: "$MISSING_ENDPOINT",
736+ Models: []provider.Model{{
743+ Providers: map[string]ProviderConfig{
749+ cfg.setDefaults("/tmp")
751+ env := env.NewFromMap(map[string]string{
752+ "OPENAI_API_KEY": "test-key",
754+ resolver := NewEnvironmentVariableResolver(env)
755+ err := cfg.configureProviders(env, resolver, knownProviders)
756+ assert.NoError(t, err)
758+ assert.Len(t, cfg.Providers, 1)
759+ _, exists := cfg.Providers["openai"]
760+ assert.True(t, exists)