package config_test import ( "testing" "github.com/router-for-me/CLIProxyAPI/v8/internal/config" ) // TestPerEntryConnectionPoolAccepted pins the per-credential opt-in shape: the // global pool stays off while a single api-keys entry turns pooling on for itself. func TestPerEntryConnectionPoolAccepted(t *testing.T) { raw := []byte(`config-version: 8 api-keys: codex: - name: ollama base-url: "https://ollama.com/v1" proxy-url: "direct" connection-pool: enabled: true idle-conn-timeout: "300s" max-idle-conns-per-host: 2 keys: - api-key: "ollama-key" `) if err := config.ValidateV8Config(raw); err != nil { t.Fatalf("ValidateV8Config: %v", err) } cfg, err := config.ParseConfigBytes(raw) if err != nil { t.Fatalf("ParseConfigBytes: %v", err) } if len(cfg.CodexKey) != 1 || cfg.CodexKey[0].ConnectionPool == nil { t.Fatalf("per-entry connection-pool not parsed: %+v", cfg.CodexKey) } pool := cfg.CodexKey[0].ConnectionPool if pool.Enabled == nil || !*pool.Enabled { t.Fatalf("enabled not parsed: %+v", pool) } if pool.IdleConnTimeout != "300s" { t.Fatalf("idle-conn-timeout = %q, want 300s", pool.IdleConnTimeout) } if pool.MaxIdleConnsPerHost == nil || *pool.MaxIdleConnsPerHost != 2 { t.Fatalf("max-idle-conns-per-host not parsed: %+v", pool) } if errLegacy := config.ValidateV8Config([]byte("config-version: 8\nrequests:\n proxy-connection-pool:\n enabled: true\n")); errLegacy == nil { t.Fatal("a global pooling switch must not exist") } } // TestPerEntryConnectionPoolRejectsUnknownKey keeps KnownFields strict inside the // new nested block. func TestPerEntryConnectionPoolRejectsUnknownKey(t *testing.T) { raw := []byte(`config-version: 8 api-keys: codex: - name: k keys: - api-key: "k" connection-pool: enabled: true idle-timeout: "30s" `) if err := config.ValidateV8Config(raw); err == nil { t.Fatal("unknown nested key must be rejected") } } // TestEmptyConnectionPoolBlockStaysOptIn pins that a block with no fields is still an // opt-in (it asks for the defaults), including when it stands alone. A nil result here // silently disabled pooling for `connection-pool: {}`. func TestEmptyConnectionPoolBlockStaysOptIn(t *testing.T) { raw := []byte(`config-version: 8 api-keys: codex: - name: empty base-url: "https://ollama.com/v1" connection-pool: {} keys: - api-key: "empty-group" - api-key: "empty-key" connection-pool: {} `) cfg, err := config.ParseConfigBytes(raw) if err != nil { t.Fatalf("ParseConfigBytes: %v", err) } if len(cfg.CodexKey) != 2 { t.Fatalf("codex keys = %d, want 2", len(cfg.CodexKey)) } for _, key := range cfg.CodexKey { pool := key.ConnectionPool if pool == nil { t.Fatalf("key %q lost its empty connection-pool block", key.APIKey) } if pool.Enabled != nil || pool.IdleConnTimeout != "" || pool.MaxIdleConnsPerHost != nil { t.Fatalf("key %q empty block = %+v, want unset fields", key.APIKey, pool) } } } // TestPerKeyConnectionPoolOverridesGroup pins per-key control inside one provider // group: keys may switch pooling on or off independently, and a partial override keeps // inheriting the group values it does not mention. func TestPerKeyConnectionPoolOverridesGroup(t *testing.T) { raw := []byte(`config-version: 8 api-keys: codex: - name: ollama base-url: "https://ollama.com/v1" connection-pool: enabled: true idle-conn-timeout: "300s" max-idle-conns-per-host: 2 keys: - api-key: "inherits-group" - api-key: "opts-out" connection-pool: enabled: false - api-key: "partial" connection-pool: idle-conn-timeout: "60s" - api-key: "null-subkey" connection-pool: idle-conn-timeout: null max-idle-conns-per-host: 4 - api-key: "empty-block" connection-pool: {} `) cfg, err := config.ParseConfigBytes(raw) if err != nil { t.Fatalf("ParseConfigBytes: %v", err) } byKey := map[string]*config.ProxyConnectionPoolConfig{} for i := range cfg.CodexKey { byKey[cfg.CodexKey[i].APIKey] = cfg.CodexKey[i].ConnectionPool } inherits := byKey["inherits-group"] if inherits == nil || inherits.Enabled == nil || !*inherits.Enabled || inherits.IdleConnTimeout != "300s" { t.Fatalf("key without an override must inherit the group block, got %+v", inherits) } optedOut := byKey["opts-out"] if optedOut == nil || optedOut.Enabled == nil || *optedOut.Enabled { t.Fatalf("per-key opt-out not honoured, got %+v", optedOut) } partial := byKey["partial"] if partial == nil || partial.Enabled == nil || !*partial.Enabled { t.Fatalf("partial override must keep the group's enabled value, got %+v", partial) } if partial.IdleConnTimeout != "60s" { t.Fatalf("partial override idle = %q, want 60s", partial.IdleConnTimeout) } if partial.MaxIdleConnsPerHost == nil || *partial.MaxIdleConnsPerHost != 2 { t.Fatalf("partial override must inherit max-idle-conns-per-host, got %+v", partial) } // An empty per-key block only overrides nothing, so it inherits the whole group block. empty := byKey["empty-block"] if empty == nil || empty.Enabled == nil || !*empty.Enabled || empty.IdleConnTimeout != "300s" { t.Fatalf("empty per-key block must inherit the group block, got %+v", empty) } nullSubkey := byKey["null-subkey"] if nullSubkey == nil || nullSubkey.Enabled == nil || !*nullSubkey.Enabled { t.Fatalf("null-subkey override must inherit group enabled, got %+v", nullSubkey) } if nullSubkey.IdleConnTimeout != "300s" { t.Fatalf("null sub-key must inherit group idle-conn-timeout 300s, got %q", nullSubkey.IdleConnTimeout) } if nullSubkey.MaxIdleConnsPerHost == nil || *nullSubkey.MaxIdleConnsPerHost != 4 { t.Fatalf("null-subkey override max-idle-conns-per-host = %v, want 4", nullSubkey.MaxIdleConnsPerHost) } } func TestCredentialDeduplicationPreservesDistinctConnectionPools(t *testing.T) { raw := []byte(`gemini-api-key: - api-key: "shared-gemini-key" base-url: "https://example.invalid" connection-pool: enabled: true max-idle-conns-per-host: 2 - api-key: "shared-gemini-key" base-url: "https://example.invalid" connection-pool: enabled: true max-idle-conns-per-host: 5 - api-key: "shared-gemini-key" base-url: "https://example.invalid" connection-pool: enabled: true max-idle-conns-per-host: 5 vertex-api-key: - api-key: "shared-vertex-key" base-url: "https://vertex.example.invalid" connection-pool: enabled: true max-idle-conns-per-host: 2 - api-key: "shared-vertex-key" base-url: "https://vertex.example.invalid" connection-pool: enabled: true max-idle-conns-per-host: 6 - api-key: "shared-vertex-key" base-url: "https://vertex.example.invalid" connection-pool: enabled: true max-idle-conns-per-host: 6 `) cfg, err := config.ParseConfigBytes(raw) if err != nil { t.Fatalf("ParseConfigBytes: %v", err) } if len(cfg.GeminiKey) != 2 { t.Fatalf("len(GeminiKey) = %d, want 2", len(cfg.GeminiKey)) } if cfg.GeminiKey[0].ConnectionPool == nil || cfg.GeminiKey[0].ConnectionPool.MaxIdleConnsPerHost == nil || *cfg.GeminiKey[0].ConnectionPool.MaxIdleConnsPerHost != 2 { t.Fatalf("GeminiKey[0].ConnectionPool = %+v, want max-idle-conns-per-host=2", cfg.GeminiKey[0].ConnectionPool) } if cfg.GeminiKey[1].ConnectionPool == nil || cfg.GeminiKey[1].ConnectionPool.MaxIdleConnsPerHost == nil || *cfg.GeminiKey[1].ConnectionPool.MaxIdleConnsPerHost != 5 { t.Fatalf("GeminiKey[1].ConnectionPool = %+v, want max-idle-conns-per-host=5", cfg.GeminiKey[1].ConnectionPool) } if len(cfg.VertexCompatAPIKey) != 2 { t.Fatalf("len(VertexCompatAPIKey) = %d, want 2", len(cfg.VertexCompatAPIKey)) } if cfg.VertexCompatAPIKey[0].ConnectionPool == nil || cfg.VertexCompatAPIKey[0].ConnectionPool.MaxIdleConnsPerHost == nil || *cfg.VertexCompatAPIKey[0].ConnectionPool.MaxIdleConnsPerHost != 2 { t.Fatalf("VertexCompatAPIKey[0].ConnectionPool = %+v, want max-idle-conns-per-host=2", cfg.VertexCompatAPIKey[0].ConnectionPool) } if cfg.VertexCompatAPIKey[1].ConnectionPool == nil || cfg.VertexCompatAPIKey[1].ConnectionPool.MaxIdleConnsPerHost == nil || *cfg.VertexCompatAPIKey[1].ConnectionPool.MaxIdleConnsPerHost != 6 { t.Fatalf("VertexCompatAPIKey[1].ConnectionPool = %+v, want max-idle-conns-per-host=6", cfg.VertexCompatAPIKey[1].ConnectionPool) } }