test(squad): add comprehensive tests for workspace, context, workflow, quality gate, and cli
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -23,4 +24,81 @@ func TestBizdevCLIHelpAndRun(t *testing.T) {
|
||||
if !strings.Contains(string(out), "-task") {
|
||||
t.Fatalf("cli output missing -task flag: %s", string(out))
|
||||
}
|
||||
if !strings.Contains(string(out), "-inspect") {
|
||||
t.Fatalf("cli output missing -inspect flag: %s", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBizdevCLIListTasks(t *testing.T) {
|
||||
cmd := exec.Command("go", "run", "./cmd/bizdev-cli", "-list-tasks")
|
||||
cmd.Dir = moduleRoot(t)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("cli -list-tasks failed: %v, out: %s", err, string(out))
|
||||
}
|
||||
if !strings.Contains(string(out), "intake") || !strings.Contains(string(out), "cdo_quote") {
|
||||
t.Fatalf("cli output missing expected tasks: %s", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBizdevCLIExecuteAndInspect(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
envContent := "ENV=dev\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\n"
|
||||
envPath := filepath.Join(tmpDir, ".env")
|
||||
_ = os.WriteFile(envPath, []byte(envContent), 0o644)
|
||||
|
||||
// 1. 执行任务
|
||||
cmd := exec.Command("go", "run", "./cmd/bizdev-cli",
|
||||
"-env", envPath,
|
||||
"-task", "intake",
|
||||
"-backend", "stub",
|
||||
"-project", "CLI集成测试项目",
|
||||
"-customer", "水木智造",
|
||||
)
|
||||
cmd.Dir = moduleRoot(t)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("cli execute failed: %v, out: %s", err, string(out))
|
||||
}
|
||||
|
||||
outStr := string(out)
|
||||
if !strings.Contains(outStr, "任务执行成功 [intake]") {
|
||||
t.Fatalf("unexpected cli output: %s", outStr)
|
||||
}
|
||||
if !strings.Contains(outStr, "质量门禁评分") {
|
||||
t.Fatalf("cli output missing quality gate score: %s", outStr)
|
||||
}
|
||||
|
||||
// 从输出提取工作区路径
|
||||
var workDir string
|
||||
for _, line := range strings.Split(outStr, "\n") {
|
||||
if strings.HasPrefix(line, "工作区沙箱: ") {
|
||||
workDir = strings.TrimSpace(strings.TrimPrefix(line, "工作区沙箱: "))
|
||||
break
|
||||
}
|
||||
}
|
||||
if workDir == "" {
|
||||
t.Fatalf("failed to find workspace path in output: %s", outStr)
|
||||
}
|
||||
|
||||
// 2. 审查工作区并动态追加步骤
|
||||
inspectCmd := exec.Command("go", "run", "./cmd/bizdev-cli",
|
||||
"-env", envPath,
|
||||
"-inspect", workDir,
|
||||
"-add-step", "writer:补写临床评价要点",
|
||||
"-backend", "stub",
|
||||
)
|
||||
inspectCmd.Dir = moduleRoot(t)
|
||||
inspectOut, err := inspectCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("cli inspect failed: %v, out: %s", err, string(inspectOut))
|
||||
}
|
||||
|
||||
inspectStr := string(inspectOut)
|
||||
if !strings.Contains(inspectStr, "工作区状态审查") {
|
||||
t.Fatalf("unexpected inspect output: %s", inspectStr)
|
||||
}
|
||||
if !strings.Contains(inspectStr, "动态步骤执行成功") {
|
||||
t.Fatalf("expected dynamic step success: %s", inspectStr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package squad_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"chat-agent-system/internal/squad"
|
||||
)
|
||||
|
||||
func TestContextManager_InjectAndFormat(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
engine := squad.NewWorkspaceEngine()
|
||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||
WorkDir: tmpDir,
|
||||
TaskKey: "research",
|
||||
StageTitle: "技术与市场调研报告",
|
||||
ProjectName: "微创吻合器",
|
||||
CustomerName: "水木测试医疗",
|
||||
Materials: []squad.MaterialItem{
|
||||
{Filename: "PIR.docx.txt", Content: []byte("客户要求符合YY 9706.210标准,具备双通道输出。")},
|
||||
},
|
||||
KnowledgeHits: []squad.KnowledgeHit{
|
||||
{
|
||||
DocCode: "GB 9706.1-2020",
|
||||
Category: "standards",
|
||||
Title: "医用电气设备",
|
||||
Content: "防电击与基本性能通用要求",
|
||||
Source: "法规知识库",
|
||||
},
|
||||
},
|
||||
PriorDocs: []squad.PriorDocItem{
|
||||
{
|
||||
DocKey: "BD-INTAKE",
|
||||
Title: "用户初始需求收集表",
|
||||
Filename: "BD-INTAKE.md",
|
||||
Content: "# 用户初始需求收集表\n\n已锁定客户为水木测试医疗。\n",
|
||||
Stage: "intake",
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("PrepareWorkspace failed: %v", err)
|
||||
}
|
||||
|
||||
cm := squad.NewContextManager()
|
||||
|
||||
// 1. 验证 LoadContextIndex
|
||||
idx, err := cm.LoadContextIndex(ws.WorkDir)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadContextIndex failed: %v", err)
|
||||
}
|
||||
if len(idx.Items) < 4 {
|
||||
t.Errorf("expected at least 4 context items, got %d", len(idx.Items))
|
||||
}
|
||||
|
||||
// 2. 验证 context/README.md 包含权威度说明
|
||||
readmePath := filepath.Join(ws.WorkDir, "context", "README.md")
|
||||
readmeContent, err := os.ReadFile(readmePath)
|
||||
if err != nil {
|
||||
t.Fatalf("read context/README.md failed: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(readmeContent), "authoritative") {
|
||||
t.Errorf("README.md missing 'authoritative' level")
|
||||
}
|
||||
|
||||
// 3. 验证 FormatPromptContext 能为专员生成清晰的上下文导引
|
||||
navPrompt, err := cm.FormatPromptContext(ws.WorkDir, "writer")
|
||||
if err != nil {
|
||||
t.Fatalf("FormatPromptContext failed: %v", err)
|
||||
}
|
||||
if !strings.Contains(navPrompt, "微创吻合器") {
|
||||
t.Errorf("navPrompt missing ProjectName")
|
||||
}
|
||||
if !strings.Contains(navPrompt, "PIR.docx.txt") {
|
||||
t.Errorf("navPrompt missing material reference PIR.docx.txt")
|
||||
}
|
||||
if !strings.Contains(navPrompt, "GB 9706.1-2020") {
|
||||
t.Errorf("navPrompt missing standard reference GB 9706.1-2020")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package squad_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"chat-agent-system/internal/agent"
|
||||
"chat-agent-system/internal/squad"
|
||||
)
|
||||
|
||||
func TestQualityGate_Validate(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
engine := squad.NewWorkspaceEngine()
|
||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||
WorkDir: tmpDir,
|
||||
TaskKey: "intake",
|
||||
StageTitle: "用户初始需求收集表",
|
||||
ProjectName: "医疗设备",
|
||||
TemplateContent: "# 用户初始需求收集表\n\n## 1. 客户与项目画像\n\n## 2. 预期用途\n\n## 3. 待确认清单\n",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("PrepareWorkspace failed: %v", err)
|
||||
}
|
||||
|
||||
gate := squad.NewQualityGate()
|
||||
|
||||
// 1. 测试违规文本:包含敷衍词「不做展开」与缺少第 2 节
|
||||
badContent := "# 用户初始需求收集表\n\n## 1. 客户与项目画像\n此处篇幅有限,不做展开。\n\n## 3. 待确认清单\n- 待确认事项\n"
|
||||
report1, err := gate.Validate(ws.WorkDir, "intake", badContent)
|
||||
if err != nil {
|
||||
t.Fatalf("Validate failed: %v", err)
|
||||
}
|
||||
if report1.Passed {
|
||||
t.Errorf("expected report1 to fail quality gate")
|
||||
}
|
||||
if len(report1.ProhibitedPhrases) == 0 {
|
||||
t.Errorf("expected prohibited phrase '不做展开' to be detected")
|
||||
}
|
||||
if len(report1.MissingSections) == 0 {
|
||||
t.Errorf("expected missing section '## 2. 预期用途' to be detected")
|
||||
}
|
||||
|
||||
// 2. 测试合规文本:包含所有章节且无违规词
|
||||
goodContent := `# 用户初始需求收集表
|
||||
|
||||
## 1. 客户与项目画像
|
||||
本项目客户为专业医疗机构〔材料: 需求表 §1〕。
|
||||
(四段式展开详细论述)
|
||||
|
||||
## 2. 预期用途
|
||||
用于临床辅助诊断与生理信号监测。
|
||||
|
||||
## 3. 待确认清单
|
||||
- 供电模块认证周期待窗口确认。
|
||||
`
|
||||
report2, err := gate.Validate(ws.WorkDir, "intake", goodContent)
|
||||
if err != nil {
|
||||
t.Fatalf("Validate failed: %v", err)
|
||||
}
|
||||
if !report2.Passed {
|
||||
t.Errorf("expected report2 to pass quality gate, violations: %v", report2.Violations)
|
||||
}
|
||||
if report2.Score < 80.0 {
|
||||
t.Errorf("expected high score, got %f", report2.Score)
|
||||
}
|
||||
|
||||
// 3. 测试 Manifest 生成
|
||||
manifest, err := gate.GenerateManifest(ws.WorkDir, "intake", *report2)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateManifest failed: %v", err)
|
||||
}
|
||||
if manifest.DocKey != "intake" {
|
||||
t.Errorf("expected DocKey 'intake', got %s", manifest.DocKey)
|
||||
}
|
||||
if len(manifest.Files) == 0 {
|
||||
t.Errorf("expected tracked files in manifest")
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(ws.WorkDir, "manifest.json")); err != nil {
|
||||
t.Errorf("manifest.json not written to disk")
|
||||
}
|
||||
}
|
||||
|
||||
func TestQualityGate_AutoRepair(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
engine := squad.NewWorkspaceEngine()
|
||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||
WorkDir: tmpDir,
|
||||
TaskKey: "intake",
|
||||
StageTitle: "用户初始需求收集表",
|
||||
TemplateContent: "# 用户初始需求收集表\n\n## 1. 结论\n\n## 2. 详细方案\n",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("PrepareWorkspace failed: %v", err)
|
||||
}
|
||||
|
||||
driver := squad.NewWorkflowDriver(engine)
|
||||
gate := squad.NewQualityGate()
|
||||
|
||||
// 模拟不合格草稿
|
||||
badDraft := "# 用户初始需求收集表\n\n## 1. 结论\n由于篇幅有限,不做展开。\n"
|
||||
_ = os.WriteFile(filepath.Join(ws.WorkDir, "output.md"), []byte(badDraft), 0o644)
|
||||
|
||||
// 触发自动修复:门禁检测到不合格,向 workflow 追加修复步骤并执行
|
||||
stubBackend := &agent.StubBackend{}
|
||||
report, err := gate.EnsureQualityOrRepair(context.Background(), ws.WorkDir, "intake", driver, stubBackend, 1, squad.ExecuteOptions{
|
||||
Timeout: 5 * time.Second,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("EnsureQualityOrRepair failed: %v", err)
|
||||
}
|
||||
|
||||
// 验证工作流中已追加过修复步骤
|
||||
wf, _ := driver.LoadWorkflow(ws.WorkDir)
|
||||
hasRepairStep := false
|
||||
for _, s := range wf.Steps {
|
||||
if s.ID == "step_repair_1" {
|
||||
hasRepairStep = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasRepairStep {
|
||||
t.Errorf("expected step_repair_1 to be appended to workflow")
|
||||
}
|
||||
|
||||
if report == nil {
|
||||
t.Errorf("expected non-nil quality report")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
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()
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package squad_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"chat-agent-system/internal/squad"
|
||||
)
|
||||
|
||||
func TestWorkspaceEngine_PrepareWorkspace(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
engine := squad.NewWorkspaceEngine()
|
||||
|
||||
req := squad.WorkspacePrepareRequest{
|
||||
WorkDir: filepath.Join(tmpDir, "test_workspace_01"),
|
||||
SessionID: 101,
|
||||
ProjectID: 45,
|
||||
TaskKey: "intake",
|
||||
StageTitle: "用户初始需求收集表",
|
||||
ProjectName: "内廷元无创神经刺激器",
|
||||
CustomerName: "漳州市圆山中医药产业研究院",
|
||||
DeviceType: "二类医疗器械",
|
||||
IntendedUse: "改善夜间打鼾与AHI",
|
||||
TargetMarket: "中国 (NMPA)",
|
||||
Backend: "stub",
|
||||
Materials: []squad.MaterialItem{
|
||||
{
|
||||
Filename: "PIR.md",
|
||||
Content: []byte("# PIR 需求表\n- 客户: 漳州市圆山中医药产业研究院\n- 重量: <=300g\n"),
|
||||
},
|
||||
},
|
||||
KnowledgeHits: []squad.KnowledgeHit{
|
||||
{
|
||||
DocCode: "GB 9706.1-2020",
|
||||
Title: "医用电气设备安全",
|
||||
Content: "基本安全要求...",
|
||||
Source: "知识库",
|
||||
},
|
||||
},
|
||||
TemplateContent: "# 用户初始需求收集表\n\n## 1. 客户与项目画像\n\n(四段式展开)\n",
|
||||
CustomAgents: []squad.AgentSpec{
|
||||
{
|
||||
Role: "organizer",
|
||||
Name: "材料整理专员",
|
||||
Model: "claude-3-7-sonnet",
|
||||
Tools: []string{"read", "write", "edit"},
|
||||
BashPermission: "deny",
|
||||
Responsibilities: []string{"事实抽取", "缺口识别"},
|
||||
ForbiddenActions: []string{"严禁写价"},
|
||||
},
|
||||
{
|
||||
Role: "writer",
|
||||
Name: "文档撰写专员",
|
||||
Model: "claude-3-7-sonnet",
|
||||
Tools: []string{"read", "write", "edit"},
|
||||
BashPermission: "deny",
|
||||
Responsibilities: []string{"撰写正文"},
|
||||
ForbiddenActions: []string{"严禁写不做展开"},
|
||||
},
|
||||
{
|
||||
Role: "leader",
|
||||
Name: "商务对接负责人",
|
||||
Model: "claude-3-7-sonnet",
|
||||
Tools: []string{"read", "write", "edit"},
|
||||
BashPermission: "deny",
|
||||
Responsibilities: []string{"二审与终审"},
|
||||
ForbiddenActions: []string{"严禁代做拍板"},
|
||||
},
|
||||
},
|
||||
WorkflowSteps: []squad.StepSpec{
|
||||
{
|
||||
ID: "step_01_organize",
|
||||
Role: "organizer",
|
||||
Title: "抽取材料事实并回填字段",
|
||||
SubWorkspace: "tasks/01_organize",
|
||||
Mode: "execute",
|
||||
Accept: []string{"回填 fields.json", "事实与缺口写入 blackboard"},
|
||||
},
|
||||
{
|
||||
ID: "step_02_draft",
|
||||
Role: "writer",
|
||||
Title: "撰写正文初稿",
|
||||
SubWorkspace: "tasks/02_draft",
|
||||
Mode: "execute",
|
||||
Needs: []string{"tasks/01_organize/output.md"},
|
||||
Accept: []string{"按自描述模板四段式展开"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ws, err := engine.PrepareWorkspace(req)
|
||||
if err != nil {
|
||||
t.Fatalf("PrepareWorkspace failed: %v", err)
|
||||
}
|
||||
|
||||
if ws.WorkDir != req.WorkDir {
|
||||
t.Errorf("expected WorkDir %s, got %s", req.WorkDir, ws.WorkDir)
|
||||
}
|
||||
|
||||
// 1. 校验 workspace.json
|
||||
wsFile := filepath.Join(ws.WorkDir, "workspace.json")
|
||||
if _, err := os.Stat(wsFile); err != nil {
|
||||
t.Fatalf("workspace.json does not exist: %v", err)
|
||||
}
|
||||
var wsData squad.WorkspaceMetadata
|
||||
wsBytes, _ := os.ReadFile(wsFile)
|
||||
if err := json.Unmarshal(wsBytes, &wsData); err != nil {
|
||||
t.Fatalf("unmarshal workspace.json failed: %v", err)
|
||||
}
|
||||
if wsData.TaskKey != "intake" {
|
||||
t.Errorf("expected TaskKey intake, got %s", wsData.TaskKey)
|
||||
}
|
||||
|
||||
// 2. 校验 squad.json
|
||||
squadFile := filepath.Join(ws.WorkDir, "squad.json")
|
||||
if _, err := os.Stat(squadFile); err != nil {
|
||||
t.Fatalf("squad.json does not exist: %v", err)
|
||||
}
|
||||
var squadData squad.SquadConfig
|
||||
squadBytes, _ := os.ReadFile(squadFile)
|
||||
if err := json.Unmarshal(squadBytes, &squadData); err != nil {
|
||||
t.Fatalf("unmarshal squad.json failed: %v", err)
|
||||
}
|
||||
if len(squadData.Agents) != 3 {
|
||||
t.Errorf("expected 3 agents in squad.json, got %d", len(squadData.Agents))
|
||||
}
|
||||
|
||||
// 3. 校验 workflow.json
|
||||
wfFile := filepath.Join(ws.WorkDir, "workflow.json")
|
||||
if _, err := os.Stat(wfFile); err != nil {
|
||||
t.Fatalf("workflow.json does not exist: %v", err)
|
||||
}
|
||||
var wfData squad.WorkflowPlan
|
||||
wfBytes, _ := os.ReadFile(wfFile)
|
||||
if err := json.Unmarshal(wfBytes, &wfData); err != nil {
|
||||
t.Fatalf("unmarshal workflow.json failed: %v", err)
|
||||
}
|
||||
if len(wfData.Steps) != 2 {
|
||||
t.Errorf("expected 2 steps in workflow.json, got %d", len(wfData.Steps))
|
||||
}
|
||||
|
||||
// 4. 校验 context/ 目录与 context/index.json
|
||||
ctxIndexFile := filepath.Join(ws.WorkDir, "context", "index.json")
|
||||
if _, err := os.Stat(ctxIndexFile); err != nil {
|
||||
t.Fatalf("context/index.json does not exist: %v", err)
|
||||
}
|
||||
var ctxIndex squad.ContextIndex
|
||||
ctxBytes, _ := os.ReadFile(ctxIndexFile)
|
||||
if err := json.Unmarshal(ctxBytes, &ctxIndex); err != nil {
|
||||
t.Fatalf("unmarshal context/index.json failed: %v", err)
|
||||
}
|
||||
if len(ctxIndex.Items) == 0 {
|
||||
t.Errorf("expected non-empty context items in context/index.json")
|
||||
}
|
||||
|
||||
// 5. 校验 blackboard.json
|
||||
bbFile := filepath.Join(ws.WorkDir, "blackboard.json")
|
||||
if _, err := os.Stat(bbFile); err != nil {
|
||||
t.Fatalf("blackboard.json does not exist: %v", err)
|
||||
}
|
||||
|
||||
// 6. 校验子任务目录及 TASK.md / AGENTS.md
|
||||
task1Dir := filepath.Join(ws.WorkDir, "tasks", "01_organize")
|
||||
if _, err := os.Stat(task1Dir); err != nil {
|
||||
t.Fatalf("tasks/01_organize does not exist: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(task1Dir, "TASK.md")); err != nil {
|
||||
t.Fatalf("tasks/01_organize/TASK.md does not exist: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(task1Dir, "AGENTS.md")); err != nil {
|
||||
t.Fatalf("tasks/01_organize/AGENTS.md does not exist: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user