Files
Agent-System-Backend/internal/runmgr/workflow_route_test.go
T
weichenandCursor ebeb6c599c Route a matched intent to its workflow after commands
Why: A hit intent should start the bound workflow, and a command that shares the same id still has to win.
Evidence: go test ./internal/intentgate/ ./internal/runmgr/ ./internal/server/ passed. Recognize keeps slots and the missing-slot question. StartWorkflowRun copies the definition and selects the workflow runner even when mock is on.
Impact: Ordinary turns are unchanged. The workflow runner body is still a stub until the executor lands.
Refs: feature/agent-harness-p4
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 16:04:49 +00:00

83 lines
2.5 KiB
Go

package runmgr
import (
"path/filepath"
"testing"
"time"
"agent-system-backend/internal/bus"
"agent-system-backend/internal/profiles"
"agent-system-backend/internal/runtime"
"agent-system-backend/internal/settings"
"agent-system-backend/internal/store"
"agent-system-backend/internal/tools"
"agent-system-backend/internal/workflow"
core "agent-system-core"
)
type launchCapture struct {
ch chan workflow.Launch
}
func (c launchCapture) Name() string { return "workflow-capture" }
func (c launchCapture) Execute(rc runtime.RunCtx) error {
src, ok := rc.(interface {
Workflow() (workflow.Launch, bool)
})
if !ok {
return nil
}
launch, ok := src.Workflow()
if ok {
c.ch <- launch
}
return nil
}
func TestStartWorkflowRunCopiesDefinition(t *testing.T) {
st, err := store.OpenSQLite(filepath.Join(t.TempDir(), "wf.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { st.Close() })
if err := st.MergeSettingsScope(store.OwnerDefault, settings.ScopeUser, map[string]any{"runtime": map[string]any{"mock": true}}); err != nil {
t.Fatal(err)
}
reg := tools.NewRegistry()
prof := profiles.New(st)
if err := prof.EnsureDefault(); err != nil {
t.Fatal(err)
}
direct := &gateRunner{parts: []core.Part{{Type: core.PartText, Text: "direct"}}}
cap := launchCapture{ch: make(chan workflow.Launch, 1)}
mgr := New(st, bus.New(), reg, prof, settings.NewService(st), direct, noopRunner{}, &runtime.MockRunner{}, &runtime.MockSubagent{})
mgr.SetWorkflowRunner(cap)
proj, err := st.CreateProject(store.OwnerDefault, "p")
if err != nil {
t.Fatal(err)
}
conv, err := st.CreateConversation(store.OwnerDefault, proj.ID, "c", "")
if err != nil {
t.Fatal(err)
}
msg := core.Message{ConversationID: conv.ID, Role: core.RoleUser, Parts: []core.Part{{Type: core.PartText, Text: "写简报"}}}
def := core.WorkflowDef{ID: "demo.brief", Title: "简报", Steps: []core.WorkflowStep{{ID: "s", Kind: "hitl"}}}
if _, err := mgr.StartWorkflowRun(conv, msg, def, map[string]string{"topic": "核能"}, []workflow.SlotQuestion{{Key: "audience", Question: "给谁看?"}}); err != nil {
t.Fatal(err)
}
def.Title = "被改掉"
select {
case launch := <-cap.ch:
if launch.Def.ID != "demo.brief" || launch.Def.Title != "简报" || launch.Slots["topic"] != "核能" {
t.Fatalf("%+v", launch)
}
if len(launch.Missing) != 1 || launch.Missing[0].Question != "给谁看?" {
t.Fatalf("missing %+v", launch.Missing)
}
case <-time.After(time.Second):
t.Fatal("workflow runner was not selected")
}
}