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 TestSpawnRequestFastModeIsBoundByHostSetting(t *testing.T) {
62 falseValue, trueValue := false, true
63 cases := []struct {
64 name string
65 hostFast bool
66 profileFast *bool
67 want bool
68 }{
69 {name: "unset inherits enabled host", hostFast: true, want: true},
70 {name: "unset inherits disabled host", hostFast: false, want: false},
71 {name: "false disables enabled host", hostFast: true, profileFast: &falseValue, want: false},
72 {name: "true cannot enable disabled host", hostFast: false, profileFast: &trueValue, want: false},
73 {name: "true preserves enabled host", hostFast: true, profileFast: &trueValue, want: true},
74 }
75 for _, tc := range cases {
76 t.Run(tc.name, func(t *testing.T) {
77 root := t.TempDir()
78 f := New(Config{
79 Root: root,
80 RepoRoot: root,
81 FastMode: tc.hostFast,
82 NewRunner: func(*Agent) Runner {
83 return RunnerFunc(func(context.Context, Sink) error { return nil })
84 },
85 })
86 a, err := f.SpawnReq(context.Background(), SpawnRequest{Task: "x", FastMode: tc.profileFast})
87 if err != nil {
88 t.Fatalf("spawn: %v", err)
89 }
90 a.Wait()
91 if a.FastMode != tc.want {
92 t.Fatalf("FastMode = %v, want %v", a.FastMode, tc.want)
93 }
94 args := defaultChildArgs("/zut", a, "/session", "/inbox")
95 wantFlag := "--no-fast-mode"
96 if tc.want {
97 wantFlag = "--fast-mode"
98 }
99 if !containsArg(args, wantFlag) {
100 t.Fatalf("child args = %v, want %s", args, wantFlag)
101 }
102 })
103 }
104}
105
106func TestSupervisorReloadPreservesExplicitFastModeOff(t *testing.T) {
107 root := t.TempDir()
108 newSupervisor := func(fastMode bool) *Supervisor {
109 return New(Config{
110 Root: root,
111 RepoRoot: root,
112 FastMode: fastMode,
113 NewRunner: func(a *Agent) Runner {
114 return RunnerFunc(func(ctx context.Context, _ Sink) error {
115 <-ctx.Done()
116 return ctx.Err()
117 })
118 },
119 })
120 }
121
122 f := newSupervisor(false)
123 a, err := f.SpawnReq(context.Background(), SpawnRequest{Task: "x"})
124 if err != nil {
125 t.Fatalf("spawn: %v", err)
126 }
127 if a.FastMode {
128 t.Fatal("spawned agent FastMode = true, want false")
129 }
130 if err := f.Stop(a.ID); err != nil {
131 t.Fatalf("stop: %v", err)
132 }
133 a.Wait()
134
135 g := newSupervisor(true)
136 if loaded, errs := g.Reload(); loaded != 1 || len(errs) != 0 {
137 t.Fatalf("reload loaded=%d errs=%v", loaded, errs)
138 }
139 if reloaded := g.Get(a.ID); reloaded == nil || reloaded.FastMode {
140 t.Fatalf("reloaded FastMode = %v, want false", reloaded != nil && reloaded.FastMode)
141 }
142}
143
144func TestSubagentWorkerArgsOmitFastModeWhenUnset(t *testing.T) {
145 args := subagentWorkerArgs(subagentWorkerArgsOpts{Exe: "/zut", Dir: "/wt", SessionPath: "/s", InboxPath: "/i"})
146 if strings.Contains(strings.Join(args, " "), "--fast-mode") || strings.Contains(strings.Join(args, " "), "--no-fast-mode") {
147 t.Fatalf("child args = %v, want fast mode omitted when unset", args)
148 }
149}
150
151func TestSubagentWorkerArgsExplicitlyDisablesFastMode(t *testing.T) {
152 args := subagentWorkerArgs(subagentWorkerArgsOpts{
153 Exe: "/zut",
154 Dir: "/wt",
155 SessionPath: "/s",
156 InboxPath: "/i",
157 FastModeSet: true,
158 })
159 if !containsArg(args, "--no-fast-mode") {
160 t.Fatalf("child args = %v, want --no-fast-mode", args)
161 }
162 if containsArg(args, "--fast-mode") {
163 t.Fatalf("child args = %v, want no positive fast-mode flag", args)
164 }
165}
166
167func TestDefaultChildArgsExplicitlyDisablesFastMode(t *testing.T) {
168 args := defaultChildArgs("/zut", &Agent{Dir: "/wt"}, "/s", "/i")
169 if !containsArg(args, "--no-fast-mode") {
170 t.Fatalf("child args = %v, want --no-fast-mode", args)
171 }
172}
173
174func containsArg(args []string, want string) bool {
175 for _, arg := range args {
176 if arg == want {
177 return true
178 }
179 }
180 return false
181}