Files
Agent-Squad/squad/registry_test.go
weichenandCursor bfec3c8930
ci / vet-and-test (push) Failing after 3s
ci / docker-checkout-hostgateway (push) Failing after 3s
Import agent-squad as a standalone repository.
Pin Agent-Native as a submodule so the module builds without the multi-repo sibling path.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-29 12:38:35 +00:00

104 lines
3.6 KiB
Go

package squad
import (
"strings"
"testing"
)
func TestRoleRegistryEmptyByDefault(t *testing.T) {
r := NewRoleRegistry()
if roles := r.List(); len(roles) != 0 {
t.Fatalf("registry must start empty, got %d roles (no preset roles allowed)", len(roles))
}
if _, ok := r.Recommend("法规调研"); ok {
t.Fatal("empty registry must not recommend")
}
}
func TestRoleRegistryRegisterAndRecommend(t *testing.T) {
r := NewRoleRegistry()
if err := r.Register(RoleSpec{Role: "researcher", Name: "调研", Keywords: []string{"调研", "市场"}, Backend: "stub"}); err != nil {
t.Fatal(err)
}
if err := r.Register(RoleSpec{Role: "writer", Name: "撰写", Keywords: []string{"撰写"}, Responsibilities: []string{"报告成文"}}); err != nil {
t.Fatal(err)
}
if err := r.Register(RoleSpec{Role: "bad", BashPermission: "sometimes"}); err == nil {
t.Fatal("invalid bash_permission must fail")
}
spec, ok := r.Recommend("帮我调研市场情况")
if !ok || spec.Role != "researcher" {
t.Fatalf("recommend want researcher, got %+v ok=%v", spec, ok)
}
if spec.BashPermission != "deny" {
t.Fatalf("default bash_permission want deny, got %q", spec.BashPermission)
}
if !r.Remove("writer") {
t.Fatal("remove writer failed")
}
if r.Remove("writer") {
t.Fatal("double remove should fail")
}
}
func TestRoleRegistryDerive(t *testing.T) {
r := NewRoleRegistry()
_ = r.Register(RoleSpec{Role: "engineer", Name: "工程师", Backend: "agent-native", Model: "m1",
Keywords: []string{"工程"}, Instructions: "基线指令"})
derived, err := r.Derive(DeriveRequest{RoleKey: "gmp_expert", DomainTopic: "GMP 洁净车间"})
if err != nil {
t.Fatal(err)
}
if derived.Source != RoleSourceDerived || derived.BaseRole != "engineer" {
t.Fatalf("derived role meta wrong: %+v", derived)
}
if derived.Backend != "agent-native" || derived.Model != "m1" || derived.Instructions != "基线指令" {
t.Fatalf("derive must inherit backend/model/instructions: %+v", derived)
}
if !strings.Contains(strings.Join(derived.Responsibilities, ";"), "GMP") {
t.Fatalf("domain topic not in responsibilities: %v", derived.Responsibilities)
}
if len(derived.ForbiddenActions) == 0 {
t.Fatal("derive must append guardrail forbidden actions")
}
}
func TestBlueprintRegistryMatch(t *testing.T) {
b := NewBlueprintRegistry()
if err := b.Register(Blueprint{Key: "x", Steps: nil}); err == nil {
t.Fatal("blueprint without steps must fail")
}
_ = b.Register(Blueprint{
Key: "report", Title: "报告", Match: []string{"报告", "调研"},
Steps: []StepSpec{{ID: "s1", Role: "writer", Title: "成文"}},
})
if bp, ok := b.Match("我要一份调研报告", ""); !ok || bp.Key != "report" {
t.Fatalf("match failed: %+v ok=%v", bp, ok)
}
if _, ok := b.Match("无关文本", ""); ok {
t.Fatal("unrelated text must not match")
}
if bp, ok := b.Match("", "report"); !ok || bp.Key != "report" {
t.Fatal("explicit task key must match")
}
// 步骤默认 ID/Targets 补全。
got, _ := b.Get("report")
if got.Steps[0].Targets[0] != "output.md" {
t.Fatalf("default targets missing: %+v", got.Steps[0])
}
}
func TestHitlPolicyMergeAndStates(t *testing.T) {
merged := mergeHitlPolicy(HitlPolicy{Mode: HitlInteractive},
&HitlPolicy{Mode: HitlAuto}, &HitlPolicy{TimeoutAfter: 5e9, ExpireAction: "cancel"})
if merged.Mode != HitlAuto || merged.TimeoutAfter != 5e9 || merged.ExpireAction != "cancel" {
t.Fatalf("merge wrong: %+v", merged)
}
if !RunAwaitingClarification.IsHeld() || !RunQualityReview.IsHeld() || RunExecuting.IsHeld() {
t.Fatal("held state classification wrong")
}
if !RunCompleted.Terminal() || RunWaitingHuman.Terminal() {
t.Fatal("terminal state classification wrong")
}
}