package sqlite import ( "context" "testing" "time" "inbox/internal/base/timeutil" "inbox/internal/domain/message" "inbox/internal/domain/role" "inbox/internal/domain/topic" "inbox/internal/domain/workspace" ) func TestCreateMessageCreatesHumanTaskOnlyForQuestionMessages(t *testing.T) { ctx := context.Background() clock := timeutil.FixedClock{Time: time.Date(2026, 3, 17, 15, 0, 0, 0, time.UTC)} store, err := OpenInMemory(clock) if err != nil { t.Fatalf("OpenInMemory() error = %v", err) } defer store.Close() now := timeutil.FormatRFC3339(clock.Now()) project, err := store.CreateProject(ctx, workspace.Project{ Slug: "demo", Name: "Demo", RootPath: t.TempDir(), DefaultBranch: "main", Status: "active", CreatedAt: now, UpdatedAt: now, }) if err != nil { t.Fatalf("CreateProject() error = %v", err) } ws, err := store.CreateWorkspace(ctx, workspace.Workspace{ ProjectID: project.ID, Slug: "demo", Name: "demo", RootPath: t.TempDir(), BaseBranch: "main", WorktreeBranch: "worktree/demo", RuntimeBackend: "host", Status: "active", CreatedAt: now, UpdatedAt: now, }) if err != nil { t.Fatalf("CreateWorkspace() error = %v", err) } if _, err := store.UpsertRole(ctx, role.Definition{ Name: "approver", Title: "Approver", ExecutorKind: role.ExecutorKindHuman, IsEnabled: true, }, "test"); err != nil { t.Fatalf("UpsertRole(approver) error = %v", err) } topicRecord, err := store.CreateTopic(ctx, topic.Record{ WorkspaceID: ws.ID, Slug: "sample", Title: "sample", Space: "workflow", Status: "plan", CreatedAt: now, UpdatedAt: now, }) if err != nil { t.Fatalf("CreateTopic() error = %v", err) } decision, err := store.CreateMessage(ctx, message.Record{ WorkspaceID: ws.ID, TopicID: topicRecord.ID, FromRoleName: "leader", ToExpr: "approver", Type: message.TypeDecision, Stage: "execution", BodyMarkdown: "Proceed with the rollout.", }) if err != nil { t.Fatalf("CreateMessage(decision) error = %v", err) } question, err := store.CreateMessage(ctx, message.Record{ WorkspaceID: ws.ID, TopicID: topicRecord.ID, FromRoleName: "leader", ToExpr: "approver", Type: message.TypeQuestion, Stage: "review", BodyMarkdown: "Should this ship tonight?", }) if err != nil { t.Fatalf("CreateMessage(question) error = %v", err) } var humanTaskCount int if err := store.DB().QueryRow(`SELECT COUNT(*) FROM human_tasks WHERE topic_id = ?`, topicRecord.ID).Scan(&humanTaskCount); err != nil { t.Fatalf("count human_tasks: %v", err) } if humanTaskCount != 1 { t.Fatalf("expected exactly one human task, got %d", humanTaskCount) } var promptMessageID string if err := store.DB().QueryRow(`SELECT prompt_message_id FROM human_tasks WHERE topic_id = ?`, topicRecord.ID).Scan(&promptMessageID); err != nil { t.Fatalf("select prompt_message_id: %v", err) } if promptMessageID != question.ID { t.Fatalf("expected human task to point at question message %s, got %s", question.ID, promptMessageID) } var decisionHumanTaskCount int if err := store.DB().QueryRow(`SELECT COUNT(*) FROM human_tasks WHERE prompt_message_id = ?`, decision.ID).Scan(&decisionHumanTaskCount); err != nil { t.Fatalf("count decision human task: %v", err) } if decisionHumanTaskCount != 0 { t.Fatalf("expected no human task for decision message, got %d", decisionHumanTaskCount) } }