feat: tasks refactor

This commit is contained in:
Carlos Florêncio
2026-01-15 18:25:42 +00:00
parent 44abff6493
commit 7d0905b8cb
84 changed files with 3384 additions and 2174 deletions
@@ -493,9 +493,20 @@ func TestSQLiteRepository_Persistence(t *testing.T) {
}
}
// Comment CRUD tests
// Message CRUD tests
func TestSQLiteRepository_CommentCRUD(t *testing.T) {
func setupSQLiteTestSession(ctx context.Context, repo *SQLiteRepository, taskID, sessionID string) string {
session := &models.AgentSession{
ID: sessionID,
TaskID: taskID,
AgentProfileID: "profile-123",
State: models.AgentSessionStateStarting,
}
_ = repo.CreateAgentSession(ctx, session)
return session.ID
}
func TestSQLiteRepository_MessageCRUD(t *testing.T) {
repo, cleanup := createTestSQLiteRepo(t)
defer cleanup()
ctx := context.Background()
@@ -509,16 +520,18 @@ func TestSQLiteRepository_CommentCRUD(t *testing.T) {
// Create a task first
task := &models.Task{ID: "task-123", BoardID: "board-123", ColumnID: "col-123", Title: "Test Task"}
_ = repo.CreateTask(ctx, task)
sessionID := setupSQLiteTestSession(ctx, repo, task.ID, "session-123")
// Create comment
comment := &models.Comment{
TaskID: "task-123",
AuthorType: models.CommentAuthorUser,
AuthorID: "user-123",
Content: "This is a test comment",
RequestsInput: false,
comment := &models.Message{
AgentSessionID: sessionID,
TaskID: "task-123",
AuthorType: models.MessageAuthorUser,
AuthorID: "user-123",
Content: "This is a test comment",
RequestsInput: false,
}
if err := repo.CreateComment(ctx, comment); err != nil {
if err := repo.CreateMessage(ctx, comment); err != nil {
t.Fatalf("failed to create comment: %v", err)
}
if comment.ID == "" {
@@ -529,44 +542,44 @@ func TestSQLiteRepository_CommentCRUD(t *testing.T) {
}
// Get comment
retrieved, err := repo.GetComment(ctx, comment.ID)
retrieved, err := repo.GetMessage(ctx, comment.ID)
if err != nil {
t.Fatalf("failed to get comment: %v", err)
}
if retrieved.Content != "This is a test comment" {
t.Errorf("expected content 'This is a test comment', got %s", retrieved.Content)
}
if retrieved.AuthorType != models.CommentAuthorUser {
if retrieved.AuthorType != models.MessageAuthorUser {
t.Errorf("expected author type 'user', got %s", retrieved.AuthorType)
}
// Delete comment
if err := repo.DeleteComment(ctx, comment.ID); err != nil {
if err := repo.DeleteMessage(ctx, comment.ID); err != nil {
t.Fatalf("failed to delete comment: %v", err)
}
_, err = repo.GetComment(ctx, comment.ID)
_, err = repo.GetMessage(ctx, comment.ID)
if err == nil {
t.Error("expected comment to be deleted")
}
}
func TestSQLiteRepository_CommentNotFound(t *testing.T) {
func TestSQLiteRepository_MessageNotFound(t *testing.T) {
repo, cleanup := createTestSQLiteRepo(t)
defer cleanup()
ctx := context.Background()
_, err := repo.GetComment(ctx, "nonexistent")
_, err := repo.GetMessage(ctx, "nonexistent")
if err == nil {
t.Error("expected error for nonexistent comment")
}
err = repo.DeleteComment(ctx, "nonexistent")
err = repo.DeleteMessage(ctx, "nonexistent")
if err == nil {
t.Error("expected error for deleting nonexistent comment")
}
}
func TestSQLiteRepository_ListComments(t *testing.T) {
func TestSQLiteRepository_ListMessages(t *testing.T) {
repo, cleanup := createTestSQLiteRepo(t)
defer cleanup()
ctx := context.Background()
@@ -582,13 +595,15 @@ func TestSQLiteRepository_ListComments(t *testing.T) {
_ = repo.CreateTask(ctx, task)
task2 := &models.Task{ID: "task-456", BoardID: "board-123", ColumnID: "col-123", Title: "Test Task 2"}
_ = repo.CreateTask(ctx, task2)
sessionID := setupSQLiteTestSession(ctx, repo, task.ID, "session-123")
_ = setupSQLiteTestSession(ctx, repo, task2.ID, "session-456")
// Create multiple comments
_ = repo.CreateComment(ctx, &models.Comment{ID: "comment-1", TaskID: "task-123", AuthorType: models.CommentAuthorUser, Content: "Comment 1"})
_ = repo.CreateComment(ctx, &models.Comment{ID: "comment-2", TaskID: "task-123", AuthorType: models.CommentAuthorAgent, Content: "Comment 2"})
_ = repo.CreateComment(ctx, &models.Comment{ID: "comment-3", TaskID: "task-456", AuthorType: models.CommentAuthorUser, Content: "Comment 3"})
_ = repo.CreateMessage(ctx, &models.Message{ID: "comment-1", AgentSessionID: sessionID, TaskID: "task-123", AuthorType: models.MessageAuthorUser, Content: "Comment 1"})
_ = repo.CreateMessage(ctx, &models.Message{ID: "comment-2", AgentSessionID: sessionID, TaskID: "task-123", AuthorType: models.MessageAuthorAgent, Content: "Comment 2"})
_ = repo.CreateMessage(ctx, &models.Message{ID: "comment-3", AgentSessionID: "session-456", TaskID: "task-456", AuthorType: models.MessageAuthorUser, Content: "Comment 3"})
comments, err := repo.ListComments(ctx, "task-123")
comments, err := repo.ListMessages(ctx, sessionID)
if err != nil {
t.Fatalf("failed to list comments: %v", err)
}
@@ -597,7 +612,7 @@ func TestSQLiteRepository_ListComments(t *testing.T) {
}
}
func TestSQLiteRepository_ListCommentsPagination(t *testing.T) {
func TestSQLiteRepository_ListMessagesPagination(t *testing.T) {
repo, cleanup := createTestSQLiteRepo(t)
defer cleanup()
ctx := context.Background()
@@ -608,31 +623,35 @@ func TestSQLiteRepository_ListCommentsPagination(t *testing.T) {
_ = repo.CreateColumn(ctx, column)
task := &models.Task{ID: "task-123", BoardID: "board-123", ColumnID: "col-123", Title: "Test Task"}
_ = repo.CreateTask(ctx, task)
sessionID := setupSQLiteTestSession(ctx, repo, task.ID, "session-123")
baseTime := time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC)
_ = repo.CreateComment(ctx, &models.Comment{
ID: "comment-1",
TaskID: "task-123",
AuthorType: models.CommentAuthorUser,
Content: "Comment 1",
CreatedAt: baseTime.Add(-2 * time.Minute),
_ = repo.CreateMessage(ctx, &models.Message{
ID: "comment-1",
AgentSessionID: sessionID,
TaskID: "task-123",
AuthorType: models.MessageAuthorUser,
Content: "Comment 1",
CreatedAt: baseTime.Add(-2 * time.Minute),
})
_ = repo.CreateComment(ctx, &models.Comment{
ID: "comment-2",
TaskID: "task-123",
AuthorType: models.CommentAuthorUser,
Content: "Comment 2",
CreatedAt: baseTime.Add(-1 * time.Minute),
_ = repo.CreateMessage(ctx, &models.Message{
ID: "comment-2",
AgentSessionID: sessionID,
TaskID: "task-123",
AuthorType: models.MessageAuthorUser,
Content: "Comment 2",
CreatedAt: baseTime.Add(-1 * time.Minute),
})
_ = repo.CreateComment(ctx, &models.Comment{
ID: "comment-3",
TaskID: "task-123",
AuthorType: models.CommentAuthorUser,
Content: "Comment 3",
CreatedAt: baseTime,
_ = repo.CreateMessage(ctx, &models.Message{
ID: "comment-3",
AgentSessionID: sessionID,
TaskID: "task-123",
AuthorType: models.MessageAuthorUser,
Content: "Comment 3",
CreatedAt: baseTime,
})
comments, hasMore, err := repo.ListCommentsPaginated(ctx, "task-123", ListCommentsOptions{
comments, hasMore, err := repo.ListMessagesPaginated(ctx, sessionID, ListMessagesOptions{
Limit: 2,
Sort: "desc",
})
@@ -650,7 +669,7 @@ func TestSQLiteRepository_ListCommentsPagination(t *testing.T) {
}
}
func TestSQLiteRepository_CommentWithRequestsInput(t *testing.T) {
func TestSQLiteRepository_MessageWithRequestsInput(t *testing.T) {
repo, cleanup := createTestSQLiteRepo(t)
defer cleanup()
ctx := context.Background()
@@ -664,27 +683,25 @@ func TestSQLiteRepository_CommentWithRequestsInput(t *testing.T) {
// Create a task
task := &models.Task{ID: "task-123", BoardID: "board-123", ColumnID: "col-123", Title: "Test Task"}
_ = repo.CreateTask(ctx, task)
sessionID := setupSQLiteTestSession(ctx, repo, task.ID, "session-123")
// Create agent comment requesting input
comment := &models.Comment{
TaskID: "task-123",
AuthorType: models.CommentAuthorAgent,
AuthorID: "agent-123",
Content: "What should I do next?",
RequestsInput: true,
ACPSessionID: "session-abc",
comment := &models.Message{
AgentSessionID: sessionID,
TaskID: "task-123",
AuthorType: models.MessageAuthorAgent,
AuthorID: "agent-123",
Content: "What should I do next?",
RequestsInput: true,
}
if err := repo.CreateComment(ctx, comment); err != nil {
if err := repo.CreateMessage(ctx, comment); err != nil {
t.Fatalf("failed to create comment: %v", err)
}
retrieved, _ := repo.GetComment(ctx, comment.ID)
retrieved, _ := repo.GetMessage(ctx, comment.ID)
if !retrieved.RequestsInput {
t.Error("expected RequestsInput to be true")
}
if retrieved.ACPSessionID != "session-abc" {
t.Errorf("expected ACPSessionID 'session-abc', got %s", retrieved.ACPSessionID)
}
}
// AgentSession CRUD tests
@@ -708,10 +725,9 @@ func TestSQLiteRepository_AgentSessionCRUD(t *testing.T) {
AgentInstanceID: "instance-abc",
ContainerID: "container-xyz",
AgentProfileID: "profile-123",
ACPSessionID: "acp-session-123",
ExecutorID: "executor-1",
EnvironmentID: "env-1",
Status: models.AgentSessionStatusPending,
State: models.AgentSessionStateStarting,
Progress: 0,
Metadata: map[string]interface{}{"key": "value"},
}
@@ -739,22 +755,22 @@ func TestSQLiteRepository_AgentSessionCRUD(t *testing.T) {
if retrieved.AgentProfileID != "profile-123" {
t.Errorf("expected AgentProfileID 'profile-123', got %s", retrieved.AgentProfileID)
}
if retrieved.Status != models.AgentSessionStatusPending {
t.Errorf("expected status 'pending', got %s", retrieved.Status)
if retrieved.State != models.AgentSessionStateStarting {
t.Errorf("expected state 'starting', got %s", retrieved.State)
}
if retrieved.Metadata["key"] != "value" {
t.Errorf("expected metadata key 'value', got %v", retrieved.Metadata["key"])
}
// Update agent session
session.Status = models.AgentSessionStatusRunning
session.State = models.AgentSessionStateRunning
session.Progress = 50
if err := repo.UpdateAgentSession(ctx, session); err != nil {
t.Fatalf("failed to update agent session: %v", err)
}
retrieved, _ = repo.GetAgentSession(ctx, session.ID)
if retrieved.Status != models.AgentSessionStatusRunning {
t.Errorf("expected status 'running', got %s", retrieved.Status)
if retrieved.State != models.AgentSessionStateRunning {
t.Errorf("expected state 'running', got %s", retrieved.State)
}
if retrieved.Progress != 50 {
t.Errorf("expected progress 50, got %d", retrieved.Progress)
@@ -809,7 +825,7 @@ func TestSQLiteRepository_AgentSessionByTaskID(t *testing.T) {
ID: "session-1",
TaskID: "task-123",
AgentProfileID: "profile-1",
Status: models.AgentSessionStatusCompleted,
State: models.AgentSessionStateCompleted,
}
_ = repo.CreateAgentSession(ctx, session1)
@@ -817,7 +833,7 @@ func TestSQLiteRepository_AgentSessionByTaskID(t *testing.T) {
ID: "session-2",
TaskID: "task-123",
AgentProfileID: "profile-2",
Status: models.AgentSessionStatusRunning,
State: models.AgentSessionStateRunning,
}
_ = repo.CreateAgentSession(ctx, session2)
@@ -838,12 +854,12 @@ func TestSQLiteRepository_AgentSessionByTaskID(t *testing.T) {
if active.ID != "session-2" {
t.Errorf("expected session-2 (active), got %s", active.ID)
}
if active.Status != models.AgentSessionStatusRunning {
t.Errorf("expected status 'running', got %s", active.Status)
if active.State != models.AgentSessionStateRunning {
t.Errorf("expected state 'running', got %s", active.State)
}
// Test when no active session exists
session2.Status = models.AgentSessionStatusCompleted
session2.State = models.AgentSessionStateCompleted
_ = repo.UpdateAgentSession(ctx, session2)
_, err = repo.GetActiveAgentSessionByTaskID(ctx, "task-123")
@@ -874,9 +890,9 @@ func TestSQLiteRepository_ListAgentSessions(t *testing.T) {
_ = repo.CreateTask(ctx, task2)
// Create sessions for different tasks
_ = repo.CreateAgentSession(ctx, &models.AgentSession{ID: "session-1", TaskID: "task-1", AgentProfileID: "profile-1", Status: models.AgentSessionStatusCompleted})
_ = repo.CreateAgentSession(ctx, &models.AgentSession{ID: "session-2", TaskID: "task-1", AgentProfileID: "profile-1", Status: models.AgentSessionStatusRunning})
_ = repo.CreateAgentSession(ctx, &models.AgentSession{ID: "session-3", TaskID: "task-2", AgentProfileID: "profile-2", Status: models.AgentSessionStatusPending})
_ = repo.CreateAgentSession(ctx, &models.AgentSession{ID: "session-1", TaskID: "task-1", AgentProfileID: "profile-1", State: models.AgentSessionStateCompleted})
_ = repo.CreateAgentSession(ctx, &models.AgentSession{ID: "session-2", TaskID: "task-1", AgentProfileID: "profile-1", State: models.AgentSessionStateRunning})
_ = repo.CreateAgentSession(ctx, &models.AgentSession{ID: "session-3", TaskID: "task-2", AgentProfileID: "profile-2", State: models.AgentSessionStateStarting})
// List sessions for task-1
sessions, err := repo.ListAgentSessions(ctx, "task-1")
@@ -898,13 +914,13 @@ func TestSQLiteRepository_ListAgentSessions(t *testing.T) {
// Verify only active statuses are returned
for _, s := range activeSessions {
if s.Status != models.AgentSessionStatusPending && s.Status != models.AgentSessionStatusRunning && s.Status != models.AgentSessionStatusWaiting {
t.Errorf("expected active status, got %s", s.Status)
if s.State != models.AgentSessionStateStarting && s.State != models.AgentSessionStateRunning && s.State != models.AgentSessionStateWaitingForInput {
t.Errorf("expected active state, got %s", s.State)
}
}
}
func TestSQLiteRepository_UpdateAgentSessionStatus(t *testing.T) {
func TestSQLiteRepository_UpdateAgentSessionState(t *testing.T) {
repo, cleanup := createTestSQLiteRepo(t)
defer cleanup()
ctx := context.Background()
@@ -922,31 +938,31 @@ func TestSQLiteRepository_UpdateAgentSessionStatus(t *testing.T) {
ID: "session-123",
TaskID: "task-123",
AgentProfileID: "profile-1",
Status: models.AgentSessionStatusPending,
State: models.AgentSessionStateStarting,
}
_ = repo.CreateAgentSession(ctx, session)
// Update to running status
err := repo.UpdateAgentSessionStatus(ctx, "session-123", models.AgentSessionStatusRunning, "")
err := repo.UpdateAgentSessionState(ctx, "session-123", models.AgentSessionStateRunning, "")
if err != nil {
t.Fatalf("failed to update agent session status: %v", err)
}
retrieved, _ := repo.GetAgentSession(ctx, "session-123")
if retrieved.Status != models.AgentSessionStatusRunning {
t.Errorf("expected status 'running', got %s", retrieved.Status)
if retrieved.State != models.AgentSessionStateRunning {
t.Errorf("expected state 'running', got %s", retrieved.State)
}
if retrieved.CompletedAt != nil {
t.Error("expected CompletedAt to be nil for running status")
}
// Update to completed status (should set CompletedAt)
err = repo.UpdateAgentSessionStatus(ctx, "session-123", models.AgentSessionStatusCompleted, "")
err = repo.UpdateAgentSessionState(ctx, "session-123", models.AgentSessionStateCompleted, "")
if err != nil {
t.Fatalf("failed to update agent session status to completed: %v", err)
}
retrieved, _ = repo.GetAgentSession(ctx, "session-123")
if retrieved.Status != models.AgentSessionStatusCompleted {
t.Errorf("expected status 'completed', got %s", retrieved.Status)
if retrieved.State != models.AgentSessionStateCompleted {
t.Errorf("expected state 'completed', got %s", retrieved.State)
}
if retrieved.CompletedAt == nil {
t.Error("expected CompletedAt to be set for completed status")
@@ -957,17 +973,17 @@ func TestSQLiteRepository_UpdateAgentSessionStatus(t *testing.T) {
ID: "session-456",
TaskID: "task-123",
AgentProfileID: "profile-1",
Status: models.AgentSessionStatusRunning,
State: models.AgentSessionStateRunning,
}
_ = repo.CreateAgentSession(ctx, session2)
err = repo.UpdateAgentSessionStatus(ctx, "session-456", models.AgentSessionStatusFailed, "connection timeout")
err = repo.UpdateAgentSessionState(ctx, "session-456", models.AgentSessionStateFailed, "connection timeout")
if err != nil {
t.Fatalf("failed to update agent session status to failed: %v", err)
}
retrieved, _ = repo.GetAgentSession(ctx, "session-456")
if retrieved.Status != models.AgentSessionStatusFailed {
t.Errorf("expected status 'failed', got %s", retrieved.Status)
if retrieved.State != models.AgentSessionStateFailed {
t.Errorf("expected state 'failed', got %s", retrieved.State)
}
if retrieved.ErrorMessage != "connection timeout" {
t.Errorf("expected error message 'connection timeout', got %s", retrieved.ErrorMessage)
@@ -981,24 +997,24 @@ func TestSQLiteRepository_UpdateAgentSessionStatus(t *testing.T) {
ID: "session-789",
TaskID: "task-123",
AgentProfileID: "profile-1",
Status: models.AgentSessionStatusRunning,
State: models.AgentSessionStateRunning,
}
_ = repo.CreateAgentSession(ctx, session3)
err = repo.UpdateAgentSessionStatus(ctx, "session-789", models.AgentSessionStatusStopped, "")
err = repo.UpdateAgentSessionState(ctx, "session-789", models.AgentSessionStateCancelled, "")
if err != nil {
t.Fatalf("failed to update agent session status to stopped: %v", err)
}
retrieved, _ = repo.GetAgentSession(ctx, "session-789")
if retrieved.Status != models.AgentSessionStatusStopped {
t.Errorf("expected status 'stopped', got %s", retrieved.Status)
if retrieved.State != models.AgentSessionStateCancelled {
t.Errorf("expected state 'cancelled', got %s", retrieved.State)
}
if retrieved.CompletedAt == nil {
t.Error("expected CompletedAt to be set for stopped status")
}
// Test nonexistent session
err = repo.UpdateAgentSessionStatus(ctx, "nonexistent", models.AgentSessionStatusRunning, "")
err = repo.UpdateAgentSessionState(ctx, "nonexistent", models.AgentSessionStateRunning, "")
if err == nil {
t.Error("expected error for updating nonexistent session status")
}