Files
agentic-coding-workflow/internal/orchestrator/planner_test.go
T
2026-06-22 08:55:57 +08:00

111 lines
3.4 KiB
Go

package orchestrator
import (
"testing"
"github.com/google/uuid"
"github.com/yan1h/agent-coding-workflow/internal/project"
)
func TestNextPhase_AdvancesLinearly(t *testing.T) {
pass := GateResult{Pass: true}
cases := []struct {
from project.Phase
wantNext project.Phase
wantTerm bool
}{
{project.PhasePlanning, project.PhasePrototyping, false},
{project.PhasePrototyping, project.PhaseAuditing, false},
{project.PhaseAuditing, project.PhaseImplementing, false},
{project.PhaseImplementing, project.PhaseReviewing, false},
{project.PhaseReviewing, project.PhaseDone, false},
{project.PhaseDone, project.PhaseDone, true},
}
for _, c := range cases {
next, term := NextPhase(c.from, pass)
if next != c.wantNext || term != c.wantTerm {
t.Errorf("NextPhase(%s,pass) = (%s,%v); want (%s,%v)",
c.from, next, term, c.wantNext, c.wantTerm)
}
}
}
func TestNextPhase_GateFailLoopsBack(t *testing.T) {
fail := GateResult{Pass: false, Retry: true}
for _, ph := range project.AllPhases {
next, term := NextPhase(ph, fail)
if next != ph || term {
t.Errorf("NextPhase(%s, fail) = (%s,%v); want (%s,false)", ph, next, term, ph)
}
}
}
func TestNextPhase_UnknownPhaseTerminates(t *testing.T) {
next, term := NextPhase(project.Phase("bogus"), GateResult{Pass: true})
if !term {
t.Errorf("unknown phase should terminate; got next=%s term=%v", next, term)
}
}
func TestPhaseAgentKind_OverridePreferredThenDefault(t *testing.T) {
override := uuid.New()
def := uuid.New()
cfg := RunConfig{PhaseAgentKinds: map[project.Phase]uuid.UUID{project.PhasePlanning: override}}
defaults := map[project.Phase]uuid.UUID{project.PhasePlanning: def, project.PhaseAuditing: def}
got, err := PhaseAgentKind(cfg, project.PhasePlanning, defaults)
if err != nil || got != override {
t.Fatalf("override path: got=%s err=%v; want %s", got, err, override)
}
got, err = PhaseAgentKind(cfg, project.PhaseAuditing, defaults)
if err != nil || got != def {
t.Fatalf("default fallback: got=%s err=%v; want %s", got, err, def)
}
}
func TestPhaseAgentKind_MissingErrors(t *testing.T) {
_, err := PhaseAgentKind(RunConfig{}, project.PhasePlanning, nil)
if err == nil {
t.Fatal("expected error when no agent kind configured")
}
}
func TestBuildPrompt_IncludesReqAndArtifacts(t *testing.T) {
req := &project.Requirement{Number: 7, Title: "Login flow", Description: "Add OAuth login"}
arts := []*project.Artifact{
{Phase: project.PhasePlanning, Version: 2, Content: "Plan: use OAuth2 PKCE\nmore detail"},
}
out := BuildPrompt(project.PhaseAuditing, req, arts, "")
for _, want := range []string{"#7", "Login flow", "Add OAuth login", "Plan: use OAuth2 PKCE", "auditing"} {
if !contains(out, want) {
t.Errorf("prompt missing %q\n%s", want, out)
}
}
}
func TestBuildPrompt_OverrideUsed(t *testing.T) {
req := &project.Requirement{Number: 1, Title: "T"}
out := BuildPrompt(project.PhasePlanning, req, nil, "DO THE SPECIAL THING")
if !contains(out, "DO THE SPECIAL THING") {
t.Errorf("override not used:\n%s", out)
}
if contains(out, "Produce a clear plan") {
t.Errorf("default instruction should be suppressed when override present:\n%s", out)
}
}
func contains(s, sub string) bool {
return len(s) >= len(sub) && (indexOf(s, sub) >= 0)
}
func indexOf(s, sub string) int {
for i := 0; i+len(sub) <= len(s); i++ {
if s[i:i+len(sub)] == sub {
return i
}
}
return -1
}