Files
Agent-System/internal/squad/workflow_test.go
T
agent-bot 2c75dcaf69
CI / go-vet (pull_request) Successful in 13s
CI / short-tests-report (pull_request) Failing after 35s
CI / backup-restore-drill (pull_request) Successful in 36s
CI / web (pull_request) Successful in 32s
refactor(squad): AssetProvider port replaces direct template dependency
KTD1 fifth step (behavior preserved):
- squad.AssetProvider{DocTemplate,RenderDoc,AgentInstruction,
  AgentInstructionFile} injected via Options.Assets into Service,
  WorkspaceEngine and AdaptivePlanner
- bizdev templateAssetProvider maps port to internal/template (assembly side)
- nil-provider paths degrade to the same fallbacks the old error branches
  used (placeholder template, role header) — fail-visible, no guessing
- squad -> template import eliminated; arch edge moved bizdev->template
- verified: build/vet/arch green, squad+bizdev short tests pass
2026-09-29 06:29:51 +00:00

128 lines
3.6 KiB
Go

package squad_test
import (
"context"
"encoding/json"
"os"
"path/filepath"
"testing"
"time"
"chat-agent-system/internal/agent"
"chat-agent-system/internal/squad"
)
func TestWorkflowDriver_DynamicAndExecute(t *testing.T) {
tmpDir := t.TempDir()
engine := squad.NewWorkspaceEngine(nil)
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
WorkDir: tmpDir,
TaskKey: "intake",
StageTitle: "用户初始需求收集表",
ProjectName: "无创神经刺激器",
CustomerName: "圆山研究院",
Backend: "stub",
WorkflowSteps: []squad.StepSpec{
{
ID: "step_01_organize",
Role: "organizer",
Title: "抽取事实并回填字段",
SubWorkspace: "tasks/01_organize",
Mode: "execute",
},
{
ID: "step_02_draft",
Role: "writer",
Title: "撰写初稿",
SubWorkspace: "tasks/02_draft",
Mode: "execute",
Needs: []string{"tasks/01_organize/output.md"},
},
},
})
if err != nil {
t.Fatalf("PrepareWorkspace failed: %v", err)
}
driver := squad.NewWorkflowDriver(engine)
// 1. 动态增加一个审核步骤
err = driver.AddStep(ws.WorkDir, squad.StepSpec{
ID: "step_03_review",
Role: "leader",
Title: "主管终审",
SubWorkspace: "tasks/03_review",
Mode: "execute",
Needs: []string{"tasks/02_draft/output.md"},
})
if err != nil {
t.Fatalf("AddStep failed: %v", err)
}
// 校验 workflow.json 是否更新为 3 步
wf, err := driver.LoadWorkflow(ws.WorkDir)
if err != nil {
t.Fatalf("LoadWorkflow failed: %v", err)
}
if len(wf.Steps) != 3 {
t.Fatalf("expected 3 steps after AddStep, got %d", len(wf.Steps))
}
// 模拟 organizer 在 tasks/01_organize 下提前预置 fields.json (验证黑板自动捕获)
task1Dir := filepath.Join(ws.WorkDir, "tasks", "01_organize")
_ = os.MkdirAll(task1Dir, 0o755)
fieldsData := map[string]string{
"customer_name": "圆山研究院",
"device_type": "二类医疗器械",
"intended_use": "改善打鼾与夜间睡眠",
}
fBytes, _ := json.Marshal(fieldsData)
_ = os.WriteFile(filepath.Join(task1Dir, "fields.json"), fBytes, 0o644)
// 2. 执行整个工作流
stubBackend := &agent.StubBackend{}
var stepEvents []squad.StepEvent
opts := squad.ExecuteOptions{
Timeout: 5 * time.Second,
StepNotify: func(ev squad.StepEvent) {
stepEvents = append(stepEvents, ev)
},
}
res, err := driver.Execute(context.Background(), ws.WorkDir, stubBackend, opts)
if err != nil {
t.Fatalf("driver.Execute failed: %v", err)
}
if !res.Success {
t.Fatalf("expected execution success, error: %s", res.ErrorReason)
}
if res.StepsRun != 3 {
t.Errorf("expected 3 steps run, got %d", res.StepsRun)
}
// 3. 校验每个子工作区是否生成了 output.md
for _, sub := range []string{"01_organize", "02_draft", "03_review"} {
p := filepath.Join(ws.WorkDir, "tasks", sub, "output.md")
if _, err := os.Stat(p); err != nil {
t.Errorf("expected %s/output.md to exist: %v", sub, err)
}
}
// 4. 校验 blackboard.json 是否成功回填了 fields.json 中的字段
bb, err := driver.LoadBlackboard(ws.WorkDir)
if err != nil {
t.Fatalf("LoadBlackboard failed: %v", err)
}
if bb.Fields["device_type"] != "二类医疗器械" {
t.Errorf("expected blackboard device_type '二类医疗器械', got '%s'", bb.Fields["device_type"])
}
// 5. 校验根目录下是否汇总生成了 output.md
rootOut := filepath.Join(ws.WorkDir, "output.md")
if _, err := os.Stat(rootOut); err != nil {
t.Fatalf("root output.md does not exist: %v", err)
}
}