package tool import ( "errors" "fmt" "testing" ) func TestLenientJSONUnmarshal_TrailingComma(t *testing.T) { t.Parallel() var v map[string]any if err := LenientJSONUnmarshal([]byte(`{"a":1,}`), &v); err != nil { t.Fatalf("LenientJSONUnmarshal: %v", err) } if v["a"] != float64(1) { t.Fatalf("a = %#v, want 1", v["a"]) } } func TestLenientJSONUnmarshal_Fenced(t *testing.T) { t.Parallel() raw := "```json\n{\"tool_calls\":[{\"name\":\"echo\",\"args\":{\"q\":\"hi\"}}]}\n```" var v struct { ToolCalls []map[string]any `json:"tool_calls"` } if err := LenientJSONUnmarshal([]byte(raw), &v); err != nil { t.Fatalf("LenientJSONUnmarshal: %v", err) } if len(v.ToolCalls) != 1 { t.Fatalf("tool_calls = %d, want 1", len(v.ToolCalls)) } } func TestLenientJSONUnmarshal_Unrecoverable(t *testing.T) { t.Parallel() // Malformed beyond repair must return an error, never panic or silently // succeed. var v map[string]any if err := LenientJSONUnmarshal([]byte(`{"a":"unterminated`), &v); err == nil { t.Fatal("expected error for unrecoverable JSON") } } func TestSetToolExecutionMode_Sequential(t *testing.T) { t.Parallel() d, _ := newDispatcher(t) if d.IsSequential("x") { t.Fatal("x should default to parallel") } if err := d.SetToolExecutionMode("x", ExecutionModeSequential); err != nil { t.Fatalf("SetToolExecutionMode: %v", err) } if !d.IsSequential("x") { t.Fatal("x should be sequential after marking") } // Clearing back to parallel. if err := d.SetToolExecutionMode("x", ExecutionModeParallel); err != nil { t.Fatalf("SetToolExecutionMode(parallel): %v", err) } if d.IsSequential("x") { t.Fatal("x should be parallel after clearing") } } func TestSetToolExecutionMode_EmptyName(t *testing.T) { t.Parallel() d, _ := newDispatcher(t) if err := d.SetToolExecutionMode("", ExecutionModeSequential); !errors.Is(err, ErrUnknownTool) { t.Fatalf("SetToolExecutionMode(\"\") error = %v, want ErrUnknownTool", err) } } func TestToolError_Kind(t *testing.T) { t.Parallel() val := &ToolError{Name: "x", Message: "bad args", cause: fmt.Errorf("validate: %w", ErrInvalidArgs)} if val.Kind() != KindArgumentValidation { t.Fatalf("validation Kind = %q, want %q", val.Kind(), KindArgumentValidation) } exec := &ToolError{Name: "x", Message: "boom", cause: errors.New("boom")} if exec.Kind() != KindExecution { t.Fatalf("execution Kind = %q, want %q", exec.Kind(), KindExecution) } none := &ToolError{Name: "x", Message: "m"} if none.Kind() != KindExecution { t.Fatalf("nil-cause Kind = %q, want %q", none.Kind(), KindExecution) } }