package config import ( "os" "testing" ) // Config is read once at startup, so a mistyped env key fails silently: the // service boots on a default and points at the wrong host, or ships with a // security switch left off. Cheap to pin. func setEnv(t *testing.T, key, value string) { t.Helper() previous, had := os.LookupEnv(key) if value == "" { _ = os.Unsetenv(key) } else { _ = os.Setenv(key, value) } t.Cleanup(func() { if had { _ = os.Setenv(key, previous) } else { _ = os.Unsetenv(key) } }) } // The geocoder settings added for the customer app. GEOCODER_URL is what the // place search proxies through — the app is never handed a map key, after the // legacy rider app's key had to be revoked. func TestGeocoderDefaultsAndOverrides(t *testing.T) { setEnv(t, "GEOCODER_URL", "") setEnv(t, "GEOCODER_EMAIL", "") cfg := Load() if cfg.GeocoderURL != "https://nominatim.openstreetmap.org" { t.Errorf("GeocoderURL default = %q, want the Nominatim host", cfg.GeocoderURL) } if cfg.GeocoderEmail != "" { t.Errorf("GeocoderEmail default = %q, want empty", cfg.GeocoderEmail) } setEnv(t, "GEOCODER_URL", "https://nominatim.internal") setEnv(t, "GEOCODER_EMAIL", "ops@doormile.com") cfg = Load() if cfg.GeocoderURL != "https://nominatim.internal" { t.Errorf("GEOCODER_URL was not read: got %q", cfg.GeocoderURL) } if cfg.GeocoderEmail != "ops@doormile.com" { t.Errorf("GEOCODER_EMAIL was not read: got %q", cfg.GeocoderEmail) } } // Every env key the service reads must actually take effect. A typo in the key // name means the override is ignored and the default is used — which is how a // deploy silently points at the wrong database. func TestEnvironmentOverridesAreRead(t *testing.T) { cases := []struct { key string value string read func(*Config) string }{ {"ENV", "production", func(c *Config) string { return c.Env }}, {"APP_PORT", "9999", func(c *Config) string { return c.Port }}, {"DB_HOST", "db.internal", func(c *Config) string { return c.DBHost }}, {"DB_NAME", "logistics_staging", func(c *Config) string { return c.DBName }}, {"DB_PORT", "6543", func(c *Config) string { return c.DBPort }}, {"REDIS_HOST", "redis.internal", func(c *Config) string { return c.RedisHost }}, {"JWT_SECRET_KEY", "a-different-secret", func(c *Config) string { return c.JWTSecret }}, {"TRUSTED_PROXIES", "10.0.0.0/8", func(c *Config) string { return c.TrustedProxies }}, {"AI_LAYER_BASE_URL", "http://ai.internal", func(c *Config) string { return c.AILayerBaseURL }}, {"ROUTE_OPTIMIZER_URL", "http://routes.internal", func(c *Config) string { return c.RouteOptimizerURL }}, } for _, tc := range cases { setEnv(t, tc.key, tc.value) if got := tc.read(Load()); got != tc.value { t.Errorf("%s set to %q but Load() read %q", tc.key, tc.value, got) } } } // Production must not boot on a secret whose fallback is committed to the repo. func TestMissingProductionSecrets(t *testing.T) { setEnv(t, "ENV", "production") setEnv(t, "JWT_SECRET_KEY", "") setEnv(t, "DB_PASSWORD", "set") setEnv(t, "NATS_PASSWORD", "") got := Load().MissingProductionSecrets() if len(got) != 2 || got[0] != "JWT_SECRET_KEY" || got[1] != "NATS_PASSWORD" { t.Fatalf("missing = %v, want [JWT_SECRET_KEY NATS_PASSWORD]", got) } setEnv(t, "JWT_SECRET_KEY", "set") setEnv(t, "NATS_PASSWORD", "set") if got := Load().MissingProductionSecrets(); len(got) != 0 { t.Errorf("all secrets set, still reported missing: %v", got) } } // Outside production the fallbacks are allowed, so local development runs // with no .env at all. func TestMissingProductionSecretsIgnoredOutsideProduction(t *testing.T) { setEnv(t, "JWT_SECRET_KEY", "") setEnv(t, "DB_PASSWORD", "") setEnv(t, "NATS_PASSWORD", "") for _, env := range []string{"", "development", "staging"} { setEnv(t, "ENV", env) if got := Load().MissingProductionSecrets(); len(got) != 0 { t.Errorf("ENV=%q reported missing secrets %v; only production should", env, got) } } } // An empty env var must fall through to the default rather than blanking the // setting — an empty DB host is a service that cannot start with no clue why. func TestEmptyEnvFallsBackToTheDefault(t *testing.T) { setEnv(t, "DB_NAME", "") if got := Load().DBName; got == "" { t.Error("DBName is empty when DB_NAME is unset; a default is required") } }