feat: use postgres
This commit is contained in:
@@ -1,14 +1,37 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
redisMasterAddrEnvVar = "USERSERVICE_REDIS_MASTER_ADDR"
|
||||
redisReplicaAddrsEnvVar = "USERSERVICE_REDIS_REPLICA_ADDRS"
|
||||
redisPasswordEnvVar = "USERSERVICE_REDIS_PASSWORD"
|
||||
redisDBEnvVar = "USERSERVICE_REDIS_DB"
|
||||
redisOperationTimeoutEnvVar = "USERSERVICE_REDIS_OPERATION_TIMEOUT"
|
||||
redisLegacyAddrEnvVar = "USERSERVICE_REDIS_ADDR"
|
||||
redisLegacyUsernameEnvVar = "USERSERVICE_REDIS_USERNAME"
|
||||
redisLegacyTLSEnabledEnvVar = "USERSERVICE_REDIS_TLS_ENABLED"
|
||||
redisLegacyKeyspacePrefixEnv = "USERSERVICE_REDIS_KEYSPACE_PREFIX"
|
||||
postgresPrimaryDSNEnvVar = "USERSERVICE_POSTGRES_PRIMARY_DSN"
|
||||
postgresReplicaDSNsEnvVar = "USERSERVICE_POSTGRES_REPLICA_DSNS"
|
||||
postgresOperationTimeoutEnvVar = "USERSERVICE_POSTGRES_OPERATION_TIMEOUT"
|
||||
postgresMaxOpenConnsEnvVar = "USERSERVICE_POSTGRES_MAX_OPEN_CONNS"
|
||||
postgresMaxIdleConnsEnvVar = "USERSERVICE_POSTGRES_MAX_IDLE_CONNS"
|
||||
postgresConnMaxLifetimeEnvVar = "USERSERVICE_POSTGRES_CONN_MAX_LIFETIME"
|
||||
|
||||
defaultPostgresDSN = "postgres://userservice:secret@127.0.0.1:5432/galaxy?search_path=user&sslmode=disable"
|
||||
)
|
||||
|
||||
func TestLoadFromEnvUsesDefaults(t *testing.T) {
|
||||
t.Setenv(redisAddrEnvVar, "127.0.0.1:6379")
|
||||
t.Setenv(redisMasterAddrEnvVar, "127.0.0.1:6379")
|
||||
t.Setenv(redisPasswordEnvVar, "secret")
|
||||
t.Setenv(postgresPrimaryDSNEnvVar, defaultPostgresDSN)
|
||||
|
||||
cfg, err := LoadFromEnv()
|
||||
require.NoError(t, err)
|
||||
@@ -18,10 +41,18 @@ func TestLoadFromEnvUsesDefaults(t *testing.T) {
|
||||
require.Equal(t, defaults.Logging.Level, cfg.Logging.Level)
|
||||
require.Equal(t, defaults.InternalHTTP, cfg.InternalHTTP)
|
||||
require.Equal(t, defaults.AdminHTTP, cfg.AdminHTTP)
|
||||
require.Equal(t, "127.0.0.1:6379", cfg.Redis.Addr)
|
||||
require.Equal(t, defaults.Redis.DB, cfg.Redis.DB)
|
||||
require.Equal(t, "127.0.0.1:6379", cfg.Redis.Conn.MasterAddr)
|
||||
require.Equal(t, "secret", cfg.Redis.Conn.Password)
|
||||
require.Equal(t, defaults.Redis.Conn.DB, cfg.Redis.Conn.DB)
|
||||
require.Equal(t, defaults.Redis.DomainEventsStream, cfg.Redis.DomainEventsStream)
|
||||
require.Equal(t, defaults.Redis.DomainEventsStreamMaxLen, cfg.Redis.DomainEventsStreamMaxLen)
|
||||
require.Equal(t, defaults.Redis.LifecycleEventsStream, cfg.Redis.LifecycleEventsStream)
|
||||
require.Equal(t, defaults.Redis.LifecycleEventsStreamMaxLen, cfg.Redis.LifecycleEventsStreamMaxLen)
|
||||
require.Equal(t, defaultPostgresDSN, cfg.Postgres.Conn.PrimaryDSN)
|
||||
require.Equal(t, defaults.Postgres.Conn.OperationTimeout, cfg.Postgres.Conn.OperationTimeout)
|
||||
require.Equal(t, defaults.Postgres.Conn.MaxOpenConns, cfg.Postgres.Conn.MaxOpenConns)
|
||||
require.Equal(t, defaults.Postgres.Conn.MaxIdleConns, cfg.Postgres.Conn.MaxIdleConns)
|
||||
require.Equal(t, defaults.Postgres.Conn.ConnMaxLifetime, cfg.Postgres.Conn.ConnMaxLifetime)
|
||||
require.Equal(t, defaults.Telemetry, cfg.Telemetry)
|
||||
}
|
||||
|
||||
@@ -33,15 +64,21 @@ func TestLoadFromEnvAppliesOverrides(t *testing.T) {
|
||||
t.Setenv(internalHTTPRequestTimeoutEnvVar, "750ms")
|
||||
t.Setenv(adminHTTPAddrEnvVar, "127.0.0.1:19091")
|
||||
t.Setenv(adminHTTPIdleTimeoutEnvVar, "90s")
|
||||
t.Setenv(redisAddrEnvVar, "127.0.0.1:6380")
|
||||
t.Setenv(redisUsernameEnvVar, "alice")
|
||||
t.Setenv(redisPasswordEnvVar, "secret")
|
||||
t.Setenv(redisMasterAddrEnvVar, "127.0.0.1:6380")
|
||||
t.Setenv(redisReplicaAddrsEnvVar, "127.0.0.1:6381,127.0.0.1:6382")
|
||||
t.Setenv(redisPasswordEnvVar, "redis-secret")
|
||||
t.Setenv(redisDBEnvVar, "3")
|
||||
t.Setenv(redisTLSEnabledEnvVar, "true")
|
||||
t.Setenv(redisOperationTimeoutEnvVar, "900ms")
|
||||
t.Setenv(redisKeyspacePrefixEnvVar, "user:custom:")
|
||||
t.Setenv(redisDomainEventsStreamEnvVar, "user:test_events")
|
||||
t.Setenv(redisDomainEventsStreamMaxLenEnvVar, "2048")
|
||||
t.Setenv(redisLifecycleEventsStreamEnvVar, "user:test_lifecycle")
|
||||
t.Setenv(redisLifecycleEventsStreamMaxLenEnvVar, "512")
|
||||
t.Setenv(postgresPrimaryDSNEnvVar, defaultPostgresDSN)
|
||||
t.Setenv(postgresReplicaDSNsEnvVar, "postgres://userservice:secret@replica-a/galaxy?sslmode=disable,postgres://userservice:secret@replica-b/galaxy?sslmode=disable")
|
||||
t.Setenv(postgresOperationTimeoutEnvVar, "2s")
|
||||
t.Setenv(postgresMaxOpenConnsEnvVar, "40")
|
||||
t.Setenv(postgresMaxIdleConnsEnvVar, "8")
|
||||
t.Setenv(postgresConnMaxLifetimeEnvVar, "45m")
|
||||
t.Setenv(otelServiceNameEnvVar, "galaxy-user-stage12")
|
||||
t.Setenv(otelTracesExporterEnvVar, "otlp")
|
||||
t.Setenv(otelMetricsExporterEnvVar, "otlp")
|
||||
@@ -60,15 +97,24 @@ func TestLoadFromEnvAppliesOverrides(t *testing.T) {
|
||||
require.Equal(t, 750*time.Millisecond, cfg.InternalHTTP.RequestTimeout)
|
||||
require.Equal(t, "127.0.0.1:19091", cfg.AdminHTTP.Addr)
|
||||
require.Equal(t, 90*time.Second, cfg.AdminHTTP.IdleTimeout)
|
||||
require.Equal(t, "127.0.0.1:6380", cfg.Redis.Addr)
|
||||
require.Equal(t, "alice", cfg.Redis.Username)
|
||||
require.Equal(t, "secret", cfg.Redis.Password)
|
||||
require.Equal(t, 3, cfg.Redis.DB)
|
||||
require.True(t, cfg.Redis.TLSEnabled)
|
||||
require.Equal(t, 900*time.Millisecond, cfg.Redis.OperationTimeout)
|
||||
require.Equal(t, "user:custom:", cfg.Redis.KeyspacePrefix)
|
||||
require.Equal(t, "127.0.0.1:6380", cfg.Redis.Conn.MasterAddr)
|
||||
require.Equal(t, []string{"127.0.0.1:6381", "127.0.0.1:6382"}, cfg.Redis.Conn.ReplicaAddrs)
|
||||
require.Equal(t, "redis-secret", cfg.Redis.Conn.Password)
|
||||
require.Equal(t, 3, cfg.Redis.Conn.DB)
|
||||
require.Equal(t, 900*time.Millisecond, cfg.Redis.Conn.OperationTimeout)
|
||||
require.Equal(t, "user:test_events", cfg.Redis.DomainEventsStream)
|
||||
require.Equal(t, int64(2048), cfg.Redis.DomainEventsStreamMaxLen)
|
||||
require.Equal(t, "user:test_lifecycle", cfg.Redis.LifecycleEventsStream)
|
||||
require.Equal(t, int64(512), cfg.Redis.LifecycleEventsStreamMaxLen)
|
||||
require.Equal(t, defaultPostgresDSN, cfg.Postgres.Conn.PrimaryDSN)
|
||||
require.Equal(t, []string{
|
||||
"postgres://userservice:secret@replica-a/galaxy?sslmode=disable",
|
||||
"postgres://userservice:secret@replica-b/galaxy?sslmode=disable",
|
||||
}, cfg.Postgres.Conn.ReplicaDSNs)
|
||||
require.Equal(t, 2*time.Second, cfg.Postgres.Conn.OperationTimeout)
|
||||
require.Equal(t, 40, cfg.Postgres.Conn.MaxOpenConns)
|
||||
require.Equal(t, 8, cfg.Postgres.Conn.MaxIdleConns)
|
||||
require.Equal(t, 45*time.Minute, cfg.Postgres.Conn.ConnMaxLifetime)
|
||||
require.Equal(t, "galaxy-user-stage12", cfg.Telemetry.ServiceName)
|
||||
require.Equal(t, "otlp", cfg.Telemetry.TracesExporter)
|
||||
require.Equal(t, "otlp", cfg.Telemetry.MetricsExporter)
|
||||
@@ -78,29 +124,90 @@ func TestLoadFromEnvAppliesOverrides(t *testing.T) {
|
||||
require.True(t, cfg.Telemetry.StdoutMetricsEnabled)
|
||||
}
|
||||
|
||||
// TestLoadFromEnvRejectsLegacyRedisVars verifies the architectural rule from
|
||||
// PG_PLAN.md §3 / ARCHITECTURE.md §Persistence Backends: legacy
|
||||
// USERSERVICE_REDIS_TLS_ENABLED and USERSERVICE_REDIS_USERNAME variables must
|
||||
// produce a startup error from `pkg/redisconn` so operators see the breaking
|
||||
// rename immediately.
|
||||
func TestLoadFromEnvRejectsLegacyRedisVars(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
envName string
|
||||
}{
|
||||
{name: "tls_enabled deprecated", envName: redisLegacyTLSEnabledEnvVar},
|
||||
{name: "username deprecated", envName: redisLegacyUsernameEnvVar},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Setenv(redisMasterAddrEnvVar, "127.0.0.1:6379")
|
||||
t.Setenv(redisPasswordEnvVar, "secret")
|
||||
t.Setenv(postgresPrimaryDSNEnvVar, defaultPostgresDSN)
|
||||
t.Setenv(tc.envName, "true")
|
||||
|
||||
_, err := LoadFromEnv()
|
||||
require.Error(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), "no longer supported"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoadFromEnvRequiresMandatoryFields covers the architectural rule that
|
||||
// Redis password, master address and Postgres primary DSN are mandatory;
|
||||
// missing any one returns a startup error.
|
||||
func TestLoadFromEnvRequiresMandatoryFields(t *testing.T) {
|
||||
t.Run("missing redis password", func(t *testing.T) {
|
||||
t.Setenv(redisMasterAddrEnvVar, "127.0.0.1:6379")
|
||||
t.Setenv(postgresPrimaryDSNEnvVar, defaultPostgresDSN)
|
||||
|
||||
_, err := LoadFromEnv()
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("missing redis master addr", func(t *testing.T) {
|
||||
t.Setenv(redisPasswordEnvVar, "secret")
|
||||
t.Setenv(postgresPrimaryDSNEnvVar, defaultPostgresDSN)
|
||||
|
||||
_, err := LoadFromEnv()
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("missing postgres dsn", func(t *testing.T) {
|
||||
t.Setenv(redisMasterAddrEnvVar, "127.0.0.1:6379")
|
||||
t.Setenv(redisPasswordEnvVar, "secret")
|
||||
|
||||
_, err := LoadFromEnv()
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLoadFromEnvRejectsInvalidValues(t *testing.T) {
|
||||
tests := []struct {
|
||||
cases := []struct {
|
||||
name string
|
||||
envName string
|
||||
envVal string
|
||||
}{
|
||||
{name: "invalid duration", envName: shutdownTimeoutEnvVar, envVal: "later"},
|
||||
{name: "invalid bool", envName: redisTLSEnabledEnvVar, envVal: "sometimes"},
|
||||
{name: "invalid log level", envName: logLevelEnvVar, envVal: "verbose"},
|
||||
{name: "invalid int", envName: redisDBEnvVar, envVal: "db-three"},
|
||||
{name: "invalid redis db", envName: redisDBEnvVar, envVal: "db-three"},
|
||||
{name: "invalid stream max len", envName: redisDomainEventsStreamMaxLenEnvVar, envVal: "many"},
|
||||
{name: "invalid traces exporter", envName: otelTracesExporterEnvVar, envVal: "zipkin"},
|
||||
{name: "invalid metrics protocol", envName: otelExporterOTLPMetricsProtocolEnvVar, envVal: "udp"},
|
||||
{name: "invalid postgres operation timeout", envName: postgresOperationTimeoutEnvVar, envVal: "soon"},
|
||||
{name: "invalid postgres max open conns", envName: postgresMaxOpenConnsEnvVar, envVal: "none"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Setenv(redisAddrEnvVar, "127.0.0.1:6379")
|
||||
t.Setenv(tt.envName, tt.envVal)
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Setenv(redisMasterAddrEnvVar, "127.0.0.1:6379")
|
||||
t.Setenv(redisPasswordEnvVar, "secret")
|
||||
t.Setenv(postgresPrimaryDSNEnvVar, defaultPostgresDSN)
|
||||
t.Setenv(tc.envName, tc.envVal)
|
||||
|
||||
_, err := LoadFromEnv()
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Suppress unused-warning for legacy keyspace prefix env reference: keep the
|
||||
// constant in test scope for documentation, though no current code uses it.
|
||||
var _ = redisLegacyAddrEnvVar
|
||||
var _ = redisLegacyKeyspacePrefixEnv
|
||||
|
||||
Reference in New Issue
Block a user