Files
ds2api/internal/toolcall/tool_prompt_test.go

141 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package toolcall
import (
"strings"
"testing"
)
func TestBuildToolCallInstructions_ExecCommandUsesCmdExample(t *testing.T) {
out := BuildToolCallInstructions([]string{"exec_command"})
if !strings.Contains(out, `<DSMLinvoke name="exec_command">`) {
t.Fatalf("expected exec_command in examples, got: %s", out)
}
if !strings.Contains(out, `<DSMLparameter name="cmd"><![CDATA[pwd]]></DSMLparameter>`) {
t.Fatalf("expected cmd parameter example for exec_command, got: %s", out)
}
}
func TestBuildToolCallInstructions_ExecuteCommandUsesCommandExample(t *testing.T) {
out := BuildToolCallInstructions([]string{"execute_command"})
if !strings.Contains(out, `<DSMLinvoke name="execute_command">`) {
t.Fatalf("expected execute_command in examples, got: %s", out)
}
if !strings.Contains(out, `<DSMLparameter name="command"><![CDATA[pwd]]></DSMLparameter>`) {
t.Fatalf("expected command parameter example for execute_command, got: %s", out)
}
}
func TestBuildToolCallInstructions_BashUsesCommandAndDescriptionExamples(t *testing.T) {
out := BuildToolCallInstructions([]string{"Bash"})
blocks := findInvokeBlocks(out, "Bash")
if len(blocks) == 0 {
t.Fatalf("expected Bash examples, got: %s", out)
}
sawDescription := false
for _, block := range blocks {
if !strings.Contains(block, `<DSMLparameter name="command">`) {
t.Fatalf("expected every Bash example to use command parameter, got: %s", block)
}
if strings.Contains(block, `<DSMLparameter name="path">`) || strings.Contains(block, `<DSMLparameter name="content">`) {
t.Fatalf("expected Bash examples not to use file write parameters, got: %s", block)
}
if strings.Contains(block, `<DSMLparameter name="description">`) {
sawDescription = true
}
}
if !sawDescription {
t.Fatalf("expected Bash long-script example to include description, got: %s", out)
}
if strings.Contains(out, `<DSMLinvoke name="Read">`) {
t.Fatalf("expected examples to avoid unavailable hard-coded Read tool, got: %s", out)
}
}
func TestBuildToolCallInstructions_ExecuteCommandLongScriptUsesCommand(t *testing.T) {
out := BuildToolCallInstructions([]string{"execute_command"})
blocks := findInvokeBlocks(out, "execute_command")
if len(blocks) == 0 {
t.Fatalf("expected execute_command examples, got: %s", out)
}
for _, block := range blocks {
if !strings.Contains(block, `<DSMLparameter name="command">`) {
t.Fatalf("expected execute_command examples to use command parameter, got: %s", block)
}
if strings.Contains(block, `<DSMLparameter name="path">`) || strings.Contains(block, `<DSMLparameter name="content">`) {
t.Fatalf("expected execute_command examples not to use file write parameters, got: %s", block)
}
}
if !strings.Contains(out, `test_escape.sh`) {
t.Fatalf("expected execute_command long-script example, got: %s", out)
}
}
func TestBuildToolCallInstructions_ExecCommandLongScriptUsesCmd(t *testing.T) {
out := BuildToolCallInstructions([]string{"exec_command"})
blocks := findInvokeBlocks(out, "exec_command")
if len(blocks) == 0 {
t.Fatalf("expected exec_command examples, got: %s", out)
}
for _, block := range blocks {
if !strings.Contains(block, `<DSMLparameter name="cmd">`) {
t.Fatalf("expected exec_command examples to use cmd parameter, got: %s", block)
}
if strings.Contains(block, `<DSMLparameter name="command">`) || strings.Contains(block, `<DSMLparameter name="path">`) || strings.Contains(block, `<DSMLparameter name="content">`) {
t.Fatalf("expected exec_command examples not to use command or file write parameters, got: %s", block)
}
}
if !strings.Contains(out, `test_escape.sh`) {
t.Fatalf("expected exec_command long-script example, got: %s", out)
}
}
func TestBuildToolCallInstructions_WriteUsesFilePathAndContent(t *testing.T) {
out := BuildToolCallInstructions([]string{"Write"})
blocks := findInvokeBlocks(out, "Write")
if len(blocks) == 0 {
t.Fatalf("expected Write examples, got: %s", out)
}
for _, block := range blocks {
if !strings.Contains(block, `<DSMLparameter name="file_path">`) || !strings.Contains(block, `<DSMLparameter name="content">`) {
t.Fatalf("expected Write examples to use file_path and content, got: %s", block)
}
if strings.Contains(block, `<DSMLparameter name="path">`) {
t.Fatalf("expected Write examples not to use path, got: %s", block)
}
}
}
func TestBuildToolCallInstructions_AnchorsMissingOpeningWrapperFailureMode(t *testing.T) {
out := BuildToolCallInstructions([]string{"read_file"})
if !strings.Contains(out, "Never omit the opening <DSMLtool_calls> tag") {
t.Fatalf("expected explicit missing-opening-tag warning, got: %s", out)
}
if !strings.Contains(out, "Wrong 3 — missing opening wrapper") {
t.Fatalf("expected missing-opening-wrapper negative example, got: %s", out)
}
}
func findInvokeBlocks(text, name string) []string {
open := `<DSMLinvoke name="` + name + `">`
remaining := text
blocks := []string{}
for {
start := strings.Index(remaining, open)
if start < 0 {
return blocks
}
remaining = remaining[start:]
end := strings.Index(remaining, `</DSMLinvoke>`)
if end < 0 {
return blocks
}
end += len(`</DSMLinvoke>`)
blocks = append(blocks, remaining[:end])
remaining = remaining[end:]
}
}