agent-orchestrator/backend/internal/config/config_test.go

161 lines
5.5 KiB
Go

package config
import (
"os"
"path/filepath"
"testing"
"time"
)
func TestLoadDefaults(t *testing.T) {
// Clear every recognised var so we observe pure defaults regardless of the
// surrounding environment.
for _, k := range []string{"AO_PORT", "AO_REQUEST_TIMEOUT", "AO_SHUTDOWN_TIMEOUT", "AO_RUN_FILE", "AO_DATA_DIR", "AO_AGENT", "AO_ALLOWED_ORIGINS", "AO_TELEMETRY_EVENTS", "AO_TELEMETRY_METRICS", "AO_TELEMETRY_REMOTE", "AO_TELEMETRY_POSTHOG_KEY", "AO_TELEMETRY_POSTHOG_HOST"} {
t.Setenv(k, "")
}
cfg, err := Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
if cfg.Host != LoopbackHost {
t.Errorf("Host = %q, want %q", cfg.Host, LoopbackHost)
}
if cfg.Port != DefaultPort {
t.Errorf("Port = %d, want %d", cfg.Port, DefaultPort)
}
if cfg.RequestTimeout != DefaultRequestTimeout {
t.Errorf("RequestTimeout = %s, want %s", cfg.RequestTimeout, DefaultRequestTimeout)
}
if cfg.ShutdownTimeout != DefaultShutdownTimeout {
t.Errorf("ShutdownTimeout = %s, want %s", cfg.ShutdownTimeout, DefaultShutdownTimeout)
}
if cfg.RunFilePath == "" {
t.Error("RunFilePath is empty, want a resolved default path")
}
homeDir, err := os.UserHomeDir()
if err != nil {
t.Fatalf("UserHomeDir: %v", err)
}
wantRunFilePath := filepath.Join(homeDir, ".ao", "running.json")
if cfg.RunFilePath != wantRunFilePath {
t.Errorf("RunFilePath = %q, want %q", cfg.RunFilePath, wantRunFilePath)
}
if cfg.DataDir == "" {
t.Error("DataDir is empty, want a resolved default path")
}
wantDataDir := filepath.Join(homeDir, ".ao", "data")
if cfg.DataDir != wantDataDir {
t.Errorf("DataDir = %q, want %q", cfg.DataDir, wantDataDir)
}
if cfg.Telemetry.Remote != TelemetryRemoteOff || cfg.Telemetry.PostHogHost != DefaultTelemetryPostHogHost {
t.Fatalf("Telemetry defaults = %+v", cfg.Telemetry)
}
}
func TestLoadOverrides(t *testing.T) {
t.Setenv("AO_PORT", "4002")
t.Setenv("AO_REQUEST_TIMEOUT", "5s")
t.Setenv("AO_SHUTDOWN_TIMEOUT", "3s")
t.Setenv("AO_RUN_FILE", "/tmp/ao-test-running.json")
t.Setenv("AO_DATA_DIR", "/tmp/ao-test-data")
t.Setenv("AO_TELEMETRY_EVENTS", "on")
t.Setenv("AO_TELEMETRY_METRICS", "off")
t.Setenv("AO_TELEMETRY_REMOTE", "posthog")
t.Setenv("AO_TELEMETRY_POSTHOG_KEY", "phc_test")
t.Setenv("AO_TELEMETRY_POSTHOG_HOST", "https://eu.i.posthog.com")
cfg, err := Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
if cfg.Addr() != "127.0.0.1:4002" {
t.Errorf("Addr() = %q, want 127.0.0.1:4002", cfg.Addr())
}
if cfg.RequestTimeout != 5*time.Second {
t.Errorf("RequestTimeout = %s, want 5s", cfg.RequestTimeout)
}
if cfg.ShutdownTimeout != 3*time.Second {
t.Errorf("ShutdownTimeout = %s, want 3s", cfg.ShutdownTimeout)
}
if cfg.RunFilePath != "/tmp/ao-test-running.json" {
t.Errorf("RunFilePath = %q, want /tmp/ao-test-running.json", cfg.RunFilePath)
}
if cfg.DataDir != "/tmp/ao-test-data" {
t.Errorf("DataDir = %q, want /tmp/ao-test-data", cfg.DataDir)
}
if !cfg.Telemetry.Events || cfg.Telemetry.Metrics {
t.Fatalf("Telemetry toggles = %+v", cfg.Telemetry)
}
if cfg.Telemetry.Remote != TelemetryRemotePostHog || cfg.Telemetry.PostHogKey != "phc_test" || cfg.Telemetry.PostHogHost != "https://eu.i.posthog.com" {
t.Fatalf("Telemetry remote = %+v", cfg.Telemetry)
}
}
func TestLoadInvalid(t *testing.T) {
tests := []struct {
name string
env map[string]string
}{
{"non-numeric port", map[string]string{"AO_PORT": "abc"}},
{"port out of range", map[string]string{"AO_PORT": "70000"}},
{"bad request timeout", map[string]string{"AO_REQUEST_TIMEOUT": "soon"}},
{"bad shutdown timeout", map[string]string{"AO_SHUTDOWN_TIMEOUT": "later"}},
{"zero request timeout", map[string]string{"AO_REQUEST_TIMEOUT": "0s"}},
{"negative request timeout", map[string]string{"AO_REQUEST_TIMEOUT": "-1s"}},
{"zero shutdown timeout", map[string]string{"AO_SHUTDOWN_TIMEOUT": "0s"}},
{"negative shutdown timeout", map[string]string{"AO_SHUTDOWN_TIMEOUT": "-5s"}},
{"null origin", map[string]string{"AO_ALLOWED_ORIGINS": "app://renderer,null"}},
{"wildcard origin", map[string]string{"AO_ALLOWED_ORIGINS": "*"}},
{"bad telemetry events", map[string]string{"AO_TELEMETRY_EVENTS": "maybe"}},
{"bad telemetry metrics", map[string]string{"AO_TELEMETRY_METRICS": "maybe"}},
{"bad telemetry remote", map[string]string{"AO_TELEMETRY_REMOTE": "otlp"}},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
for k, v := range tc.env {
t.Setenv(k, v)
}
if _, err := Load(); err == nil {
t.Fatal("Load() = nil error, want error")
}
})
}
}
func TestLoadAllowedOrigins(t *testing.T) {
t.Run("default includes the packaged renderer origin", func(t *testing.T) {
t.Setenv("AO_ALLOWED_ORIGINS", "")
cfg, err := Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
found := false
for _, origin := range cfg.AllowedOrigins {
if origin == "app://renderer" {
found = true
}
}
if !found {
t.Errorf("AllowedOrigins = %v, want app://renderer included", cfg.AllowedOrigins)
}
})
t.Run("override replaces defaults and trims entries", func(t *testing.T) {
t.Setenv("AO_ALLOWED_ORIGINS", " app://renderer , http://localhost:9999 ,")
cfg, err := Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
want := []string{"app://renderer", "http://localhost:9999"}
if len(cfg.AllowedOrigins) != len(want) {
t.Fatalf("AllowedOrigins = %v, want %v", cfg.AllowedOrigins, want)
}
for i, origin := range want {
if cfg.AllowedOrigins[i] != origin {
t.Errorf("AllowedOrigins[%d] = %q, want %q", i, cfg.AllowedOrigins[i], origin)
}
}
})
}