Files
joakim b02c40f716 feat: improve CLI output with relative dates, rich feedback, and recurring task info
Add relative date formatting (today, tomorrow, in 3d, etc.) for list and
detail views. Add structured feedback helpers for add/complete/delete
operations showing display IDs and parsed modifiers. Change Complete() to
return spawned recurring instance so callers can display recurrence info.
Add AppendTask to working set for immediate display ID assignment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 13:44:56 +01:00

294 lines
6.0 KiB
Go

package engine
import (
"os"
"testing"
"time"
"github.com/google/uuid"
)
func TestMain(m *testing.M) {
// Setup test database with explicit directory overrides
testDir := "/tmp/opal-test"
// Set directory overrides to use test directory
SetConfigDirOverride(testDir)
SetDataDirOverride(testDir)
// Ensure test directory exists
if err := os.MkdirAll(testDir, 0755); err != nil {
panic(err)
}
if err := InitDB(); err != nil {
panic(err)
}
defer CloseDB()
// Run tests
code := m.Run()
// Cleanup
os.RemoveAll(testDir)
os.Exit(code)
}
func TestCreateTask(t *testing.T) {
task, err := CreateTask("Test task")
if err != nil {
t.Fatalf("Failed to create task: %v", err)
}
if task.UUID == uuid.Nil {
t.Error("Task UUID should not be nil")
}
if task.Description != "Test task" {
t.Errorf("Expected description 'Test task', got '%s'", task.Description)
}
if task.Status != StatusPending {
t.Errorf("Expected status Pending, got %v", task.Status)
}
if task.Priority != PriorityDefault {
t.Errorf("Expected priority Default, got %v", task.Priority)
}
}
func TestGetTask(t *testing.T) {
// Create a task
created, err := CreateTask("Test get task")
if err != nil {
t.Fatalf("Failed to create task: %v", err)
}
// Retrieve it
retrieved, err := GetTask(created.UUID)
if err != nil {
t.Fatalf("Failed to get task: %v", err)
}
if retrieved.UUID != created.UUID {
t.Error("UUIDs don't match")
}
if retrieved.Description != created.Description {
t.Error("Descriptions don't match")
}
}
func TestTaskSave(t *testing.T) {
task, err := CreateTask("Test save")
if err != nil {
t.Fatalf("Failed to create task: %v", err)
}
// Modify task
task.Description = "Modified description"
task.Priority = PriorityHigh
project := "test-project"
task.Project = &project
if err := task.Save(); err != nil {
t.Fatalf("Failed to save task: %v", err)
}
// Retrieve and verify
retrieved, err := GetTask(task.UUID)
if err != nil {
t.Fatalf("Failed to get task: %v", err)
}
if retrieved.Description != "Modified description" {
t.Error("Description not updated")
}
if retrieved.Priority != PriorityHigh {
t.Error("Priority not updated")
}
if retrieved.Project == nil || *retrieved.Project != "test-project" {
t.Error("Project not updated")
}
}
func TestTaskTags(t *testing.T) {
task, err := CreateTask("Test tags")
if err != nil {
t.Fatalf("Failed to create task: %v", err)
}
// Add tags
if err := task.AddTag("urgent"); err != nil {
t.Fatalf("Failed to add tag: %v", err)
}
if err := task.AddTag("work"); err != nil {
t.Fatalf("Failed to add tag: %v", err)
}
// Retrieve and verify tags
retrieved, err := GetTask(task.UUID)
if err != nil {
t.Fatalf("Failed to get task: %v", err)
}
if len(retrieved.Tags) != 2 {
t.Errorf("Expected 2 tags, got %d", len(retrieved.Tags))
}
// Remove a tag
if err := retrieved.RemoveTag("urgent"); err != nil {
t.Fatalf("Failed to remove tag: %v", err)
}
// Verify removal
retrieved2, err := GetTask(task.UUID)
if err != nil {
t.Fatalf("Failed to get task: %v", err)
}
if len(retrieved2.Tags) != 1 {
t.Errorf("Expected 1 tag after removal, got %d", len(retrieved2.Tags))
}
if retrieved2.Tags[0] != "work" {
t.Errorf("Expected remaining tag 'work', got '%s'", retrieved2.Tags[0])
}
}
func TestTaskComplete(t *testing.T) {
task, err := CreateTask("Test complete")
if err != nil {
t.Fatalf("Failed to create task: %v", err)
}
if _, err := task.Complete(); err != nil {
t.Fatalf("Failed to complete task: %v", err)
}
if task.Status != StatusCompleted {
t.Error("Status should be Completed")
}
if task.End == nil {
t.Error("End time should be set")
}
// Verify in database
retrieved, err := GetTask(task.UUID)
if err != nil {
t.Fatalf("Failed to get task: %v", err)
}
if retrieved.Status != StatusCompleted {
t.Error("Retrieved task should be completed")
}
}
func TestTaskDelete(t *testing.T) {
// Test soft delete
task1, err := CreateTask("Test soft delete")
if err != nil {
t.Fatalf("Failed to create task: %v", err)
}
if err := task1.Delete(false); err != nil {
t.Fatalf("Failed to soft delete: %v", err)
}
retrieved, err := GetTask(task1.UUID)
if err != nil {
t.Fatalf("Failed to get deleted task: %v", err)
}
if retrieved.Status != StatusDeleted {
t.Error("Task should be marked as deleted")
}
// Test hard delete
task2, err := CreateTask("Test hard delete")
if err != nil {
t.Fatalf("Failed to create task: %v", err)
}
if err := task2.Delete(true); err != nil {
t.Fatalf("Failed to hard delete: %v", err)
}
_, err = GetTask(task2.UUID)
if err == nil {
t.Error("Should not be able to retrieve hard-deleted task")
}
}
func TestTaskStartStop(t *testing.T) {
task, err := CreateTask("Test start/stop")
if err != nil {
t.Fatalf("Failed to create task: %v", err)
}
// Start task
if err := task.StartTask(); err != nil {
t.Fatalf("Failed to start task: %v", err)
}
if task.Start == nil {
t.Error("Start time should be set")
}
// Stop task
if err := task.StopTask(); err != nil {
t.Fatalf("Failed to stop task: %v", err)
}
if task.Start != nil {
t.Error("Start time should be nil after stop")
}
}
func TestGetTasks(t *testing.T) {
// Create multiple tasks
CreateTask("Task 1")
CreateTask("Task 2")
CreateTask("Task 3")
tasks, err := GetTasks(nil)
if err != nil {
t.Fatalf("Failed to get tasks: %v", err)
}
if len(tasks) < 3 {
t.Errorf("Expected at least 3 tasks, got %d", len(tasks))
}
}
func TestTaskWithDueDate(t *testing.T) {
task, err := CreateTask("Task with due date")
if err != nil {
t.Fatalf("Failed to create task: %v", err)
}
dueDate := time.Now().Add(24 * time.Hour)
task.Due = &dueDate
if err := task.Save(); err != nil {
t.Fatalf("Failed to save task with due date: %v", err)
}
retrieved, err := GetTask(task.UUID)
if err != nil {
t.Fatalf("Failed to get task: %v", err)
}
if retrieved.Due == nil {
t.Error("Due date should be set")
}
if retrieved.Due.Unix() != dueDate.Unix() {
t.Error("Due dates don't match")
}
}