package config import ( "os" "testing" "time" ) func TestDefault(t *testing.T) { cfg := Default() if cfg.Server.GrpcAddr != ":50051" { t.Errorf("expected default grpc_addr :50051, got %s", cfg.Server.GrpcAddr) } if cfg.Database.Path != "relay.db" { t.Errorf("expected default db path relay.db, got %s", cfg.Database.Path) } if cfg.Metrics.Enabled != true { t.Error("expected metrics enabled by default") } } func TestLoadYAML(t *testing.T) { // Create temporary config file tmpfile, err := os.CreateTemp("", "config-*.yaml") if err != nil { t.Fatal(err) } defer os.Remove(tmpfile.Name()) configData := ` server: grpc_addr: ":9999" http_addr: ":8888" database: path: "test.db" auth: enabled: true required: true timestamp_window: 120 rate_limit: enabled: true default_rps: 50 default_burst: 100 metrics: enabled: true addr: ":9191" namespace: "test" ` if _, err := tmpfile.Write([]byte(configData)); err != nil { t.Fatal(err) } tmpfile.Close() // Load config cfg, err := Load(tmpfile.Name()) if err != nil { t.Fatalf("failed to load config: %v", err) } // Verify values if cfg.Server.GrpcAddr != ":9999" { t.Errorf("expected grpc_addr :9999, got %s", cfg.Server.GrpcAddr) } if cfg.Database.Path != "test.db" { t.Errorf("expected db path test.db, got %s", cfg.Database.Path) } if !cfg.Auth.Enabled { t.Error("expected auth enabled") } if !cfg.Auth.Required { t.Error("expected auth required") } if cfg.Auth.TimestampWindow != 120 { t.Errorf("expected timestamp window 120, got %d", cfg.Auth.TimestampWindow) } if cfg.RateLimit.DefaultRPS != 50 { t.Errorf("expected rate limit 50, got %.1f", cfg.RateLimit.DefaultRPS) } if cfg.Metrics.Namespace != "test" { t.Errorf("expected metrics namespace test, got %s", cfg.Metrics.Namespace) } } func TestEnvOverrides(t *testing.T) { // Set environment variables os.Setenv("MUXSTR_SERVER_GRPC_ADDR", ":7777") os.Setenv("MUXSTR_AUTH_ENABLED", "true") os.Setenv("MUXSTR_RATE_LIMIT_DEFAULT_RPS", "200") defer func() { os.Unsetenv("MUXSTR_SERVER_GRPC_ADDR") os.Unsetenv("MUXSTR_AUTH_ENABLED") os.Unsetenv("MUXSTR_RATE_LIMIT_DEFAULT_RPS") }() // Load with empty file (just defaults + env) cfg, err := Load("") if err != nil { t.Fatalf("failed to load config: %v", err) } // Verify env overrides if cfg.Server.GrpcAddr != ":7777" { t.Errorf("expected env override :7777, got %s", cfg.Server.GrpcAddr) } if !cfg.Auth.Enabled { t.Error("expected auth enabled from env") } if cfg.RateLimit.DefaultRPS != 200 { t.Errorf("expected rate limit 200 from env, got %.1f", cfg.RateLimit.DefaultRPS) } } func TestValidation(t *testing.T) { tests := []struct { name string cfg *Config wantErr bool }{ { name: "valid default config", cfg: Default(), wantErr: false, }, { name: "missing grpc_addr", cfg: &Config{ Server: ServerConfig{ HttpAddr: ":8080", }, Database: DatabaseConfig{ Path: "test.db", }, }, wantErr: true, }, { name: "missing http_addr", cfg: &Config{ Server: ServerConfig{ GrpcAddr: ":50051", }, Database: DatabaseConfig{ Path: "test.db", }, }, wantErr: true, }, { name: "missing database path", cfg: &Config{ Server: ServerConfig{ GrpcAddr: ":50051", HttpAddr: ":8080", }, Database: DatabaseConfig{}, }, wantErr: true, }, { name: "invalid log level", cfg: &Config{ Server: ServerConfig{ GrpcAddr: ":50051", HttpAddr: ":8080", }, Database: DatabaseConfig{ Path: "test.db", }, Logging: LoggingConfig{ Level: "invalid", Format: "json", }, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.cfg.Validate() if (err != nil) != tt.wantErr { t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestSaveAndLoad(t *testing.T) { // Create config cfg := Default() cfg.Server.GrpcAddr = ":9999" cfg.Auth.Enabled = true cfg.RateLimit.DefaultRPS = 100 // Save to temp file tmpfile, err := os.CreateTemp("", "config-*.yaml") if err != nil { t.Fatal(err) } defer os.Remove(tmpfile.Name()) tmpfile.Close() if err := cfg.Save(tmpfile.Name()); err != nil { t.Fatalf("failed to save config: %v", err) } // Load it back loaded, err := Load(tmpfile.Name()) if err != nil { t.Fatalf("failed to load config: %v", err) } // Verify if loaded.Server.GrpcAddr != ":9999" { t.Errorf("expected grpc_addr :9999, got %s", loaded.Server.GrpcAddr) } if !loaded.Auth.Enabled { t.Error("expected auth enabled") } if loaded.RateLimit.DefaultRPS != 100 { t.Errorf("expected rate limit 100, got %.1f", loaded.RateLimit.DefaultRPS) } } func TestDurationParsing(t *testing.T) { // Create config with durations tmpfile, err := os.CreateTemp("", "config-*.yaml") if err != nil { t.Fatal(err) } defer os.Remove(tmpfile.Name()) configData := ` server: grpc_addr: ":50051" http_addr: ":8080" read_timeout: "1m" write_timeout: "2m" database: path: "test.db" max_lifetime: "30m" rate_limit: cleanup_interval: "10m" max_idle_time: "20m" ` if _, err := tmpfile.Write([]byte(configData)); err != nil { t.Fatal(err) } tmpfile.Close() cfg, err := Load(tmpfile.Name()) if err != nil { t.Fatalf("failed to load config: %v", err) } if cfg.Server.ReadTimeout != 1*time.Minute { t.Errorf("expected read timeout 1m, got %v", cfg.Server.ReadTimeout) } if cfg.Server.WriteTimeout != 2*time.Minute { t.Errorf("expected write timeout 2m, got %v", cfg.Server.WriteTimeout) } if cfg.Database.MaxLifetime != 30*time.Minute { t.Errorf("expected max lifetime 30m, got %v", cfg.Database.MaxLifetime) } }