| 1 | package agents |
| 2 | |
| 3 | import ( |
| 4 | "os" |
| 5 | "path/filepath" |
| 6 | "testing" |
| 7 | ) |
| 8 | |
| 9 | func writeConfig(t *testing.T, body string) string { |
| 10 | t.Helper() |
| 11 | p := filepath.Join(t.TempDir(), "agents.json") |
| 12 | if err := os.WriteFile(p, []byte(body), 0o600); err != nil { |
| 13 | t.Fatal(err) |
| 14 | } |
| 15 | return p |
| 16 | } |
| 17 | |
| 18 | func TestLoadConfigHappyPath(t *testing.T) { |
| 19 | root := t.TempDir() |
| 20 | cfg, err := LoadConfig(writeConfig(t, `{"groups":{"th1":{"roots":["`+root+`"]}}}`)) |
| 21 | if err != nil { |
| 22 | t.Fatal(err) |
| 23 | } |
| 24 | g := cfg.Groups["th1"] |
| 25 | if len(g.Roots) != 1 || g.Roots[0] != root || g.Plain { |
| 26 | t.Errorf("group: %+v", g) |
| 27 | } |
| 28 | if cfg.Command != "claude" { |
| 29 | t.Errorf("command default: %q", cfg.Command) |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | // Fail-closed: the config is the blast radius, so every broken shape must be |
| 34 | // an error, never a silent empty (or widened) grant. |
| 35 | func TestLoadConfigFailsClosed(t *testing.T) { |
| 36 | missingDir := filepath.Join(t.TempDir(), "nope") |
| 37 | cases := map[string]string{ |
| 38 | "empty groups": `{"groups":{}}`, |
| 39 | "broken json": `{"groups":`, |
| 40 | "no roots no plain": `{"groups":{"th1":{"roots":[]}}}`, |
| 41 | "empty root": `{"groups":{"th1":{"roots":[""]}}}`, |
| 42 | "missing root dir": `{"groups":{"th1":{"roots":["` + missingDir + `"]}}}`, |
| 43 | } |
| 44 | for name, body := range cases { |
| 45 | if _, err := LoadConfig(writeConfig(t, body)); err == nil { |
| 46 | t.Errorf("%s: LoadConfig accepted it", name) |
| 47 | } |
| 48 | } |
| 49 | if _, err := LoadConfig(filepath.Join(t.TempDir(), "absent.json")); err == nil { |
| 50 | t.Error("missing file: LoadConfig accepted it") |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | func TestLoadConfigPlainNeedsNoRoots(t *testing.T) { |
| 55 | cfg, err := LoadConfig(writeConfig(t, `{"groups":{"th1":{"plain":true}}}`)) |
| 56 | if err != nil { |
| 57 | t.Fatal(err) |
| 58 | } |
| 59 | if !cfg.Groups["th1"].Plain { |
| 60 | t.Error("plain not kept") |
| 61 | } |
| 62 | } |
| 63 | |