fix: validate workflows and preserve execution outcomes

This commit is contained in:
Ed1s0nZ committed 2026-10-06 02:32:32 +08:00
1 parent bdefa535b0
commit 8550325dfe
16 files changed
+494 -35

No files matched your search

@@ -0,0 +1,35 @@
package workflow
import (
"testing"
)
func TestToolArgumentTemplatesResolveAfterJSONParsing(t *testing.T) {
state := newWorkflowLocalState(map[string]interface{}{"target": "https://example.test/a?x=\"quoted\"\nnext"}, "run")
cfg := map[string]any{"arguments": `{"target":"{{inputs.target}}","headers":{"label":"probe {{inputs.target}}"},"items":["{{inputs.target}}",{"count":2}],"empty":"","enabled":true}`}
args, err := resolveToolArguments(cfg, state)
if err != nil {
t.Fatal(err)
}
if args["target"] != state.Inputs["target"] {
t.Fatalf("unexpanded target: %#v", args)
}
if args["headers"].(map[string]interface{})["label"] != "probe "+state.Inputs["target"].(string) {
t.Fatal("nested template did not resolve")
}
if args["items"].([]interface{})[0] != state.Inputs["target"] {
t.Fatal("array template did not resolve")
}
if args["empty"] != "" || args["enabled"] != true {
t.Fatal("non-template values changed")
}
}
func TestExplicitToolArgumentBindingsRemainAuthoritative(t *testing.T) {
state := newWorkflowLocalState(map[string]interface{}{"port": 8080}, "run")
cfg := map[string]any{"arguments": `{"port":"wrong"}`, "argument_bindings": map[string]any{"port": map[string]any{"from": "inputs", "field": "port"}}}
args, err := resolveToolArguments(cfg, state)
if err != nil || args["port"] != 8080 {
t.Fatalf("binding changed: %#v %v", args, err)
}
}
+23
View File
@@ -149,5 +149,28 @@ func resolveToolArguments(cfg map[string]any, state *WorkflowLocalState) (map[st
if args == nil {
args = map[string]interface{}{}
}
resolveArgumentTemplates(args, state)
return args, nil
}
// Expand after parsing JSON so quotes and newlines in state values remain data.
func resolveArgumentTemplates(value any, state *WorkflowLocalState) {
switch v := value.(type) {
case map[string]interface{}:
for key, item := range v {
if text, ok := item.(string); ok && templateVarRe.MatchString(text) {
v[key] = resolveTemplate(text, state)
} else {
resolveArgumentTemplates(item, state)
}
}
case []interface{}:
for i, item := range v {
if text, ok := item.(string); ok && templateVarRe.MatchString(text) {
v[i] = resolveTemplate(text, state)
} else {
resolveArgumentTemplates(item, state)
}
}
}
}