1package agent
2
3import (
4 "testing"
5
6 "github.com/bnema/zut/packages/agent/subagents"
7)
8
9func TestParseArgsSubagentAndReasoning(t *testing.T) {
10 args, err := ParseArgs([]string{"--subagent-worker", "/tmp/in.sock", "--subagent", "reviewer", "--reasoning", "high", "task"})
11 if err != nil {
12 t.Fatal(err)
13 }
14 if args.Mode != ModeSubagentWorker || args.Subagent != "reviewer" || args.Reasoning != "high" || args.Prompt != "task" {
15 t.Fatalf("parsed args = mode=%q subagent=%q reasoning=%q prompt=%q", args.Mode, args.Subagent, args.Reasoning, args.Prompt)
16 }
17}
18
19func TestParseArgsNoLSP(t *testing.T) {
20 args, err := ParseArgs([]string{"--no-lsp"})
21 if err != nil {
22 t.Fatal(err)
23 }
24 if !args.NoLSP {
25 t.Fatal("--no-lsp did not disable LSP")
26 }
27}
28
29func TestParseArgsAllowsLeadingDashPromptAfterTerminator(t *testing.T) {
30 args, err := ParseArgs([]string{"--subagent-worker", "/tmp/in.sock", "--", "--inspect", "auth flow"})
31 if err != nil {
32 t.Fatal(err)
33 }
34 if args.Prompt != "--inspect auth flow" {
35 t.Fatalf("prompt = %q, want leading-dash task preserved", args.Prompt)
36 }
37}
38
39func TestParseArgsTemperatureAllowsZero(t *testing.T) {
40 args, err := ParseArgs([]string{"--temperature", "0"})
41 if err != nil {
42 t.Fatalf("ParseArgs returned %v", err)
43 }
44 if args.Temperature == nil || *args.Temperature != 0 {
45 t.Fatalf("Temperature = %v; want 0", args.Temperature)
46 }
47}
48
49func TestParseArgsTemperatureRejectsOutOfRange(t *testing.T) {
50 if _, err := ParseArgs([]string{"--temperature", "2.1"}); err == nil {
51 t.Fatal("ParseArgs accepted out-of-range temperature")
52 }
53}
54
55func TestParseArgsYes(t *testing.T) {
56 for _, flag := range []string{"-y", "--yes"} {
57 args, err := ParseArgs([]string{flag, "--print", "hi"})
58 if err != nil {
59 t.Fatalf("ParseArgs(%q): %v", flag, err)
60 }
61 if !args.Yes {
62 t.Fatalf("ParseArgs(%q): Yes = false", flag)
63 }
64 if args.Mode != ModePrint || args.Prompt != "hi" {
65 t.Fatalf("ParseArgs(%q): Mode=%q Prompt=%q", flag, args.Mode, args.Prompt)
66 }
67 }
68}
69
70func TestParseArgsStatsRequiresPrintMode(t *testing.T) {
71 args, err := ParseArgs([]string{"-p", "--stats", "stats.json", "hi"})
72 if err != nil {
73 t.Fatal(err)
74 }
75 if args.StatsPath != "stats.json" || args.Mode != ModePrint {
76 t.Fatalf("StatsPath=%q Mode=%q", args.StatsPath, args.Mode)
77 }
78
79 if _, err := ParseArgs([]string{"--stats", "stats.json", "hi"}); err == nil {
80 t.Fatal("ParseArgs accepted --stats without print mode")
81 }
82}
83
84func TestParseArgsStream(t *testing.T) {
85 args, err := ParseArgs([]string{"--stream", "hi"})
86 if err != nil {
87 t.Fatal(err)
88 }
89 if args.Mode != ModeStream || args.Prompt != "hi" {
90 t.Fatalf("Mode=%q Prompt=%q", args.Mode, args.Prompt)
91 }
92}
93
94func TestParseArgsWebSearchPolicyIsWorkerOnlyRegardlessOfOrder(t *testing.T) {
95 for _, argv := range [][]string{
96 {"--web-search-policy", "allow"},
97 {"--web-search-policy", "allow", "--subagent-worker", "/tmp/in.sock", "--print"},
98 {"--subagent-worker", "/tmp/in.sock", "--web-search-policy", "deny", "--json"},
99 } {
100 if _, err := ParseArgs(argv); err == nil {
101 t.Fatalf("ordinary CLI mode accepted internal policy from %q", argv)
102 }
103 }
104
105 for _, tc := range []struct {
106 name string
107 argv []string
108 policy subagents.WebSearchPolicy
109 }{
110 {
111 name: "policy before worker",
112 argv: []string{"--web-search-policy", "allow", "--subagent-worker", "/tmp/in.sock", "--tools", "web_search"},
113 policy: subagents.WebSearchAllow,
114 },
115 {
116 name: "policy after worker",
117 argv: []string{"--print", "--subagent-worker", "/tmp/in.sock", "--web-search-policy", "deny"},
118 policy: subagents.WebSearchDeny,
119 },
120 } {
121 t.Run(tc.name, func(t *testing.T) {
122 worker, err := ParseArgs(tc.argv)
123 if err != nil {
124 t.Fatal(err)
125 }
126 if worker.Mode != ModeSubagentWorker || worker.WebSearchPolicy != tc.policy {
127 t.Fatalf("worker policy args = %#v", worker)
128 }
129 })
130 }
131}
132
133func TestParseArgsTracksExplicitToolListProvenance(t *testing.T) {
134 absent, err := ParseArgs(nil)
135 if err != nil {
136 t.Fatal(err)
137 }
138 if absent.ToolsSet || len(absent.Tools) != 0 {
139 t.Fatalf("absent --tools = %#v", absent)
140 }
141
142 empty, err := ParseArgs([]string{"--tools", ""})
143 if err != nil {
144 t.Fatal(err)
145 }
146 if !empty.ToolsSet || len(empty.Tools) != 0 {
147 t.Fatalf("empty --tools = %#v", empty)
148 }
149
150 selected, err := ParseArgs([]string{"--tools", "read, web_search"})
151 if err != nil {
152 t.Fatal(err)
153 }
154 if !selected.ToolsSet || len(selected.Tools) != 2 || selected.Tools[1] != "web_search" {
155 t.Fatalf("selected --tools = %#v", selected)
156 }
157}