package preset import ( "os" "path/filepath" "testing" ) func TestLoadUsesBuiltInDefaultsWithoutWritingConfig(t *testing.T) { configHome := filepath.Join(t.TempDir(), "config") t.Setenv("XDG_CONFIG_HOME", configHome) set, err := Load() if err != nil { t.Fatalf("Load: %v", err) } if _, err := os.Stat(filepath.Join(configHome, "patterm")); !os.IsNotExist(err) { t.Fatalf("Load created config dir or unexpected stat error: %v", err) } if len(set.Agents) != 3 { t.Fatalf("agents len = %d, want 3", len(set.Agents)) } claude := presetByName(set.Agents, "claude") if claude == nil { t.Fatal("missing built-in claude preset") } if claude.IdleDetection == nil || len(claude.IdleDetection.PermissionPatterns) == 0 { t.Fatalf("built-in claude missing permission patterns: %+v", claude.IdleDetection) } codex := presetByName(set.Agents, "codex") if codex == nil { t.Fatal("missing built-in codex preset") } if codex.IdleDetection == nil || len(codex.IdleDetection.ThinkingPatterns) == 0 { t.Fatalf("built-in codex missing thinking patterns: %+v", codex.IdleDetection) } } func TestLoadMergesUserOverlayIntoBuiltInPreset(t *testing.T) { configHome := t.TempDir() t.Setenv("XDG_CONFIG_HOME", configHome) dir := filepath.Join(configHome, "patterm", "presets", "agents") if err := os.MkdirAll(dir, 0o700); err != nil { t.Fatal(err) } writeFile(t, filepath.Join(dir, "claude.json"), `{ "name": "claude", "argv": ["claude", "--model", "sonnet"], "idle_detection": { "idle_threshold_ms": 3500 } }`) set, err := Load() if err != nil { t.Fatalf("Load: %v", err) } claude := presetByName(set.Agents, "claude") if claude == nil { t.Fatal("missing claude preset") } if got := claude.Argv; len(got) != 3 || got[0] != "claude" || got[2] != "sonnet" { t.Fatalf("argv = %#v", got) } if claude.IdleDetection.IdleThresholdMS != 3500 { t.Fatalf("idle threshold = %d", claude.IdleDetection.IdleThresholdMS) } if len(claude.IdleDetection.PermissionPatterns) == 0 { t.Fatalf("permission patterns were not inherited: %+v", claude.IdleDetection) } if claude.MCPInjection == nil || claude.MCPInjection.Kind != "flag" { t.Fatalf("mcp injection was not inherited: %+v", claude.MCPInjection) } } func TestLoadCanDisableBuiltInPreset(t *testing.T) { configHome := t.TempDir() t.Setenv("XDG_CONFIG_HOME", configHome) dir := filepath.Join(configHome, "patterm", "presets", "agents") if err := os.MkdirAll(dir, 0o700); err != nil { t.Fatal(err) } writeFile(t, filepath.Join(dir, "opencode.json"), `{"name":"opencode","disabled":true}`) set, err := Load() if err != nil { t.Fatalf("Load: %v", err) } if presetByName(set.Agents, "opencode") != nil { t.Fatal("opencode preset was not disabled") } if presetByName(set.Agents, "claude") == nil || presetByName(set.Agents, "codex") == nil { t.Fatalf("other built-ins missing: %+v", set.Agents) } } func TestLoadAddsCustomUserPreset(t *testing.T) { configHome := t.TempDir() t.Setenv("XDG_CONFIG_HOME", configHome) dir := filepath.Join(configHome, "patterm", "presets", "processes") if err := os.MkdirAll(dir, 0o700); err != nil { t.Fatal(err) } writeFile(t, filepath.Join(dir, "test.json"), `{"name":"test","argv":["go","test","./..."]}`) set, err := Load() if err != nil { t.Fatalf("Load: %v", err) } proc := presetByName(set.Processes, "test") if proc == nil { t.Fatal("missing custom process preset") } if proc.Kind != KindCommand { t.Fatalf("kind = %q", proc.Kind) } } func presetByName(ps []*Preset, name string) *Preset { for _, p := range ps { if p.Name == name { return p } } return nil } func writeFile(t *testing.T, path, body string) { t.Helper() if err := os.WriteFile(path, []byte(body), 0o600); err != nil { t.Fatal(err) } }