package config import ( "strings" "testing" "time" ) // envFrom returns a Getenv backed by a map. func envFrom(m map[string]string) Getenv { return func(k string) string { return m[k] } } func TestLoadDefaults(t *testing.T) { c, err := Load(envFrom(map[string]string{ "WAITFORDB_USER": "appuser", "WAITFORDB_DATABASE": "appdb", "WAITFORDB_HOST": "db", })) if err != nil { t.Fatalf("unexpected error: %v", err) } if c.Driver != "postgres" { t.Errorf("driver = %q, want postgres", c.Driver) } if c.Port != "5432" { t.Errorf("port = %q, want default 5432", c.Port) } if c.Interval != 2*time.Second { t.Errorf("interval = %v, want 2s", c.Interval) } if c.ConnectTimeout != 5*time.Second { t.Errorf("connect_timeout = %v, want 5s", c.ConnectTimeout) } if c.Timeout != 0 { t.Errorf("timeout = %v, want 0 (forever)", c.Timeout) } } func TestPrecedenceDSNWins(t *testing.T) { c, err := Load(envFrom(map[string]string{ "WAITFORDB_DSN": "postgres://u:p@h:5432/d", "WAITFORDB_HOST": "ignored", "WAITFORDB_USER": "ignored", "WAITFORDB_DATABASE": "ignored", })) if err != nil { t.Fatalf("unexpected error: %v", err) } if c.DSN == "" { t.Fatal("DSN should be set") } } func TestPrecedenceWaitfordbOverPG(t *testing.T) { c, err := Load(envFrom(map[string]string{ "WAITFORDB_HOST": "native-host", "PGHOST": "pg-host", "WAITFORDB_USER": "native-user", "PGUSER": "pg-user", "PGDATABASE": "pg-db", // only PG* set for database -> fallback used })) if err != nil { t.Fatalf("unexpected error: %v", err) } if c.Host != "native-host" { t.Errorf("host = %q, want native-host (WAITFORDB_* wins)", c.Host) } if c.User != "native-user" { t.Errorf("user = %q, want native-user", c.User) } if c.Database != "pg-db" { t.Errorf("database = %q, want pg-db (PG* fallback)", c.Database) } } func TestPGFallbackPostgresOnly(t *testing.T) { // For a non-postgres driver the PG* fallback must not apply. _, err := Load(envFrom(map[string]string{ "WAITFORDB_DRIVER": "mysql", "PGUSER": "pg-user", "PGDATABASE": "pg-db", // No WAITFORDB_USER/DATABASE -> must be a config error, PG* ignored. })) if err == nil { t.Fatal("expected config error: PG* must not satisfy mysql config") } } func TestMySQLDefaultPort(t *testing.T) { c, err := Load(envFrom(map[string]string{ "WAITFORDB_DRIVER": "mysql", "WAITFORDB_USER": "u", "WAITFORDB_DATABASE": "d", })) if err != nil { t.Fatalf("unexpected error: %v", err) } if c.Port != "3306" { t.Errorf("port = %q, want 3306", c.Port) } } func TestValidationErrors(t *testing.T) { cases := map[string]map[string]string{ "missing database": {"WAITFORDB_USER": "u"}, "missing user": {"WAITFORDB_DATABASE": "d"}, "bad driver": {"WAITFORDB_DRIVER": "oracle", "WAITFORDB_USER": "u", "WAITFORDB_DATABASE": "d"}, "bad timeout": {"WAITFORDB_USER": "u", "WAITFORDB_DATABASE": "d", "WAITFORDB_TIMEOUT": "nope"}, "zero interval": {"WAITFORDB_USER": "u", "WAITFORDB_DATABASE": "d", "WAITFORDB_INTERVAL": "0"}, } for name, env := range cases { t.Run(name, func(t *testing.T) { if _, err := Load(envFrom(env)); err == nil { t.Fatalf("expected error for %s", name) } else if _, ok := err.(*Error); !ok { t.Fatalf("expected *config.Error, got %T", err) } }) } } func TestRedactedHidesPassword(t *testing.T) { c, err := Load(envFrom(map[string]string{ "WAITFORDB_HOST": "h", "WAITFORDB_USER": "u", "WAITFORDB_PASSWORD": "sup3r-s3cret", "WAITFORDB_DATABASE": "d", "WAITFORDB_TIMEOUT": "5m", })) if err != nil { t.Fatalf("unexpected error: %v", err) } got := c.Redacted() if strings.Contains(got, "sup3r-s3cret") { t.Fatalf("redacted output leaked password: %q", got) } for _, want := range []string{"driver=postgres", "database=d", "user=u", "password=***", "timeout=5m", "interval=2s"} { if !strings.Contains(got, want) { t.Errorf("redacted output missing %q: %q", want, got) } } } func TestRedactedForeverTimeout(t *testing.T) { c, _ := Load(envFrom(map[string]string{"WAITFORDB_USER": "u", "WAITFORDB_DATABASE": "d"})) if !strings.Contains(c.Redacted(), "timeout=forever") { t.Errorf("want timeout=forever, got %q", c.Redacted()) } } func TestRedactedDSNMasksPassword(t *testing.T) { cases := []struct { name string dsn string }{ {"url", "postgres://user:topsecret@host:5432/db?sslmode=disable"}, {"keyword", "host=h user=u password=topsecret dbname=d"}, {"mysql", "user:topsecret@tcp(h:3306)/d?timeout=5s"}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { c := Config{DSN: tc.dsn, Timeout: 0, Interval: time.Second, ConnectTimeout: time.Second, Driver: "postgres"} got := c.Redacted() if strings.Contains(got, "topsecret") { t.Fatalf("leaked password: %q", got) } if !strings.Contains(got, "***") { t.Errorf("expected redaction marker in %q", got) } }) } } func TestCNPGAutodetect(t *testing.T) { // Shape of a CNPG -app secret injected wholesale via envFrom. c, err := Load(envFrom(map[string]string{ "host": "mydb-rw.ns.svc", "port": "5432", "dbname": "appdb", "user": "appuser", "username": "appuser", "password": "pw", })) if err != nil { t.Fatalf("unexpected error: %v", err) } if c.Driver != "postgres" || c.SecretFormat != FormatCNPG { t.Errorf("driver=%q format=%q, want postgres/cnpg", c.Driver, c.SecretFormat) } if c.Host != "mydb-rw.ns.svc" || c.Database != "appdb" || c.User != "appuser" || c.Password != "pw" { t.Errorf("fields not filled from CNPG keys: %+v", c) } } func TestCNPGExplicitFormat(t *testing.T) { c, err := Load(envFrom(map[string]string{ "WAITFORDB_SECRET_FORMAT": "cnpg", "host": "h", "dbname": "d", "username": "u", "password": "pw", })) if err != nil { t.Fatalf("unexpected error: %v", err) } if c.SecretFormat != FormatCNPG || c.User != "u" { t.Errorf("format=%q user=%q, want cnpg/u", c.SecretFormat, c.User) } } func TestWaitfordbOverridesCNPGKeys(t *testing.T) { c, err := Load(envFrom(map[string]string{ "WAITFORDB_HOST": "explicit-host", "host": "secret-host", "dbname": "d", "user": "u", "password": "pw", })) if err != nil { t.Fatalf("unexpected error: %v", err) } if c.Host != "explicit-host" { t.Errorf("host = %q, want explicit-host (WAITFORDB_* wins over secret keys)", c.Host) } if c.Database != "d" || c.Password != "pw" { t.Errorf("unset fields should still fill from secret: %+v", c) } } func TestValkeySystemSecretAutodetect(t *testing.T) { // Shape of the valkey-operator system-passwords secret: key = username. c, err := Load(envFrom(map[string]string{ "WAITFORDB_HOST": "myapp-valkey.ns.svc", "_operator": "op-pass", "_replication": "repl-pass", })) if err != nil { t.Fatalf("unexpected error: %v", err) } if c.Driver != "valkey" || c.SecretFormat != FormatValkey { t.Errorf("driver=%q format=%q, want valkey/valkey", c.Driver, c.SecretFormat) } if c.User != "_operator" || c.Password != "op-pass" { t.Errorf("user=%q password=%q, want _operator/op-pass", c.User, c.Password) } if c.Port != "6379" { t.Errorf("port = %q, want default 6379", c.Port) } } func TestValkeyACLUserSecret(t *testing.T) { // An explicit ACL username whose password arrives as an env key of the // same name (valkey-operator user secret via envFrom). c, err := Load(envFrom(map[string]string{ "WAITFORDB_DRIVER": "valkey", "WAITFORDB_USER": "appuser", "appuser": "app-pass", })) if err != nil { t.Fatalf("unexpected error: %v", err) } if c.SecretFormat != FormatValkey || c.Password != "app-pass" { t.Errorf("format=%q password=%q, want valkey/app-pass", c.SecretFormat, c.Password) } } func TestValkeyExplicitFlagsNoSecret(t *testing.T) { c, err := Load(envFrom(map[string]string{ "WAITFORDB_DRIVER": "valkey", "WAITFORDB_HOST": "h", "WAITFORDB_PASSWORD": "pw", })) if err != nil { t.Fatalf("valkey needs no user/database: %v", err) } if c.SecretFormat != "" { t.Errorf("format = %q, want none without operator secret keys", c.SecretFormat) } } func TestRedisDriverAlias(t *testing.T) { c, err := Load(envFrom(map[string]string{"WAITFORDB_DRIVER": "redis"})) if err != nil { t.Fatalf("unexpected error: %v", err) } if c.Driver != "valkey" || c.Port != "6379" { t.Errorf("driver=%q port=%q, want valkey/6379", c.Driver, c.Port) } } func TestExplicitDriverIgnoresMismatchedDetect(t *testing.T) { // CNPG-shaped keys with an explicit valkey driver: format must not apply. c, err := Load(envFrom(map[string]string{ "WAITFORDB_DRIVER": "valkey", "host": "secret-host", "dbname": "d", "user": "u", "password": "pw", })) if err != nil { t.Fatalf("unexpected error: %v", err) } if c.SecretFormat != "" { t.Errorf("format = %q, want none (cnpg does not apply to valkey)", c.SecretFormat) } if c.Host != DefaultHost { t.Errorf("host = %q, want default (secret keys must not fill)", c.Host) } } func TestSecretFormatErrors(t *testing.T) { cases := map[string]map[string]string{ "unknown format": {"WAITFORDB_SECRET_FORMAT": "vault"}, "format/driver mismatch": { "WAITFORDB_SECRET_FORMAT": "cnpg", "WAITFORDB_DRIVER": "valkey", }, } for name, env := range cases { t.Run(name, func(t *testing.T) { if _, err := Load(envFrom(env)); err == nil { t.Fatalf("expected error for %s", name) } else if _, ok := err.(*Error); !ok { t.Fatalf("expected *config.Error, got %T", err) } }) } }