294 lines
9 KiB
Go
294 lines
9 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestLoadDefaults(t *testing.T) {
|
|
// Clear any environment variables that might interfere
|
|
envVars := []string{
|
|
"APP_NAME", "BACKEND_PORT", "DATABASE_URL",
|
|
"MERCADOPAGO_BASE_URL", "MARKETPLACE_COMMISSION", "JWT_SECRET", "JWT_EXPIRES_IN",
|
|
"PASSWORD_PEPPER", "CORS_ORIGINS",
|
|
}
|
|
origEnvs := make(map[string]string)
|
|
for _, key := range envVars {
|
|
origEnvs[key] = os.Getenv(key)
|
|
os.Unsetenv(key)
|
|
}
|
|
defer func() {
|
|
for key, val := range origEnvs {
|
|
if val != "" {
|
|
os.Setenv(key, val)
|
|
}
|
|
}
|
|
}()
|
|
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("failed to load config: %v", err)
|
|
}
|
|
|
|
if cfg.AppName != "saveinmed-performance-core" {
|
|
t.Errorf("expected AppName 'saveinmed-performance-core', got '%s'", cfg.AppName)
|
|
}
|
|
if cfg.Port != "8214" {
|
|
t.Errorf("expected Port '8214', got '%s'", cfg.Port)
|
|
}
|
|
if cfg.JWTSecret != "dev-secret" {
|
|
t.Errorf("expected JWTSecret 'dev-secret', got '%s'", cfg.JWTSecret)
|
|
}
|
|
if cfg.JWTExpiresIn != 24*time.Hour {
|
|
t.Errorf("expected JWTExpiresIn 24h, got %v", cfg.JWTExpiresIn)
|
|
}
|
|
if cfg.MarketplaceCommission != 2.5 {
|
|
t.Errorf("expected MarketplaceCommission 2.5, got %f", cfg.MarketplaceCommission)
|
|
}
|
|
if len(cfg.CORSOrigins) != 1 || cfg.CORSOrigins[0] != "*" {
|
|
t.Errorf("expected CORSOrigins ['*'], got %v", cfg.CORSOrigins)
|
|
}
|
|
}
|
|
|
|
func TestLoadFromEnv(t *testing.T) {
|
|
os.Setenv("APP_NAME", "test-app")
|
|
os.Setenv("BACKEND_PORT", "9999")
|
|
os.Setenv("DATABASE_URL", "postgres://test:test@localhost:5432/test")
|
|
os.Setenv("MARKETPLACE_COMMISSION", "5.0")
|
|
os.Setenv("BUYER_FEE_RATE", "0.2")
|
|
os.Setenv("JWT_SECRET", "super-secret")
|
|
os.Setenv("JWT_EXPIRES_IN", "12h")
|
|
os.Setenv("PASSWORD_PEPPER", "pepper123")
|
|
os.Setenv("CORS_ORIGINS", "https://example.com,https://app.example.com")
|
|
os.Setenv("BACKEND_HOST", "api.test.local")
|
|
os.Setenv("SWAGGER_SCHEMES", "https, http")
|
|
defer func() {
|
|
os.Unsetenv("APP_NAME")
|
|
os.Unsetenv("BACKEND_PORT")
|
|
os.Unsetenv("DATABASE_URL")
|
|
os.Unsetenv("MARKETPLACE_COMMISSION")
|
|
os.Unsetenv("BUYER_FEE_RATE")
|
|
os.Unsetenv("JWT_SECRET")
|
|
os.Unsetenv("JWT_EXPIRES_IN")
|
|
os.Unsetenv("PASSWORD_PEPPER")
|
|
os.Unsetenv("CORS_ORIGINS")
|
|
os.Unsetenv("BACKEND_HOST")
|
|
os.Unsetenv("SWAGGER_SCHEMES")
|
|
}()
|
|
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("failed to load config: %v", err)
|
|
}
|
|
|
|
if cfg.AppName != "test-app" {
|
|
t.Errorf("expected AppName 'test-app', got '%s'", cfg.AppName)
|
|
}
|
|
if cfg.Port != "9999" {
|
|
t.Errorf("expected Port '9999', got '%s'", cfg.Port)
|
|
}
|
|
if cfg.DatabaseURL != "postgres://test:test@localhost:5432/test" {
|
|
t.Errorf("expected custom DatabaseURL, got '%s'", cfg.DatabaseURL)
|
|
}
|
|
if cfg.MarketplaceCommission != 5.0 {
|
|
t.Errorf("expected MarketplaceCommission 5.0, got %f", cfg.MarketplaceCommission)
|
|
}
|
|
if cfg.BuyerFeeRate != 0.2 {
|
|
t.Errorf("expected BuyerFeeRate 0.2, got %f", cfg.BuyerFeeRate)
|
|
}
|
|
if cfg.JWTSecret != "super-secret" {
|
|
t.Errorf("expected JWTSecret 'super-secret', got '%s'", cfg.JWTSecret)
|
|
}
|
|
if cfg.JWTExpiresIn != 12*time.Hour {
|
|
t.Errorf("expected JWTExpiresIn 12h, got %v", cfg.JWTExpiresIn)
|
|
}
|
|
if cfg.PasswordPepper != "pepper123" {
|
|
t.Errorf("expected PasswordPepper 'pepper123', got '%s'", cfg.PasswordPepper)
|
|
}
|
|
if len(cfg.CORSOrigins) != 2 {
|
|
t.Errorf("expected 2 CORS origins, got %d", len(cfg.CORSOrigins))
|
|
}
|
|
if cfg.BackendHost != "api.test.local" {
|
|
t.Errorf("expected BackendHost 'api.test.local', got '%s'", cfg.BackendHost)
|
|
}
|
|
if len(cfg.SwaggerSchemes) != 2 || cfg.SwaggerSchemes[0] != "https" || cfg.SwaggerSchemes[1] != "http" {
|
|
t.Errorf("expected SwaggerSchemes [https http], got %v", cfg.SwaggerSchemes)
|
|
}
|
|
}
|
|
|
|
func TestAddr(t *testing.T) {
|
|
cfg := Config{Port: "3000"}
|
|
expected := ":3000"
|
|
if cfg.Addr() != expected {
|
|
t.Errorf("expected Addr '%s', got '%s'", expected, cfg.Addr())
|
|
}
|
|
}
|
|
|
|
func TestInvalidEnvValues(t *testing.T) {
|
|
os.Setenv("MARKETPLACE_COMMISSION", "invalid")
|
|
os.Setenv("JWT_EXPIRES_IN", "not-a-duration")
|
|
os.Setenv("BUYER_FEE_RATE", "invalid-rate")
|
|
|
|
defer func() {
|
|
os.Unsetenv("MARKETPLACE_COMMISSION")
|
|
os.Unsetenv("JWT_EXPIRES_IN")
|
|
os.Unsetenv("BUYER_FEE_RATE")
|
|
}()
|
|
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("failed to load config: %v", err)
|
|
}
|
|
|
|
// Should use defaults when values are invalid
|
|
if cfg.MarketplaceCommission != 2.5 {
|
|
t.Errorf("expected fallback MarketplaceCommission 2.5, got %f", cfg.MarketplaceCommission)
|
|
}
|
|
if cfg.JWTExpiresIn != 24*time.Hour {
|
|
t.Errorf("expected fallback JWTExpiresIn 24h, got %v", cfg.JWTExpiresIn)
|
|
}
|
|
if cfg.BuyerFeeRate != 0.12 {
|
|
t.Errorf("expected fallback BuyerFeeRate 0.12, got %f", cfg.BuyerFeeRate)
|
|
}
|
|
}
|
|
|
|
func TestEmptyCORSOrigins(t *testing.T) {
|
|
os.Setenv("CORS_ORIGINS", "")
|
|
defer os.Unsetenv("CORS_ORIGINS")
|
|
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("failed to load config: %v", err)
|
|
}
|
|
|
|
if len(cfg.CORSOrigins) != 1 || cfg.CORSOrigins[0] != "*" {
|
|
t.Errorf("expected fallback CORSOrigins ['*'], got %v", cfg.CORSOrigins)
|
|
}
|
|
}
|
|
|
|
func TestSwaggerSchemesTrimmed(t *testing.T) {
|
|
os.Setenv("SWAGGER_SCHEMES", " https , ,http,")
|
|
defer os.Unsetenv("SWAGGER_SCHEMES")
|
|
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("failed to load config: %v", err)
|
|
}
|
|
|
|
if len(cfg.SwaggerSchemes) != 2 || cfg.SwaggerSchemes[0] != "https" || cfg.SwaggerSchemes[1] != "http" {
|
|
t.Errorf("expected SwaggerSchemes [https http], got %v", cfg.SwaggerSchemes)
|
|
}
|
|
}
|
|
|
|
func TestGetEnv(t *testing.T) {
|
|
t.Setenv("TEST_CONFIG_STRING", "value")
|
|
|
|
if got := getEnv("TEST_CONFIG_STRING", "fallback"); got != "value" {
|
|
t.Errorf("expected getEnv to return value, got %q", got)
|
|
}
|
|
if got := getEnv("MISSING_CONFIG_STRING", "fallback"); got != "fallback" {
|
|
t.Errorf("expected getEnv to return fallback, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestGetEnvInt(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value string
|
|
fallback int
|
|
expected int
|
|
}{
|
|
{name: "valid integer", value: "42", fallback: 10, expected: 42},
|
|
{name: "invalid integer", value: "not-a-number", fallback: 10, expected: 10},
|
|
{name: "empty value", value: "", fallback: 7, expected: 7},
|
|
{name: "negative integer", value: "-5", fallback: 3, expected: -5},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Setenv("TEST_CONFIG_INT", tt.value)
|
|
if got := getEnvInt("TEST_CONFIG_INT", tt.fallback); got != tt.expected {
|
|
t.Errorf("expected %d, got %d", tt.expected, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetEnvDuration(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value string
|
|
fallback time.Duration
|
|
expected time.Duration
|
|
}{
|
|
{name: "valid duration", value: "45m", fallback: time.Hour, expected: 45 * time.Minute},
|
|
{name: "invalid duration", value: "not-a-duration", fallback: time.Minute, expected: time.Minute},
|
|
{name: "empty value", value: "", fallback: 30 * time.Second, expected: 30 * time.Second},
|
|
{name: "complex duration", value: "1h30m", fallback: time.Minute, expected: 90 * time.Minute},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Setenv("TEST_CONFIG_DURATION", tt.value)
|
|
if got := getEnvDuration("TEST_CONFIG_DURATION", tt.fallback); got != tt.expected {
|
|
t.Errorf("expected %v, got %v", tt.expected, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetEnvFloat(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value string
|
|
fallback float64
|
|
expected float64
|
|
}{
|
|
{name: "valid float", value: "3.14", fallback: 1.2, expected: 3.14},
|
|
{name: "valid integer string", value: "2", fallback: 1.2, expected: 2},
|
|
{name: "invalid float", value: "invalid", fallback: 1.2, expected: 1.2},
|
|
{name: "empty value", value: "", fallback: 2.5, expected: 2.5},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Setenv("TEST_CONFIG_FLOAT", tt.value)
|
|
if got := getEnvFloat("TEST_CONFIG_FLOAT", tt.fallback); got != tt.expected {
|
|
t.Errorf("expected %v, got %v", tt.expected, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetEnvStringSlice(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value string
|
|
fallback []string
|
|
expected []string
|
|
}{
|
|
{name: "comma separated", value: "a,b,c", fallback: []string{"fallback"}, expected: []string{"a", "b", "c"}},
|
|
{name: "single value", value: "solo", fallback: []string{"fallback"}, expected: []string{"solo"}},
|
|
{name: "trim spaces", value: " a , b ", fallback: []string{"fallback"}, expected: []string{"a", "b"}},
|
|
{name: "empty entries", value: "a,,b,", fallback: []string{"fallback"}, expected: []string{"a", "b"}},
|
|
{name: "trailing spaces", value: "one ,two ", fallback: []string{"fallback"}, expected: []string{"one", "two"}},
|
|
{name: "only separators", value: " , , ", fallback: []string{"fallback"}, expected: []string{"fallback"}},
|
|
{name: "empty value", value: "", fallback: []string{"fallback"}, expected: []string{"fallback"}},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Setenv("TEST_CONFIG_SLICE", tt.value)
|
|
got := getEnvStringSlice("TEST_CONFIG_SLICE", tt.fallback)
|
|
if len(got) != len(tt.expected) {
|
|
t.Fatalf("expected %v, got %v", tt.expected, got)
|
|
}
|
|
for i, expected := range tt.expected {
|
|
if got[i] != expected {
|
|
t.Fatalf("expected %v, got %v", tt.expected, got)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|