2026-07-24 21:11:28 +02:00
|
|
|
package config
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func setRequiredEnv(t *testing.T) {
|
|
|
|
|
t.Helper()
|
2026-07-25 23:00:35 +02:00
|
|
|
t.Setenv("GENIUSRUN_BACKEND_URL", "https://geniusrun.example.com")
|
2026-07-24 21:11:28 +02:00
|
|
|
t.Setenv("GENIUSRUN_OIDC_ISSUER_URL", "https://keycloak.example.com/realms/myrealm")
|
|
|
|
|
t.Setenv("GENIUSRUN_OIDC_CLIENT_ID", "geniusrun")
|
|
|
|
|
t.Setenv("GENIUSRUN_OIDC_CLIENT_SECRET", "client-secret")
|
|
|
|
|
t.Setenv("GENIUSRUN_SESSION_SECRET", "a-session-secret-that-is-at-least-32-bytes-long")
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-25 23:00:35 +02:00
|
|
|
func TestLoad_DerivesOIDCSettingsFromBackendURL(t *testing.T) {
|
2026-07-24 21:11:28 +02:00
|
|
|
setRequiredEnv(t)
|
|
|
|
|
|
2026-08-04 16:04:18 +02:00
|
|
|
cfg, err := LoadEnv()
|
2026-07-24 21:11:28 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if cfg.OIDCRedirectURL != "https://geniusrun.example.com/api/session/callback" {
|
|
|
|
|
t.Errorf("OIDCRedirectURL = %q", cfg.OIDCRedirectURL)
|
|
|
|
|
}
|
|
|
|
|
if !cfg.SessionSecure {
|
|
|
|
|
t.Error("SessionSecure = false, want true for an https base URL")
|
|
|
|
|
}
|
|
|
|
|
if cfg.OIDCRequiredRole != "geniusrun-user" {
|
|
|
|
|
t.Errorf("OIDCRequiredRole = %q, want default", cfg.OIDCRequiredRole)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLoad_HTTPBaseURLYieldsInsecureCookies(t *testing.T) {
|
|
|
|
|
setRequiredEnv(t)
|
2026-07-25 23:00:35 +02:00
|
|
|
t.Setenv("GENIUSRUN_BACKEND_URL", "http://localhost:8080")
|
2026-07-24 21:11:28 +02:00
|
|
|
|
2026-08-04 16:04:18 +02:00
|
|
|
cfg, err := LoadEnv()
|
2026-07-24 21:11:28 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if cfg.SessionSecure {
|
|
|
|
|
t.Error("SessionSecure = true, want false for an http base URL")
|
|
|
|
|
}
|
|
|
|
|
if cfg.OIDCRedirectURL != "http://localhost:8080/api/session/callback" {
|
|
|
|
|
t.Errorf("OIDCRedirectURL = %q", cfg.OIDCRedirectURL)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLoad_MissingRequiredOIDCVars(t *testing.T) {
|
|
|
|
|
cases := []string{
|
2026-07-25 23:00:35 +02:00
|
|
|
"GENIUSRUN_BACKEND_URL",
|
2026-07-24 21:11:28 +02:00
|
|
|
"GENIUSRUN_OIDC_ISSUER_URL",
|
|
|
|
|
"GENIUSRUN_OIDC_CLIENT_ID",
|
|
|
|
|
"GENIUSRUN_OIDC_CLIENT_SECRET",
|
|
|
|
|
}
|
|
|
|
|
for _, missing := range cases {
|
|
|
|
|
t.Run(missing, func(t *testing.T) {
|
|
|
|
|
setRequiredEnv(t)
|
|
|
|
|
t.Setenv(missing, "")
|
2026-08-04 16:04:18 +02:00
|
|
|
if _, err := LoadEnv(); err == nil {
|
2026-07-24 21:11:28 +02:00
|
|
|
t.Fatalf("expected error when %s is unset", missing)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLoad_SessionSecretTooShort(t *testing.T) {
|
|
|
|
|
setRequiredEnv(t)
|
|
|
|
|
t.Setenv("GENIUSRUN_SESSION_SECRET", "too-short")
|
|
|
|
|
|
2026-08-04 16:04:18 +02:00
|
|
|
if _, err := LoadEnv(); err == nil {
|
2026-07-24 21:11:28 +02:00
|
|
|
t.Fatal("expected error for a session secret under 32 characters")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-26 21:45:39 +02:00
|
|
|
func TestLoad_GarminTokenStoreRootDefaultsIndependentlyOfDBPath(t *testing.T) {
|
2026-07-25 18:55:53 +02:00
|
|
|
setRequiredEnv(t)
|
2026-08-04 00:11:56 +02:00
|
|
|
t.Setenv("GENIUSRUN_TOKENSTORE_PATH", "")
|
2026-07-25 18:55:53 +02:00
|
|
|
t.Setenv("GENIUSRUN_DB_PATH", "/var/lib/geniusrun/geniusrun.db")
|
|
|
|
|
|
2026-08-04 16:04:18 +02:00
|
|
|
cfg, err := LoadEnv()
|
2026-07-25 18:55:53 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load: %v", err)
|
|
|
|
|
}
|
2026-08-04 16:04:18 +02:00
|
|
|
if cfg.TokenStoreRoot != ".garmin" {
|
|
|
|
|
t.Errorf("GarminTokenStoreRoot = %q, want %q (never derived from DBPath's directory)", cfg.TokenStoreRoot, ".garmin")
|
2026-07-25 18:55:53 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLoad_GarminTokenStoreRootExplicitOverridesDefault(t *testing.T) {
|
|
|
|
|
setRequiredEnv(t)
|
2026-08-04 00:11:56 +02:00
|
|
|
t.Setenv("GENIUSRUN_TOKENSTORE_PATH", "/custom/tokenstores")
|
2026-07-25 18:55:53 +02:00
|
|
|
t.Setenv("GENIUSRUN_DB_PATH", "/var/lib/geniusrun/geniusrun.db")
|
|
|
|
|
|
2026-08-04 16:04:18 +02:00
|
|
|
cfg, err := LoadEnv()
|
2026-07-25 18:55:53 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load: %v", err)
|
|
|
|
|
}
|
2026-08-04 16:04:18 +02:00
|
|
|
if cfg.TokenStoreRoot != "/custom/tokenstores" {
|
|
|
|
|
t.Errorf("GarminTokenStoreRoot = %q, want explicit override to take precedence", cfg.TokenStoreRoot)
|
2026-07-25 18:55:53 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-26 14:30:39 +02:00
|
|
|
func TestLoad_LogLevelDefaultsToInfo(t *testing.T) {
|
|
|
|
|
setRequiredEnv(t)
|
|
|
|
|
t.Setenv("GENIUSRUN_LOG_LEVEL", "")
|
|
|
|
|
|
2026-08-04 16:04:18 +02:00
|
|
|
cfg, err := LoadEnv()
|
2026-07-26 14:30:39 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if cfg.LogLevel != "info" {
|
|
|
|
|
t.Errorf("LogLevel = %q, want default %q", cfg.LogLevel, "info")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLoad_LogLevelExplicitOverridesDefault(t *testing.T) {
|
|
|
|
|
setRequiredEnv(t)
|
|
|
|
|
t.Setenv("GENIUSRUN_LOG_LEVEL", "debug")
|
|
|
|
|
|
2026-08-04 16:04:18 +02:00
|
|
|
cfg, err := LoadEnv()
|
2026-07-26 14:30:39 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if cfg.LogLevel != "debug" {
|
|
|
|
|
t.Errorf("LogLevel = %q, want debug", cfg.LogLevel)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-25 21:07:04 +02:00
|
|
|
func TestLoad_GarminPythonPathDefaultsToPython3(t *testing.T) {
|
|
|
|
|
setRequiredEnv(t)
|
2026-08-04 00:11:56 +02:00
|
|
|
t.Setenv("GENIUSRUN_PYTHON_PATH", "")
|
2026-07-25 21:07:04 +02:00
|
|
|
|
2026-08-04 16:04:18 +02:00
|
|
|
cfg, err := LoadEnv()
|
2026-07-25 21:07:04 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load: %v", err)
|
|
|
|
|
}
|
2026-08-04 16:04:18 +02:00
|
|
|
if cfg.PythonPath != "python3" {
|
|
|
|
|
t.Errorf("GarminPythonPath = %q, want default %q", cfg.PythonPath, "python3")
|
2026-07-25 21:07:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLoad_GarminPythonPathExplicitOverridesDefault(t *testing.T) {
|
|
|
|
|
setRequiredEnv(t)
|
2026-08-04 00:11:56 +02:00
|
|
|
t.Setenv("GENIUSRUN_PYTHON_PATH", "/opt/venv/bin/python3")
|
2026-07-25 21:07:04 +02:00
|
|
|
|
2026-08-04 16:04:18 +02:00
|
|
|
cfg, err := LoadEnv()
|
2026-07-25 21:07:04 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load: %v", err)
|
|
|
|
|
}
|
2026-08-04 16:04:18 +02:00
|
|
|
if cfg.PythonPath != "/opt/venv/bin/python3" {
|
|
|
|
|
t.Errorf("GarminPythonPath = %q, want explicit override", cfg.PythonPath)
|
2026-07-25 21:07:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-25 23:00:35 +02:00
|
|
|
func TestLoad_FrontendURLDefaultsToBackendURL(t *testing.T) {
|
2026-07-25 22:50:46 +02:00
|
|
|
setRequiredEnv(t)
|
|
|
|
|
t.Setenv("GENIUSRUN_FRONTEND_URL", "")
|
|
|
|
|
|
2026-08-04 16:04:18 +02:00
|
|
|
cfg, err := LoadEnv()
|
2026-07-25 22:50:46 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load: %v", err)
|
|
|
|
|
}
|
2026-07-25 23:00:35 +02:00
|
|
|
if cfg.FrontendURL != cfg.BackendURL {
|
|
|
|
|
t.Errorf("FrontendURL = %q, want it to default to BackendURL %q", cfg.FrontendURL, cfg.BackendURL)
|
2026-07-25 22:50:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLoad_FrontendURLExplicitOverridesDefault(t *testing.T) {
|
|
|
|
|
setRequiredEnv(t)
|
|
|
|
|
t.Setenv("GENIUSRUN_FRONTEND_URL", "http://localhost:5173/")
|
|
|
|
|
|
2026-08-04 16:04:18 +02:00
|
|
|
cfg, err := LoadEnv()
|
2026-07-25 22:50:46 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if cfg.FrontendURL != "http://localhost:5173" {
|
|
|
|
|
t.Errorf("FrontendURL = %q, want http://localhost:5173 (trailing slash trimmed)", cfg.FrontendURL)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-04 00:11:56 +02:00
|
|
|
func TestLoad_CustomRole(t *testing.T) {
|
2026-07-24 21:11:28 +02:00
|
|
|
setRequiredEnv(t)
|
|
|
|
|
t.Setenv("GENIUSRUN_OIDC_REQUIRED_ROLE", "admin")
|
|
|
|
|
|
2026-08-04 16:04:18 +02:00
|
|
|
cfg, err := LoadEnv()
|
2026-07-24 21:11:28 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if cfg.OIDCRequiredRole != "admin" {
|
|
|
|
|
t.Errorf("OIDCRequiredRole = %q", cfg.OIDCRequiredRole)
|
|
|
|
|
}
|
|
|
|
|
}
|