1package agent
2
3import (
4 "strings"
5 "testing"
6)
7
8func TestAutoSubagentsSystemAddendumForListsOnlyEnabledLifecycleTools(t *testing.T) {
9 for _, tc := range []struct {
10 name string
11 spawn bool
12 stop bool
13 resume bool
14 want []string
15 unwanted []string
16 wantUnavailable bool
17 }{
18 {name: "none", spawn: true, unwanted: []string{"subagent_stop", "subagent_resume"}},
19 {name: "fully unavailable", unwanted: []string{"subagent_stop", "subagent_resume"}, wantUnavailable: true},
20 {name: "stop", spawn: true, stop: true, want: []string{"subagent_stop"}, unwanted: []string{"subagent_resume"}},
21 {name: "resume", spawn: true, resume: true, want: []string{"subagent_resume"}, unwanted: []string{"subagent_stop"}},
22 {name: "both", spawn: true, stop: true, resume: true, want: []string{"subagent_stop", "subagent_resume"}},
23 {name: "stop without spawn", stop: true, want: []string{"subagent_stop", "Spawning new workers is unavailable"}, unwanted: []string{"subagent_resume"}},
24 {name: "resume without spawn", resume: true, want: []string{"subagent_resume", "Spawning new workers is unavailable"}, unwanted: []string{"subagent_stop"}},
25 } {
26 t.Run(tc.name, func(t *testing.T) {
27 got := AutoSubagentsSystemAddendumFor(tc.spawn, tc.stop, tc.resume)
28 for _, want := range tc.want {
29 if !strings.Contains(got, want) {
30 t.Fatalf("addendum missing enabled lifecycle tool %q:\n%s", want, got)
31 }
32 }
33 for _, unwanted := range tc.unwanted {
34 if strings.Contains(got, unwanted) {
35 t.Fatalf("addendum mentions unavailable lifecycle tool %q:\n%s", unwanted, got)
36 }
37 }
38 if gotUnavailable := strings.Contains(got, AutoSubagentsDelegationUnavailableAddendum); gotUnavailable != tc.wantUnavailable {
39 t.Fatalf("delegation-unavailable guidance present = %t, want %t:\n%s", gotUnavailable, tc.wantUnavailable, got)
40 }
41 })
42 }
43}
44
45func TestConfigSettingsStorePersistsShowInstructionsAtStartup(t *testing.T) {
46 t.Setenv("ZUT_HOME", t.TempDir())
47 if err := SaveConfig(Config{Theme: "dark"}); err != nil {
48 t.Fatal(err)
49 }
50
51 if err := (configSettingsStore{}).SetShowInstructionsAtStartup(true); err != nil {
52 t.Fatal(err)
53 }
54 cfg, err := LoadConfig()
55 if err != nil {
56 t.Fatal(err)
57 }
58 if cfg.ShowInstructionsAtStartup == nil || !*cfg.ShowInstructionsAtStartup {
59 t.Fatal("show_instructions_at_startup was not persisted as enabled")
60 }
61 if cfg.Theme != "dark" {
62 t.Fatalf("unrelated config changed: theme = %q, want dark", cfg.Theme)
63 }
64}
65
66func TestConfigSettingsStorePersistsTerminalAlerts(t *testing.T) {
67 t.Setenv("ZUT_HOME", t.TempDir())
68 if err := SaveConfig(Config{Theme: "dark"}); err != nil {
69 t.Fatal(err)
70 }
71
72 if err := (configSettingsStore{}).SetTerminalAlertsEnabled(false); err != nil {
73 t.Fatal(err)
74 }
75 cfg, err := LoadConfig()
76 if err != nil {
77 t.Fatal(err)
78 }
79 if cfg.TerminalAlertsEnabled == nil || *cfg.TerminalAlertsEnabled {
80 t.Fatal("terminal_alerts_enabled was not persisted as disabled")
81 }
82 if cfg.Theme != "dark" {
83 t.Fatalf("unrelated config changed: theme = %q, want dark", cfg.Theme)
84 }
85}
86
87func TestConfigSettingsStorePersistsTerminalTitle(t *testing.T) {
88 t.Setenv("ZUT_HOME", t.TempDir())
89 if err := SaveConfig(Config{Theme: "dark"}); err != nil {
90 t.Fatal(err)
91 }
92
93 if err := (configSettingsStore{}).SetTerminalTitleEnabled(false); err != nil {
94 t.Fatal(err)
95 }
96 cfg, err := LoadConfig()
97 if err != nil {
98 t.Fatal(err)
99 }
100 if cfg.TerminalTitleEnabled == nil || *cfg.TerminalTitleEnabled {
101 t.Fatal("terminal_title_enabled was not persisted as disabled")
102 }
103 if cfg.Theme != "dark" {
104 t.Fatalf("unrelated config changed: theme = %q, want dark", cfg.Theme)
105 }
106}
107
108func TestConfigSettingsStorePersistsJailByDefault(t *testing.T) {
109 t.Setenv("ZUT_HOME", t.TempDir())
110 if err := SaveConfig(Config{Theme: "dark"}); err != nil {
111 t.Fatal(err)
112 }
113
114 if err := (configSettingsStore{}).SetJailByDefault(true); err != nil {
115 t.Fatal(err)
116 }
117 cfg, err := LoadConfig()
118 if err != nil {
119 t.Fatal(err)
120 }
121 if cfg.JailByDefault == nil || !*cfg.JailByDefault {
122 t.Fatal("jail_by_default was not persisted as enabled")
123 }
124 if cfg.Theme != "dark" {
125 t.Fatalf("unrelated config changed: theme = %q, want dark", cfg.Theme)
126 }
127}
128
129func TestConfigSettingsStorePersistsAutoCompactThreshold(t *testing.T) {
130 t.Setenv("ZUT_HOME", t.TempDir())
131 if err := SaveConfig(Config{Theme: "dark"}); err != nil {
132 t.Fatal(err)
133 }
134
135 if err := (configSettingsStore{}).SetAutoCompactThreshold(70); err != nil {
136 t.Fatal(err)
137 }
138 cfg, err := LoadConfig()
139 if err != nil {
140 t.Fatal(err)
141 }
142 if cfg.AutoCompactThreshold == nil || *cfg.AutoCompactThreshold != 70 {
143 t.Fatalf("auto_compact_threshold = %v, want 70", cfg.AutoCompactThreshold)
144 }
145 if cfg.Theme != "dark" {
146 t.Fatalf("unrelated config changed: theme = %q, want dark", cfg.Theme)
147 }
148}
149
150func TestConfigSettingsStorePersistsLSPDefaults(t *testing.T) {
151 t.Setenv("ZUT_HOME", t.TempDir())
152 if err := SaveConfig(Config{Theme: "dark"}); err != nil {
153 t.Fatal(err)
154 }
155
156 if err := (configSettingsStore{}).SetLSPEnabled(false); err != nil {
157 t.Fatal(err)
158 }
159 if err := (configSettingsStore{}).SetSubagentLSPEnabled(false); err != nil {
160 t.Fatal(err)
161 }
162 cfg, err := LoadConfig()
163 if err != nil {
164 t.Fatal(err)
165 }
166 if cfg.LSPEnabled == nil || *cfg.LSPEnabled {
167 t.Fatal("lsp_enabled was not persisted as disabled")
168 }
169 if cfg.SubagentLSPEnabled == nil || *cfg.SubagentLSPEnabled {
170 t.Fatal("subagent_lsp_enabled was not persisted as disabled")
171 }
172 if cfg.Theme != "dark" {
173 t.Fatalf("unrelated config changed: theme = %q, want dark", cfg.Theme)
174 }
175}
176
177func TestConfigLSPEnabledForDefaultsToTrue(t *testing.T) {
178 cfg := Config{}
179 if !cfg.LSPEnabledFor(false) {
180 t.Fatal("main-session LSP should default to enabled")
181 }
182 if !cfg.LSPEnabledFor(true) {
183 t.Fatal("sub-agent LSP should default to enabled")
184 }
185
186 noMain, noSub := false, false
187 cfg.LSPEnabled = &noMain
188 cfg.SubagentLSPEnabled = &noSub
189 if cfg.LSPEnabledFor(false) || cfg.LSPEnabledFor(true) {
190 t.Fatal("explicitly disabled LSP settings were ignored")
191 }
192 yesSub := true
193 cfg.SubagentLSPEnabled = &yesSub
194 write, edit := true, true
195 cfg.LSPDiagnosticsOnWrite = &write
196 cfg.LSPDiagnosticsOnEdit = &edit
197 if cfg.LSPDiagnosticsOnWriteEnabled(false) || cfg.LSPDiagnosticsOnEditEnabled(false) {
198 t.Fatal("diagnostics remained enabled with main-session LSP disabled")
199 }
200 if !cfg.LSPDiagnosticsOnWriteEnabled(true) || !cfg.LSPDiagnosticsOnEditEnabled(true) {
201 t.Fatal("sub-agent diagnostics did not follow sub-agent LSP settings")
202 }
203}
204
205func TestConfigSettingsStorePersistsSubagentPosition(t *testing.T) {
206 t.Setenv("ZUT_HOME", t.TempDir())
207 if err := SaveConfig(Config{Theme: "dark"}); err != nil {
208 t.Fatal(err)
209 }
210
211 if err := (configSettingsStore{}).SetTUISubagentPosition("above_input"); err != nil {
212 t.Fatal(err)
213 }
214 cfg, err := LoadConfig()
215 if err != nil {
216 t.Fatal(err)
217 }
218 if cfg.TUISubagentPosition != "above_input" {
219 t.Fatalf("tui_subagent_position = %q, want above_input", cfg.TUISubagentPosition)
220 }
221 if cfg.Theme != "dark" {
222 t.Fatalf("unrelated config changed: theme = %q, want dark", cfg.Theme)
223 }
224
225 if err := (configSettingsStore{}).SetTUISubagentPosition("below_input"); err != nil {
226 t.Fatal(err)
227 }
228 cfg, err = LoadConfig()
229 if err != nil {
230 t.Fatal(err)
231 }
232 if cfg.TUISubagentPosition != "" {
233 t.Fatalf("default tui_subagent_position = %q, want omitted", cfg.TUISubagentPosition)
234 }
235}
236
237func TestConfigSettingsStorePersistsInheritedTheme(t *testing.T) {
238 t.Setenv("ZUT_HOME", t.TempDir())
239 if err := SaveConfig(Config{Theme: "dark"}); err != nil {
240 t.Fatal(err)
241 }
242
243 if err := (configSettingsStore{}).SetTheme("inherited"); err != nil {
244 t.Fatal(err)
245 }
246 cfg, err := LoadConfig()
247 if err != nil {
248 t.Fatal(err)
249 }
250 if cfg.Theme != "inherited" {
251 t.Fatalf("theme = %q, want inherited", cfg.Theme)
252 }
253}
254
255func TestConfigWebSearchEnabledForCLIDefaultsToTrue(t *testing.T) {
256 cfg := Config{}
257 if !cfg.WebSearchEnabledForCLI() {
258 t.Fatal("web search should default to enabled when the config field is absent")
259 }
260 disabled := false
261 cfg.WebSearchEnabled = &disabled
262 if cfg.WebSearchEnabledForCLI() {
263 t.Fatal("explicitly disabled web search setting was ignored")
264 }
265}
266
267func TestConfigSettingsStorePersistsWebSearch(t *testing.T) {
268 t.Setenv("ZUT_HOME", t.TempDir())
269 if err := SaveConfig(Config{Theme: "dark"}); err != nil {
270 t.Fatal(err)
271 }
272
273 if err := (configSettingsStore{}).SetWebSearchEnabled(false); err != nil {
274 t.Fatal(err)
275 }
276 cfg, err := LoadConfig()
277 if err != nil {
278 t.Fatal(err)
279 }
280 if cfg.WebSearchEnabled == nil || *cfg.WebSearchEnabled {
281 t.Fatal("web_search_enabled was not persisted as disabled")
282 }
283 if cfg.Theme != "dark" {
284 t.Fatalf("unrelated config changed: theme = %q, want dark", cfg.Theme)
285 }
286}