package config import ( "os" "testing" ) const historicKey = "hfb-sys-2024-secret-key-32bytes!" func TestIsProductionEnv(t *testing.T) { cases := []struct { in string want bool }{ {"production", true}, {"PRODUCTION", true}, {" production ", true}, {"development", false}, {"", false}, {"staging", false}, } for _, c := range cases { if got := IsProductionEnv(c.in); got != c.want { t.Fatalf("IsProductionEnv(%q) = %v, want %v", c.in, got, c.want) } } } // TestFieldEncryptionLegacyKeyNonProductionDefaultsToHistoric 验证非生产环境未设置 legacy 时 // 回退到历史硬编码密钥(开发态零配置兼容旧密文)。 func TestFieldEncryptionLegacyKeyNonProductionDefaultsToHistoric(t *testing.T) { unsetEnv(t, "FIELD_ENCRYPTION_LEGACY_KEY") if got := fieldEncryptionLegacyKey("development"); got != historicKey { t.Fatalf("non-production default legacy = %q, want %q", got, historicKey) } } // TestFieldEncryptionLegacyKeyNonProductionExplicitOverride 验证非生产环境显式设置 legacy 时用该值。 func TestFieldEncryptionLegacyKeyNonProductionExplicitOverride(t *testing.T) { t.Setenv("FIELD_ENCRYPTION_LEGACY_KEY", "explicit-legacy-16bytes!") if got := fieldEncryptionLegacyKey("development"); got != "explicit-legacy-16bytes!" { t.Fatalf("non-production explicit legacy = %q, want explicit-legacy-16bytes!", got) } } // TestFieldEncryptionLegacyKeyProductionEmptyDisablesFallback 验证生产环境未设置/留空 legacy // 返回空(禁用回退),而不是注入历史硬编码密钥。 // 这是密钥轮换闭环的关键:删除 env 必须真正关闭旧密钥。 func TestFieldEncryptionLegacyKeyProductionEmptyDisablesFallback(t *testing.T) { t.Setenv("FIELD_ENCRYPTION_LEGACY_KEY", "") if got := fieldEncryptionLegacyKey("production"); got != "" { t.Fatalf("production empty legacy = %q, want empty (fallback disabled); injecting historic key breaks rotation closure", got) } } // TestFieldEncryptionLegacyKeyProductionExplicitValue 验证生产环境显式设置 legacy 时用该值 // (轮换期间保留旧密钥解密存量)。 func TestFieldEncryptionLegacyKeyProductionExplicitValue(t *testing.T) { t.Setenv("FIELD_ENCRYPTION_LEGACY_KEY", "prod-old-key-32bytes-0123456789") if got := fieldEncryptionLegacyKey("production"); got != "prod-old-key-32bytes-0123456789" { t.Fatalf("production explicit legacy = %q, want prod-old-key-32bytes-0123456789", got) } } // TestValidateProductionSecurityRejectsPrimaryEqualsLegacy 验证生产环境 primary 等于 legacy 时报错。 func TestValidateProductionSecurityRejectsPrimaryEqualsLegacy(t *testing.T) { t.Setenv("APP_ENV", "production") t.Setenv("JWT_SECRET", "a-very-long-random-jwt-secret-at-least-32-bytes!!") t.Setenv("PAYMENT_CONFIG_ENCRYPTION_KEY", "0123456789abcdef0123456789abcdef") t.Setenv("FIELD_ENCRYPTION_KEY", "0123456789abcdef0123456789abcdef") t.Setenv("FIELD_ENCRYPTION_LEGACY_KEY", "0123456789abcdef0123456789abcdef") cfg := Load() if err := cfg.ValidateProductionSecurity(); err == nil { t.Fatal("ValidateProductionSecurity should reject FIELD_ENCRYPTION_KEY == LEGACY_KEY") } } // TestValidateProductionSecurityRejectsInvalidLegacyKey 验证生产环境显式设置 legacy 时也校验长度。 func TestValidateProductionSecurityRejectsInvalidLegacyKey(t *testing.T) { t.Setenv("APP_ENV", "PRODUCTION") t.Setenv("JWT_SECRET", "a-very-long-random-jwt-secret-at-least-32-bytes!!") t.Setenv("PAYMENT_CONFIG_ENCRYPTION_KEY", "0123456789abcdef0123456789abcdef") t.Setenv("FIELD_ENCRYPTION_KEY", "abcdef0123456789abcdef0123456789") t.Setenv("FIELD_ENCRYPTION_LEGACY_KEY", "too-short") cfg := Load() if err := cfg.ValidateProductionSecurity(); err == nil { t.Fatal("ValidateProductionSecurity should reject invalid FIELD_ENCRYPTION_LEGACY_KEY") } } func unsetEnv(t *testing.T, key string) { t.Helper() old, ok := os.LookupEnv(key) if err := os.Unsetenv(key); err != nil { t.Fatalf("unset %s: %v", key, err) } t.Cleanup(func() { if ok { _ = os.Setenv(key, old) return } _ = os.Unsetenv(key) }) }