1package subagents
2
3import (
4 "context"
5 "strings"
6 "testing"
7)
8
9func TestSupervisorFastModePropagatesToChild(t *testing.T) {
10 root := t.TempDir()
11 f := New(Config{
12 Root: root,
13 RepoRoot: root,
14 FastMode: true,
15 NewRunner: func(a *Agent) Runner {
16 return RunnerFunc(func(ctx context.Context, _ Sink) error {
17 <-ctx.Done()
18 return ctx.Err()
19 })
20 },
21 })
22
23 a, err := f.SpawnReq(context.Background(), SpawnRequest{Task: "x"})
24 if err != nil {
25 t.Fatalf("spawn: %v", err)
26 }
27 cleanedUp := false
28 t.Cleanup(func() {
29 if cleanedUp {
30 return
31 }
32 if err := f.Stop(a.ID); err != nil {
33 t.Errorf("cleanup stop: %v", err)
34 }
35 a.Wait()
36 })
37 if !a.FastMode {
38 t.Fatal("spawned agent FastMode = false, want true")
39 }
40 args := defaultChildArgs("/zut", a, "/session", "/inbox")
41 if !containsArg(args, "--fast-mode") {
42 t.Fatalf("child args = %v, want --fast-mode", args)
43 }
44
45 if err := f.Stop(a.ID); err != nil {
46 t.Fatalf("stop: %v", err)
47 }
48 a.Wait()
49 cleanedUp = true
50
51 g := New(Config{Root: root, RepoRoot: root})
52 if loaded, errs := g.Reload(); loaded != 1 || len(errs) != 0 {
53 t.Fatalf("reload loaded=%d errs=%v", loaded, errs)
54 }
55 reloaded := g.Get(a.ID)
56 if reloaded == nil || !reloaded.FastMode {
57 t.Fatalf("reloaded FastMode = %v, want true", reloaded != nil && reloaded.FastMode)
58 }
59}
60
61func TestSpawnRequestFastModeProfileOverridesHostSetting(t *testing.T) {
62 falseValue, trueValue := false, true
63 cases := []struct {
64 name string
65 hostFast bool
66 profileFast *bool
67 want bool
68 wantHostOverride bool
69 }{
70 {name: "unset inherits enabled host", hostFast: true, want: true},
71 {name: "unset inherits disabled host", hostFast: false, want: false},
72 {name: "false disables enabled host", hostFast: true, profileFast: &falseValue, want: false},
73 {name: "true enables disabled host", hostFast: false, profileFast: &trueValue, want: true, wantHostOverride: true},
74 {name: "true preserves enabled host", hostFast: true, profileFast: &trueValue, want: true},
75 }
76 for _, tc := range cases {
77 t.Run(tc.name, func(t *testing.T) {
78 root := t.TempDir()
79 f := New(Config{
80 Root: root,
81 RepoRoot: root,
82 FastMode: tc.hostFast,
83 NewRunner: func(*Agent) Runner {
84 return RunnerFunc(func(context.Context, Sink) error { return nil })
85 },
86 })
87 a, err := f.SpawnReq(context.Background(), SpawnRequest{Task: "x", FastMode: tc.profileFast})
88 if err != nil {
89 t.Fatalf("spawn: %v", err)
90 }
91 a.Wait()
92 if a.FastMode != tc.want {
93 t.Fatalf("FastMode = %v, want %v", a.FastMode, tc.want)
94 }
95 if a.FastModeOverridesHost() != tc.wantHostOverride {
96 t.Fatalf("FastModeOverridesHost() = %v, want %v", a.FastModeOverridesHost(), tc.wantHostOverride)
97 }
98 args := defaultChildArgs("/zut", a, "/session", "/inbox")
99 wantFlag := "--no-fast-mode"
100 if tc.want {
101 wantFlag = "--fast-mode"
102 }
103 if !containsArg(args, wantFlag) {
104 t.Fatalf("child args = %v, want %s", args, wantFlag)
105 }
106 })
107 }
108}
109
110func TestSupervisorReloadPreservesExplicitFastModeOff(t *testing.T) {
111 root := t.TempDir()
112 newSupervisor := func(fastMode bool) *Supervisor {
113 return New(Config{
114 Root: root,
115 RepoRoot: root,
116 FastMode: fastMode,
117 NewRunner: func(a *Agent) Runner {
118 return RunnerFunc(func(ctx context.Context, _ Sink) error {
119 <-ctx.Done()
120 return ctx.Err()
121 })
122 },
123 })
124 }
125
126 f := newSupervisor(false)
127 a, err := f.SpawnReq(context.Background(), SpawnRequest{Task: "x"})
128 if err != nil {
129 t.Fatalf("spawn: %v", err)
130 }
131 if a.FastMode {
132 t.Fatal("spawned agent FastMode = true, want false")
133 }
134 if err := f.Stop(a.ID); err != nil {
135 t.Fatalf("stop: %v", err)
136 }
137 a.Wait()
138
139 g := newSupervisor(true)
140 if loaded, errs := g.Reload(); loaded != 1 || len(errs) != 0 {
141 t.Fatalf("reload loaded=%d errs=%v", loaded, errs)
142 }
143 if reloaded := g.Get(a.ID); reloaded == nil || reloaded.FastMode {
144 t.Fatalf("reloaded FastMode = %v, want false", reloaded != nil && reloaded.FastMode)
145 }
146}
147
148func TestSubagentWorkerArgsOmitFastModeWhenUnset(t *testing.T) {
149 args := subagentWorkerArgs(subagentWorkerArgsOpts{Exe: "/zut", Dir: "/wt", SessionPath: "/s", InboxPath: "/i"})
150 if strings.Contains(strings.Join(args, " "), "--fast-mode") || strings.Contains(strings.Join(args, " "), "--no-fast-mode") {
151 t.Fatalf("child args = %v, want fast mode omitted when unset", args)
152 }
153}
154
155func TestSubagentWorkerArgsExplicitlyDisablesFastMode(t *testing.T) {
156 args := subagentWorkerArgs(subagentWorkerArgsOpts{
157 Exe: "/zut",
158 Dir: "/wt",
159 SessionPath: "/s",
160 InboxPath: "/i",
161 FastModeSet: true,
162 })
163 if !containsArg(args, "--no-fast-mode") {
164 t.Fatalf("child args = %v, want --no-fast-mode", args)
165 }
166 if containsArg(args, "--fast-mode") {
167 t.Fatalf("child args = %v, want no positive fast-mode flag", args)
168 }
169}
170
171func TestDefaultChildArgsExplicitlyDisablesFastMode(t *testing.T) {
172 args := defaultChildArgs("/zut", &Agent{Dir: "/wt"}, "/s", "/i")
173 if !containsArg(args, "--no-fast-mode") {
174 t.Fatalf("child args = %v, want --no-fast-mode", args)
175 }
176}
177
178func containsArg(args []string, want string) bool {
179 for _, arg := range args {
180 if arg == want {
181 return true
182 }
183 }
184 return false
185}