package runtimeconfig import ( "testing" "time" ) func setAgentEnvironment(t *testing.T) { t.Helper() t.Setenv("PULSE_DATABASE_URL", "postgres://pulse:secret@postgres:5432/pulse?sslmode=disable") t.Setenv("PULSE_AGENT_ID", "") t.Setenv("PULSE_AGENT_COLLECT_INTERVAL", "") t.Setenv("PULSE_AGENT_PROC_ROOT", "") t.Setenv("PULSE_AGENT_SYS_ROOT", "") t.Setenv("PULSE_AGENT_FS_ROOT", "") t.Setenv("PULSE_AGENT_HOST_NAME", "") t.Setenv("PULSE_AGENT_MAX_PROCESSES", "") t.Setenv("PULSE_UNRAID_URL", "") t.Setenv("PULSE_UNRAID_API_TOKEN", "") t.Setenv("PULSE_UNRAID_CA_FILE", "") t.Setenv("PULSE_HEARTBEAT_FILE", "") } func TestAgentPrivateCARequiresConfiguredHTTPSUnraidSource(t *testing.T) { setAgentEnvironment(t) t.Setenv("PULSE_UNRAID_CA_FILE", "/run/pulse/unraid-ca.pem") if _, err := LoadAgent(); err == nil { t.Fatal("expected CA without Unraid source rejection") } t.Setenv("PULSE_UNRAID_URL", "https://unraid.example.test:5001/graphql") t.Setenv("PULSE_UNRAID_API_TOKEN", "viewer-key") config, err := LoadAgent() if err != nil { t.Fatal(err) } if config.UnraidCAFile != "/run/pulse/unraid-ca.pem" { t.Fatalf("CA file = %q", config.UnraidCAFile) } } func TestLoadAgentRequiresCompleteOptionalUnraidSource(t *testing.T) { setAgentEnvironment(t) t.Setenv("PULSE_UNRAID_URL", "https://tower.example.invalid/graphql") if _, err := LoadAgent(); err == nil { t.Fatal("expected incomplete Unraid configuration to fail") } t.Setenv("PULSE_UNRAID_API_TOKEN", "test-key") if _, err := LoadAgent(); err != nil { t.Fatalf("complete HTTPS Unraid configuration failed: %v", err) } t.Setenv("PULSE_UNRAID_URL", "http://tower.example.invalid/graphql") if _, err := LoadAgent(); err == nil { t.Fatal("expected non-HTTPS Unraid URL to fail") } } func TestLoadAgentUsesSafeDefaults(t *testing.T) { setAgentEnvironment(t) config, err := LoadAgent() if err != nil { t.Fatalf("LoadAgent returned error: %v", err) } if config.AgentID != defaultAgentID { t.Fatalf("agent id = %q", config.AgentID) } if config.CollectInterval != defaultCollectInterval { t.Fatalf("interval = %s", config.CollectInterval) } if config.ProcRoot != "/proc" || config.SysRoot != "/sys" { t.Fatalf("unexpected roots: %+v", config) } if config.FilesystemRoot != "" { t.Fatal("filesystem collection must stay opt-in") } if config.Service.HeartbeatFile != defaultHeartbeatFile { t.Fatalf("heartbeat file = %q", config.Service.HeartbeatFile) } } func TestLoadAgentRequiresADatabaseURL(t *testing.T) { setAgentEnvironment(t) t.Setenv("PULSE_DATABASE_URL", "") if _, err := LoadAgent(); err == nil { t.Fatal("expected a missing database URL to fail") } t.Setenv("PULSE_DATABASE_URL", "mysql://nope") if _, err := LoadAgent(); err == nil { t.Fatal("expected a non-PostgreSQL URL to fail") } } func TestLoadAgentRejectsOutOfRangeValues(t *testing.T) { cases := map[string]map[string]string{ "interval too small": {"PULSE_AGENT_COLLECT_INTERVAL": "10ms"}, "interval too large": {"PULSE_AGENT_COLLECT_INTERVAL": "10m"}, "interval malformed": {"PULSE_AGENT_COLLECT_INTERVAL": "soon"}, "relative proc root": {"PULSE_AGENT_PROC_ROOT": "proc"}, "relative sys root": {"PULSE_AGENT_SYS_ROOT": "sys"}, "relative fs root": {"PULSE_AGENT_FS_ROOT": "host"}, "row count too large": {"PULSE_AGENT_MAX_PROCESSES": "99999"}, "row count malformed": {"PULSE_AGENT_MAX_PROCESSES": "many"}, "relative heartbeat": {"PULSE_HEARTBEAT_FILE": "healthy"}, } for name, environment := range cases { t.Run(name, func(t *testing.T) { setAgentEnvironment(t) for key, value := range environment { t.Setenv(key, value) } if _, err := LoadAgent(); err == nil { t.Fatal("expected a validation error") } }) } } func TestLoadAgentAcceptsExplicitSettings(t *testing.T) { setAgentEnvironment(t) t.Setenv("PULSE_AGENT_ID", "tower-agent") t.Setenv("PULSE_AGENT_COLLECT_INTERVAL", "30s") t.Setenv("PULSE_AGENT_PROC_ROOT", "/host/proc") t.Setenv("PULSE_AGENT_SYS_ROOT", "/host/sys") t.Setenv("PULSE_AGENT_FS_ROOT", "/host/root") t.Setenv("PULSE_AGENT_HOST_NAME", "tower") t.Setenv("PULSE_AGENT_MAX_PROCESSES", "250") t.Setenv("PULSE_HEARTBEAT_FILE", "/tmp/agent-healthy") config, err := LoadAgent() if err != nil { t.Fatalf("LoadAgent returned error: %v", err) } if config.AgentID != "tower-agent" || config.CollectInterval != 30*time.Second { t.Fatalf("unexpected config: %+v", config) } if config.ProcRoot != "/host/proc" || config.SysRoot != "/host/sys" || config.FilesystemRoot != "/host/root" { t.Fatalf("unexpected roots: %+v", config) } if config.HostName != "tower" || config.MaxProcesses != 250 { t.Fatalf("unexpected identity or limits: %+v", config) } if config.Service.HeartbeatFile != "/tmp/agent-healthy" { t.Fatalf("heartbeat file = %q", config.Service.HeartbeatFile) } }