package config import ( "reflect" "strings" "testing" "time" ) func TestLoadConfigYAML(t *testing.T) { cfg, err := Load("../../configs/config.yaml") if err != nil { t.Fatalf("Load() error = %v", err) } if cfg.HTTPAddr != ":13100" { t.Fatalf("HTTPAddr = %q", cfg.HTTPAddr) } if cfg.MySQLDSN != "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_admin?parseTime=true&charset=utf8mb4&loc=UTC" { t.Fatalf("MySQLDSN = %q", cfg.MySQLDSN) } if cfg.UserMySQLDSN != "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_user?parseTime=true&charset=utf8mb4&loc=UTC" { t.Fatalf("UserMySQLDSN = %q", cfg.UserMySQLDSN) } if cfg.AccessTokenTTL != 12*time.Hour { t.Fatalf("AccessTokenTTL = %s", cfg.AccessTokenTTL) } if cfg.RefreshTokenTTL != 7*24*time.Hour { t.Fatalf("RefreshTokenTTL = %s", cfg.RefreshTokenTTL) } if !cfg.Migrations.AllowChecksumRepair { t.Fatalf("Migrations.AllowChecksumRepair = false, want true for local config") } if !cfg.TencentCOS.Enabled || cfg.TencentCOS.BucketName != "yumi-assets-1420526837" || cfg.TencentCOS.ObjectPrefix != "admin" { t.Fatalf("TencentCOS = %#v", cfg.TencentCOS) } if !cfg.FinanceNotifications.DingTalk.Enabled { t.Fatalf("finance dingtalk notification should be enabled by default") } if cfg.FinanceNotifications.DingTalk.WebhookURL == "" || cfg.FinanceNotifications.DingTalk.Secret == "" { t.Fatalf("finance dingtalk notification config should be populated: %#v", cfg.FinanceNotifications.DingTalk) } } func TestLoadEmptyPathUsesDefault(t *testing.T) { cfg, err := Load("") if err != nil { t.Fatalf("Load() error = %v", err) } if !reflect.DeepEqual(cfg, Default()) { t.Fatalf("Load(\"\") = %#v, want default", cfg) } } func TestFinanceDingTalkEnvOverride(t *testing.T) { t.Setenv("HYAPP_ADMIN_FINANCE_DINGTALK_WEBHOOK_URL", "https://oapi.dingtalk.com/robot/send?access_token=test") t.Setenv("HYAPP_ADMIN_FINANCE_DINGTALK_SECRET", "SEC_TEST") t.Setenv("HYAPP_ADMIN_FINANCE_DINGTALK_AT_MOBILES", "13800138000, 13900139000") t.Setenv("HYAPP_ADMIN_FINANCE_DINGTALK_AT_ALL", "false") cfg := Default() cfg.Normalize() if !cfg.FinanceNotifications.DingTalk.Enabled { t.Fatal("DingTalk should be enabled when webhook env is provided") } if cfg.FinanceNotifications.DingTalk.Secret != "SEC_TEST" { t.Fatalf("secret mismatch: %q", cfg.FinanceNotifications.DingTalk.Secret) } if len(cfg.FinanceNotifications.DingTalk.AtMobiles) != 2 { t.Fatalf("at mobiles mismatch: %#v", cfg.FinanceNotifications.DingTalk.AtMobiles) } } func TestAslanMongoEnvOverride(t *testing.T) { t.Setenv("HYAPP_ADMIN_ASLAN_MONGO_URI", "mongodb://mongouser:secret@example:27017/test?authSource=admin") t.Setenv("HYAPP_ADMIN_ASLAN_DASHBOARD_MONGO_DATABASE", "test_dashboard") t.Setenv("HYAPP_ADMIN_ASLAN_DASHBOARD_LEGACY_MYSQL_DSN", "likei:secret@tcp(example:3306)/likei?parseTime=true&loc=Asia%2FShanghai") t.Setenv("HYAPP_ADMIN_ASLAN_DASHBOARD_LEGACY_WALLET_DATABASE", "test_wallet") t.Setenv("HYAPP_ADMIN_ASLAN_MONEY_REGION_MONGO_DATABASE", "tarab_region") cfg := Default() cfg.Normalize() var dashboardSource DashboardExternalSourceConfig for _, source := range cfg.DashboardExternalSources { if source.AppCode == "aslan" { dashboardSource = source break } } if dashboardSource.MongoURI != "mongodb://mongouser:secret@example:27017/test?authSource=admin" { t.Fatalf("dashboard mongo uri mismatch: %q", dashboardSource.MongoURI) } if dashboardSource.MongoDatabase != "test_dashboard" { t.Fatalf("dashboard mongo database mismatch: %q", dashboardSource.MongoDatabase) } if dashboardSource.LegacyMySQLDSN != "likei:secret@tcp(example:3306)/likei?parseTime=true&loc=Asia%2FShanghai" { t.Fatalf("dashboard legacy mysql dsn mismatch: %q", dashboardSource.LegacyMySQLDSN) } if dashboardSource.LegacyWalletDatabase != "test_wallet" { t.Fatalf("dashboard legacy wallet database mismatch: %q", dashboardSource.LegacyWalletDatabase) } var regionSource MoneyRegionSourceConfig for _, source := range cfg.MoneyRegionSources { if source.AppCode == "aslan" { regionSource = source break } } if regionSource.MongoURI != "mongodb://mongouser:secret@example:27017/test?authSource=admin" { t.Fatalf("region mongo uri mismatch: %q", regionSource.MongoURI) } if regionSource.MongoDatabase != "tarab_region" { t.Fatalf("region mongo database mismatch: %q", regionSource.MongoDatabase) } } func TestAslanMongoLegacyMySQLFallbackEnvOverride(t *testing.T) { t.Setenv("HYAPP_ADMIN_ASLAN_LEGACY_MYSQL_DSN", "likei:secret@tcp(example:3306)/likei?parseTime=true&loc=Asia%2FShanghai") t.Setenv("HYAPP_ADMIN_ASLAN_LEGACY_WALLET_DATABASE", "atyou_wallet") cfg := Default() cfg.Normalize() for _, source := range cfg.DashboardExternalSources { if source.AppCode != "aslan" { continue } if source.LegacyMySQLDSN != "likei:secret@tcp(example:3306)/likei?parseTime=true&loc=Asia%2FShanghai" { t.Fatalf("dashboard legacy mysql fallback dsn mismatch: %q", source.LegacyMySQLDSN) } if source.LegacyWalletDatabase != "atyou_wallet" { t.Fatalf("dashboard legacy wallet database fallback mismatch: %q", source.LegacyWalletDatabase) } return } t.Fatal("aslan dashboard source not found") } func TestTencentExampleRequiresRuntimeLegacyMySQLDSN(t *testing.T) { t.Setenv("HYAPP_ADMIN_ASLAN_DASHBOARD_LEGACY_MYSQL_DSN", "") t.Setenv("HYAPP_ADMIN_ASLAN_LEGACY_MYSQL_DSN", "") _, err := Load("../../configs/config.tencent.example.yaml") if err == nil || !strings.Contains(err.Error(), "legacy_mysql_dsn must be injected by environment") { t.Fatalf("Load() error = %v, want legacy mysql placeholder rejection", err) } t.Setenv("HYAPP_ADMIN_ASLAN_DASHBOARD_LEGACY_MYSQL_DSN", "aslan:runtime-secret@tcp(example:3306)/atyou?parseTime=true&loc=Asia%2FShanghai") cfg, err := Load("../../configs/config.tencent.example.yaml") if err != nil { t.Fatalf("Load() with runtime legacy dsn error = %v", err) } for _, source := range cfg.DashboardExternalSources { if source.AppCode != "aslan" { continue } if source.LegacyMySQLDSN != "aslan:runtime-secret@tcp(example:3306)/atyou?parseTime=true&loc=Asia%2FShanghai" { t.Fatalf("dashboard legacy mysql dsn mismatch: %q", source.LegacyMySQLDSN) } if source.LegacyWalletDatabase != "atyou_wallet" { t.Fatalf("dashboard legacy wallet database mismatch: %q", source.LegacyWalletDatabase) } return } t.Fatal("aslan dashboard source not found") } func TestAslanMongoLegacyWalletDatabaseFollowsMongoDatabaseEnv(t *testing.T) { t.Setenv("HYAPP_ADMIN_ASLAN_DASHBOARD_MONGO_DATABASE", "atyou") cfg := Default() cfg.Normalize() for _, source := range cfg.DashboardExternalSources { if source.AppCode != "aslan" { continue } if source.MongoDatabase != "atyou" { t.Fatalf("dashboard mongo database mismatch: %q", source.MongoDatabase) } if source.LegacyWalletDatabase != "atyou_wallet" { t.Fatalf("dashboard legacy wallet database should follow mongo database, got %q", source.LegacyWalletDatabase) } return } t.Fatal("aslan dashboard source not found") } func TestAslanMongoRequiresLegacyMySQLDSNWhenEnabled(t *testing.T) { cfg := Default() for index := range cfg.DashboardExternalSources { source := &cfg.DashboardExternalSources[index] if source.AppCode != "aslan" { continue } source.Enabled = true source.MongoURI = "mongodb://example:27017/test" source.MongoDatabase = "atyou" source.LegacyMySQLDSN = "" break } cfg.Normalize() err := cfg.Validate() if err == nil || !strings.Contains(err.Error(), "legacy_mysql_dsn is required") { t.Fatalf("Validate() error = %v, want legacy mysql requirement", err) } } func TestAslanMongoRejectsPlaceholderLegacyMySQLDSN(t *testing.T) { cfg := Default() for index := range cfg.DashboardExternalSources { source := &cfg.DashboardExternalSources[index] if source.AppCode != "aslan" { continue } source.Enabled = true source.MongoURI = "mongodb://example:27017/test" source.MongoDatabase = "atyou" source.LegacyMySQLDSN = "aslan:REPLACE_ME@tcp(example:3306)/atyou?parseTime=true&loc=Asia%2FShanghai" source.LegacyWalletDatabase = "atyou_wallet" break } cfg.Normalize() err := cfg.Validate() if err == nil || !strings.Contains(err.Error(), "not left as a placeholder") { t.Fatalf("Validate() error = %v, want placeholder rejection", err) } }