134 lines
4.0 KiB
Go
134 lines
4.0 KiB
Go
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")
|
|
}
|
|
}
|