Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions cmd/task/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ func TestCLICreateTask(t *testing.T) {
defer database.Close()
defer os.Remove(dbPath)

// Create the myproject project for testing
if err := database.CreateProject(&db.Project{Name: "myproject", Path: tmpDir}); err != nil {
t.Fatalf("failed to create myproject: %v", err)
}

tests := []struct {
name string
task *db.Task
Expand Down Expand Up @@ -97,6 +102,11 @@ func TestCLIListTasks(t *testing.T) {
defer database.Close()
defer os.Remove(dbPath)

// Create the proj1 project for testing
if err := database.CreateProject(&db.Project{Name: "proj1", Path: tmpDir}); err != nil {
t.Fatalf("failed to create proj1: %v", err)
}

// Create some tasks
tasks := []*db.Task{
{Title: "Task 1", Status: db.StatusBacklog, Type: db.TypeCode},
Expand Down
5 changes: 5 additions & 0 deletions internal/db/memories_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ func TestMemoryWithSourceTask(t *testing.T) {
defer db.Close()
defer os.Remove(dbPath)

// Create the project first
if err := db.CreateProject(&Project{Name: "testproject", Path: tmpDir}); err != nil {
t.Fatalf("failed to create project: %v", err)
}

// Create a task first
task := &Task{
Title: "Source Task",
Expand Down
5 changes: 5 additions & 0 deletions internal/db/sqlite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ func TestTimestampLocalization(t *testing.T) {
defer db.Close()
defer os.Remove(dbPath)

// Create the test project first
if err := db.CreateProject(&Project{Name: "test", Path: tmpDir}); err != nil {
t.Fatalf("failed to create test project: %v", err)
}

// Create a task
task := &Task{
Title: "Test Task",
Expand Down
12 changes: 12 additions & 0 deletions internal/db/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,25 @@ type TaskType struct {
CreatedAt LocalTime
}

// ErrProjectNotFound is returned when a task is created with a non-existent project.
var ErrProjectNotFound = fmt.Errorf("project not found")

// CreateTask creates a new task.
func (db *DB) CreateTask(t *Task) error {
// Default to 'personal' project if not specified
if t.Project == "" {
t.Project = "personal"
}

// Validate that the project exists
project, err := db.GetProjectByName(t.Project)
if err != nil {
return fmt.Errorf("validate project: %w", err)
}
if project == nil {
return fmt.Errorf("%w: %s", ErrProjectNotFound, t.Project)
}

result, err := db.Exec(`
INSERT INTO tasks (title, body, status, type, project)
VALUES (?, ?, ?, ?, ?)
Expand Down
192 changes: 192 additions & 0 deletions internal/db/tasks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package db
import (
"os"
"path/filepath"
"strings"
"testing"
)

Expand All @@ -18,6 +19,11 @@ func TestGetLastQuestion(t *testing.T) {
defer db.Close()
defer os.Remove(dbPath)

// Create the test project first
if err := db.CreateProject(&Project{Name: "test", Path: tmpDir}); err != nil {
t.Fatalf("failed to create test project: %v", err)
}

// Create a task
task := &Task{
Title: "Test Task",
Expand Down Expand Up @@ -78,6 +84,11 @@ func TestTaskLogsWithQuestion(t *testing.T) {
defer db.Close()
defer os.Remove(dbPath)

// Create the test project first
if err := db.CreateProject(&Project{Name: "test", Path: tmpDir}); err != nil {
t.Fatalf("failed to create test project: %v", err)
}

// Create a task
task := &Task{
Title: "Test Task",
Expand Down Expand Up @@ -598,6 +609,11 @@ func TestListTasksClosedSortedByCompletedAt(t *testing.T) {
defer database.Close()
defer os.Remove(dbPath)

// Create the test project first
if err := database.CreateProject(&Project{Name: "test", Path: tmpDir}); err != nil {
t.Fatalf("failed to create test project: %v", err)
}

// Create tasks in specific order - older tasks first
task1 := &Task{
Title: "First task - closed early",
Expand Down Expand Up @@ -755,6 +771,11 @@ func TestCompactionSummaries(t *testing.T) {
defer db.Close()
defer os.Remove(dbPath)

// Create the test project first
if err := db.CreateProject(&Project{Name: "test", Path: tmpDir}); err != nil {
t.Fatalf("failed to create test project: %v", err)
}

// Create a task
task := &Task{
Title: "Test Task for Compaction",
Expand Down Expand Up @@ -888,6 +909,11 @@ func TestCompactionSummariesCascadeDelete(t *testing.T) {
defer db.Close()
defer os.Remove(dbPath)

// Create the test project first
if err := db.CreateProject(&Project{Name: "test", Path: tmpDir}); err != nil {
t.Fatalf("failed to create test project: %v", err)
}

// Create a task
task := &Task{
Title: "Task for Cascade Delete Test",
Expand Down Expand Up @@ -1150,3 +1176,169 @@ func TestGetActiveTaskPorts(t *testing.T) {
}
}
}

func TestCreateTaskRejectsNonExistentProject(t *testing.T) {
// Create temporary database
tmpDir := t.TempDir()
dbPath := filepath.Join(tmpDir, "test.db")

db, err := Open(dbPath)
if err != nil {
t.Fatalf("failed to open database: %v", err)
}
defer db.Close()
defer os.Remove(dbPath)

// Try to create a task with a non-existent project
task := &Task{
Title: "Test Task",
Status: StatusBacklog,
Type: TypeCode,
Project: "nonexistent-project",
}
err = db.CreateTask(task)

// Should fail with project not found error
if err == nil {
t.Fatal("expected error when creating task with non-existent project, got nil")
}
if !strings.Contains(err.Error(), "project not found") {
t.Errorf("expected 'project not found' error, got: %v", err)
}

// Verify task was not created
tasks, err := db.ListTasks(ListTasksOptions{IncludeClosed: true})
if err != nil {
t.Fatalf("failed to list tasks: %v", err)
}
if len(tasks) != 0 {
t.Errorf("expected 0 tasks after failed creation, got %d", len(tasks))
}
}

func TestCreateTaskAllowsExistingProject(t *testing.T) {
// Create temporary database
tmpDir := t.TempDir()
dbPath := filepath.Join(tmpDir, "test.db")

db, err := Open(dbPath)
if err != nil {
t.Fatalf("failed to open database: %v", err)
}
defer db.Close()
defer os.Remove(dbPath)

// Create a project first
project := &Project{
Name: "my-project",
Path: tmpDir,
}
if err := db.CreateProject(project); err != nil {
t.Fatalf("failed to create project: %v", err)
}

// Now create a task with that project
task := &Task{
Title: "Test Task",
Status: StatusBacklog,
Type: TypeCode,
Project: "my-project",
}
err = db.CreateTask(task)

// Should succeed
if err != nil {
t.Fatalf("expected task creation to succeed, got error: %v", err)
}

// Verify task was created with correct project
retrieved, err := db.GetTask(task.ID)
if err != nil {
t.Fatalf("failed to get task: %v", err)
}
if retrieved.Project != "my-project" {
t.Errorf("expected project 'my-project', got '%s'", retrieved.Project)
}
}

func TestCreateTaskAllowsProjectAlias(t *testing.T) {
// Create temporary database
tmpDir := t.TempDir()
dbPath := filepath.Join(tmpDir, "test.db")

db, err := Open(dbPath)
if err != nil {
t.Fatalf("failed to open database: %v", err)
}
defer db.Close()
defer os.Remove(dbPath)

// Create a project with an alias
project := &Project{
Name: "my-long-project-name",
Path: tmpDir,
Aliases: "myproj, proj",
}
if err := db.CreateProject(project); err != nil {
t.Fatalf("failed to create project: %v", err)
}

// Create a task using the alias
task := &Task{
Title: "Test Task",
Status: StatusBacklog,
Type: TypeCode,
Project: "myproj",
}
err = db.CreateTask(task)

// Should succeed
if err != nil {
t.Fatalf("expected task creation with alias to succeed, got error: %v", err)
}

// Verify task was created with the alias (not the full name)
retrieved, err := db.GetTask(task.ID)
if err != nil {
t.Fatalf("failed to get task: %v", err)
}
if retrieved.Project != "myproj" {
t.Errorf("expected project 'myproj', got '%s'", retrieved.Project)
}
}

func TestCreateTaskDefaultsToPersonal(t *testing.T) {
// Create temporary database
tmpDir := t.TempDir()
dbPath := filepath.Join(tmpDir, "test.db")

db, err := Open(dbPath)
if err != nil {
t.Fatalf("failed to open database: %v", err)
}
defer db.Close()
defer os.Remove(dbPath)

// Create a task with empty project (should default to 'personal')
task := &Task{
Title: "Test Task",
Status: StatusBacklog,
Type: TypeCode,
Project: "",
}
err = db.CreateTask(task)

// Should succeed because 'personal' project is auto-created
if err != nil {
t.Fatalf("expected task creation to succeed with default project, got error: %v", err)
}

// Verify task was created with 'personal' project
retrieved, err := db.GetTask(task.ID)
if err != nil {
t.Fatalf("failed to get task: %v", err)
}
if retrieved.Project != "personal" {
t.Errorf("expected project 'personal', got '%s'", retrieved.Project)
}
}
15 changes: 15 additions & 0 deletions internal/executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ func TestInterrupt(t *testing.T) {
}
defer database.Close()

// Create the test project first
if err := database.CreateProject(&db.Project{Name: "test", Path: "/tmp/test"}); err != nil {
t.Fatal(err)
}

cfg := &config.Config{}
exec := New(database, cfg)

Expand Down Expand Up @@ -206,6 +211,11 @@ func TestAttachmentsInPrompt(t *testing.T) {
}
defer database.Close()

// Create the test project first
if err := database.CreateProject(&db.Project{Name: "test", Path: "/tmp/test"}); err != nil {
t.Fatal(err)
}

cfg := &config.Config{}
exec := New(database, cfg)

Expand Down Expand Up @@ -294,6 +304,11 @@ func TestConversationHistory(t *testing.T) {
}
defer database.Close()

// Create the test project first
if err := database.CreateProject(&db.Project{Name: "test", Path: "/tmp/test"}); err != nil {
t.Fatal(err)
}

cfg := &config.Config{}
exec := New(database, cfg)

Expand Down
5 changes: 5 additions & 0 deletions internal/mcp/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ func testDB(t *testing.T) *db.DB {

// createTestTask creates a task for testing.
func createTestTask(t *testing.T, database *db.DB) *db.Task {
// First create the test-project
if err := database.CreateProject(&db.Project{Name: "test-project", Path: "/tmp/test-project"}); err != nil {
t.Fatalf("failed to create test-project: %v", err)
}

task := &db.Task{
Title: "Test Task",
Status: db.StatusProcessing,
Expand Down