package config_test import ( "os" "path/filepath" "testing" "workflow/config" ) func TestLoad_Defaults(t *testing.T) { // No file, no env vars — should return built-in defaults cfg := config.Load("/nonexistent/path/that/does/not/exist") if cfg.LLM.URL != "http://localhost:4000" { t.Errorf("LLM.URL = %q, want default", cfg.LLM.URL) } if cfg.LLM.Model != "gpt-4o" { t.Errorf("LLM.Model = %q, want default", cfg.LLM.Model) } if cfg.Workspace.Root != "./workspace" { t.Errorf("Workspace.Root = %q, want default", cfg.Workspace.Root) } if cfg.Server.Port != "8080" { t.Errorf("Server.Port = %q, want default", cfg.Server.Port) } } func TestLoad_FromFile(t *testing.T) { content := ` # comment LLM_URL=http://my-llm:5000 LLM_KEY=sk-test-key LLM_MODEL=gpt-4-turbo WORKSPACE_ROOT=/tmp/ws PORT=9090 ` f := filepath.Join(t.TempDir(), "test.env") if err := os.WriteFile(f, []byte(content), 0600); err != nil { t.Fatal(err) } cfg := config.Load(f) if cfg.LLM.URL != "http://my-llm:5000" { t.Errorf("LLM.URL = %q", cfg.LLM.URL) } if cfg.LLM.Key != "sk-test-key" { t.Errorf("LLM.Key = %q", cfg.LLM.Key) } if cfg.LLM.Model != "gpt-4-turbo" { t.Errorf("LLM.Model = %q", cfg.LLM.Model) } if cfg.Workspace.Root != "/tmp/ws" { t.Errorf("Workspace.Root = %q", cfg.Workspace.Root) } if cfg.Server.Port != "9090" { t.Errorf("Server.Port = %q", cfg.Server.Port) } } func TestLoad_QuotedValues(t *testing.T) { content := `LLM_KEY="sk-quoted-key"` + "\n" + `LLM_MODEL='gpt-4o'` + "\n" f := filepath.Join(t.TempDir(), "test.env") os.WriteFile(f, []byte(content), 0600) cfg := config.Load(f) if cfg.LLM.Key != "sk-quoted-key" { t.Errorf("quoted double: LLM.Key = %q, want sk-quoted-key", cfg.LLM.Key) } if cfg.LLM.Model != "gpt-4o" { t.Errorf("quoted single: LLM.Model = %q, want gpt-4o", cfg.LLM.Model) } } func TestLoad_EnvVarOverridesFile(t *testing.T) { content := "LLM_KEY=from-file\nLLM_MODEL=from-file-model\n" f := filepath.Join(t.TempDir(), "test.env") os.WriteFile(f, []byte(content), 0600) // Set env var that should override file t.Setenv("LLM_KEY", "from-env") cfg := config.Load(f) if cfg.LLM.Key != "from-env" { t.Errorf("env var should win: LLM.Key = %q, want from-env", cfg.LLM.Key) } // Non-overridden field still comes from file if cfg.LLM.Model != "from-file-model" { t.Errorf("file value: LLM.Model = %q, want from-file-model", cfg.LLM.Model) } } func TestLoad_WorkflowConfigEnv(t *testing.T) { content := "LLM_KEY=from-workflow-config\n" f := filepath.Join(t.TempDir(), "custom.env") os.WriteFile(f, []byte(content), 0600) t.Setenv("WORKFLOW_CONFIG", f) cfg := config.Load("") // empty explicit path — should use WORKFLOW_CONFIG if cfg.LLM.Key != "from-workflow-config" { t.Errorf("WORKFLOW_CONFIG: LLM.Key = %q, want from-workflow-config", cfg.LLM.Key) } } func TestLoad_MissingFileUsesDefaults(t *testing.T) { cfg := config.Load("") // Should not panic; returns defaults if cfg.LLM.URL == "" { t.Error("LLM.URL should not be empty") } } func TestMaskKey(t *testing.T) { got := config.MaskKey("") if got != "(not set)" { t.Errorf("MaskKey(\"\") = %q, want \"(not set)\"", got) } got = config.MaskKey("sk-1234567890abcdef") if got == "sk-1234567890abcdef" { t.Error("MaskKey should not return plaintext key") } if len(got) == 0 { t.Error("MaskKey should not return empty string") } // Short key (≤ 8 chars) → all stars got = config.MaskKey("abc") for _, c := range got { if c != '*' { t.Errorf("MaskKey(short): expected all stars, got %q", got) break } } }