177 lines
4.8 KiB
Go
177 lines
4.8 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestFileConfigPath_XDGOverride(t *testing.T) {
|
|
t.Setenv("XDG_CONFIG_HOME", "/tmp/xdgroot")
|
|
got := FileConfigPath()
|
|
want := filepath.Join("/tmp/xdgroot", "pigo", "config.toml")
|
|
if got != want {
|
|
t.Fatalf("FileConfigPath() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFileConfigPath_DefaultHome(t *testing.T) {
|
|
t.Setenv("XDG_CONFIG_HOME", "")
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
t.Skip("no home dir")
|
|
}
|
|
got := FileConfigPath()
|
|
want := filepath.Join(home, ".config", "pigo", "config.toml")
|
|
if got != want {
|
|
t.Fatalf("FileConfigPath() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestLoadFileConfig_Missing(t *testing.T) {
|
|
cfg, err := LoadFileConfig(filepath.Join(t.TempDir(), "does-not-exist.toml"))
|
|
if err != nil {
|
|
t.Fatalf("missing file should not error, got %v", err)
|
|
}
|
|
if !reflect.DeepEqual(cfg, FileConfig{}) {
|
|
t.Fatalf("missing file should yield zero config, got %+v", cfg)
|
|
}
|
|
}
|
|
|
|
func TestLoadFileConfig_EmptyPath(t *testing.T) {
|
|
cfg, err := LoadFileConfig("")
|
|
if err != nil {
|
|
t.Fatalf("empty path should not error, got %v", err)
|
|
}
|
|
if !reflect.DeepEqual(cfg, FileConfig{}) {
|
|
t.Fatalf("empty path should yield zero config, got %+v", cfg)
|
|
}
|
|
}
|
|
|
|
func TestLoadFileConfig_Valid(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "config.toml")
|
|
content := `
|
|
model = "claude-opus-4-8"
|
|
base_url = "https://example.com"
|
|
api_key = "sk-test"
|
|
protocol = "anthropic"
|
|
provider = "deepseek"
|
|
thinking_level = "high"
|
|
output_format = "stream-json"
|
|
no_tools = true
|
|
no_skills = true
|
|
approve = true
|
|
system_prompt = "be terse"
|
|
`
|
|
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg, err := LoadFileConfig(path)
|
|
if err != nil {
|
|
t.Fatalf("valid file should parse, got %v", err)
|
|
}
|
|
want := FileConfig{
|
|
Model: "claude-opus-4-8",
|
|
BaseURL: "https://example.com",
|
|
APIKey: "sk-test",
|
|
Protocol: "anthropic",
|
|
Provider: "deepseek",
|
|
ThinkingLevel: "high",
|
|
OutputFormat: "stream-json",
|
|
NoTools: true,
|
|
NoSkills: true,
|
|
Approve: true,
|
|
SystemPrompt: "be terse",
|
|
}
|
|
if !reflect.DeepEqual(cfg, want) {
|
|
t.Fatalf("parsed config = %+v, want %+v", cfg, want)
|
|
}
|
|
}
|
|
|
|
func TestLoadFileConfig_Malformed(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "bad.toml")
|
|
if err := os.WriteFile(path, []byte("model = = ="), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := LoadFileConfig(path); err == nil {
|
|
t.Fatal("malformed file should error")
|
|
}
|
|
}
|
|
|
|
func TestLoadFileConfigPromptsArray(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "config.toml")
|
|
content := "prompts = [\"./my-prompts\", \"/abs/x.md\"]\n"
|
|
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg, err := LoadFileConfig(path)
|
|
if err != nil {
|
|
t.Fatalf("LoadFileConfig: %v", err)
|
|
}
|
|
if len(cfg.Prompts) != 2 || cfg.Prompts[0] != "./my-prompts" || cfg.Prompts[1] != "/abs/x.md" {
|
|
t.Errorf("Prompts = %v, want [./my-prompts /abs/x.md]", cfg.Prompts)
|
|
}
|
|
}
|
|
|
|
// TestGenericBaseURLEnvVar verifies the <PROVIDER>_BASE_URL name derivation,
|
|
// especially the hyphen→underscore conversion and uppercasing.
|
|
func TestGenericBaseURLEnvVar(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
want string
|
|
}{
|
|
{"deepseek", "DEEPSEEK_BASE_URL"},
|
|
{"zai-coding-cn", "ZAI_CODING_CN_BASE_URL"},
|
|
{"vercel-ai-gateway", "VERCEL_AI_GATEWAY_BASE_URL"},
|
|
{"", ""},
|
|
}
|
|
for _, c := range cases {
|
|
if got := GenericBaseURLEnvVar(c.name); got != c.want {
|
|
t.Errorf("GenericBaseURLEnvVar(%q) = %q, want %q", c.name, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLoadFileConfig_DreamTable(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "config.toml")
|
|
content := `
|
|
[dream]
|
|
enabled = false
|
|
interval_days = 14
|
|
recent_sessions = 50
|
|
`
|
|
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg, err := LoadFileConfig(path)
|
|
if err != nil {
|
|
t.Fatalf("LoadFileConfig: %v", err)
|
|
}
|
|
if cfg.Dream.Enabled == nil || *cfg.Dream.Enabled {
|
|
t.Errorf("Dream.Enabled = %v, want explicit false", cfg.Dream.Enabled)
|
|
}
|
|
if cfg.Dream.IntervalDays != 14 {
|
|
t.Errorf("Dream.IntervalDays = %d, want 14", cfg.Dream.IntervalDays)
|
|
}
|
|
if cfg.Dream.RecentSessions != 50 {
|
|
t.Errorf("Dream.RecentSessions = %d, want 50", cfg.Dream.RecentSessions)
|
|
}
|
|
}
|
|
|
|
func TestLoadFileConfig_DreamTableAbsent(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "config.toml")
|
|
if err := os.WriteFile(path, []byte("model = \"foo\"\n"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg, err := LoadFileConfig(path)
|
|
if err != nil {
|
|
t.Fatalf("LoadFileConfig: %v", err)
|
|
}
|
|
// Absent [dream] table: Enabled pointer nil (→ default true downstream),
|
|
// ints zero (→ defaults downstream). Parsing must not error.
|
|
if cfg.Dream.Enabled != nil || cfg.Dream.IntervalDays != 0 || cfg.Dream.RecentSessions != 0 {
|
|
t.Errorf("absent dream table = %+v, want zero-value", cfg.Dream)
|
|
}
|
|
}
|