package config import ( "strings" "testing" "time" "github.com/stretchr/testify/require" ) func validEnv(t *testing.T) { t.Helper() t.Setenv("RTMANAGER_POSTGRES_PRIMARY_DSN", "postgres://rtm:secret@localhost:5432/galaxy?search_path=rtmanager&sslmode=disable") t.Setenv("RTMANAGER_REDIS_MASTER_ADDR", "localhost:6379") t.Setenv("RTMANAGER_REDIS_PASSWORD", "secret") t.Setenv("RTMANAGER_GAME_STATE_ROOT", "/var/lib/galaxy/games") t.Setenv("RTMANAGER_LOBBY_INTERNAL_BASE_URL", "http://lobby:8095") } func TestLoadFromEnvAcceptsDefaults(t *testing.T) { validEnv(t) cfg, err := LoadFromEnv() require.NoError(t, err) require.Equal(t, ":8096", cfg.InternalHTTP.Addr) require.Equal(t, "unix:///var/run/docker.sock", cfg.Docker.Host) require.Equal(t, "galaxy-net", cfg.Docker.Network) require.Equal(t, "json-file", cfg.Docker.LogDriver) require.Equal(t, ImagePullPolicyIfMissing, cfg.Docker.PullPolicy) require.Equal(t, "runtime:start_jobs", cfg.Streams.StartJobs) require.Equal(t, "runtime:stop_jobs", cfg.Streams.StopJobs) require.Equal(t, "runtime:job_results", cfg.Streams.JobResults) require.Equal(t, "runtime:health_events", cfg.Streams.HealthEvents) require.Equal(t, "notification:intents", cfg.Streams.NotificationIntents) require.Equal(t, 30*time.Second, cfg.Container.StopTimeout) require.Equal(t, 30*24*time.Hour, cfg.Container.Retention) require.Equal(t, "/var/lib/galaxy-game", cfg.Container.EngineStateMountPath) require.Equal(t, "GAME_STATE_PATH", cfg.Container.EngineStateEnvName) require.EqualValues(t, 0o750, cfg.Container.GameStateDirMode) require.Equal(t, 60*time.Second, cfg.Coordination.GameLeaseTTL) require.Equal(t, "http://lobby:8095", cfg.Lobby.BaseURL) require.Equal(t, 2*time.Second, cfg.Lobby.Timeout) require.Equal(t, "galaxy-rtmanager", cfg.Telemetry.ServiceName) } func TestLoadFromEnvHonoursOverrides(t *testing.T) { validEnv(t) t.Setenv("RTMANAGER_INTERNAL_HTTP_ADDR", ":9000") t.Setenv("RTMANAGER_DOCKER_NETWORK", "custom-net") t.Setenv("RTMANAGER_IMAGE_PULL_POLICY", "always") t.Setenv("RTMANAGER_REDIS_START_JOBS_STREAM", "custom:start_jobs") t.Setenv("RTMANAGER_GAME_LEASE_TTL_SECONDS", "120") t.Setenv("RTMANAGER_CONTAINER_STOP_TIMEOUT_SECONDS", "45") t.Setenv("RTMANAGER_CONTAINER_RETENTION_DAYS", "7") t.Setenv("RTMANAGER_GAME_STATE_DIR_MODE", "0700") cfg, err := LoadFromEnv() require.NoError(t, err) require.Equal(t, ":9000", cfg.InternalHTTP.Addr) require.Equal(t, "custom-net", cfg.Docker.Network) require.Equal(t, ImagePullPolicyAlways, cfg.Docker.PullPolicy) require.Equal(t, "custom:start_jobs", cfg.Streams.StartJobs) require.Equal(t, 120*time.Second, cfg.Coordination.GameLeaseTTL) require.Equal(t, 45*time.Second, cfg.Container.StopTimeout) require.Equal(t, 7*24*time.Hour, cfg.Container.Retention) require.EqualValues(t, 0o700, cfg.Container.GameStateDirMode) } func TestLoadFromEnvRejectsUnknownPullPolicy(t *testing.T) { validEnv(t) t.Setenv("RTMANAGER_IMAGE_PULL_POLICY", "weekly") _, err := LoadFromEnv() require.Error(t, err) require.Contains(t, err.Error(), "image pull policy") } func TestLoadFromEnvRequiresGameStateRoot(t *testing.T) { t.Setenv("RTMANAGER_POSTGRES_PRIMARY_DSN", "postgres://rtm:secret@localhost:5432/galaxy") t.Setenv("RTMANAGER_REDIS_MASTER_ADDR", "localhost:6379") t.Setenv("RTMANAGER_REDIS_PASSWORD", "secret") t.Setenv("RTMANAGER_LOBBY_INTERNAL_BASE_URL", "http://lobby:8095") _, err := LoadFromEnv() require.Error(t, err) require.Contains(t, err.Error(), "RTMANAGER_GAME_STATE_ROOT") } func TestLoadFromEnvRequiresLobbyBaseURL(t *testing.T) { t.Setenv("RTMANAGER_POSTGRES_PRIMARY_DSN", "postgres://rtm:secret@localhost:5432/galaxy") t.Setenv("RTMANAGER_REDIS_MASTER_ADDR", "localhost:6379") t.Setenv("RTMANAGER_REDIS_PASSWORD", "secret") t.Setenv("RTMANAGER_GAME_STATE_ROOT", "/var/lib/galaxy/games") _, err := LoadFromEnv() require.Error(t, err) require.Contains(t, err.Error(), "RTMANAGER_LOBBY_INTERNAL_BASE_URL") } func TestLoadFromEnvRejectsRelativeStateRoot(t *testing.T) { validEnv(t) t.Setenv("RTMANAGER_GAME_STATE_ROOT", "relative/path") _, err := LoadFromEnv() require.Error(t, err) require.Contains(t, err.Error(), "absolute path") } func TestLoadFromEnvRejectsBadLogLevel(t *testing.T) { validEnv(t) t.Setenv("RTMANAGER_LOG_LEVEL", "verbose") _, err := LoadFromEnv() require.Error(t, err) require.Contains(t, err.Error(), "RTMANAGER_LOG_LEVEL") } func TestImagePullPolicyValidate(t *testing.T) { require.NoError(t, ImagePullPolicyIfMissing.Validate()) require.NoError(t, ImagePullPolicyAlways.Validate()) require.NoError(t, ImagePullPolicyNever.Validate()) require.Error(t, ImagePullPolicy("monthly").Validate()) } func TestInternalHTTPValidateRejectsBadAddr(t *testing.T) { cfg := DefaultConfig().InternalHTTP cfg.Addr = "not-an-addr" err := cfg.Validate() require.Error(t, err) require.Contains(t, err.Error(), "host:port") } func TestStreamsValidateRequiresAllNames(t *testing.T) { cfg := DefaultConfig().Streams cfg.StartJobs = " " err := cfg.Validate() require.Error(t, err) require.True(t, strings.Contains(err.Error(), "start jobs")) }