1package agent
2
3import (
4 "context"
5 "os"
6 "strings"
7 "testing"
8 "time"
9
10 "github.com/bnema/zut/packages/agent/subagents"
11)
12
13func TestSubagentPolicyParsesStartupTimeout(t *testing.T) {
14 policy := subagentPolicyFromConfig(SubagentsConfig{StartupTimeout: "42s"})
15 if got := policy.StartupTimeout; got != 42*time.Second {
16 t.Fatalf("StartupTimeout = %s, want 42s", got)
17 }
18}
19
20func TestLoadConfigRejectsMalformedSubagentDuration(t *testing.T) {
21 t.Setenv("ZUT_HOME", t.TempDir())
22 if err := os.WriteFile(ConfigPath(), []byte(`{"subagents":{"default_timeout":"20"}}`), 0o600); err != nil {
23 t.Fatal(err)
24 }
25 _, err := LoadConfig()
26 if err == nil || !strings.Contains(err.Error(), "subagents.default_timeout") {
27 t.Fatalf("LoadConfig error = %v, want field-qualified duration error", err)
28 }
29}
30
31func TestSubagentPolicyIgnoresLegacyMaxTotalSpawned(t *testing.T) {
32 t.Setenv("ZUT_HOME", t.TempDir())
33 if err := os.WriteFile(ConfigPath(), []byte(`{"subagents":{"max_concurrent":1,"max_total_spawned":1}}`), 0o600); err != nil {
34 t.Fatal(err)
35 }
36 cfg, err := LoadConfig()
37 if err != nil {
38 t.Fatal(err)
39 }
40 if got := cfg.Subagents.MaxTotalSpawned; got != 1 {
41 t.Fatalf("legacy max_total_spawned = %d, want 1", got)
42 }
43
44 supervisor := subagents.New(subagents.Config{
45 Root: t.TempDir(),
46 RepoRoot: t.TempDir(),
47 Policy: subagentPolicyFromConfig(cfg.Subagents),
48 NewRunner: func(*subagents.Agent) subagents.Runner {
49 return subagents.RunnerFunc(func(context.Context, subagents.Sink) error { return nil })
50 },
51 })
52 t.Cleanup(supervisor.StopAll)
53
54 for _, task := range []string{"first", "second"} {
55 agent, err := supervisor.Spawn(context.Background(), task)
56 if err != nil {
57 t.Fatalf("Spawn(%q): %v", task, err)
58 }
59 agent.Wait()
60 if got := agent.Status(); got != subagents.StatusDone {
61 t.Fatalf("%s status = %s, want %s", task, got, subagents.StatusDone)
62 }
63 }
64}