1package agent
2
3import (
4 "testing"
5
6 "github.com/bnema/zut/packages/agent/tools"
7)
8
9// TestSubagentActionAllowedMatrix pins the launch-time gating contract: a bare
10// "subagent" entry grants every action, "subagent:<action>" grants one action,
11// and every pre-existing disable path still disables all of them.
12func TestSubagentActionAllowedMatrix(t *testing.T) {
13 cases := []struct {
14 name string
15 args Args
16 spawn bool
17 status bool
18 stop bool
19 resume bool
20 interrupt bool
21 }{
22 {name: "default grants every action", args: Args{}, spawn: true, status: true, stop: true, resume: true, interrupt: true},
23 {name: "unrelated allowlist denies all", args: Args{ToolsSet: true, Tools: []string{"read"}}},
24 {name: "bare subagent grants every action", args: Args{ToolsSet: true, Tools: []string{"subagent"}}, spawn: true, status: true, stop: true, resume: true, interrupt: true},
25 {name: "bare subagent wins beside a suffix", args: Args{ToolsSet: true, Tools: []string{"read", "subagent:resume", "subagent"}}, spawn: true, status: true, stop: true, resume: true, interrupt: true},
26 {name: "one action suffix grants one action", args: Args{ToolsSet: true, Tools: []string{"subagent:resume"}}, resume: true},
27 {name: "interrupt suffix grants interrupt only", args: Args{ToolsSet: true, Tools: []string{"subagent:interrupt"}}, interrupt: true},
28 {name: "two action suffixes grant two actions", args: Args{ToolsSet: true, Tools: []string{"subagent:spawn", "subagent:stop"}}, spawn: true, stop: true},
29 {name: "unknown action suffix denies all", args: Args{ToolsSet: true, Tools: []string{"subagent:bogus"}}},
30 {name: "empty allowlist denies all", args: Args{ToolsSet: true, Tools: []string{}}},
31 {name: "no tools denies all", args: Args{NoTools: true}},
32 {name: "packaged permission set denies all", args: Args{PermissionSet: &tools.PermissionSet{}}},
33 }
34 for _, tc := range cases {
35 t.Run(tc.name, func(t *testing.T) {
36 got := [5]bool{
37 autoSubagentsToolAllowed(tc.args),
38 autoSubagentsStatusToolAllowed(tc.args),
39 autoSubagentsStopToolAllowed(tc.args),
40 autoSubagentsResumeToolAllowed(tc.args),
41 autoSubagentsInterruptToolAllowed(tc.args),
42 }
43 want := [5]bool{tc.spawn, tc.status, tc.stop, tc.resume, tc.interrupt}
44 if got != want {
45 t.Fatalf("actions = %v, want %v (args %+v)", got, want, tc.args)
46 }
47 wantAny := tc.spawn || tc.status || tc.stop || tc.resume || tc.interrupt
48 if any := autoSubagentsAnyToolAllowed(tc.args); any != wantAny {
49 t.Fatalf("autoSubagentsAnyToolAllowed = %v, want %v (args %+v)", any, wantAny, tc.args)
50 }
51 })
52 }
53}