Files
ragflow/internal/agent/component/exesql_component_test.go
Hz_ ea18ab3ba0 fix(go-agent): align ExeSQL component inputs with Python (#16896)
## Summary

- Resolve configured ExeSQL SQL references before tool invocation.
- Align ExeSQL parameters, defaults, JSON tags, and tool schema with
Python.
- Preserve SQL string literals and restore Canvas output fields.

## Testing

- `bash build.sh --test ./internal/agent/tool/...`
- `bash build.sh --test ./internal/agent/component/...`
- `bash build.sh --test ./internal/agent/runtime/...`

<img width="2039" height="1041" alt="image"
src="https://github.com/user-attachments/assets/9f4beca7-ca28-4641-adda-0570415fcaa1"
/>
2026-07-14 15:02:28 +08:00

101 lines
2.9 KiB
Go

package component
import (
"context"
"encoding/json"
"errors"
"strings"
"testing"
einotool "github.com/cloudwego/eino/components/tool"
"ragflow/internal/agent/runtime"
)
type exesqlInvokerStub struct {
arguments string
result string
err error
}
func (s *exesqlInvokerStub) InvokableRun(_ context.Context, arguments string, _ ...einotool.Option) (string, error) {
s.arguments = arguments
return s.result, s.err
}
func TestExeSQLComponentResolvesConfiguredSQL(t *testing.T) {
stub := &exesqlInvokerStub{
result: `{"columns":["id","status"],"rows":[{"id":1,"status":"Completed"}]}`,
}
state := runtime.NewCanvasState("run", "task")
state.SetVar("Agent:SparklyMooseDivide", "content", "SELECT id FROM orders WHERE status = 'Completed'")
c := &exesqlComponent{
inner: stub,
sql: "{Agent:SparklyMooseDivide@content}",
}
out, err := c.Invoke(runtime.WithState(context.Background(), state), map[string]any{
"content": "this must not be used as SQL",
})
if err != nil {
t.Fatalf("Invoke: %v", err)
}
var arguments map[string]any
if err := json.Unmarshal([]byte(stub.arguments), &arguments); err != nil {
t.Fatalf("decode tool arguments: %v", err)
}
if got := arguments["sql"]; got != "SELECT id FROM orders WHERE status = 'Completed'" {
t.Fatalf("sql argument = %#v, want resolved SQL", got)
}
if _, exists := arguments["content"]; exists {
t.Fatalf("tool arguments unexpectedly contain upstream content: %s", stub.arguments)
}
formalized, ok := out["formalized_content"].(string)
if !ok || !strings.Contains(formalized, "Completed") {
t.Fatalf("formalized_content = %#v, want rendered row", out["formalized_content"])
}
jsonResult, ok := out["json"].([]any)
if !ok || len(jsonResult) != 1 {
t.Fatalf("json = %#v, want one statement result", out["json"])
}
}
func TestNewExeSQLComponentKeepsConfiguredSQL(t *testing.T) {
component, err := newExeSQLComponent(map[string]any{
"db_type": "mysql",
"database": "demo",
"username": "root",
"host": "db.example.com",
"port": 3306,
"password": "secret",
"max_records": 100,
"sql": "{Agent:SparklyMooseDivide@content}",
})
if err != nil {
t.Fatalf("newExeSQLComponent: %v", err)
}
exeSQL, ok := component.(*exesqlComponent)
if !ok {
t.Fatalf("component type = %T, want *exesqlComponent", component)
}
if exeSQL.sql != "{Agent:SparklyMooseDivide@content}" {
t.Fatalf("configured SQL = %q", exeSQL.sql)
}
}
func TestExeSQLComponentPreservesToolErrorAsCanvasOutput(t *testing.T) {
stub := &exesqlInvokerStub{
result: `{"_ERROR":"exesql: empty sql"}`,
err: errors.New("exesql: empty sql"),
}
c := &exesqlComponent{inner: stub, sql: ""}
out, err := c.Invoke(context.Background(), map[string]any{"sql": ""})
if err != nil {
t.Fatalf("Invoke returned a hard error: %v", err)
}
if got := out["_ERROR"]; got != "exesql: empty sql" {
t.Fatalf("_ERROR = %#v, want tool error", got)
}
}