blob: 87ab10a759d0f64b76ea14bb61fc8386a6861793 [file] [log] [blame]
Philip Zeyliger064f8ae2025-05-14 00:47:41 +00001package test
2
3import (
4 "context"
5 "encoding/json"
6 "testing"
7
8 "sketch.dev/claudetool"
9 "sketch.dev/llm"
10)
11
12func TestBashTimeout(t *testing.T) {
13 // Create a bash tool
14 bashTool := claudetool.NewBashTool(nil)
15
16 // Create a command that will output text and then sleep
17 cmd := `echo "Starting command..."; echo "This should appear in partial output"; sleep 5; echo "This shouldn't appear"`
18
19 // Prepare the input with a very short timeout
Philip Zeyligerec398cb2025-05-14 00:48:06 +000020 input := map[string]any{
Philip Zeyliger064f8ae2025-05-14 00:47:41 +000021 "command": cmd,
22 "timeout": "1s", // Very short timeout to trigger the timeout case
23 }
24
25 // Marshal the input to JSON
26 inputJSON, err := json.Marshal(input)
27 if err != nil {
28 t.Fatalf("Failed to marshal input: %v", err)
29 }
30
31 // Run the bash tool
32 ctx := context.Background()
33 result, err := bashTool.Run(ctx, inputJSON)
34
35 // Check that we got an error (due to timeout)
36 if err == nil {
37 t.Fatalf("Expected timeout error, got nil")
38 }
39
40 // Error should mention timeout
41 if !containsString(err.Error(), "timed out") {
42 t.Errorf("Error doesn't mention timeout: %v", err)
43 }
44
45 // Check that we got partial output despite the error
46 if len(result) == 0 {
47 t.Fatalf("Expected partial output, got empty result")
48 }
49
50 // Verify the error mentions that partial output is included
51 if !containsString(err.Error(), "partial output included") {
52 t.Errorf("Error should mention that partial output is included: %v", err)
53 }
54
55 // The partial output should contain the initial output but not the text after sleep
56 text := ""
57 for _, content := range result {
58 if content.Type == llm.ContentTypeText {
59 text += content.Text
60 }
61 }
62
63 if !containsString(text, "Starting command") || !containsString(text, "should appear in partial output") {
64 t.Errorf("Partial output is missing expected content: %s", text)
65 }
66
67 if containsString(text, "shouldn't appear") {
68 t.Errorf("Partial output contains unexpected content (after timeout): %s", text)
69 }
70}
71
72func containsString(s, substr string) bool {
73 return s != "" && s != "<nil>" && stringIndexOf(s, substr) >= 0
74}
75
76func stringIndexOf(s, substr string) int {
77 for i := 0; i <= len(s)-len(substr); i++ {
78 if s[i:i+len(substr)] == substr {
79 return i
80 }
81 }
82 return -1
83}