package tools import ( "context" "encoding/json" "os" "path/filepath" "slices" "strings" "testing" ) func TestFileTools(t *testing.T) { dir := t.TempDir() ctx := context.Background() write := NewFileWriteTool(dir) if out, err := write.Execute(ctx, json.RawMessage(`{"path":"notes/a.txt","content":"hello world"}`)); err != nil { t.Fatal(err) } else if !strings.Contains(out, "wrote 11 bytes") { t.Fatalf("write output = %q", out) } read := NewFileReadTool(dir) if out, err := read.Execute(ctx, json.RawMessage(`{"path":"notes/a.txt"}`)); err != nil { t.Fatal(err) } else if out != "hello world" { t.Fatalf("read output = %q", out) } list := NewFileListTool(dir) if out, err := list.Execute(ctx, json.RawMessage(`{"path":"notes"}`)); err != nil { t.Fatal(err) } else if out != "a.txt" { t.Fatalf("list output = %q", out) } search := NewFileSearchTool(dir) if out, err := search.Execute(ctx, json.RawMessage(`{"query":"hello","path":"notes"}`)); err != nil { t.Fatal(err) } else if !strings.Contains(out, "hello world") { t.Fatalf("search output = %q", out) } } func TestShellRunTool(t *testing.T) { dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "x.txt"), []byte("x"), 0o644); err != nil { t.Fatal(err) } tool := NewShellRunTool(dir) out, err := tool.Execute(context.Background(), json.RawMessage(`{"command":"pwd && ls"}`)) if err != nil { t.Fatal(err) } if !strings.Contains(out, dir) || !strings.Contains(out, "x.txt") { t.Fatalf("output = %q", out) } } func TestReadOnlyBuiltinsExposeOpenAICompatibleNamesAndAliases(t *testing.T) { registry := ReadOnlyBuiltins(t.TempDir()) var names []string for _, def := range registry.Definitions() { names = append(names, def.Function.Name) if strings.Contains(def.Function.Name, ".") { t.Fatalf("tool name contains dot: %s", def.Function.Name) } } for _, want := range []string{"file_list", "file_read", "file_search"} { if !slices.Contains(names, want) { t.Fatalf("definitions missing %s: %#v", want, names) } } for _, alias := range []string{"file.list", "file.read", "file.search"} { if _, ok := registry.Get(alias); !ok { t.Fatalf("alias missing: %s", alias) } } if _, ok := registry.Get("file_write"); ok { t.Fatal("read-only registry should not expose file_write") } } func TestBuiltinsExposeYoloTools(t *testing.T) { registry := Builtins(t.TempDir()) for _, name := range []string{"file_write", "shell_run", "file.write", "shell.run"} { if _, ok := registry.Get(name); !ok { t.Fatalf("tool missing: %s", name) } } } func TestAgentInstructionsExplainCommandModes(t *testing.T) { readOnly := AgentInstructions("/tmp/project", false) for _, want := range []string{"Shell command execution is disabled", "--yolo", "Do not call tools for greetings", "Do not explore the project preemptively", "Do not call file_list just to discover context"} { if !strings.Contains(readOnly, want) { t.Fatalf("read-only instructions missing %q:\n%s", want, readOnly) } } yolo := AgentInstructions("/tmp/project", true) for _, want := range []string{"use shell_run", "commands written in backticks", "Do not start interactive", "non-interactive local commands", "Do not call tools for greetings"} { if !strings.Contains(yolo, want) { t.Fatalf("yolo instructions missing %q:\n%s", want, yolo) } } }