Files
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

185 lines
5.2 KiB
Go

package squad
import (
"strings"
"testing"
)
func TestRDevCatalogSizeAndKinds(t *testing.T) {
if len(RDevCatalog) != 60 {
t.Fatalf("want 60 catalog entries, got %d", len(RDevCatalog))
}
var doc, rev, up int
for _, e := range RDevCatalog {
switch e.Kind {
case RDevKindDocument:
doc++
case RDevKindReviewMinutes:
rev++
case RDevKindUploadIndex:
up++
default:
t.Fatalf("unknown kind %q for %s", e.Kind, e.Key)
}
}
if doc != 47 || rev != 6 || up != 7 {
t.Fatalf("kind counts document=%d review=%d upload=%d", doc, rev, up)
}
if len(SupportedTasks) < 69 {
t.Fatalf("SupportedTasks should include base+rdev, got %d", len(SupportedTasks))
}
}
func TestRDevWorkflowStepsByKind(t *testing.T) {
docSteps := DefaultWorkflowSteps("rd_proposal")
if len(docSteps) < 4 {
t.Fatalf("document steps want >=4, got %d", len(docSteps))
}
revSteps := DefaultWorkflowSteps("rd_planning_review")
if len(revSteps) != 3 {
t.Fatalf("review_minutes want 3 steps, got %d", len(revSteps))
}
upSteps := DefaultWorkflowSteps("rd_prototype_photos")
if len(upSteps) != 4 {
t.Fatalf("upload_index want 4 steps, got %d", len(upSteps))
}
agents := DefaultSquadAgents("rd_proposal")
foundLeader := false
for _, a := range agents {
if a.Role == "leader" && a.Name == "研发设计控制负责人" {
foundLeader = true
}
}
if !foundLeader {
t.Fatal("leader display name missing")
}
}
func TestRDevRegulatoryFollowsComplexityTier(t *testing.T) {
if !RDevNeedsRegulatory("rd_sw_plan") {
t.Fatal("unset tier must keep the static regulatory list")
}
if !RDevNeedsRegulatoryAt("rd_sw_plan", "high") {
t.Fatal("high tier must keep regulatory for listed tasks")
}
if RDevNeedsRegulatoryAt("rd_sw_plan", "medium") || RDevNeedsRegulatoryAt("rd_sw_plan", "low") {
t.Fatal("medium and low must not inject regulatory")
}
if RDevNeedsRegulatoryAt("rd_proposal", "high") {
t.Fatal("tasks outside the list stay without regulatory even at high")
}
planner := NewAdaptivePlanner(nil, nil)
low, err := planner.Plan(PlannerRequest{
TaskKey: "rd_sw_plan",
Fields: map[string]string{"complexity_tier": "low"},
})
if err != nil {
t.Fatal(err)
}
for _, step := range low.Workflow.Steps {
if step.Role == "regulatory" {
t.Fatalf("low tier plan must not include regulatory step: %+v", low.Workflow.Steps)
}
}
high, err := planner.Plan(PlannerRequest{
TaskKey: "rd_sw_plan",
Fields: map[string]string{"complexity_tier": "high"},
})
if err != nil {
t.Fatal(err)
}
found := false
for _, step := range high.Workflow.Steps {
if step.Role == "regulatory" {
found = true
}
}
if !found {
t.Fatal("high tier plan must include regulatory step")
}
}
func TestRDevModelFromEnv(t *testing.T) {
t.Setenv("SQUAD_MODEL_ID", "")
t.Setenv("MINIMAX_MODEL_ID", "")
if got := rdevModel(); got != "MiniMax-M3" {
t.Fatalf("default model want MiniMax-M3 got %s", got)
}
t.Setenv("MINIMAX_MODEL_ID", "MiniMax-M2.7-highspeed")
if got := rdevModel(); got != "MiniMax-M2.7-highspeed" {
t.Fatalf("MINIMAX_MODEL_ID want highspeed got %s", got)
}
t.Setenv("SQUAD_MODEL_ID", "custom-squad")
if got := rdevModel(); got != "custom-squad" {
t.Fatalf("SQUAD_MODEL_ID must win, got %s", got)
}
}
func TestRDevStageOverrideAndAliases(t *testing.T) {
for _, item := range RDevCatalog {
if item.Hint == "" || len(item.Aliases) == 0 {
t.Fatalf("%s missing hint or alias", item.Key)
}
}
got, ok := LookupRDevTask("项目建议书")
if !ok || got.Key != "rd_proposal" {
t.Fatalf("alias lookup got %+v ok=%v", got, ok)
}
roles := func(task, tier string) []string {
t.Helper()
var out []string
for _, step := range rdevWorkflowSteps(task, tier) {
out = append(out, step.Role)
}
return out
}
want := func(task, tier string, expect ...string) {
t.Helper()
got := roles(task, tier)
if len(got) != len(expect) {
t.Fatalf("%s tier=%q roles %v want %v", task, tier, got, expect)
}
for i := range expect {
if got[i] != expect[i] {
t.Fatalf("%s tier=%q roles %v want %v", task, tier, got, expect)
}
}
}
want("rd_design_vv", "low", "organizer", "regulatory", "engineer", "writer", "leader")
want("rd_user_needs", "high", "organizer", "engineer", "writer", "leader")
want("rd_emc_safety", "high", "organizer", "engineer", "writer", "leader")
want("rd_regs_stds", "", "organizer", "searcher", "engineer", "regulatory", "writer", "leader")
want("rd_sw_sys_test", "", "organizer", "regulatory", "engineer", "writer", "leader")
want("rd_sw_sys_test", "low", "organizer", "engineer", "writer", "leader")
want("rd_system_reqs", "", "organizer", "engineer", "writer", "leader")
want("rd_system_reqs", "high", "organizer", "engineer", "regulatory", "writer", "leader")
steps := rdevWorkflowSteps("rd_proposal", "")
foundHint := false
for _, step := range steps {
if step.Role == "writer" {
for _, line := range step.Accept {
if strings.Contains(line, "项目建议书") {
foundHint = true
}
}
}
}
if !foundHint {
t.Fatal("writer accept must carry the stage hint")
}
agents := rdevSquadAgents("rd_regs_stds", "")
foundSearcher := false
for _, agent := range agents {
if agent.Role == "searcher" {
foundSearcher = true
}
}
if !foundSearcher {
t.Fatal("regs_stds squad must include searcher")
}
}