Files
kandev/apps/backend/internal/workflow/engine/phase2_test.go
T
João Salavisa bb72d70586 refactor(backend): remove dead code and deprecated shims (#1965)
Delete unreachable/unused backend symbols confirmed by staticcheck U1000
and golang.org/x/tools/deadcode, plus deprecated back-compat aliases kept
alive only by their own tests.

Removed (unused):
- parseGitHubRepoURL, syncTaskStateForBulkWorkflowMove (task/service)
- resolvePrimaryRepoInfo (orchestrator/executor)
- resolveWorkflowAgentProfile, workflowDefaultAgentProfile wrappers (mcp/handlers)
- cleanupExitedContainer (runtime/lifecycle) + orphaned fmt import
- VscodeManager.setEnv (agentctl/server/process)
- repoRootDir const (cmd/preview)

Removed (deprecated shims):
- Canceller.CancelSessionAndNotify alias -> callers use DetachSessionAndNotify
- ScriptPlaceholder type alias (live scriptPlaceholders var kept)
- Placeholder{QueueRun,ClearDecisions,QueueRunForEachParticipant}Callback
  no-op structs and their self-referential test

No behavior change; build, vet, and affected-package tests pass.

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-26 17:54:12 +01:00

179 lines
5.0 KiB
Go

package engine
import (
"context"
"testing"
wfmodels "github.com/kandev/kandev/internal/workflow/models"
)
func TestPhase2_NewTriggerConstantsAreDistinct(t *testing.T) {
triggers := []Trigger{
TriggerOnEnter, TriggerOnTurnStart, TriggerOnTurnComplete, TriggerOnExit,
TriggerOnComment, TriggerOnBlockerResolved, TriggerOnChildrenCompleted,
TriggerOnApprovalResolved, TriggerOnHeartbeat, TriggerOnBudgetAlert,
TriggerOnAgentError,
}
seen := make(map[Trigger]bool, len(triggers))
for _, tr := range triggers {
if tr == "" {
t.Fatalf("trigger constant must not be empty string")
}
if seen[tr] {
t.Fatalf("duplicate trigger constant %q", tr)
}
seen[tr] = true
}
}
func TestPhase2_NewActionKindsAreDistinct(t *testing.T) {
kinds := []ActionKind{
ActionMoveToNext, ActionMoveToPrevious, ActionMoveToStep,
ActionEnablePlanMode, ActionDisablePlanMode, ActionAutoStartAgent,
ActionResetAgentContext, ActionSetWorkflowData,
ActionQueueRun, ActionClearDecisions, ActionQueueRunForEachParticipant,
}
seen := make(map[ActionKind]bool, len(kinds))
for _, k := range kinds {
if k == "" {
t.Fatalf("action kind constant must not be empty string")
}
if seen[k] {
t.Fatalf("duplicate action kind %q", k)
}
seen[k] = true
}
}
func TestQueueRunAction_RoundTripsThroughActionStruct(t *testing.T) {
a := Action{
Kind: ActionQueueRun,
QueueRun: &QueueRunAction{
Target: "primary",
TaskID: "this",
Reason: "task_assigned",
Payload: map[string]any{"comment_id": "c-1"},
},
}
if a.QueueRun == nil || a.QueueRun.Target != "primary" {
t.Fatalf("QueueRun struct did not round-trip")
}
if a.QueueRun.Payload["comment_id"] != "c-1" {
t.Fatalf("QueueRun payload did not round-trip")
}
}
func TestQueueRunForEachParticipantAction_Construction(t *testing.T) {
a := Action{
Kind: ActionQueueRunForEachParticipant,
QueueRunForEachParticipant: &QueueRunForEachParticipantAction{
Role: string(wfmodels.ParticipantRoleReviewer),
Reason: "review_started",
},
}
if a.QueueRunForEachParticipant == nil {
t.Fatalf("expected QueueRunForEachParticipant to be set")
}
if a.QueueRunForEachParticipant.Role != "reviewer" {
t.Fatalf("unexpected role: %q", a.QueueRunForEachParticipant.Role)
}
}
func TestClearDecisionsAction_Construction(t *testing.T) {
a := Action{
Kind: ActionClearDecisions,
ClearDecisions: &ClearDecisionsAction{},
}
if a.ClearDecisions == nil {
t.Fatalf("expected ClearDecisions to be set")
}
}
func TestPhase2Placeholders_NotRegisteredInDefaultRegistry(t *testing.T) {
// Empty MapRegistry must not resolve the new action kinds. This pins the
// invariant that the Phase 2 actions are declared but inert until the
// engine integration slice wires them up.
r := MapRegistry{}
for _, kind := range []ActionKind{
ActionQueueRun, ActionClearDecisions, ActionQueueRunForEachParticipant,
} {
if _, ok := r.Get(kind); ok {
t.Fatalf("action %q must not be registered by default", kind)
}
}
}
func TestValidParticipantRole(t *testing.T) {
valid := []string{"reviewer", "approver", "watcher", "collaborator"}
for _, v := range valid {
if !ValidParticipantRole(v) {
t.Fatalf("expected %q to be a valid role", v)
}
}
for _, v := range []string{"", "ceo", "owner"} {
if ValidParticipantRole(v) {
t.Fatalf("expected %q to be rejected", v)
}
}
}
func TestValidStageType(t *testing.T) {
for _, v := range []string{"work", "review", "approval", "custom"} {
if !ValidStageType(v) {
t.Fatalf("expected stage_type %q to be valid", v)
}
}
for _, v := range []string{"", "ship", "release"} {
if ValidStageType(v) {
t.Fatalf("expected stage_type %q to be rejected", v)
}
}
}
func TestValidWorkflowStyle(t *testing.T) {
for _, v := range []string{"kanban", "office", "custom"} {
if !ValidWorkflowStyle(v) {
t.Fatalf("expected workflow style %q to be valid", v)
}
}
for _, v := range []string{"", "agile", "scrum"} {
if ValidWorkflowStyle(v) {
t.Fatalf("expected workflow style %q to be rejected", v)
}
}
}
func TestExistingEngineHandleTrigger_UnaffectedByNewKinds(t *testing.T) {
// Pin the invariant that the existing dispatch path does not "see" the
// new kinds — a step compiled today cannot accidentally surface them.
store := newFakeStoreForTest()
eng := New(store, MapRegistry{})
res, err := eng.HandleTrigger(context.Background(), HandleInput{
TaskID: "t1",
SessionID: "s1",
Trigger: TriggerOnEnter,
})
if err != nil {
t.Fatalf("HandleTrigger returned unexpected error: %v", err)
}
if res.Transitioned {
t.Fatalf("expected no transition for trigger with no actions")
}
}
// newFakeStoreForTest provides a minimal TransitionStore for trigger smoke tests.
func newFakeStoreForTest() *fakeStore {
return &fakeStore{
state: MachineState{
TaskID: "t1",
SessionID: "s1",
WorkflowID: "wf",
CurrentStepID: "s1",
},
stepsByID: map[string]StepSpec{
"s1": {ID: "s1", WorkflowID: "wf", Position: 0, Events: map[Trigger][]Action{}},
},
applied: map[string]bool{},
}
}