You've already forked agentic-coding-workflow
111 lines
3.4 KiB
Go
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
|
|
}
|