package config import ( "strings" "testing" "time" "github.com/stretchr/testify/assert" ) // ============================================================================= // Test: Config Domain Split — Type Definitions & Struct Integrity // 验证拆分后的 15 个域文件中的所有类型定义正确、字段完整 // ============================================================================= func TestConfigStructIntegrity(t *testing.T) { t.Parallel() cfg := Config{} assert.IsType(t, ServerConfig{}, cfg.Server) assert.IsType(t, H2CConfig{}, cfg.Server.H2C) assert.IsType(t, CORSConfig{}, CORSConfig{}) assert.IsType(t, ConcurrencyConfig{}, ConcurrencyConfig{}) assert.IsType(t, SecurityConfig{}, cfg.Security) assert.IsType(t, URLAllowlistConfig{}, cfg.Security.URLAllowlist) assert.IsType(t, ResponseHeaderConfig{}, cfg.Security.ResponseHeaders) assert.IsType(t, CSPConfig{}, cfg.Security.CSP) assert.IsType(t, ProxyFallbackConfig{}, cfg.Security.ProxyFallback) assert.IsType(t, ProxyProbeConfig{}, cfg.Security.ProxyProbe) assert.IsType(t, DatabaseConfig{}, cfg.Database) assert.IsType(t, RedisConfig{}, cfg.Redis) assert.IsType(t, JWTConfig{}, cfg.JWT) assert.IsType(t, TotpConfig{}, cfg.Totp) assert.IsType(t, TurnstileConfig{}, cfg.Turnstile) assert.IsType(t, DefaultConfig{}, cfg.Default) assert.IsType(t, RateLimitConfig{}, cfg.RateLimit) assert.IsType(t, BillingConfig{}, cfg.Billing) assert.IsType(t, PricingConfig{}, cfg.Pricing) assert.IsType(t, GatewayConfig{}, cfg.Gateway) assert.IsType(t, GatewayOpenAIWSConfig{}, cfg.Gateway.OpenAIWS) assert.IsType(t, GatewayUsageRecordConfig{}, cfg.Gateway.UsageRecord) assert.IsType(t, TLSFingerprintConfig{}, cfg.Gateway.TLSFingerprint) assert.IsType(t, UserMessageQueueConfig{}, cfg.Gateway.UserMessageQueue) assert.IsType(t, GatewaySchedulingConfig{}, cfg.Gateway.Scheduling) assert.IsType(t, GatewayOpenAIWSSchedulerScoreWeights{}, cfg.Gateway.OpenAIWS.SchedulerScoreWeights) assert.IsType(t, SoraConfig{}, cfg.Sora) assert.IsType(t, GeminiConfig{}, cfg.Gemini) assert.IsType(t, UpdateConfig{}, cfg.Update) assert.IsType(t, IdempotencyConfig{}, cfg.Idempotency) assert.IsType(t, LinuxDoConnectConfig{}, cfg.LinuxDo) assert.IsType(t, OIDCConnectConfig{}, cfg.OIDC) assert.IsType(t, OpsConfig{}, cfg.Ops) assert.IsType(t, LogConfig{}, cfg.Log) assert.IsType(t, DashboardCacheConfig{}, cfg.Dashboard) assert.IsType(t, DashboardAggregationConfig{}, cfg.DashboardAgg) assert.IsType(t, UsageCleanupConfig{}, cfg.UsageCleanup) assert.IsType(t, ConcurrencyConfig{}, cfg.Concurrency) assert.IsType(t, TokenRefreshConfig{}, cfg.TokenRefresh) assert.IsType(t, APIKeyAuthCacheConfig{}, cfg.APIKeyAuth) assert.IsType(t, SubscriptionCacheConfig{}, cfg.SubscriptionCache) } func TestServerConfigDefaults(t *testing.T) { s := ServerConfig{} if s.Host != "" { t.Error("Host should default to empty") } if s.Port != 0 { t.Error("Port should default to 0") } } func TestH2CConfigFields(t *testing.T) { h := H2CConfig{ Enabled: true, MaxConcurrentStreams: 100, IdleTimeout: 75, MaxReadFrameSize: 1 << 20, MaxUploadBufferPerConnection: 2 << 20, } if !h.Enabled { t.Error("Enabled should be true") } if h.MaxConcurrentStreams != 100 { t.Error("MaxConcurrentStreams mismatch") } } func TestSecurityConfigFields(t *testing.T) { s := SecurityConfig{} if s.URLAllowlist.Enabled { t.Error("URLAllowlist.Enabled should be false") } if s.ProxyFallback.AllowDirectOnError { t.Error("AllowDirectOnError should be false") } if s.ProxyProbe.InsecureSkipVerify { t.Error("InsecureSkipVerify should be false") } } func TestDatabaseConfig_DSN(t *testing.T) { tests := []struct { name string cfg DatabaseConfig check func(DatabaseConfig) string contains []string exclude []string }{ {"no password", DatabaseConfig{Host: "localhost", Port: 5432, User: "u", DBName: "db", SSLMode: "s"}, func(c DatabaseConfig) string { return c.DSN() }, []string{"host=localhost", "port=5432"}, []string{"password="}}, {"with password", DatabaseConfig{Host: "h", Port: 1, User: "u", Password: "p", DBName: "d", SSLMode: "s"}, func(c DatabaseConfig) string { return c.DSN() }, []string{"password=p"}, nil}, {"tz default", DatabaseConfig{Host: "h", Port: 1, User: "u", DBName: "d", SSLMode: "s"}, func(c DatabaseConfig) string { return c.DSNWithTimezone("") }, []string{"TimeZone=Asia/Shanghai"}, nil}, {"tz custom", DatabaseConfig{Host: "h", Port: 1, User: "u", DBName: "d", SSLMode: "s"}, func(c DatabaseConfig) string { return c.DSNWithTimezone("UTC") }, []string{"TimeZone=UTC"}, nil}, } for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { got := tc.check(tc.cfg) for _, sub := range tc.contains { assert.Contains(t, got, sub) } for _, sub := range tc.exclude { assert.NotContains(t, got, sub) } }) } } func TestRedisConfig_Address(t *testing.T) { r := RedisConfig{Host: "redis.local", Port: 6380} if r.Address() != "redis.local:6380" { t.Errorf("Address = %q", r.Address()) } } func TestJWTConfigFields(t *testing.T) { j := JWTConfig{Secret: strings.Repeat("x", 32), ExpireHour: 24, RefreshTokenExpireDays: 30} if j.RefreshTokenExpireDays != 30 { t.Error("RefreshTokenExpireDays mismatch") } } func TestTotpConfigFields(t *testing.T) { tc := TotpConfig{EncryptionKeyConfigured: true} if !tc.EncryptionKeyConfigured { t.Error("should be true") } } func TestGatewayConstants(t *testing.T) { policies := []string{UsageRecordOverflowPolicyDrop, UsageRecordOverflowPolicySample, UsageRecordOverflowPolicySync} unique := make(map[string]bool, len(policies)) for _, p := range policies { unique[p] = true } if len(unique) != len(policies) { t.Error("overflow policies must be unique") } if UMQModeSerialize == UMQModeThrottle { t.Error("modes must differ") } strategies := []string{ConnectionPoolIsolationProxy, ConnectionPoolIsolationAccount, ConnectionPoolIsolationAccountProxy} uniqueS := map[string]bool{} for _, s := range strategies { uniqueS[s] = true } if len(uniqueS) != len(strategies) { t.Error("strategies must be unique") } } func TestUserMessageQueueConfig_Methods(t *testing.T) { q := UserMessageQueueConfig{} if q.WaitTimeout() != 30*time.Second { t.Error("default WaitTimeout should be 30s") } q.WaitTimeoutMs = 5000 if q.WaitTimeout() != 5*time.Second { t.Error("custom WaitTimeout should be 5s") } q.Mode = UMQModeThrottle if q.GetEffectiveMode() != UMQModeThrottle { t.Error("mode should be throttle") } q.Mode = "" q.Enabled = true if q.GetEffectiveMode() != UMQModeSerialize { t.Error("enabled+empty → serialize") } q.Enabled = false if q.GetEffectiveMode() != "" { t.Error("disabled+empty → empty") } } func TestSoraConfigFields(t *testing.T) { s := SoraConfig{ Client: SoraClientConfig{BaseURL: "https://sora.example.com"}, Storage: SoraStorageConfig{Type: "local"}, } if s.Client.BaseURL != "https://sora.example.com" { t.Error("BaseURL mismatch") } } func TestGeminiConfigFields(t *testing.T) { g := GeminiConfig{Quota: GeminiQuotaConfig{Policy: "conservative"}} if g.Quota.Policy != "conservative" { t.Error("Policy mismatch") } } func TestOpsAndCacheConfigFields(t *testing.T) { lc := LogConfig{Level: "info"} if lc.Level != "info" { t.Error("Level mismatch") } r := DashboardAggregationRetentionConfig{UsageLogsDays: 90, UsageBillingDedupDays: 365} if r.UsageBillingDedupDays < r.UsageLogsDays { t.Error("invariant violation: dedup >= logs") } } func TestBillingAndPricingConfig(t *testing.T) { bc := BillingConfig{CircuitBreaker: CircuitBreakerConfig{Enabled: true}} if !bc.CircuitBreaker.Enabled { t.Error("should be enabled") } } func TestConstants(t *testing.T) { if RunModeStandard != "standard" { t.Error("RunModeStandard wrong") } if RunModeSimple != "simple" { t.Error("RunModeSimple wrong") } if !strings.Contains(DefaultCSPPolicy, "__CSP_NONCE__") { t.Error("CSPPolicy missing nonce placeholder") } }