package config import ( "os" "path/filepath" "testing" ) const exampleHCL = ` db_path = "/tmp/orca/test.db" listen_addr = "127.0.0.1:9999" ca_path = "/tmp/orca/ca.crt" server_cert_path = "/tmp/orca/server.crt" server_key_path = "/tmp/orca/server.key" node_capacity { cpu = 4 memory_mb = 8192 } ` func writeFile(t *testing.T, dir, name, content string) string { t.Helper() p := filepath.Join(dir, name) if err := os.WriteFile(p, []byte(content), 0644); err != nil { t.Fatalf("write %s: %v", p, err) } return p } func TestLoad_Valid(t *testing.T) { p := writeFile(t, t.TempDir(), "config.hcl", exampleHCL) cfg, err := Load(p) if err != nil { t.Fatalf("Load: %v", err) } if cfg.DBPath != "/tmp/orca/test.db" { t.Errorf("DBPath=%q", cfg.DBPath) } if cfg.ListenAddr != "127.0.0.1:9999" { t.Errorf("ListenAddr=%q", cfg.ListenAddr) } if cfg.CAPath != "/tmp/orca/ca.crt" { t.Errorf("CAPath=%q", cfg.CAPath) } if cfg.ServerCertPath != "/tmp/orca/server.crt" { t.Errorf("ServerCertPath=%q", cfg.ServerCertPath) } if cfg.ServerKeyPath != "/tmp/orca/server.key" { t.Errorf("ServerKeyPath=%q", cfg.ServerKeyPath) } if cfg.NodeCapacity == nil { t.Fatal("NodeCapacity nil") } if cfg.NodeCapacity.CPU != 4 { t.Errorf("CPU=%d", cfg.NodeCapacity.CPU) } if cfg.NodeCapacity.MemoryMB != 8192 { t.Errorf("MemoryMB=%d", cfg.NodeCapacity.MemoryMB) } } func TestLoad_Missing(t *testing.T) { cfg, err := Load(filepath.Join(t.TempDir(), "nope.hcl")) if err != nil { t.Fatalf("Load: %v", err) } if cfg == nil { t.Fatal("nil config") } if cfg.DBPath != "" || cfg.ListenAddr != "" || cfg.NodeCapacity != nil { t.Errorf("expected zero config, got %+v", cfg) } } func TestLoad_Malformed(t *testing.T) { p := writeFile(t, t.TempDir(), "bad.hcl", "db_path = ") cfg, err := Load(p) if err == nil { t.Fatalf("expected error, got %+v", cfg) } } func TestLoad_FirstExisting(t *testing.T) { dir := t.TempDir() existing := writeFile(t, dir, "real.hcl", exampleHCL) missing := filepath.Join(dir, "missing.hcl") cfg, err := Load(missing, existing) if err != nil { t.Fatalf("Load: %v", err) } if cfg.DBPath != "/tmp/orca/test.db" { t.Errorf("DBPath=%q", cfg.DBPath) } } func strPtr(s string) *string { return &s } func intPtr(i int) *int { return &i } func TestMergeOverrides_FlagWins(t *testing.T) { cfg := &Config{ DBPath: "/file.db", ListenAddr: "127.0.0.1:9000", NodeCapacity: &CapacityConfig{ CPU: 4, MemoryMB: 8192, }, } flags := Flags{ DBPath: strPtr("/flag.db"), ListenAddr: strPtr("0.0.0.0:1234"), } env := Environ{"ORCA_DB": "/env.db"} out := cfg.MergeOverrides(flags, env) if out.DBPath != "/flag.db" { t.Errorf("DBPath=%q want /flag.db", out.DBPath) } if out.ListenAddr != "0.0.0.0:1234" { t.Errorf("ListenAddr=%q want 0.0.0.0:1234", out.ListenAddr) } if cfg.DBPath != "/file.db" { t.Errorf("receiver mutated: %q", cfg.DBPath) } } func TestMergeOverrides_EnvWinsOverFile(t *testing.T) { cfg := &Config{DBPath: "/file.db", ListenAddr: "127.0.0.1:9000"} env := Environ{"ORCA_DB": "/env.db"} out := cfg.MergeOverrides(Flags{}, env) if out.DBPath != "/env.db" { t.Errorf("DBPath=%q want /env.db", out.DBPath) } if out.ListenAddr != "127.0.0.1:9000" { t.Errorf("ListenAddr=%q want 127.0.0.1:9000", out.ListenAddr) } } func TestMergeOverrides_FileWinsOverDefault(t *testing.T) { cfg := &Config{DBPath: "/file.db", ListenAddr: "127.0.0.1:9000"} out := cfg.MergeOverrides(Flags{}, Environ{}) if out.DBPath != "/file.db" { t.Errorf("DBPath=%q want /file.db", out.DBPath) } if out.ListenAddr != "127.0.0.1:9000" { t.Errorf("ListenAddr=%q want 127.0.0.1:9000", out.ListenAddr) } } func TestMergeOverrides_EmptyFlagDoesNotOverride(t *testing.T) { cfg := &Config{DBPath: "/file.db"} env := Environ{"ORCA_DB": "/env.db"} out := cfg.MergeOverrides(Flags{}, env) if out.DBPath != "/env.db" { t.Errorf("DBPath=%q want /env.db", out.DBPath) } } func TestMergeOverrides_EmptyEnvDoesNotOverride(t *testing.T) { cfg := &Config{DBPath: "/file.db"} env := Environ{"ORCA_DB": ""} out := cfg.MergeOverrides(Flags{}, env) if out.DBPath != "/file.db" { t.Errorf("DBPath=%q want /file.db", out.DBPath) } } func TestMergeOverrides_NodeCapacity(t *testing.T) { cfg := &Config{ NodeCapacity: &CapacityConfig{CPU: 4, MemoryMB: 8192}, } out := cfg.MergeOverrides(Flags{}, Environ{}) if out.NodeCapacity == nil { t.Fatal("NodeCapacity nil") } if out.NodeCapacity.CPU != 4 { t.Errorf("CPU=%d want 4", out.NodeCapacity.CPU) } if out.NodeCapacity.MemoryMB != 8192 { t.Errorf("MemoryMB=%d want 8192", out.NodeCapacity.MemoryMB) } if cfg.NodeCapacity == out.NodeCapacity { t.Error("NodeCapacity not cloned") } } func TestMergeOverrides_NodeCapacityFlagAndEnv(t *testing.T) { cfg := &Config{NodeCapacity: &CapacityConfig{CPU: 4, MemoryMB: 8192}} flags := Flags{CPU: intPtr(8)} env := Environ{"ORCA_NODE_MEMORY_MB": "16384"} out := cfg.MergeOverrides(flags, env) if out.NodeCapacity.CPU != 8 { t.Errorf("CPU=%d want 8", out.NodeCapacity.CPU) } if out.NodeCapacity.MemoryMB != 16384 { t.Errorf("MemoryMB=%d want 16384", out.NodeCapacity.MemoryMB) } }