1package agent
2
3import (
4 "bytes"
5 "os"
6 "os/exec"
7 "strings"
8 "testing"
9)
10
11func TestParseArgsExplicitEmptySystemPromptIsSet(t *testing.T) {
12 args, err := ParseArgs([]string{"--system-prompt", ""})
13 if err != nil {
14 t.Fatalf("ParseArgs returned %v", err)
15 }
16 if !args.SystemPromptSet {
17 t.Fatal("SystemPromptSet = false; want true for an explicitly empty flag value")
18 }
19 if args.SystemPrompt != "" {
20 t.Fatalf("SystemPrompt = %q; want empty", args.SystemPrompt)
21 }
22}
23
24func TestParseArgsTemperatureAllowsZero(t *testing.T) {
25 args, err := ParseArgs([]string{"--temperature", "0"})
26 if err != nil {
27 t.Fatalf("ParseArgs returned %v", err)
28 }
29 if args.Temperature == nil || *args.Temperature != 0 {
30 t.Fatalf("Temperature = %v; want 0", args.Temperature)
31 }
32}
33
34func TestParseArgsTemperatureRejectsOutOfRange(t *testing.T) {
35 if _, err := ParseArgs([]string{"--temperature", "2.1"}); err == nil {
36 t.Fatal("ParseArgs accepted out-of-range temperature")
37 }
38}
39
40func TestParseArgsYes(t *testing.T) {
41 for _, flag := range []string{"-y", "--yes"} {
42 args, err := ParseArgs([]string{flag, "--print", "hi"})
43 if err != nil {
44 t.Fatalf("ParseArgs(%q): %v", flag, err)
45 }
46 if !args.Yes {
47 t.Fatalf("ParseArgs(%q): Yes = false", flag)
48 }
49 if args.Mode != ModePrint || args.Prompt != "hi" {
50 t.Fatalf("ParseArgs(%q): Mode=%q Prompt=%q", flag, args.Mode, args.Prompt)
51 }
52 }
53}
54
55func TestParseArgsStatsRequiresPrintMode(t *testing.T) {
56 args, err := ParseArgs([]string{"-p", "--stats", "stats.json", "hi"})
57 if err != nil {
58 t.Fatal(err)
59 }
60 if args.StatsPath != "stats.json" || args.Mode != ModePrint {
61 t.Fatalf("StatsPath=%q Mode=%q", args.StatsPath, args.Mode)
62 }
63
64 if _, err := ParseArgs([]string{"--stats", "stats.json", "hi"}); err == nil {
65 t.Fatal("ParseArgs accepted --stats without print mode")
66 }
67}
68
69func TestParseArgsNoContextFiles(t *testing.T) {
70 for _, flag := range []string{"--no-context-files", "-nc"} {
71 args, err := ParseArgs([]string{flag})
72 if err != nil {
73 t.Fatalf("ParseArgs(%q): %v", flag, err)
74 }
75 if !args.NoContextFiles {
76 t.Fatalf("ParseArgs(%q): NoContextFiles = false", flag)
77 }
78 }
79}
80
81func TestParseArgsStream(t *testing.T) {
82 args, err := ParseArgs([]string{"--stream", "hi"})
83 if err != nil {
84 t.Fatal(err)
85 }
86 if args.Mode != ModeStream || args.Prompt != "hi" {
87 t.Fatalf("Mode=%q Prompt=%q", args.Mode, args.Prompt)
88 }
89}
90
91func TestRunHelpHelperProcess(t *testing.T) {
92 if os.Getenv("ZOT_HELP_HELPER") == "" {
93 return
94 }
95 if err := runWithArgsRaw(strings.Fields(os.Getenv("ZOT_HELP_HELPER")), "test"); err != nil {
96 os.Exit(1)
97 }
98 os.Exit(0)
99}
100
101func TestHelpOutputStreams(t *testing.T) {
102 run := func(args string) (stdout, stderr string, err error) {
103 t.Helper()
104 cmd := exec.Command(os.Args[0], "-test.run=^TestRunHelpHelperProcess$")
105 cmd.Env = append(os.Environ(), "ZOT_HELP_HELPER="+args)
106 var outBuf, errBuf bytes.Buffer
107 cmd.Stdout = &outBuf
108 cmd.Stderr = &errBuf
109 err = cmd.Run()
110 return outBuf.String(), errBuf.String(), err
111 }
112
113 stdout, stderr, err := run("--help")
114 if err != nil {
115 t.Fatalf("--help returned %v; stderr:\n%s", err, stderr)
116 }
117 if !strings.Contains(stdout, "zot. yet another coding agent harness.") {
118 t.Fatalf("stdout does not contain help text:\n%s", stdout)
119 }
120 if stderr != "" {
121 t.Fatalf("stderr = %q, want empty", stderr)
122 }
123 if strings.Contains(stdout, "\x1b[") {
124 t.Fatalf("redirected help contains ANSI escapes: %q", stdout)
125 }
126
127 stdout, stderr, err = run("--unknown")
128 if err == nil {
129 t.Fatal("unknown flag exited successfully")
130 }
131 if stdout != "" {
132 t.Fatalf("stdout = %q, want empty for an argument error", stdout)
133 }
134 if !strings.Contains(stderr, "zot. yet another coding agent harness.") {
135 t.Fatalf("stderr does not contain help text:\n%s", stderr)
136 }
137}