Files
doormile_backend/config/secrets_test.go

143 lines
4.7 KiB
Go

package config
import (
"strings"
"testing"
)
// DM-06: config.go used to default JWT_SECRET_KEY, the NATS URL and its
// credentials, and the AI/optimiser hosts to the REAL production values. Two
// consequences: anyone holding the repository could mint a valid token for any
// account against a deployment that had not overridden the secret, and any
// local run silently joined the live NATS stream.
// The literals that must never come back. Written out so a revert is a test
// failure rather than something noticed in review.
func TestProductionValuesAreNotDefaults(t *testing.T) {
for _, key := range []string{
"JWT_SECRET_KEY", "NATS_URL", "NATS_USER", "NATS_PASSWORD",
"AI_LAYER_BASE_URL", "ROUTE_OPTIMIZER_URL", "DB_PASSWORD",
} {
setEnv(t, key, "")
}
setEnv(t, "ENV", "development")
cfg := Load()
banned := map[string]string{
"NatsURL": cfg.NatsURL,
"NatsUser": cfg.NatsUser,
"NatsPassword": cfg.NatsPassword,
"AILayerBaseURL": cfg.AILayerBaseURL,
"RouteOptimizerURL": cfg.RouteOptimizerURL,
"DBPassword": cfg.DBPassword,
}
for field, got := range banned {
if got != "" {
t.Errorf("%s defaulted to %q — production values must not be defaults", field, got)
}
}
// The old hardcoded secret must not be what we sign with.
if cfg.JWTSecret == "DoormileSuperSecretJWTKey2026!" {
t.Error("JWTSecret fell back to the literal that used to be in config.go")
}
}
// With no secret configured outside production the service still runs, but on
// a key that exists only for this process.
func TestUnsetSecretOutsideProductionIsEphemeralNotShared(t *testing.T) {
setEnv(t, "JWT_SECRET_KEY", "")
setEnv(t, "ENV", "development")
first := Load().JWTSecret
second := Load().JWTSecret
if first == "" || second == "" {
t.Fatal("an unset secret produced an empty signing key; tokens would be forgeable")
}
if first == second {
t.Error("two loads produced the same generated key — it is not ephemeral")
}
if len(first) < 32 {
t.Errorf("generated key is %d chars, too short to be a signing key", len(first))
}
}
// In production an absent secret is NOT quietly replaced — it is left absent so
// Validate can refuse the boot with a message that says why. Silently
// generating one would invalidate every live session on each restart.
func TestProductionRefusesToStartWithoutASecret(t *testing.T) {
setEnv(t, "JWT_SECRET_KEY", "")
setEnv(t, "ENV", "production")
cfg := Load()
if cfg.JWTSecret != "" {
t.Errorf("production generated a secret (%q); it must stay empty so Validate fails", cfg.JWTSecret)
}
err := cfg.Validate()
if err == nil {
t.Fatal("Validate accepted an empty JWT secret in production")
}
if !strings.Contains(err.Error(), "JWT_SECRET_KEY") {
t.Errorf("Validate error does not name the variable: %v", err)
}
}
// Outside production the ephemeral key is enough to pass validation, so local
// development needs no configuration at all.
func TestValidatePassesOutsideProductionWithNoSecret(t *testing.T) {
setEnv(t, "JWT_SECRET_KEY", "")
setEnv(t, "ENV", "development")
if err := Load().Validate(); err != nil {
t.Errorf("development should boot without a configured secret: %v", err)
}
}
// A configured secret always validates, production or not.
func TestValidatePassesWithAConfiguredSecret(t *testing.T) {
setEnv(t, "JWT_SECRET_KEY", "a-real-configured-secret")
setEnv(t, "ENV", "production")
if err := Load().Validate(); err != nil {
t.Errorf("a configured secret must validate: %v", err)
}
}
// An explicitly configured secret is always used verbatim.
func TestConfiguredSecretIsUsedVerbatim(t *testing.T) {
setEnv(t, "JWT_SECRET_KEY", "a-real-configured-secret")
setEnv(t, "ENV", "production")
if got := Load().JWTSecret; got != "a-real-configured-secret" {
t.Errorf("JWTSecret = %q, want the configured value", got)
}
}
func TestIsProductionIsCaseAndSpaceInsensitive(t *testing.T) {
setEnv(t, "JWT_SECRET_KEY", "x")
for _, env := range []string{"production", "Production", "PRODUCTION", " production "} {
setEnv(t, "ENV", env)
if !Load().IsProduction() {
t.Errorf("ENV=%q was not treated as production", env)
}
}
for _, env := range []string{"development", "staging", "test", ""} {
setEnv(t, "ENV", env)
if Load().IsProduction() {
t.Errorf("ENV=%q was treated as production", env)
}
}
}
// The geocoder is a PUBLIC service, not a Doormile host, so it keeps its
// default — removing it would break place search for no security gain.
func TestGeocoderKeepsItsPublicDefault(t *testing.T) {
setEnv(t, "JWT_SECRET_KEY", "x")
setEnv(t, "GEOCODER_URL", "")
if got := Load().GeocoderURL; !strings.Contains(got, "nominatim") {
t.Errorf("GeocoderURL = %q, want the public Nominatim default", got)
}
}