package config import ( "encoding/base64" "strings" "testing" ) // validEnv sets the minimum required variables for a passing Load. func validEnv(t *testing.T) { t.Helper() t.Setenv("SESSION_SECRET", "0123456789abcdef0123456789abcdef") t.Setenv("CREDENTIALS_ENC_KEY", base64.StdEncoding.EncodeToString(make([]byte, 32))) } func TestLoadDefaults(t *testing.T) { validEnv(t) c, err := Load() if err != nil { t.Fatalf("Load: %v", err) } if c.AppPort != 8787 { t.Errorf("AppPort = %d, want 8787", c.AppPort) } if c.MongoDB != "bountyboard" { t.Errorf("MongoDB = %q", c.MongoDB) } if c.AtomizerTimeout.Seconds() != 120 { t.Errorf("AtomizerTimeout = %v", c.AtomizerTimeout) } if c.CookieSecure() { t.Error("CookieSecure should be false for http base URL in auto mode") } if len(c.CredentialsEncKey) != 32 { t.Errorf("CredentialsEncKey len = %d", len(c.CredentialsEncKey)) } } func TestLoadValidationErrors(t *testing.T) { tests := []struct { name string key string value string wantSub string }{ {"bad port", "APP_PORT", "99999", "APP_PORT"}, {"non-numeric port", "APP_PORT", "abc", "APP_PORT"}, {"short session secret", "SESSION_SECRET", "short", "SESSION_SECRET"}, {"bad enc key", "CREDENTIALS_ENC_KEY", "not-base64!!", "CREDENTIALS_ENC_KEY"}, {"wrong size enc key", "CREDENTIALS_ENC_KEY", base64.StdEncoding.EncodeToString(make([]byte, 16)), "CREDENTIALS_ENC_KEY"}, {"bad base url", "APP_BASE_URL", "not a url", "APP_BASE_URL"}, {"bad atomizer url", "ATOMIZER_BASE_URL", "::::", "ATOMIZER_BASE_URL"}, {"bad cidr", "TRUSTED_PROXY_CIDRS", "10.0.0.0/8,banana", "TRUSTED_PROXY_CIDRS"}, {"bad cookie mode", "COOKIE_SECURE", "maybe", "COOKIE_SECURE"}, {"bad upload size", "MAX_UPLOAD_MB", "0", "MAX_UPLOAD_MB"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { validEnv(t) t.Setenv(tt.key, tt.value) _, err := Load() if err == nil { t.Fatal("want error, got nil") } if !strings.Contains(err.Error(), tt.wantSub) { t.Errorf("error %q does not mention %q", err, tt.wantSub) } }) } } func TestLoadMissingEncKey(t *testing.T) { t.Setenv("SESSION_SECRET", "0123456789abcdef0123456789abcdef") t.Setenv("CREDENTIALS_ENC_KEY", "") if _, err := Load(); err == nil { t.Fatal("want error for missing CREDENTIALS_ENC_KEY") } } func TestTrustedProxyCIDRs(t *testing.T) { validEnv(t) t.Setenv("TRUSTED_PROXY_CIDRS", "172.16.0.0/12, 10.0.0.0/8") c, err := Load() if err != nil { t.Fatalf("Load: %v", err) } if len(c.TrustedProxyCIDRs) != 2 { t.Fatalf("got %d prefixes, want 2", len(c.TrustedProxyCIDRs)) } } func TestCookieSecureModes(t *testing.T) { tests := []struct { mode, baseURL string want bool }{ {"auto", "http://localhost:8787", false}, {"auto", "https://bounty.example.com", true}, {"true", "http://localhost:8787", true}, {"false", "https://bounty.example.com", false}, } for _, tt := range tests { validEnv(t) t.Setenv("COOKIE_SECURE", tt.mode) t.Setenv("APP_BASE_URL", tt.baseURL) c, err := Load() if err != nil { t.Fatalf("Load: %v", err) } if got := c.CookieSecure(); got != tt.want { t.Errorf("mode=%s url=%s: CookieSecure()=%v, want %v", tt.mode, tt.baseURL, got, tt.want) } } } func TestOIDCAndSMTPEnabled(t *testing.T) { validEnv(t) c, err := Load() if err != nil { t.Fatal(err) } if c.OIDC.Enabled() || c.SMTP.Enabled() { t.Error("OIDC and SMTP must be disabled when unset") } t.Setenv("OIDC_ISSUER_URL", "https://issuer.example.com") t.Setenv("OIDC_CLIENT_ID", "id") t.Setenv("OIDC_CLIENT_SECRET", "secret") t.Setenv("SMTP_HOST", "smtp.example.com") c, err = Load() if err != nil { t.Fatal(err) } if !c.OIDC.Enabled() || !c.SMTP.Enabled() { t.Error("OIDC and SMTP must be enabled when set") } }