1package modes
2
3import (
4 "context"
5 "testing"
6
7 "github.com/bnema/zut/packages/agent/subagents"
8 "github.com/bnema/zut/packages/agent/tools"
9 "github.com/bnema/zut/packages/core"
10)
11
12// TestApplyAutoSubagentsToolReplacesOneFacadeEntry covers the interactive
13// refresh path: the previously installed facade is replaced by exactly one
14// entry whose non-nil fields are the currently granted actions, and unrelated
15// tools survive.
16func TestApplyAutoSubagentsToolReplacesOneFacadeEntry(t *testing.T) {
17 manager := subagents.NewResidentManager(t.TempDir(), nil)
18 t.Cleanup(func() { _ = manager.Close(context.Background()) })
19 preSeeded := &tools.SubagentTool{}
20 agent := core.NewAgent(nil, "model", "", core.Registry{
21 "read": &tools.GrepTool{},
22 // A previously installed facade, as a prior refresh would have left it.
23 "subagent": preSeeded,
24 })
25 allowed, resumeAllowed := true, true
26 interactive := NewInteractive(InteractiveConfig{
27 Agent: agent,
28 ResidentManager: manager,
29 AutoSubagentsToolAllowed: &allowed,
30 AutoSubagentsStatusToolAllowed: &allowed,
31 AutoSubagentsStopToolAllowed: &allowed,
32 AutoSubagentsResumeToolAllowed: &resumeAllowed,
33 })
34
35 interactive.applyAutoSubagentsTool()
36
37 assertFacade := func(wantResume bool) {
38 t.Helper()
39 snapshot := agent.ToolsSnapshot()
40 if _, ok := snapshot["read"]; !ok {
41 t.Fatal("interactive refresh dropped an unrelated tool")
42 }
43 found := 0
44 for name := range snapshot {
45 switch name {
46 case tools.SubagentToolName:
47 found++
48 case "subagent_spawn", "subagent_status", "subagent_stop", "subagent_resume":
49 t.Fatalf("legacy tool key %q is present", name)
50 }
51 }
52 if found != 1 {
53 t.Fatalf("registry has %d %q entries, want 1", found, tools.SubagentToolName)
54 }
55 facade, ok := snapshot[tools.SubagentToolName].(*tools.SubagentTool)
56 if !ok {
57 t.Fatalf("registry entry %q = %T, want *tools.SubagentTool", tools.SubagentToolName, snapshot[tools.SubagentToolName])
58 }
59 if facade == preSeeded {
60 t.Fatal("interactive refresh reused the pre-seeded facade instead of replacing it")
61 }
62 if facade.Spawn == nil || facade.Status == nil || facade.Stop == nil {
63 t.Fatalf("facade actions = spawn:%v status:%v stop:%v", facade.Spawn != nil, facade.Status != nil, facade.Stop != nil)
64 }
65 if (facade.Resume != nil) != wantResume {
66 t.Fatalf("facade resume = %v, want %v", facade.Resume != nil, wantResume)
67 }
68 }
69
70 assertFacade(true)
71
72 // A later policy change must replace the installed facade, not merge or
73 // duplicate it.
74 resumeAllowed = false
75 interactive.applyAutoSubagentsTool()
76 assertFacade(false)
77}