224 lines
6.7 KiB
Go
224 lines
6.7 KiB
Go
package orchestrator
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/kandev/kandev/internal/task/models"
|
|
)
|
|
|
|
func TestResolveIntent(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
req LaunchSessionRequest
|
|
want SessionIntent
|
|
}{
|
|
// Explicit intents take priority
|
|
{
|
|
name: "explicit start intent",
|
|
req: LaunchSessionRequest{TaskID: "t1", Intent: IntentStart},
|
|
want: IntentStart,
|
|
},
|
|
{
|
|
name: "explicit resume intent",
|
|
req: LaunchSessionRequest{TaskID: "t1", Intent: IntentResume, SessionID: "s1"},
|
|
want: IntentResume,
|
|
},
|
|
{
|
|
name: "explicit prepare intent",
|
|
req: LaunchSessionRequest{TaskID: "t1", Intent: IntentPrepare},
|
|
want: IntentPrepare,
|
|
},
|
|
{
|
|
name: "explicit start_created intent",
|
|
req: LaunchSessionRequest{TaskID: "t1", Intent: IntentStartCreated, SessionID: "s1"},
|
|
want: IntentStartCreated,
|
|
},
|
|
{
|
|
name: "explicit workflow_step intent",
|
|
req: LaunchSessionRequest{TaskID: "t1", Intent: IntentWorkflowStep, SessionID: "s1", WorkflowStepID: "ws1"},
|
|
want: IntentWorkflowStep,
|
|
},
|
|
|
|
// Inferred intents (no explicit intent set)
|
|
{
|
|
name: "workflow_step inferred from session_id + workflow_step_id",
|
|
req: LaunchSessionRequest{TaskID: "t1", SessionID: "s1", WorkflowStepID: "ws1"},
|
|
want: IntentWorkflowStep,
|
|
},
|
|
{
|
|
name: "resume inferred from session_id only",
|
|
req: LaunchSessionRequest{TaskID: "t1", SessionID: "s1"},
|
|
want: IntentResume,
|
|
},
|
|
{
|
|
name: "resume inferred from session_id with no prompt and no agent_profile",
|
|
req: LaunchSessionRequest{TaskID: "t1", SessionID: "s1"},
|
|
want: IntentResume,
|
|
},
|
|
{
|
|
name: "start_created inferred from session_id + prompt",
|
|
req: LaunchSessionRequest{TaskID: "t1", SessionID: "s1", Prompt: "hello"},
|
|
want: IntentStartCreated,
|
|
},
|
|
{
|
|
name: "start_created inferred from session_id + agent_profile_id",
|
|
req: LaunchSessionRequest{TaskID: "t1", SessionID: "s1", AgentProfileID: "ap1"},
|
|
want: IntentStartCreated,
|
|
},
|
|
{
|
|
name: "start_created inferred from session_id + prompt + agent_profile_id",
|
|
req: LaunchSessionRequest{TaskID: "t1", SessionID: "s1", Prompt: "hello", AgentProfileID: "ap1"},
|
|
want: IntentStartCreated,
|
|
},
|
|
{
|
|
name: "prepare inferred from launch_workspace without prompt",
|
|
req: LaunchSessionRequest{TaskID: "t1", LaunchWorkspace: true},
|
|
want: IntentPrepare,
|
|
},
|
|
{
|
|
name: "start inferred from minimal request",
|
|
req: LaunchSessionRequest{TaskID: "t1"},
|
|
want: IntentStart,
|
|
},
|
|
{
|
|
name: "start inferred when prompt provided without session_id",
|
|
req: LaunchSessionRequest{TaskID: "t1", Prompt: "do something"},
|
|
want: IntentStart,
|
|
},
|
|
{
|
|
name: "start inferred when launch_workspace + prompt (not prepare)",
|
|
req: LaunchSessionRequest{TaskID: "t1", LaunchWorkspace: true, Prompt: "do something"},
|
|
want: IntentStart,
|
|
},
|
|
|
|
// Edge cases
|
|
{
|
|
name: "resume wins over start_created when session_id set, no prompt, no agent_profile",
|
|
req: LaunchSessionRequest{TaskID: "t1", SessionID: "s1", ExecutorID: "e1"},
|
|
want: IntentResume,
|
|
},
|
|
{
|
|
name: "workflow_step wins over resume when both session_id and workflow_step_id set",
|
|
req: LaunchSessionRequest{TaskID: "t1", SessionID: "s1", WorkflowStepID: "ws1"},
|
|
want: IntentWorkflowStep,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := ResolveIntent(&tt.req)
|
|
if got != tt.want {
|
|
t.Errorf("ResolveIntent() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// --- launchRestoreWorkspace ---
|
|
|
|
func TestLaunchRestoreWorkspace_MissingSessionID(t *testing.T) {
|
|
repo := setupTestRepo(t)
|
|
svc := createTestService(repo, newMockStepGetter(), newMockTaskRepo())
|
|
|
|
_, err := svc.LaunchSession(context.Background(), &LaunchSessionRequest{
|
|
TaskID: "task1",
|
|
Intent: IntentRestoreWorkspace,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected error when session_id is empty")
|
|
}
|
|
}
|
|
|
|
func TestLaunchRestoreWorkspace_SessionNotFound(t *testing.T) {
|
|
repo := setupTestRepo(t)
|
|
svc := createTestService(repo, newMockStepGetter(), newMockTaskRepo())
|
|
|
|
_, err := svc.LaunchSession(context.Background(), &LaunchSessionRequest{
|
|
TaskID: "task1",
|
|
Intent: IntentRestoreWorkspace,
|
|
SessionID: "nonexistent",
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected error when session does not exist")
|
|
}
|
|
}
|
|
|
|
func TestLaunchRestoreWorkspace_WrongTask(t *testing.T) {
|
|
repo := setupTestRepo(t)
|
|
svc := createTestService(repo, newMockStepGetter(), newMockTaskRepo())
|
|
|
|
seedTaskAndSession(t, repo, "task-other", "session1", models.TaskSessionStateCompleted)
|
|
|
|
_, err := svc.LaunchSession(context.Background(), &LaunchSessionRequest{
|
|
TaskID: "task-wrong",
|
|
Intent: IntentRestoreWorkspace,
|
|
SessionID: "session1",
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected error when session does not belong to task")
|
|
}
|
|
}
|
|
|
|
func TestLaunchRestoreWorkspace_Success(t *testing.T) {
|
|
repo := setupTestRepo(t)
|
|
agentMgr := &mockAgentManager{}
|
|
svc := createTestServiceWithAgent(repo, newMockStepGetter(), newMockTaskRepo(), agentMgr)
|
|
|
|
seedTaskAndSession(t, repo, "task1", "session1", models.TaskSessionStateCompleted)
|
|
|
|
resp, err := svc.LaunchSession(context.Background(), &LaunchSessionRequest{
|
|
TaskID: "task1",
|
|
Intent: IntentRestoreWorkspace,
|
|
SessionID: "session1",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("expected no error, got: %v", err)
|
|
}
|
|
if !resp.Success {
|
|
t.Fatal("expected success=true")
|
|
}
|
|
if resp.SessionID != "session1" {
|
|
t.Errorf("expected session_id 'session1', got %q", resp.SessionID)
|
|
}
|
|
if resp.State != string(models.TaskSessionStateCompleted) {
|
|
t.Errorf("expected state %q, got %q", models.TaskSessionStateCompleted, resp.State)
|
|
}
|
|
}
|
|
|
|
func TestLaunchRestoreWorkspace_IncludesWorktreeInfo(t *testing.T) {
|
|
ctx := context.Background()
|
|
repo := setupTestRepo(t)
|
|
agentMgr := &mockAgentManager{}
|
|
svc := createTestServiceWithAgent(repo, newMockStepGetter(), newMockTaskRepo(), agentMgr)
|
|
|
|
seedTaskAndSession(t, repo, "task1", "session1", models.TaskSessionStateFailed)
|
|
|
|
// Add worktree to the session
|
|
if err := repo.CreateTaskSessionWorktree(ctx, &models.TaskSessionWorktree{
|
|
ID: "wt1",
|
|
SessionID: "session1",
|
|
WorktreeID: "wid1",
|
|
RepositoryID: "repo1",
|
|
WorktreePath: "/tmp/worktrees/session1",
|
|
WorktreeBranch: "feature/test",
|
|
}); err != nil {
|
|
t.Fatalf("failed to create worktree: %v", err)
|
|
}
|
|
|
|
resp, err := svc.LaunchSession(ctx, &LaunchSessionRequest{
|
|
TaskID: "task1",
|
|
Intent: IntentRestoreWorkspace,
|
|
SessionID: "session1",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("expected no error, got: %v", err)
|
|
}
|
|
if resp.WorktreePath == nil || *resp.WorktreePath != "/tmp/worktrees/session1" {
|
|
t.Errorf("expected worktree_path '/tmp/worktrees/session1', got %v", resp.WorktreePath)
|
|
}
|
|
if resp.WorktreeBranch == nil || *resp.WorktreeBranch != "feature/test" {
|
|
t.Errorf("expected worktree_branch 'feature/test', got %v", resp.WorktreeBranch)
|
|
}
|
|
}
|